fa976dd2bb
- aggiunto InitializeAsync - gestione override x ogni IOB
2233 lines
76 KiB
C#
2233 lines
76 KiB
C#
using IOB_UT_NEXT;
|
|
using IOB_UT_NEXT.Config;
|
|
using MapoSDK;
|
|
using Newtonsoft.Json;
|
|
using NLog;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
|
|
namespace IOB_WIN_FORM
|
|
{
|
|
public partial class AdapterForm : Form
|
|
{
|
|
#region Public Fields
|
|
|
|
/// <summary>
|
|
/// oggetto logging
|
|
/// </summary>
|
|
public static Logger lg;
|
|
|
|
/// <summary>
|
|
/// Configurazione gerarchica completa (v 4.x.x.x)
|
|
/// </summary>
|
|
public IobConfTree IOBConfFull;
|
|
|
|
/// <summary>
|
|
/// Oggetto x gestione dell'adapter GENERICO (x poter usare metodi di ognuno...)
|
|
/// </summary>
|
|
public Iob.Generic iobObj;
|
|
|
|
#endregion Public Fields
|
|
|
|
#region Public Constructors
|
|
|
|
/// <summary>
|
|
/// Avvio MainForm
|
|
/// </summary>
|
|
/// <param name="codIOB"></param>
|
|
public AdapterForm(string codIOB)
|
|
{
|
|
CurrIOB = codIOB;
|
|
// continuo avvio...
|
|
InitializeComponent();
|
|
myGraphInitForm();
|
|
|
|
checkEditMes2Plc();
|
|
|
|
// inizializzo orologi
|
|
firstStart = DateTime.Now;
|
|
lastStartTry = DateTime.Now.AddHours(-1);
|
|
|
|
initDatamonitor();
|
|
|
|
waitRecMSec = utils.CRI("waitRecMSec");
|
|
|
|
LogManager.ReconfigExistingLoggers();
|
|
|
|
lg = LogManager.GetCurrentClassLogger();
|
|
displayTaskAndLog("AdapterForm Starting");
|
|
}
|
|
|
|
#endregion Public Constructors
|
|
|
|
#region Public Properties
|
|
|
|
public long alQueueLen
|
|
{
|
|
set
|
|
{
|
|
qAlLen = value;
|
|
lblQueueAlarmLen.Text = qAlLen.ToString();
|
|
showQueueData();
|
|
// se supero max precedente, ed è > 10... loggo!
|
|
if (qAlLen > maxAlQueue && qAlLen > 10)
|
|
{
|
|
maxAlQueue = qAlLen;
|
|
lgInfo($"[WARN] Coda FLog di {value} record");
|
|
}
|
|
else
|
|
{
|
|
maxAlQueue--;
|
|
maxAlQueue = maxAlQueue < qAlLen ? qAlLen : maxAlQueue;
|
|
}
|
|
}
|
|
get
|
|
{
|
|
return qAlLen;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Button restart encapsulato x gestione da classi derivate
|
|
/// </summary>
|
|
public Button btnRestart
|
|
{
|
|
get => restart;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Button start encapsulato x gestione da classi derivate
|
|
/// </summary>
|
|
public Button btnStart
|
|
{
|
|
get => start;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Button stop encapsulato x gestione da classi derivate
|
|
/// </summary>
|
|
public Button btnStop
|
|
{
|
|
get => stop;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Visualizzazione stato di comunicazione attiva con PLC
|
|
/// </summary>
|
|
public bool commPlcActive
|
|
{
|
|
get
|
|
{
|
|
return _commPlcActive;
|
|
}
|
|
set
|
|
{
|
|
if (_commPlcActive != value)
|
|
{
|
|
_commPlcActive = value;
|
|
// se true --> comunica/verde, altrimenti grigio
|
|
this.UIThread(delegate
|
|
{
|
|
lblCNC.ForeColor = value ? Color.SeaGreen : Color.Black;
|
|
//statusStrip1.Refresh();
|
|
statusStrip1.Invalidate();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Visualizzazione stato di comunicazione attiva con PLC: 0 = NO PING 1 = PING OK 2 = IOB enabled
|
|
/// </summary>
|
|
public int commSrvActive
|
|
{
|
|
get
|
|
{
|
|
return _commSrvActive;
|
|
}
|
|
set
|
|
{
|
|
_commSrvActive = value;
|
|
this.UIThread(delegate
|
|
{
|
|
switch (value)
|
|
{
|
|
case 0:
|
|
lblSrvUrl.ForeColor = Color.Red;
|
|
break;
|
|
|
|
case 1:
|
|
lblSrvUrl.ForeColor = Color.OrangeRed;
|
|
break;
|
|
|
|
case 2:
|
|
lblSrvUrl.ForeColor = Color.SeaGreen;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
statusStrip1.Refresh();
|
|
});
|
|
}
|
|
}
|
|
|
|
public string counterIob
|
|
{
|
|
get
|
|
{
|
|
return lblPzCountIob.Text;
|
|
}
|
|
set
|
|
{
|
|
lblPzCountIob.Text = value;
|
|
}
|
|
}
|
|
|
|
public string counterMac
|
|
{
|
|
get
|
|
{
|
|
return lblPzCountMac.Text;
|
|
}
|
|
set
|
|
{
|
|
lblPzCountMac.Text = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Modello macchina
|
|
/// </summary>
|
|
public string curModel
|
|
{
|
|
get => IOBConfFull != null ? IOBConfFull.Device.Model : "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Vendor macchina
|
|
/// </summary>
|
|
public string curVendor
|
|
{
|
|
get => IOBConfFull != null ? IOBConfFull.Device.Vendor : "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stringa dati monitoraggio mostrata (1 SX)...
|
|
/// </summary>
|
|
public string dataMonitor_0
|
|
{
|
|
set
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
// salvo in cima alla lista...
|
|
dMonValues[0].Insert(0, value);
|
|
// se supero limite --> trim!
|
|
if (dMonValues[0].Count > nLine2show)
|
|
{
|
|
dMonValues[0] = dMonValues[0].Take(nLine2show).ToList();
|
|
}
|
|
if (dMonDisplVetoVeto[0] < adesso)
|
|
{
|
|
// se scaduto veto --> display!
|
|
lblRawData.Text = String.Join(Environment.NewLine, dMonValues[0]);
|
|
// update veto!
|
|
dMonDisplVetoVeto[0] = adesso.AddMilliseconds(delayShowLogMs);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stringa dati monitoraggio mostrata (1 SX)...
|
|
/// </summary>
|
|
public string dataMonitor_1
|
|
{
|
|
set
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
// salvo nell'array...
|
|
dMonValues[1].Insert(0, value);
|
|
// se supero limite --> trim!
|
|
if (dMonValues[1].Count > nLine2show)
|
|
{
|
|
dMonValues[1] = dMonValues[1].Take(nLine2show).ToList();
|
|
}
|
|
if (dMonDisplVetoVeto[1] < adesso)
|
|
{
|
|
// se scaduto veto --> display!
|
|
lblOutMessage.Text = String.Join(Environment.NewLine, dMonValues[1]);
|
|
// update veto!
|
|
dMonDisplVetoVeto[1] = adesso.AddMilliseconds(delayShowLogMs);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stringa dati monitoraggio mostrata (2 centro)...
|
|
/// </summary>
|
|
public string dataMonitor_2
|
|
{
|
|
set
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
// salvo nell'array...
|
|
dMonValues[2].Insert(0, value);
|
|
// se supero limite --> trim!
|
|
if (dMonValues[2].Count > nLine2show)
|
|
{
|
|
dMonValues[2] = dMonValues[2].Take(nLine2show).ToList();
|
|
}
|
|
if (dMonDisplVetoVeto[2] < adesso)
|
|
{
|
|
// se scaduto veto --> display!
|
|
lblOutMessage2.Text = String.Join(Environment.NewLine, dMonValues[2]);
|
|
// update veto!
|
|
dMonDisplVetoVeto[2] = adesso.AddMilliseconds(delayShowLogMs);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stringa dati monitoraggio mostrata (3 dx)...
|
|
/// </summary>
|
|
public string dataMonitor_3
|
|
{
|
|
set
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
// salvo nell'array...
|
|
dMonValues[3].Insert(0, value);
|
|
// se supero limite --> trim!
|
|
if (dMonValues[3].Count > nLine2show)
|
|
{
|
|
dMonValues[3] = dMonValues[3].Take(nLine2show).ToList();
|
|
}
|
|
if (dMonDisplVetoVeto[3] < adesso)
|
|
{
|
|
// se scaduto veto --> display!
|
|
lblOutMessage3.Text = String.Join(Environment.NewLine, dMonValues[3]);
|
|
// update veto!
|
|
dMonDisplVetoVeto[3] = adesso.AddMilliseconds(delayShowLogMs);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// label del numero di record processati (libera)
|
|
/// </summary>
|
|
public string dataProcLabel
|
|
{
|
|
get
|
|
{
|
|
return lblDataProc.Text;
|
|
}
|
|
set
|
|
{
|
|
lblDataProc.Text = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// File configurazione default
|
|
/// </summary>
|
|
public string defConfFilePath
|
|
{
|
|
get
|
|
{
|
|
return Path.Combine(utils.confDir, $"{CurrIOB}.ini");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// File configurazione json (completo) se presente
|
|
/// </summary>
|
|
public string defConfFilePathJson
|
|
{
|
|
get
|
|
{
|
|
return Path.Combine(utils.confDir, $"{CurrIOB}.json");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// File configurazione yaml (completo) se presente
|
|
/// </summary>
|
|
public string defConfFilePathYaml
|
|
{
|
|
get
|
|
{
|
|
return Path.Combine(utils.confDir, $"{CurrIOB}.yaml");
|
|
}
|
|
}
|
|
|
|
public bool enableEditMes2Plc { get; set; }
|
|
|
|
public long evQueueLen
|
|
{
|
|
set
|
|
{
|
|
qEvLen = value;
|
|
lblQueueLen.Text = qEvLen.ToString();
|
|
showQueueData();
|
|
// se supero max precedente, ed è > 10... loggo!
|
|
if (qEvLen > maxEvQueue && qEvLen > 10)
|
|
{
|
|
maxEvQueue = qEvLen;
|
|
lgInfo($"[WARN] Coda EV di {value} record");
|
|
}
|
|
else
|
|
{
|
|
maxEvQueue--;
|
|
maxEvQueue = maxEvQueue < qEvLen ? qEvLen : maxEvQueue;
|
|
}
|
|
}
|
|
get
|
|
{
|
|
return qEvLen;
|
|
}
|
|
}
|
|
|
|
public long flQueueLen
|
|
{
|
|
set
|
|
{
|
|
qFlLen = value;
|
|
lblQueueFLogLen.Text = qFlLen.ToString();
|
|
showQueueData();
|
|
// se supero max precedente, ed è > 10... loggo!
|
|
if (qFlLen > maxFlQueue && qFlLen > 10)
|
|
{
|
|
maxFlQueue = qFlLen;
|
|
lgInfo($"[WARN] Coda FLog di {value} record");
|
|
}
|
|
else
|
|
{
|
|
maxFlQueue--;
|
|
maxFlQueue = maxFlQueue < qFlLen ? qFlLen : maxFlQueue;
|
|
}
|
|
}
|
|
get
|
|
{
|
|
return qFlLen;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Log verboso da configurazione (SOLO CHIAVE "verbose"...
|
|
/// </summary>
|
|
public bool isVerboseLog { get; set; } = utils.CRB("verbose");
|
|
|
|
public string lblCncText
|
|
{
|
|
get => lblCNC.Text;
|
|
set => lblCNC.Text = value;
|
|
}
|
|
|
|
public string lblSrvUrlText
|
|
{
|
|
get => lblSrvUrl.Text;
|
|
set => lblSrvUrl.Text = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logwatcher (in modalità "accodamento in testa" ultimi messaggi...)
|
|
/// </summary>
|
|
public string logWatcher
|
|
{
|
|
get
|
|
{
|
|
return lblLogfile.Text;
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
// aggiungo in testa la NUOVA stringa (eventualmente multiline)
|
|
logWatchString.Enqueue(value);
|
|
// se supero limite --> trim!
|
|
string sTemp = "";
|
|
while (logWatchString.Count > nLine2show)
|
|
{
|
|
logWatchString.TryDequeue(out sTemp);
|
|
//logWatchString = logWatchString.Take(nLine2show).ToList();
|
|
}
|
|
DateTime adesso = DateTime.Now;
|
|
if (logWatchWriteVeto < adesso)
|
|
{
|
|
this.UIThread(delegate
|
|
{
|
|
lblLogfile.Text = string.Join(Environment.NewLine, logWatchString);
|
|
lblLogfile.Refresh();
|
|
});
|
|
|
|
logWatchWriteVeto = adesso.AddMilliseconds(delayShowLogMs);
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Errore in esecuzione logWatcher{Environment.NewLine}--> {value}");
|
|
if (isVerboseLog)
|
|
{
|
|
lgError($"{exc}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public long msQueueLen
|
|
{
|
|
set
|
|
{
|
|
qMsLen = value;
|
|
lblQueueMessLen.Text = qMsLen.ToString();
|
|
showQueueData();
|
|
// se supero max precedente, ed è > 10... loggo!
|
|
if (qMsLen > maxMsQueue && qMsLen > 10)
|
|
{
|
|
maxMsQueue = qMsLen;
|
|
lgInfo($"[WARN] Coda Ms di {value} record");
|
|
}
|
|
else
|
|
{
|
|
maxMsQueue--;
|
|
maxMsQueue = maxMsQueue < qMsLen ? qMsLen : maxMsQueue;
|
|
}
|
|
}
|
|
get
|
|
{
|
|
return qMsLen;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Numero max linee da mostrare (da controllo)...
|
|
/// </summary>
|
|
public int nLine2show
|
|
{
|
|
get
|
|
{
|
|
int answ = 5;
|
|
try
|
|
{
|
|
Int32.TryParse(nLines.Text, out answ);
|
|
}
|
|
catch
|
|
{ }
|
|
return answ;
|
|
}
|
|
set
|
|
{
|
|
nLines.Text = value.ToString();
|
|
}
|
|
}
|
|
|
|
public long rtrQueueLen
|
|
{
|
|
set
|
|
{
|
|
qRTrLen = value;
|
|
lblQueueRwTrLog.Text = qRTrLen.ToString();
|
|
showQueueData();
|
|
// se supero max precedente, ed è > 10... loggo!
|
|
if (qRTrLen > maxRwTrQueue && qRTrLen > 10)
|
|
{
|
|
maxRwTrQueue = qRTrLen;
|
|
lgInfo($"[WARN] Coda RawTransf di {value} record");
|
|
}
|
|
else
|
|
{
|
|
maxRwTrQueue--;
|
|
maxRwTrQueue = maxRwTrQueue < qRTrLen ? qRTrLen : maxRwTrQueue;
|
|
}
|
|
}
|
|
get
|
|
{
|
|
return qRTrLen;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Imposta COLORE SFONDO x Semaforo IN
|
|
/// </summary>
|
|
public Semaforo sIN
|
|
{
|
|
get
|
|
{
|
|
return _sIN;
|
|
}
|
|
set
|
|
{
|
|
_sIN = value;
|
|
var newColor = decSemaforo(value);
|
|
if (newColor != bIN.BackColor)
|
|
{
|
|
this.UIThread(delegate
|
|
{
|
|
bIN.BackColor = newColor;
|
|
bIN.Refresh();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Imposta COLORE SFONDO x Semaforo OUT
|
|
/// </summary>
|
|
public Semaforo sOUT
|
|
{
|
|
get
|
|
{
|
|
return _sOUT;
|
|
}
|
|
set
|
|
{
|
|
_sOUT = value;
|
|
var newColor = decSemaforo(value);
|
|
if (newColor != bOUT.BackColor)
|
|
{
|
|
this.UIThread(delegate
|
|
{
|
|
bOUT.BackColor = newColor;
|
|
bOUT.Refresh();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Task watcher (in modalità "accodamento in testa" ultimi messaggi...)
|
|
/// </summary>
|
|
public string taskWatcher
|
|
{
|
|
get
|
|
{
|
|
return lblTaskLog.Text;
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
logTaskString.Insert(0, value);
|
|
// se supero limite --> trim!
|
|
if (logTaskString.Count > nLine2show)
|
|
{
|
|
logTaskString = logTaskString.Take(nLine2show).ToList();
|
|
}
|
|
this.UIThread(delegate
|
|
{
|
|
lblTaskLog.Text = string.Join(Environment.NewLine, logTaskString);
|
|
lblTaskLog.Refresh();
|
|
});
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Errore in esecuzione taskWatcher{Environment.NewLine}--> {value}");
|
|
if (isVerboseLog)
|
|
{
|
|
lgError($"{exc}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// tipo di adapter prescelto...
|
|
/// </summary>
|
|
public tipoAdapter tipoScelto
|
|
{
|
|
get => IOBConfFull != null ? IOBConfFull.General.IobType : tipoAdapter.ND;
|
|
}
|
|
|
|
public long ulQueueLen
|
|
{
|
|
set
|
|
{
|
|
qUlLen = value;
|
|
lblQueueULog.Text = qUlLen.ToString();
|
|
showQueueData();
|
|
// se supero max precedente, ed è > 10... loggo!
|
|
if (qUlLen > maxUlQueue && qUlLen > 10)
|
|
{
|
|
maxUlQueue = qUlLen;
|
|
lgInfo($"[WARN] Coda UL di {value} record");
|
|
}
|
|
else
|
|
{
|
|
maxUlQueue--;
|
|
maxUlQueue = maxUlQueue < qUlLen ? qUlLen : maxUlQueue;
|
|
}
|
|
}
|
|
get
|
|
{
|
|
return qUlLen;
|
|
}
|
|
}
|
|
|
|
#endregion Public Properties
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Decodifica colore da valore semaforico
|
|
/// </summary>
|
|
/// <param name="valore"></param>
|
|
/// <returns></returns>
|
|
public static Color decSemaforo(Semaforo valore)
|
|
{
|
|
Color colore = Color.LightGray;
|
|
switch (valore)
|
|
{
|
|
case Semaforo.SV:
|
|
colore = Color.LightGreen;
|
|
break;
|
|
|
|
case Semaforo.SG:
|
|
colore = Color.LightGoldenrodYellow;
|
|
break;
|
|
|
|
case Semaforo.SR:
|
|
colore = Color.Red;
|
|
break;
|
|
|
|
case Semaforo.SS:
|
|
colore = Color.DarkGray;
|
|
break;
|
|
|
|
default:
|
|
colore = Color.LightGray;
|
|
break;
|
|
}
|
|
return colore;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Avvio l'adapter
|
|
/// </summary>
|
|
/// <param name="resetQueue">indica se sia richiesto di SVUOTARE le code delle info</param>
|
|
public void AvviaAdapter(bool resetQueue)
|
|
{
|
|
displayTaskAndLog("Adapter starting", true);
|
|
// se NON sta girando...
|
|
if (!iobObj.adpRunning)
|
|
{
|
|
iobObj.startAdapter(resetQueue);
|
|
displayTaskAndLog("Adapter started!");
|
|
|
|
// fix buttons start/stop/dump
|
|
start.Enabled = false;
|
|
stop.Enabled = true;
|
|
restart.Enabled = true;
|
|
// 2026.01.02 fix timers al restart
|
|
gather.Enabled = true;
|
|
// gestione worker
|
|
StartWorker();
|
|
displayTaskAndLog("Start Timers", true);
|
|
// inizializzo le scadenze dei timers...
|
|
TimersVeto = new Dictionary<gatherCycle, DateTime>();
|
|
TimersCycleCount = new Dictionary<gatherCycle, int>();
|
|
TimersCycleElaps = new Dictionary<gatherCycle, double>();
|
|
DateTime adesso = DateTime.Now;
|
|
foreach (gatherCycle item in Enum.GetValues(typeof(gatherCycle)))
|
|
{
|
|
// setup iniziale deterministico
|
|
TimersVeto.Add(item, adesso.AddMilliseconds(iobObj.IOBConfFull.TimerMs(item, 0)));
|
|
TimersCycleCount.Add(item, 0);
|
|
TimersCycleElaps.Add(item, 0);
|
|
}
|
|
sendStartFLog = utils.CRB("sendStartFLog");
|
|
displayTaskAndLog("Adapter Running...", true);
|
|
// init max queue
|
|
maxEvQueue = 1;
|
|
maxFlQueue = 1;
|
|
|
|
try
|
|
{
|
|
if (sendStartFLog)
|
|
{
|
|
// segnalo reboot (programma)...
|
|
iobObj.QueueFLog.Enqueue(iobObj.qEncodeFLog("IOB-STATUS", "IOB Adapter Started"));
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError(string.Format("EXCEPTION in fase di chiamata URL di segnalazione AVVIO IOB:{0}{1}", Environment.NewLine, exc));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
displayTaskAndLog("Adapter STILL Running...", true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// mostra un testo sulla status bar + LOG
|
|
/// </summary>
|
|
/// <param name="txt2show"></param>
|
|
/// <param name="forceDebug"></param>
|
|
public void displayTaskAndLog(string txt2show, bool forceDebug = false)
|
|
{
|
|
lblStatus.Text = txt2show;
|
|
lblStatus.Invalidate();
|
|
if (forceDebug)
|
|
{
|
|
lgDebug(txt2show);
|
|
}
|
|
else
|
|
{
|
|
lgInfo(txt2show);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ferma l'adapter
|
|
/// </summary>
|
|
/// <param name="tryRestart">
|
|
/// determina se si debba tentare riavvio automatico (per caduta connessione)
|
|
/// </param>
|
|
/// <param name="forceDequeue">indica se sia richiesto di SVUOTARE le code delle info</param>
|
|
/// <param name="updateForm">indica se aggiornare la form prima di fermare</param>
|
|
public void fermaAdapter(bool tryRestart, bool forceDequeue, bool updateForm)
|
|
{
|
|
lgInfo($"fermaAdapter | tryRestart: {tryRestart} | forceDequeue: {forceDequeue} | updateForm: {updateForm}");
|
|
//fermaTutto(false, tryRestart, forceDequeue, updateForm);
|
|
fermaTutto(false, tryRestart, forceDequeue, updateForm);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mostra update delle statistiche di comunicazione (numero chiamate, tempo medio...)
|
|
/// </summary>
|
|
/// <param name="txt2show"></param>
|
|
public void updateComStats(string txt2show)
|
|
{
|
|
lblComStats.Text = string.Format("{0} | ", txt2show);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua update form una volta ricevuto OBJ che contiene le varie innovazioni...
|
|
/// </summary>
|
|
/// <param name="currDispData"></param>
|
|
public void updateFormDisplay(newDisplayData currDispData)
|
|
{
|
|
this.UIThread(delegate
|
|
{
|
|
// ciclo x ogni oggetto x caricare le innovazioni...
|
|
if (currDispData != null && currDispData.hasData)
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
// RealTime display
|
|
if (!string.IsNullOrWhiteSpace(currDispData.newInData))
|
|
{
|
|
dataMonitor_0 = currDispData.newInData;
|
|
}
|
|
if (!string.IsNullOrWhiteSpace(currDispData.newSignalData))
|
|
{
|
|
dataMonitor_1 = currDispData.newSignalData;
|
|
}
|
|
if (!string.IsNullOrWhiteSpace(currDispData.newFLogData))
|
|
{
|
|
dataMonitor_3 = currDispData.newFLogData;
|
|
}
|
|
if (!string.IsNullOrWhiteSpace(currDispData.newUrlCallData))
|
|
{
|
|
dataMonitor_2 = currDispData.newUrlCallData;
|
|
}
|
|
|
|
// Bitmap lettura attuale
|
|
if (currDispData.counter >= 0)
|
|
{
|
|
if (logCounterVeto < adesso || lblCounter.Text != $"{currDispData.counter}")
|
|
{
|
|
lblCounter.Text = $"{currDispData.counter}";
|
|
lblCounter.Refresh();
|
|
logCounterVeto = adesso.AddMilliseconds(delayShowLogMs);
|
|
}
|
|
}
|
|
// Bitmap lettura attuale
|
|
if (!string.IsNullOrWhiteSpace(currDispData.currBitmap))
|
|
{
|
|
lblBitmap.Text = currDispData.currBitmap;
|
|
lblBitmap.Refresh();
|
|
}
|
|
// LiveLog
|
|
if (!string.IsNullOrWhiteSpace(currDispData.newLiveLogData))
|
|
{
|
|
logWatcher = currDispData.newLiveLogData;
|
|
}
|
|
// semafori
|
|
if (currDispData.semOut != Semaforo.ND)
|
|
{
|
|
// aggiorno SE diverso
|
|
if (sOUT != currDispData.semOut)
|
|
{
|
|
sOUT = currDispData.semOut;
|
|
}
|
|
}
|
|
if (currDispData.semIn != Semaforo.ND)
|
|
{
|
|
//aggiorno SE diverso
|
|
if (sIN != currDispData.semIn)
|
|
{
|
|
sIN = currDispData.semIn;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void WriteTextSafe(string text)
|
|
{
|
|
this.UIThread(delegate
|
|
{
|
|
// accoda messaggio in gestione
|
|
dataMonitor_3 = text;
|
|
});
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
#region Protected Fields
|
|
|
|
protected static string baseUrl = @"https://liman.egalware.com/MP/IO/";
|
|
|
|
/// <summary>
|
|
/// Data-Ora prima apertura FORM...
|
|
/// </summary>
|
|
protected DateTime firstStart;
|
|
|
|
/// <summary>
|
|
/// Oggetto ultimo inviato stato IOB x REDIS
|
|
/// </summary>
|
|
protected IobWinStatus lastIobStatus = new IobWinStatus() { lastUpdate = DateTime.Now.AddHours(-1), lastDataOut = DateTime.Now.AddHours(-1) };
|
|
|
|
/// <summary>
|
|
/// Oggetto ultimo inviato stato MP-IO x REDIS
|
|
/// </summary>
|
|
protected ServerMpStatus lastSrvStatus = new ServerMpStatus();
|
|
|
|
/// <summary>
|
|
/// ultimo tentativo riavvio...
|
|
/// </summary>
|
|
protected DateTime lastStartTry;
|
|
|
|
/// <summary>
|
|
/// Generatore numeri random
|
|
/// </summary>
|
|
protected Random rndGen = new Random();
|
|
|
|
/// <summary>
|
|
/// Contatore campionamento memoria
|
|
/// </summary>
|
|
protected int sampleMemCount;
|
|
|
|
/// <summary>
|
|
/// Indica se inviare avvio adapter con FluxLog
|
|
/// </summary>
|
|
protected bool sendStartFLog = false;
|
|
|
|
/// <summary>
|
|
/// Temnpo attesa std in MS
|
|
/// </summary>
|
|
protected int waitRecMSec = 30000;
|
|
|
|
#endregion Protected Fields
|
|
|
|
#region Protected Properties
|
|
|
|
/// <summary>
|
|
/// Valore protected comunicazione PLC
|
|
/// </summary>
|
|
protected bool _commPlcActive { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// Valore protetto stato comunicazione
|
|
/// </summary>
|
|
protected int _commSrvActive { get; set; } = 0;
|
|
|
|
/// <summary>
|
|
/// Valore protected semaforo IN
|
|
/// </summary>
|
|
protected Semaforo _sIN { get; set; } = Semaforo.ND;
|
|
|
|
/// <summary>
|
|
/// Valore protected semaforo OUT
|
|
/// </summary>
|
|
protected Semaforo _sOUT { get; set; } = Semaforo.ND;
|
|
|
|
/// <summary>
|
|
/// Codice IOB della macchina cui connettersi (x scegliere corretto file di conf...)
|
|
/// </summary>
|
|
protected string CurrIOB { get; set; }
|
|
|
|
protected int delayShowLogMs { get; set; } = utils.CRI("delayShowLogMs");
|
|
|
|
/// <summary>
|
|
/// Dictionary dei divieti del datamonitor
|
|
/// </summary>
|
|
protected Dictionary<int, DateTime> dMonDisplVetoVeto { get; set; } = new Dictionary<int, DateTime>();
|
|
|
|
/// <summary>
|
|
/// array degli oggetti datamonitor da mostrare
|
|
/// </summary>
|
|
protected Dictionary<int, List<string>> dMonValues { get; set; } = new Dictionary<int, List<string>>();
|
|
|
|
/// <summary>
|
|
/// Veto a NUOVE scritture in COUNTER...
|
|
/// </summary>
|
|
protected DateTime logCounterVeto { get; set; } = DateTime.Now;
|
|
|
|
/// <summary>
|
|
/// Lista String da mostrare quale TaskLog corrente...
|
|
/// </summary>
|
|
protected List<string> logTaskString { get; set; } = new List<string>();
|
|
|
|
/// <summary>
|
|
/// Lista String da mostrare quale WatchLog corrente...
|
|
/// </summary>
|
|
protected ConcurrentQueue<string> logWatchString { get; set; } = new ConcurrentQueue<string>();
|
|
|
|
/// <summary>
|
|
/// Veto a NUOVE scritture in logWatch...
|
|
/// </summary>
|
|
protected DateTime logWatchWriteVeto { get; set; } = DateTime.Now;
|
|
|
|
protected long maxAlQueue { get; set; }
|
|
|
|
protected long maxEvQueue { get; set; }
|
|
|
|
protected long maxFlQueue { get; set; }
|
|
|
|
protected long maxMsQueue { get; set; }
|
|
|
|
protected long maxRwTrQueue { get; set; }
|
|
|
|
protected long maxUlQueue { get; set; }
|
|
|
|
protected long qAlLen { get; set; }
|
|
|
|
protected long qEvLen { get; set; }
|
|
|
|
protected long qFlLen { get; set; }
|
|
|
|
protected long qMsLen { get; set; }
|
|
|
|
protected long qRTrLen { get; set; }
|
|
|
|
protected long qUlLen { get; set; }
|
|
|
|
protected long totQueue
|
|
{
|
|
get
|
|
{
|
|
return qEvLen + qFlLen + qAlLen + qMsLen + qUlLen;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// URL per salvare i file dell'IOB (SENZA IOB)
|
|
/// </summary>
|
|
protected string urlUploadFile
|
|
{
|
|
get => $"http://{IOBConfFull.MapoMes.IpAddr}{IOBConfFull.MapoMes.BaseAppUrl}IOB/uploadFile/";
|
|
}
|
|
|
|
/// <summary>
|
|
/// URL per salvare i file dell'IOB su CLOUD (SENZA IOB)
|
|
/// </summary>
|
|
protected string urlUploadFileCloud
|
|
{
|
|
get => $"{baseUrl}IOB/uploadFile/";
|
|
}
|
|
|
|
#endregion Protected Properties
|
|
|
|
#region Protected Methods
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing && (components != null))
|
|
{
|
|
components.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
/// <summary>
|
|
/// GEstione evento refresh
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
protected void IobObj_eh_refreshed(object sender, iobRefreshedEventArgs e)
|
|
{
|
|
// aggiorno!
|
|
this.UIThread(delegate
|
|
{
|
|
updateFormDisplay(e.DisplayDataObject);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua logging DEBUG corretto impostanto anche la variabile IOB prima di scrivere...
|
|
/// </summary>
|
|
/// <param name="txt2log"></param>
|
|
protected void lgDebug(string txt2log)
|
|
{
|
|
lg.Factory.Configuration.Variables["codIOB"] = this.CurrIOB;//IOBConfFull.General.FilenameIOB ??this.CurrIOB;
|
|
lg.Debug(txt2log);
|
|
// salvo anche in logwatcher...
|
|
newDisplayData currDispData = new newDisplayData();
|
|
currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | INFO | {txt2log}";
|
|
updateFormDisplay(currDispData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
|
|
/// </summary>
|
|
/// <param name="txt2log"></param>
|
|
protected void lgError(string txt2log)
|
|
{
|
|
if (!string.IsNullOrEmpty(txt2log))
|
|
{
|
|
lg.Factory.Configuration.Variables["codIOB"] = this.CurrIOB;//IOBConfFull.General.FilenameIOB ??this.CurrIOB;
|
|
lg.Error(txt2log);
|
|
// salvo anche in logwatcher... SE non si dimostra ricorsivo...
|
|
if (!txt2log.Contains("logWatcher"))
|
|
{
|
|
newDisplayData currDispData = new newDisplayData();
|
|
currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | ERROR | {txt2log}";
|
|
updateFormDisplay(currDispData);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
|
|
/// </summary>
|
|
/// <param name="txt2log"></param>
|
|
protected void lgInfo(string txt2log)
|
|
{
|
|
lg.Factory.Configuration.Variables["codIOB"] = this.CurrIOB;//IOBConfFull.General.FilenameIOB ??this.CurrIOB;
|
|
lg.Info(txt2log);
|
|
// salvo anche in logwatcher...
|
|
newDisplayData currDispData = new newDisplayData();
|
|
currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | INFO | {txt2log}";
|
|
updateFormDisplay(currDispData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua logging TRACE corretto impostanto anche la variabile IOB prima di scrivere...
|
|
/// </summary>
|
|
/// <param name="txt2log"></param>
|
|
protected void lgTrace(string txt2log)
|
|
{
|
|
lg.Factory.Configuration.Variables["codIOB"] = this.CurrIOB;//IOBConfFull.General.FilenameIOB ??this.CurrIOB;
|
|
lg.Trace(txt2log);
|
|
// salvo anche in logwatcher...
|
|
newDisplayData currDispData = new newDisplayData();
|
|
currDispData.newLiveLogData = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} | TRACE | {txt2log}";
|
|
updateFormDisplay(currDispData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// carica IOB richiesto
|
|
/// </summary>
|
|
protected virtual async Task loadIobType()
|
|
{
|
|
if (IOBConfFull != null)
|
|
{
|
|
switch (tipoScelto)
|
|
{
|
|
case tipoAdapter.ND:
|
|
default:
|
|
iobObj = new Iob.Simula(this, IOBConfFull);
|
|
start.Enabled = false;
|
|
break;
|
|
}
|
|
if (!await iobInitAsync())
|
|
{
|
|
return;
|
|
}
|
|
UpdateDisplTypeIobSel();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esegue logica di init async x IOB
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected async Task<bool> iobInitAsync()
|
|
{
|
|
try
|
|
{
|
|
this.Cursor = Cursors.WaitCursor;
|
|
await iobObj.InitializeAsync();
|
|
this.Cursor = Cursors.Default;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lgError($"Inizializzazione fallita per {tipoScelto}: {ex.Message}");
|
|
iobObj = null; // Evitiamo di usare un oggetto malfunzionante
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Init form (grafica) con helper x testi e varie
|
|
/// </summary>
|
|
protected void myGraphInitForm()
|
|
{
|
|
lblStatus.Text = "Loading";
|
|
|
|
// aggiunta tooltip x send forzato
|
|
ToolTip ttForceSend = new ToolTip();
|
|
|
|
// imposto delay.
|
|
ttForceSend.AutoPopDelay = 3000;
|
|
ttForceSend.InitialDelay = 500;
|
|
ttForceSend.ReshowDelay = 500;
|
|
// sempre visibile (che sia o meno attiva la form).
|
|
ttForceSend.ShowAlways = true;
|
|
|
|
// imposto tooltip
|
|
ttForceSend.SetToolTip(this.chkForceDequeue, "Forza invio eventi allo stop");
|
|
}
|
|
|
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
|
{
|
|
CloseTimers();
|
|
base.OnFormClosing(e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// impostazione valori defaults
|
|
/// </summary>
|
|
protected void setDefaults()
|
|
{
|
|
stop.Enabled = false;
|
|
|
|
alQueueLen = 0;
|
|
flQueueLen = 0;
|
|
msQueueLen = 0;
|
|
rtrQueueLen = 0;
|
|
ulQueueLen = 0;
|
|
nLine2show = utils.CRI("numRowConsole");
|
|
}
|
|
|
|
/// <summary>
|
|
/// MOstra info su coda complessiva
|
|
/// </summary>
|
|
protected void showQueueData()
|
|
{
|
|
lblQueueLenTop.Text = totQueue == 0 ? "realtime" : $"ev: {qEvLen} | flog: {qFlLen} | tot: {totQueue}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update display info tipo IOB selezionato
|
|
/// </summary>
|
|
protected void UpdateDisplTypeIobSel()
|
|
{
|
|
lblCNC.Text = $"CNC: {IOBConfFull.General.IobType} [{IOBConfFull.Device.Connect.IpAddr}:{IOBConfFull.Device.Connect.Port}]";
|
|
lblSrvUrl.Text = $"SRV: {IOBConfFull.MapoMes.IpAddr} | BaseURL: {IOBConfFull.MapoMes.ApiUrl("")}";
|
|
|
|
// aggancio evento refresh
|
|
iobObj.eh_refreshed += IobObj_eh_refreshed;
|
|
|
|
// carico i default values su interfaccia
|
|
setDefaults();
|
|
|
|
displayTaskAndLog($"Caricata conf per adapter {tipoScelto}");
|
|
}
|
|
|
|
#endregion Protected Methods
|
|
|
|
#region Private Fields
|
|
|
|
private readonly object _lock = new object();
|
|
|
|
private CancellationTokenSource _cts;
|
|
|
|
// Per thread-safety
|
|
private bool _isSuspended = false;
|
|
|
|
private Task _workerTask;
|
|
|
|
/// <summary>
|
|
/// Contatore chiamate timers x debug timing
|
|
/// </summary>
|
|
private Dictionary<gatherCycle, int> TimersCycleCount = new Dictionary<gatherCycle, int>();
|
|
|
|
/// <summary>
|
|
/// Contatore durata exec x debug timing
|
|
/// </summary>
|
|
private Dictionary<gatherCycle, double> TimersCycleElaps = new Dictionary<gatherCycle, double>();
|
|
|
|
/// <summary>
|
|
/// Dizionario scadenza timers interni x esecuzione dei vari task a differente frequenza di chiamata
|
|
/// </summary>
|
|
private Dictionary<gatherCycle, DateTime> TimersVeto = new Dictionary<gatherCycle, DateTime>();
|
|
|
|
/// <summary>
|
|
/// Dizionario dei valori bloccati x evitare log eccessivo
|
|
/// </summary>
|
|
private Dictionary<string, DateTime> vetoLogError = new Dictionary<string, DateTime>();
|
|
|
|
/// <summary>
|
|
/// Periodo di veto log in minuti
|
|
/// </summary>
|
|
private int vetoPeriodMin = 15;
|
|
|
|
#endregion Private Fields
|
|
|
|
#region Private Delegates
|
|
|
|
private delegate void SafeCallDelegate(string text);
|
|
|
|
#endregion Private Delegates
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// Esecuzione dei metodi in modalità async dopo il load della Form
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private async void AdapterForm_Load(object sender, EventArgs e)
|
|
{
|
|
// se abilitato autoload conf leggo file corretto...
|
|
if (utils.CRB("autoLoadConf"))
|
|
{
|
|
try
|
|
{
|
|
if (File.Exists(defConfFilePathYaml))
|
|
{
|
|
loadYamlFile(defConfFilePathYaml);
|
|
await sendConfFile(defConfFilePathYaml);
|
|
}
|
|
else
|
|
{
|
|
loadIniFile(defConfFilePath);
|
|
await sendConfFile(defConfFilePath);
|
|
}
|
|
lgInfo("INI LOADED");
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
displayTaskAndLog(string.Format("Eccezione in autoLoadConf: {0}", exc));
|
|
}
|
|
}
|
|
|
|
if (IOBConfFull == null || !utils.CRB("autoLoadConf"))
|
|
{
|
|
IOBConfFull = new IobConfTree();
|
|
loadIobType();
|
|
displayTaskAndLog("Waiting for config file selection");
|
|
}
|
|
|
|
// Start timer periodico comunicazione
|
|
gather.Interval = IOBConfFull.General.Timers.MsUI;
|
|
gather.Enabled = true;
|
|
displayTaskAndLog($"Main UI timer set: {gather.Interval}ms", true);
|
|
|
|
// Start timer periodico interfaccia
|
|
displTimer.Interval = utils.CRI("timerIntMs");
|
|
displTimer.Enabled = true;
|
|
displayTaskAndLog("UI Program Running", true);
|
|
|
|
// check oggetto not null
|
|
if (iobObj != null)
|
|
{
|
|
//verifico sia da inviare, ovvero non ci sia veto redis (da 12h..)
|
|
bool needSend = false;
|
|
string redKey = iobObj.redisMan.redHash($"IOB:SendReboot:{CurrIOB}");
|
|
var rawVal = iobObj.redisMan.getRSV(redKey);
|
|
// verifico uguaglianza MD5...
|
|
if (string.IsNullOrEmpty(rawVal))
|
|
{
|
|
needSend = true;
|
|
// attendo meno di 1 min x il prox...
|
|
int ttlSec = rndGen.Next(20, 30);
|
|
iobObj.redisMan.setRSV(redKey, $"{DateTime.Now}", ttlSec);
|
|
}
|
|
try
|
|
{
|
|
// verifico server online...
|
|
bool srvAlive = iobObj.CheckServerAlive();
|
|
if (srvAlive)
|
|
{
|
|
if (needSend)
|
|
{
|
|
// segnalo reboot (programma - url file)...
|
|
await Iob.Generic.callUrl(iobObj.urlReboot, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
displayTaskAndLog("AdapterForm: Server OFFLINE");
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"AdapterForm: EXCEPTION in fase di chiamata URL di reboot:{iobObj.urlReboot}{Environment.NewLine}{exc}");
|
|
}
|
|
|
|
// avvio timer secondario x esecuzione (periodo di base: VHF!!!)
|
|
StartWorker();
|
|
displayTaskAndLog($"Main workerTimer set: {IOBConfFull.General.Timers.MsVHF}ms", true);
|
|
}
|
|
displayTaskAndLog("Main Form OK", true);
|
|
}
|
|
|
|
private void btnForceAutoOdl_Click(object sender, EventArgs e)
|
|
{
|
|
iobObj.forceResetOdl();
|
|
}
|
|
|
|
private void BtnOpenLog_Click(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
string logPath = $"{System.AppDomain.CurrentDomain.BaseDirectory}logs\\{CurrIOB}\\{DateTime.Today:yyyy-MM-dd}.log";
|
|
Process.Start(logPath);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in show log (MAIN):{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
|
|
private void BtnSendPLC_Click(object sender, EventArgs e)
|
|
{
|
|
// invia a MES il parametro selezionato (es ART / ODL / PRG NAME, come task2exe..)
|
|
Dictionary<string, string> forcedTask = new Dictionary<string, string>();
|
|
// guardo i parametri...
|
|
if (!string.IsNullOrEmpty(txtValue.Text))
|
|
{
|
|
forcedTask.Add(cmbParamValues.SelectedValue.ToString(), txtValue.Text);
|
|
}
|
|
// chiedo esecuzione task! forzato SENZA tavola
|
|
_ = iobObj.ProcessTask(forcedTask, "");
|
|
chkEdit.Checked = false;
|
|
toggleEditMes2Plc();
|
|
}
|
|
|
|
private void checkAssignSize()
|
|
{
|
|
int valSize = 0;
|
|
int.TryParse(txtMReadSize.Text, out valSize);
|
|
// verifico sia valida con lenght array...
|
|
int maxSize = iobObj.RawInput.Length - iobObj.RawDataInputStart;
|
|
valSize = valSize > maxSize ? maxSize : valSize;
|
|
// riporto
|
|
iobObj.RawDataInputSize = valSize;
|
|
}
|
|
|
|
private void checkAssignStart()
|
|
{
|
|
int valStart = 0;
|
|
int.TryParse(txtMReadStart.Text, out valStart);
|
|
// verifico sia valida con start/lenght array...
|
|
int maxStart = iobObj.RawInput.Length - 1;
|
|
valStart = valStart > maxStart ? maxStart : valStart;
|
|
// riporto
|
|
iobObj.RawDataInputStart = valStart;
|
|
}
|
|
|
|
private void checkEditMes2Plc()
|
|
{
|
|
cmbParamValues.Enabled = enableEditMes2Plc;
|
|
txtValue.Enabled = enableEditMes2Plc;
|
|
if (enableEditMes2Plc)
|
|
{
|
|
// aggiorno (se possibile) i parametri selezionabili...
|
|
fixComboParameters();
|
|
}
|
|
}
|
|
|
|
private void checkSampleMem()
|
|
{
|
|
// decremento contatore...
|
|
sampleMemCount--;
|
|
if (sampleMemCount <= 0)
|
|
{
|
|
sampleMemCount = utils.CRI("sampleMemCount");
|
|
// avvio fase raccolta dati e invio con adapter
|
|
iobObj.saveMemDump(dumpType.SAMPLE);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifica scadenza task
|
|
/// </summary>
|
|
/// <param name="ciclo"></param>
|
|
private async Task checkScad()
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
bool sendDone = false;
|
|
// ciclo su tutti i timers enum superiori a VHF
|
|
var priorities = (gatherCycle[])Enum.GetValues(typeof(gatherCycle));
|
|
// prendo solo > VHF e ordino discendenti x dare priorità ai più rari...
|
|
var ordered = priorities.Where(x => x > gatherCycle.VHF).OrderByDescending(x => x);
|
|
foreach (gatherCycle item in ordered)
|
|
{
|
|
if (TimersVeto.ContainsKey(item))
|
|
{
|
|
if (TimersVeto[item] <= adesso)
|
|
{
|
|
// se non ho già processato a questo giro...
|
|
if (!sendDone)
|
|
{
|
|
TimersCycleCount[item]++;
|
|
Stopwatch sw = Stopwatch.StartNew();
|
|
await iobObj.getAndSendAsync(item);
|
|
|
|
sw.Stop();
|
|
sendDone = true;
|
|
TimersCycleElaps[item] += sw.Elapsed.TotalMilliseconds;
|
|
// metto nuova scadenza perturbata 10%
|
|
TimersVeto[item] = adesso.AddMilliseconds(iobObj.IOBConfFull.TimerMs(item, 0.1));
|
|
}
|
|
// log + reset se ho contato ALMENO 100 exec o il tempo totale supera 1000msec
|
|
int limMs = 10000;
|
|
double totalMs = TimersCycleElaps.ContainsKey(item) ? TimersCycleElaps[item] : 0;
|
|
if (TimersCycleCount[item] >= 100 || totalMs > limMs)
|
|
{
|
|
int nCount = Math.Max(TimersCycleCount[item], 1);
|
|
lgDebug($"checkScad | {item} | {totalMs / nCount:F1}ms x {nCount}");
|
|
TimersCycleCount[item] = 0;
|
|
TimersCycleElaps[item] = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TimersVeto.Add(item, adesso.AddMilliseconds(iobObj.IOBConfFull.TimerMs(item, 0)));
|
|
TimersCycleCount.Add(item, 0);
|
|
if (!TimersCycleElaps.ContainsKey(item)) TimersCycleElaps.Add(item, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ChkEdit_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
toggleEditMes2Plc();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Chiusura adapter
|
|
/// </summary>
|
|
private void closeAdapter()
|
|
{
|
|
lgDebug("closeAdapter | true | false | true | false");
|
|
fermaTutto(true, false, true, false);
|
|
}
|
|
|
|
private void CloseTimers()
|
|
{
|
|
displTimer?.Stop();
|
|
displTimer?.Dispose();
|
|
gather?.Stop();
|
|
gather?.Dispose();
|
|
_isSuspended = true;
|
|
_ = StopWorker();
|
|
}
|
|
|
|
private void displTimer_Tick(object sender, EventArgs e)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Metodo principale esecuzione task in thread background (no interferenza con UI) x processi IO bound
|
|
/// </summary>
|
|
private async Task DoExecTasks()
|
|
{
|
|
bool doLog = false;
|
|
// procedo!
|
|
if (iobObj.periodicLog)
|
|
{
|
|
doLog = true;
|
|
}
|
|
try
|
|
{
|
|
// check esecuzione SendTask (MsVHF) COMUNQUE...
|
|
await iobObj.getAndSendAsync(gatherCycle.VHF);
|
|
// eseguo cicli attivi SOLO se adapter è in EFFETTIVO running...
|
|
if (iobObj.adpRunning)
|
|
{
|
|
if (iobObj.connectionOk)
|
|
{
|
|
// se richiesto faccio memory DUMP INIZIALE!
|
|
if (iobObj.doStartMemDump)
|
|
{
|
|
lgInfo("Inizio dump memoria");
|
|
iobObj.saveMemDump(dumpType.STARTUP);
|
|
// fatto! non ripeto...
|
|
iobObj.doStartMemDump = false;
|
|
lgInfo("Finito dump memoria");
|
|
}
|
|
// controllo se sia abilitato sampleDump della meoria (periodico)
|
|
if (iobObj.doSampleMemory)
|
|
{
|
|
checkSampleMem();
|
|
}
|
|
// controllo TUTTE le scadenze...
|
|
checkScad();
|
|
if (utils.CRI("waitEndCycle") > 0)
|
|
{
|
|
await Task.Delay(utils.CRI("waitEndCycle"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// qui attende meno...
|
|
DateTime dtVeto = lastStartTry.AddMilliseconds(waitRecMSec / 2);
|
|
if (iobObj.adpTryRestart && (DateTime.Now > dtVeto))
|
|
{
|
|
if (doLog)
|
|
{
|
|
lgInfo($"Retry Time Elapsed ({waitRecMSec / 2} ms)--> tryConnect");
|
|
}
|
|
lastStartTry = DateTime.Now;
|
|
iobObj.tryConnect();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (doLog)
|
|
{
|
|
lgInfo("Adapter stopped");
|
|
}
|
|
// verifico SE debba tentare il riavvio, ovvero NON running ma adpTryRestart
|
|
// e non ho riprovato x oltre waitRecMSec
|
|
DateTime dtVeto = lastStartTry.AddMilliseconds(waitRecMSec);
|
|
if (iobObj.adpTryRestart && (DateTime.Now > dtVeto))
|
|
{
|
|
lastStartTry = DateTime.Now;
|
|
AvviaAdapter(chkForceDequeue.Checked);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError(string.Format("Eccezione in fase di gatherTick: {0}{1}", Environment.NewLine, exc));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ferma tutti i componenti adapter + update buttons
|
|
/// </summary>
|
|
/// <param name="stopTimer">
|
|
/// indica se fermare il timer (gather) principale (solo se non si chiude)
|
|
/// </param>
|
|
/// <param name="tryRestart">indica se tentare di riconnettersi</param>
|
|
/// <param name="forceDequeue">indica se sia richiesto di SVUOTARE le code delle info</param>
|
|
/// <param name="updateForm">indica se si debba aggiornare la form (no se si sta chiudendo...)</param>
|
|
private void fermaTutto(bool stopTimer, bool tryRestart, bool forceDequeue, bool updateForm)
|
|
{
|
|
if (iobObj != null)
|
|
{
|
|
try
|
|
{
|
|
try
|
|
{
|
|
// PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
|
|
Task.Run(async () =>
|
|
{
|
|
await iobObj.stopAdapter(tryRestart, forceDequeue);
|
|
stop.Enabled = false;
|
|
start.Enabled = true;
|
|
restart.Enabled = false;
|
|
_isSuspended = true;
|
|
if (stopTimer)
|
|
{
|
|
gather.Enabled = false;
|
|
await StopWorker();
|
|
}
|
|
})
|
|
.GetAwaiter()
|
|
.GetResult();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lgError($"Errore in fermaTutto |stopTimer: {stopTimer} | tryRestart: {tryRestart} | forceDequeue: {forceDequeue} | updateForm: {updateForm}{Environment.NewLine}{ex.Message}");
|
|
}
|
|
|
|
|
|
|
|
newDisplayData currDispData = new newDisplayData();
|
|
currDispData.semIn = Semaforo.SS;
|
|
currDispData.semOut = Semaforo.SS;
|
|
if (updateForm)
|
|
{
|
|
updateFormDisplay(currDispData);
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Errore in chiusura:{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void fixComboParameters()
|
|
{
|
|
if (iobObj != null)
|
|
{
|
|
if (iobObj.memMap != null)
|
|
{
|
|
if (iobObj.memMap.mMapWrite != null)
|
|
{
|
|
if (iobObj.memMap.mMapWrite.Count > 0)
|
|
{
|
|
var oldParams = cmbParamValues.DataSource;
|
|
List<string> parametri = new List<string>();
|
|
foreach (var item in iobObj.memMap.mMapWrite)
|
|
{
|
|
parametri.Add(item.Key);
|
|
}
|
|
// salvo selezione
|
|
int oldIdx = cmbParamValues.SelectedIndex;
|
|
// riassegno e ri-seleziono
|
|
cmbParamValues.DataSource = parametri;
|
|
cmbParamValues.SelectedIndex = oldIdx >= 0 ? oldIdx : 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Evento principale scadenza del timer interno MsUI da cui scaturire le varie esecuzioni
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void gather_Tick(object sender, EventArgs e)
|
|
{
|
|
if (iobObj != null)
|
|
{
|
|
// fermo il timer...
|
|
gather.Stop();
|
|
try
|
|
{
|
|
refreshFormData();
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError(string.Format("Eccezione in fase di gatherTick: {0}{1}", Environment.NewLine, exc));
|
|
}
|
|
|
|
// riavvio il timer...
|
|
gather.Start();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Init oggetti datamonitor
|
|
/// </summary>
|
|
private void initDatamonitor()
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
dMonDisplVetoVeto.Add(i, DateTime.Now);
|
|
dMonValues.Add(i, new List<string>());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Carica file ini della configurazione richiesta
|
|
/// </summary>
|
|
/// <param name="iniConfFile"></param>
|
|
private void loadIniFile(string iniConfFile)
|
|
{
|
|
// out di cosa faccio...
|
|
displayTaskAndLog($"[STARTUP] Loading ConfFile: {iniConfFile}");
|
|
var appVers = Assembly.GetExecutingAssembly().GetName().Version;
|
|
string path = fileMover.GetExecutingDirectoryName(); // Directory.GetCurrentDirectory();
|
|
string fileName = Path.GetFileName(iniConfFile).Replace(".ini", ".iob");
|
|
string dirPath = Path.Combine(path, "DATA", "CONF_RUN");
|
|
// verifica directory
|
|
baseUtils.checkDir(dirPath);
|
|
string fullPath = Path.Combine(dirPath, fileName);
|
|
// preparazione file conf con nuovo formato e salvataggio...
|
|
IOBConfFull = IobConfTree.LoadFromINI(iniConfFile);
|
|
if (IOBConfFull.General.FileName.EndsWith("ini"))
|
|
{
|
|
IOBConfFull.General.FileName = IOBConfFull.General.FileName.Replace("ini", "yaml");
|
|
}
|
|
// salvo anche yaml...
|
|
IOBConfFull.SaveYaml(fullPath.Replace("iob", "yaml"));
|
|
|
|
// carico IOB
|
|
loadIobType();
|
|
// invio file di conf!
|
|
IOBConfFull.SendConfYaml(iobObj.urlSaveConfYaml);
|
|
|
|
// avvio macchina con adapter specificato...
|
|
if (utils.CRB("autoStartOnLoad"))
|
|
{
|
|
displayTaskAndLog("Auto Starting...", true);
|
|
// avvio!
|
|
AvviaAdapter(chkForceDequeue.Checked);
|
|
displayTaskAndLog("Auto Started!", true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Carica file yaml della configurazione richiesta
|
|
/// </summary>
|
|
/// <param name="yamlConfFile"></param>
|
|
private void loadYamlFile(string yamlConfFile)
|
|
{
|
|
// out di cosa faccio...
|
|
displayTaskAndLog($"[STARTUP] Loading yamlConfFile: {yamlConfFile}");
|
|
// leggo file
|
|
IniFile fIni = new IniFile(yamlConfFile);
|
|
|
|
// carivo vettore parametri opzionai
|
|
Dictionary<string, string> optParRead = new Dictionary<string, string>();
|
|
string[] optParRows = fIni.ReadSection("OPTPAR");
|
|
if (optParRows.Length > 0)
|
|
{
|
|
try
|
|
{
|
|
string[] kvp;
|
|
foreach (var item in optParRows)
|
|
{
|
|
kvp = item.Split('=');
|
|
optParRead.Add(kvp[0], kvp[1]);
|
|
}
|
|
lgDebug($"Caricati {optParRead.Count} parametri opzionali da OPTPAR");
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError(string.Format("EXCEPTION in fase di lettura OPTPAR: {0}{1}", Environment.NewLine, exc));
|
|
}
|
|
}
|
|
var appVers = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
|
|
|
loadIobType();
|
|
// avvio macchina con adapter specificato...
|
|
if (utils.CRB("autoStartOnLoad"))
|
|
{
|
|
displayTaskAndLog("Auto Starting...", true);
|
|
// avvio!
|
|
AvviaAdapter(chkForceDequeue.Checked);
|
|
displayTaskAndLog("Auto Started!", true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifica se il log di un dato errore sia permesso
|
|
/// </summary>
|
|
/// <param name="logKey">ID del valore log da loggare/verificare</param>
|
|
/// <returns></returns>
|
|
private bool logValuePermit(string logKey)
|
|
{
|
|
bool doLog = false;
|
|
if (vetoLogError.ContainsKey(logKey))
|
|
{
|
|
// verifico se veto scaduto...
|
|
if (DateTime.Now > vetoLogError[logKey])
|
|
{
|
|
doLog = true;
|
|
vetoLogError[logKey] = DateTime.Now.AddMinutes(vetoPeriodMin);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
doLog = true;
|
|
vetoLogError.Add(logKey, DateTime.Now.AddMinutes(vetoPeriodMin));
|
|
}
|
|
|
|
return doLog;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fase chiusura Form
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
lgTrace("MainForm_FormClosing");
|
|
closeAdapter();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Completato resize form
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void MainForm_Resize(object sender, EventArgs e)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mostrata form
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void MainForm_Shown(object sender, EventArgs e)
|
|
{
|
|
displayTaskAndLog("Main Form SHOWN (Adapter)", true);
|
|
}
|
|
|
|
private void mLoadConf_Click(object sender, EventArgs e)
|
|
{
|
|
// mostro selettore file x leggere adapter..
|
|
OpenFileDialog openFileDial = new OpenFileDialog();
|
|
|
|
// directory iniziale
|
|
openFileDial.InitialDirectory = utils.confDir; // string.Format(@"{0}\{1}", Application.StartupPath, utils.CRS("dataConfPath"));
|
|
// Set filter options and filter index.
|
|
openFileDial.Filter = "INI Files (.ini)|*.ini|All Files (*.*)|*.*";
|
|
openFileDial.FilterIndex = 1;
|
|
// altre opzioni
|
|
openFileDial.Multiselect = false;
|
|
|
|
// Call the ShowDialog method to show the dialog box.
|
|
DialogResult userClickedOK = openFileDial.ShowDialog();
|
|
|
|
// Process input if the user clicked OK.
|
|
if (userClickedOK == DialogResult.OK)
|
|
{
|
|
string iniConfFile = openFileDial.FileName;
|
|
loadIniFile(iniConfFile);
|
|
lgInfo("INI LOADED");
|
|
}
|
|
}
|
|
|
|
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;
|
|
// aggiorno labels counters...
|
|
counterIob = $"pz MES {iobObj.contapezziIOB}";
|
|
counterMac = $"pz PLC {iobObj.contapezziPLC}";
|
|
Dictionary<string, string> setPar = new Dictionary<string, string>();
|
|
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}";
|
|
// verifico IOB status
|
|
IobWinStatus currIobStatus = new IobWinStatus()
|
|
{
|
|
CodIob = iobObj.IOBConfFull.General.FilenameIOB,
|
|
IobType = $"{iobObj.IOBConfFull.General.IobType}",//iobObj.cIobConf.tipoIob.ToString(),
|
|
queueAlLen = alQueueLen,
|
|
queueEvLen = evQueueLen,
|
|
queueFlLen = flQueueLen,
|
|
queueMsLen = msQueueLen,
|
|
queueRawTransfLen = rtrQueueLen,
|
|
queueUlLen = ulQueueLen,
|
|
counterIOB = iobObj.contapezziIOB,
|
|
counterMAC = iobObj.contapezziPLC,
|
|
lastUpdate = lastIobStatus.lastUpdate > iobObj.lastWatchDog ? lastIobStatus.lastUpdate : iobObj.lastWatchDog,
|
|
online = utils.IOB_Online,
|
|
lastDataIn = iobObj.lastReadPLC,
|
|
lastDataOut = iobObj.lastIobOnline,
|
|
setupParams = setPar,
|
|
freeNotes = note
|
|
};
|
|
// se diverso SALVO!
|
|
if (!currIobStatus.Equals(lastIobStatus))
|
|
{
|
|
// aggiorno data
|
|
currIobStatus.lastUpdate = DateTime.Now;
|
|
// salvo su redis e in obj corrente
|
|
iobObj.redisMan.iobStatus = currIobStatus;
|
|
lastIobStatus = currIobStatus;
|
|
}
|
|
// se diverso SALVO MP IO status
|
|
if (lastSrvStatus.online != utils.MPIO_Online)
|
|
{
|
|
// aggiorno
|
|
ServerMpStatus currSrvStatus = iobObj.redisMan.servStatus;
|
|
currSrvStatus.online = utils.MPIO_Online;
|
|
currSrvStatus.lastUpdate = DateTime.Now;
|
|
// salvo su redis e in obj corrente
|
|
iobObj.redisMan.servStatus = currSrvStatus;
|
|
lastSrvStatus = currSrvStatus;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Button restart
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void restart_Click(object sender, EventArgs e)
|
|
{
|
|
string message = "Attenzione: con l'operazione di reset veranno persi (e non inviati) i dati eventualmente accumulati (indipendentemente dalla selezione del checkbox 'svuota coda').";
|
|
string caption = "Conferma Reset Dati IOB";
|
|
MessageBoxButtons buttons = MessageBoxButtons.YesNo;
|
|
DialogResult result;
|
|
|
|
// verifica con messagebox
|
|
result = MessageBox.Show(message, caption, buttons);
|
|
// solo se ho conferma da utente
|
|
if (result == DialogResult.Yes)
|
|
{
|
|
// faccio stop... SENZA inviare
|
|
fermaAdapter(false, false, true);
|
|
displayTaskAndLog("RESTARTING: Adapter Stopped", true);
|
|
// rileggo INI
|
|
loadIniFile(defConfFilePath);
|
|
displayTaskAndLog("RESTARTING: Ini File Reloaded", true);
|
|
// faccio start... CON RESET delle code
|
|
AvviaAdapter(true);
|
|
displayTaskAndLog("RESTARTING: Adapter Started", true);
|
|
// resetto i data monitor...
|
|
dataMonitor_0 = "";
|
|
dataMonitor_1 = "";
|
|
dataMonitor_2 = "";
|
|
dataMonitor_3 = "";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invio per backup contenuto file conf
|
|
/// </summary>
|
|
/// <param name="confFilePath"></param>
|
|
private async Task sendConfFile(string confFilePath)
|
|
{
|
|
string answ = "";
|
|
fileEmbed objFiles = new fileEmbed();
|
|
try
|
|
{
|
|
if (File.Exists(confFilePath))
|
|
{
|
|
string fileContent = System.IO.File.ReadAllText($"{confFilePath}");
|
|
smallFile currFile = new smallFile()
|
|
{
|
|
fileName = confFilePath,
|
|
content = fileContent.Replace("\r\n", Environment.NewLine)
|
|
};
|
|
objFiles.fileList.Add(currFile);
|
|
// serializzo
|
|
string rawData = JsonConvert.SerializeObject(objFiles);
|
|
// mod 2022.12.30: verifico se diverso da copia redis ultimo MD5 del set
|
|
// salvato, nel caso NON invio...
|
|
bool needSend = false;
|
|
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
|
|
{
|
|
var hash = md5.ComputeHash(Encoding.ASCII.GetBytes(rawData));
|
|
string newMD5 = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
|
|
string redKey = iobObj.redisMan.redHash($"IOB:ConfFileMD5:{CurrIOB}");
|
|
var rawVal = iobObj.redisMan.getRSV(redKey);
|
|
// verifico uguaglianza MD5...
|
|
if (string.IsNullOrEmpty(rawVal) || !newMD5.Equals(rawVal))
|
|
{
|
|
needSend = true;
|
|
int CacheConfToCloudDuratHour = utils.CRI("CacheConfToCloudDuratHour");
|
|
// calcolo attesa con errore random -/+ 10%
|
|
int ttlSec = 60 * 60 * CacheConfToCloudDuratHour * rndGen.Next(900, 1100) / 1000;
|
|
iobObj.redisMan.setRSV(redKey, newMD5, ttlSec);
|
|
}
|
|
}
|
|
// invio solo se necessario
|
|
if (needSend)
|
|
{
|
|
if (utils.CRB("ConfToCloud"))
|
|
{
|
|
// invio su cloud...
|
|
answ = await utils.callUrlAsync($"{urlUploadFileCloud}{CurrIOB}", rawData);
|
|
}
|
|
else
|
|
{
|
|
// provo invio locale
|
|
answ = utils.callUrl($"{urlUploadFile}{CurrIOB}", rawData);
|
|
// se va male invio cloud...
|
|
if (answ.ToUpper() != "OK")
|
|
{
|
|
answ = await utils.callUrlAsync($"{urlUploadFileCloud}{CurrIOB}", rawData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in upload IOB conf files{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
|
|
private void splitContainer1_Panel1_Paint(object sender, PaintEventArgs e)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Avvio dell'adapter
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void start_Click(object sender, EventArgs e)
|
|
{
|
|
stopForced = false;
|
|
AvviaAdapter(chkForceDequeue.Checked);
|
|
// salvo che ho avviato adapter
|
|
lgInfo("Completato LOAD Adapter");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Metodo per avviare il worker (es. nel Form_Load)
|
|
/// </summary>
|
|
private void StartWorker()
|
|
{
|
|
// rimozione isSuspended
|
|
_isSuspended = false;
|
|
// per prima cosa disattivo il disabled...
|
|
lock (_lock)
|
|
{
|
|
// 1. Controllo se è già in esecuzione
|
|
if (_workerTask != null && !_workerTask.IsCompleted)
|
|
{
|
|
lgTrace("Worker già in esecuzione. Richiesta ignorata.");
|
|
return;
|
|
}
|
|
|
|
_cts = new CancellationTokenSource();
|
|
// Assegniamo il Task alla variabile per poterne monitorare lo stato
|
|
_workerTask = Task.Run(() => WorkerLoopAsync(_cts.Token));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// fermata dell'adapter
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void stop_Click(object sender, EventArgs e)
|
|
{
|
|
stopForced = true;
|
|
fermaAdapter(false, chkForceDequeue.Checked, true);
|
|
// salvo che ho fermato adapter
|
|
lgInfo("UNLOAD Adapter");
|
|
}
|
|
|
|
private bool stopForced = false;
|
|
|
|
// Metodo per fermare tutto (es. nel Form_Closing o Dispose)
|
|
private async Task StopWorker()
|
|
{
|
|
if (_cts == null) return;
|
|
|
|
_cts.Cancel();
|
|
|
|
try
|
|
{
|
|
// Opzionale: attendi che il task finisca davvero prima di fare il dispose
|
|
if (_workerTask != null)
|
|
{
|
|
await _workerTask;
|
|
}
|
|
}
|
|
catch (OperationCanceledException) { /* Normale amministrazione */ }
|
|
finally
|
|
{
|
|
if (_cts != null)
|
|
{
|
|
_cts.Dispose();
|
|
_cts = null;
|
|
}
|
|
if (_workerTask != null)
|
|
{
|
|
_workerTask = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void TabData_Selected(object sender, TabControlEventArgs e)
|
|
{
|
|
}
|
|
|
|
private void toggleEditMes2Plc()
|
|
{
|
|
// abilita i campi --> PLC per editing
|
|
enableEditMes2Plc = !enableEditMes2Plc;
|
|
checkEditMes2Plc();
|
|
}
|
|
|
|
private void txtMReadSize_TextChanged(object sender, EventArgs e)
|
|
{
|
|
checkAssignSize();
|
|
}
|
|
|
|
private void txtMReadStart_TextChanged(object sender, EventArgs e)
|
|
{
|
|
checkAssignStart();
|
|
checkAssignSize();
|
|
}
|
|
|
|
private async Task WorkerLoopAsync(CancellationToken ct)
|
|
{
|
|
try
|
|
{
|
|
while (!ct.IsCancellationRequested)
|
|
{
|
|
if (!_isSuspended)
|
|
{
|
|
try
|
|
{
|
|
await DoExecTasks();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lgError("Errore durante i task: " + ex.Message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// se non fosse stop richiesto da utente...
|
|
if (!stopForced)
|
|
{
|
|
DateTime dtVeto = lastStartTry.AddMilliseconds(waitRecMSec / 2);
|
|
// verifica scadenza controllo SE fosse offline x eseguire test riconnessione...
|
|
if (iobObj.adpTryRestart && (DateTime.Now > dtVeto) && iobObj.adpRunning && !iobObj.connectionOk)
|
|
{
|
|
lgTrace($"WorkerLoopAsync sospeso: tentativo riavvio periodico");
|
|
lastStartTry = DateTime.Now;
|
|
iobObj.tryConnect();
|
|
}
|
|
else
|
|
{
|
|
lgTrace($"WorkerLoopAsync sospeso: NON esegue task specifici | _isSuspended: {_isSuspended}");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calcolo del delay dinamico da Timers.MsVHF
|
|
int currentDelay = _isSuspended ? 10 * IOBConfFull.General.Timers.MsVHF : IOBConfFull.General.Timers.MsVHF;
|
|
|
|
// Il Task.Delay accetta il CancellationToken.
|
|
// Se chiudi l'app mentre sta "dormendo", l'attesa si interrompe IMMEDIATAMENTE
|
|
// senza dover aspettare la fine del timer, velocizzando la chiusura.
|
|
await Task.Delay(currentDelay, ct);
|
|
}
|
|
lgInfo("Worker interrotto per cancellation token.");
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// Eccezione normale quando si preme Stop/Cancel
|
|
lgInfo("Worker interrotto correttamente.");
|
|
}
|
|
}
|
|
|
|
#endregion Private Methods
|
|
}
|
|
} |