Files
Mapo-IOB-WIN/IOB-WIN-SQL/IobSql/SqlServPama.cs
T
2026-01-08 08:30:01 +01:00

720 lines
30 KiB
C#

using EgwProxy.SqlDb.Controllers;
using EgwProxy.SqlDb.DbModels;
using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
using static IOB_UT_NEXT.CustomObj;
namespace IOB_WIN_SQL.IobSql
{
/// <summary>
/// Adapter specializzato per PAMA e le chiamate tramite DB per i dati
/// - PODL (ordini MES --&gt; MACCHINA)
/// - SignLog (da stato macchina)
/// - FluxLog (da stato macchina)
/// - ProdLog (da stato Prod)
/// </summary>
public class SqlServPama : Iob.GenericNext
{
#region Public Constructors
/// <summary>
/// Costruttore dell'IOB DB PAMA
/// </summary>
/// <param name="caller">Form chiamante</param>
/// <param name="IOBConf">Configurazione (legacy)</param>
/// <param name="IobConfFull">Configurazione (v 4.x)</param>
public SqlServPama(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
{
string SyncStateServ = getOptPar("SyncStateServer");
string SyncStateDb = getOptPar("SyncStateDb");
string SyncStateUser = getOptPar("SyncStateUser");
string SyncStatePwd = getOptPar("SyncStatePwd");
string SyncStateCTout = getOptPar("SyncStateCTout");
string VetoReadSec = getOptPar("VetoReadSec");
string VetoSyncSec = getOptPar("VetoSyncSec");
string sFullPodlUrl = getOptPar("FullPodlUrl");
string connSyncState = $"data source={SyncStateServ};initial catalog={SyncStateDb};persist security info=True;user id={SyncStateUser};password={SyncStatePwd};MultipleActiveResultSets=True;App=IOB-WIN-NEXT";
// gestione command timeout da https://erikej.github.io/sqlclient/2020/10/26/sqlclient-commandtimeout-preview.html
if (!string.IsNullOrEmpty(SyncStateCTout))
{
connSyncState = $"{connSyncState};Command Timeout={SyncStateCTout}";
}
if (!string.IsNullOrEmpty(VetoReadSec))
{
int.TryParse(VetoReadSec, out vetoReadDbSec);
}
if (!string.IsNullOrEmpty(VetoSyncSec))
{
int.TryParse(VetoSyncSec, out VetoSyncDbSec);
}
if (!string.IsNullOrEmpty(sFullPodlUrl))
{
bool.TryParse(sFullPodlUrl, out FullPodlUrl);
}
// eccezione: NON TROVA EntityFramework 6.0.0 o successivo... why?!?
dbProxy = new DbController(connSyncState);
lastPING = DateTime.Now.AddHours(-1);
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Implementazione custom esecuzione task specifici
/// </summary>
/// <param name="task2exe"></param>
/// <returns></returns>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe, string codTav)
{
DateTime adesso = DateTime.Now;
// unico task ammissibile: fare un SYNC forzato...
Dictionary<string, string> taskDone = new Dictionary<string, string>();
if (task2exe != null)
{
// controllo se memMap != null...
if (memMap != null)
{
bool taskOk = false;
string taskVal = "";
// cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
foreach (var item in task2exe)
{
taskOk = false;
taskVal = "";
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY...
switch (tName)
{
case taskType.setComm:
case taskType.syncDbData:
lgInfo($"executeTasks --> {tName}");
// per prima cosa recupero elenco PODL attivi x la macchina
List<PODLModel> listaPODL = new List<PODLModel>();
bool okPodl = false;
string rawListPODL = "";
try
{
// PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
Task.Run(async () =>
{
rawListPODL = await callUrl(urlGetNextPODL, false);
})
.GetAwaiter()
.GetResult();
}
catch (Exception ex)
{
lgError($"Errore in callUrl per {urlGetNextPODL}: {ex.Message}");
}
if (!string.IsNullOrEmpty(rawListPODL))
{
try
{
listaPODL = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
okPodl = listaPODL.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco PODL ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco PODL ({urlGetNextPODL}) ha restituito valore vuoto");
}
if (okPodl)
{
//mando elenco PODL...
List<MesPODLReqModel> CurrPodlReq = listaPODL
.Select(x => new MesPODLReqModel()
{
Attivabile = x.Attivabile,
CodArticolo = x.CodArticolo,
CodCli = x.CodCli,
CodGruppo = x.CodGruppo,
DueDate = x.DueDate ?? DateTime.Now,
IdxMacchina = x.IdxMacchina,
IdxODL = x.IdxOdl,
IdxPromessa = x.IdxPromessa,
InsertDate = x.InsertDate,
KeyBCode = x.KeyBCode,
KeyRichiesta = x.KeyRichiesta,
Note = x.Note,
NumPezzi = x.NumPezzi,
Priorita = x.Priorita,
PzPallet = x.PzPallet,
TCAssegnato = x.Tcassegnato
})
.ToList();
// scrivo i record richiesti
bool fatto = dbProxy.MesPodlWriteReq(CurrPodlReq);
if (fatto)
{
taskVal = $"EXECUTED task: {item.Key} / {item.Value}";
// registro evento chiamata scrittura in syncState...
string tabName = item.Key;
var currSyncState = elencoSyncState.FirstOrDefault(x => x.TableName == tabName);
// verifico x registrare azione
if (currSyncState == null)
{
currSyncState = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabName,
Note = "Init"
};
}
else
{
var lastRec = CurrPodlReq.OrderBy(x => x.IdxPromessa).LastOrDefault();
currSyncState.LastIdx = lastRec != null ? lastRec.IdxPromessa : -1;
currSyncState.Note = $"Esecuzione {tabName} per {CurrPodlReq.Count} rec | last code: {currSyncState.LastIdx}";
currSyncState.LastUpdate = adesso;
}
// salvo sync...
dbProxy.SyncStateUpsert(currSyncState);
}
// effettua sync scrivendo i dati in export (PODL)
execExportAll();
}
break;
default:
taskVal = $"IobSqlServPama | taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
lgInfo($"IobSqlServPama | chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
break;
}
// aggiungo task!
taskDone.Add(item.Key, taskVal);
}
}
}
lastReadPLC = DateTime.Now;
return taskDone;
}
/// <summary>
/// Recupero dati dinamici...
/// </summary>
public override Dictionary<string, string> getDynData()
{
DateTime adesso = DateTime.Now;
// dizionario vuoto / faccio direttamente accodamento in FluxLog
Dictionary<string, string> outVal = new Dictionary<string, string>();
// processo ed accodo!
processFluxLogTable(adesso);
lastReadPLC = DateTime.Now;
return outVal;
}
/// <summary>
/// Effettua processing CUSTOM:
/// - esegue tutti gli import
/// </summary>
public override void processCustomTaskLF()
{
lgInfo($"Richiesto processCustomTaskLF");
// effettua sync
execImportAll();
lastReadPLC = DateTime.Now;
}
/// <summary>
/// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
/// aggiornare x display in form</paramref>
/// </summary>
public override void readSemafori(ref newDisplayData currDispData)
{
DateTime adesso = DateTime.Now;
if (connectionOk)
{
// controllo veto checkDB
if (adesso > vetoDataRead)
{
// predispongo prox veto...
vetoDataRead = adesso.AddSeconds(vetoReadDbSec);
if (adesso > vetoDataSync)
{
// effettua sync
execImportAll();
vetoDataSync = adesso.AddSeconds(VetoSyncDbSec);
}
// semaforo
currDispData.semIn = Semaforo.SV;
// recupero syncState
elencoSyncState = dbProxy.SyncStateGetAll();
// verifico SignLog e processo
bool sentSignLog = processSignLogTable(adesso);
// verifico ProdData e processo
bool sentProdData = processProdDataTable(adesso);
}
}
else
{
B_input = 0;
currDispData.semIn = Semaforo.SR;
}
}
/// <summary>
/// Override connessione
/// </summary>
public override void tryConnect()
{
if (!connectionOk)
{
// controllo che il ping sia stato tentato almeno pingTestSec fa...
if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
{
if (verboseLog || periodicLog)
{
lgInfo("SqlDb PAMA: ConnKO - tryConnect");
}
// in primis salvo data ping...
lastPING = DateTime.Now;
// se passa il ping faccio il resto...
if (testPingMachine == IPStatus.Success)
{
string szStatusConnection = "";
try
{
// ora provo connessione...
parentForm.commPlcActive = true;
if (dbProxy != null)
{
elencoSyncState = dbProxy.SyncStateDoImportAll();
if (elencoSyncState != null && elencoSyncState.Count > 0)
{
parentForm.commPlcActive = false;
connectionOk = true;
}
}
// refresh stato connessione!!!
if (connectionOk)
{
queueInEnabCurr = true;
if (adpRunning)
{
lgInfo("Connessione OK");
lastReadPLC = DateTime.Now;
}
}
else
{
lgError("Impossibile procedere, connessione mancante...");
}
}
catch (Exception exc)
{
lgFatal($"Errore nella connessione all'adapter SqlDb PAMA: {szStatusConnection}{Environment.NewLine}{exc}");
connectionOk = false;
lgInfo($"Eccezione in TryConnect, Adapter SqlDb PAMA NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione");
}
}
else
{
// loggo no risposta ping ...
connectionOk = false;
if (verboseLog || periodicLog)
{
lgInfo($"Attenzione: SqlDb PAMA controllo PING fallito per IP {IOBConfFull.Device.Connect.PingIpAddr}");
}
}
}
}
else
{
needRefresh = true;
}
}
public override void tryDisconnect()
{
// registro solo che è disconnesso
connectionOk = false;
queueInEnabCurr = false;
}
#endregion Public Methods
#region Protected Fields
protected bool FullPodlUrl = false;
protected int vetoReadDbSec = 3;
protected int VetoSyncDbSec = 20;
#endregion Protected Fields
#region Protected Properties
protected DbController dbProxy { get; set; } = null;
/// <summary>
/// Stato di sync delle tab gestite
/// </summary>
protected List<SyncStateModel> elencoSyncState { get; set; } = new List<SyncStateModel>();
#endregion Protected Properties
#region Private Methods
/// <summary>
/// Esegue task EXPORT (MES PODL to MACHINE)
/// </summary>
private void execExportAll()
{
Stopwatch sw = new Stopwatch();
sw.Start();
elencoSyncState = dbProxy.SyncStateDoExportAll();
sw.Stop();
DateTime adesso = DateTime.Now;
lastReadPLC = adesso;
lgInfo($"DB: esecuzione task dbProxy.SyncStateDoExportAll() in {sw.ElapsedMilliseconds} ms");
if (elencoSyncState != null)
{
// registro evento chiamata scrittura in syncState...
string tabName = "ExportAll";
var currSyncState = elencoSyncState.FirstOrDefault(x => x.TableName == tabName);
// verifico x registrare azione
if (currSyncState == null)
{
currSyncState = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabName,
Note = "Init"
};
}
else
{
// calcolo lastIDX valore da datetime
int valInt = -1;
var nowString = $"{adesso:HHmmss}";
int.TryParse(nowString, out valInt);
currSyncState.LastIdx = valInt;
currSyncState.Note = $"Esecuzione {tabName} | DT code: {currSyncState.LastIdx}";
currSyncState.LastUpdate = adesso;
}
// salvo sync...
dbProxy.SyncStateUpsert(currSyncState);
foreach (var item in elencoSyncState)
{
lgTrace($"TAB {item.TableName} | LastIdx {item.LastIdx} | Note {item.Note} | Last Upd {item.LastUpdate}");
}
}
}
/// <summary>
/// Esegue task IMPORT (MES PODL to MACHINE)
/// </summary>
private void execImportAll()
{
Stopwatch sw = new Stopwatch();
sw.Start();
elencoSyncState = dbProxy.SyncStateDoImportAll();
sw.Stop();
lastReadPLC = DateTime.Now;
lgInfo($"DB: esecuzione task dbProxy.SyncStateDoImportAll() in {sw.ElapsedMilliseconds} ms");
if (elencoSyncState != null)
{
foreach (var item in elencoSyncState)
{
lgTrace($"TAB {item.TableName} | LastIdx {item.LastIdx} | Note {item.Note} | Last Upd {item.LastUpdate}");
}
}
}
/// <summary>
/// Esegue processing + invio dati tab SignLog
/// </summary>
/// <param name="adesso"></param>
/// <returns></returns>
private bool processFluxLogTable(DateTime adesso)
{
bool fatto = false;
string tabNameIn = "FluxLog";
string tabNameOut = "FluxLogToMes";
// cerco info sui FluxLog (ToMes e letti)
var currFluxLogRead = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameIn);
// se nullo inizializzo
if (currFluxLogRead == null)
{
currFluxLogRead = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabNameIn,
Note = "Init"
};
}
var currFluxLogSent = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameOut);
// se nullo inizializzo
if (currFluxLogSent == null)
{
currFluxLogSent = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabNameOut,
Note = "Init"
};
}
// verifica se ci siano dati da trasmettere (sui valori LastIdx)
if (currFluxLogRead.LastIdx > currFluxLogSent.LastIdx)
{
// recupero i dati dal DB...
var data2send = dbProxy.MachFluxLogGetNew(currFluxLogSent.LastIdx);
// se ho dati preparo invio
if (data2send != null && data2send.Count > 0)
{
string sVal = "";
foreach (var fLog2send in data2send)
{
sVal = $"[DYNDATA] |{fLog2send.DtEvento:yyyy-MM-dd HH:mm:ss}|{fLog2send.CodFlux}|{fLog2send.Valore}";
// chiamo accodamento con dataora corretta...
bool sent = accodaFLog(fLog2send.CodFlux, sVal, qEncodeFLog(fLog2send.DtEvento, fLog2send.CodFlux, fLog2send.Valore));
if (sent)
{ // traccio valore DynData x analisi
trackDynData(fLog2send.CodFlux, fLog2send.Valore);
}
}
fatto = true;
}
// aggiorno idx inviato...
currFluxLogSent.Note = $"Updated from {currFluxLogRead.LastIdx}";
currFluxLogSent.LastUpdate = adesso;
currFluxLogSent.LastIdx = currFluxLogRead.LastIdx;
}
// alla fine aggiorno i dati inviati!
dbProxy.SyncStateUpsert(currFluxLogSent);
return fatto;
}
/// <summary>
/// Esegue processing + invio dati tab ProdData
/// </summary>
/// <param name="adesso"></param>
/// <returns></returns>
private bool processProdDataTable(DateTime adesso)
{
bool fatto = false;
string tabNameIn = "ProdData";
string tabNameOut = "ProdDataToMes";
// cerco info sui SignLog (ToMes e letti)
var currSignLogRead = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameIn);
// se nullo inizializzo
if (currSignLogRead == null)
{
currSignLogRead = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabNameIn,
Note = "Init"
};
}
var currSignLogSent = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameOut);
// se nullo inizializzo
if (currSignLogSent == null)
{
currSignLogSent = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabNameOut,
Note = "Init"
};
}
// verifica se ci siano dati da trasmettere (sui valori LastIdx)
if (currSignLogRead.LastIdx > currSignLogSent.LastIdx)
{
// recupero i dati dal DB...
var data2send = dbProxy.MachProdDataGetNew(currSignLogSent.LastIdx);
// se ho dati preparo invio
if (data2send != null && data2send.Count > 0)
{
foreach (var sLog2send in data2send)
{
if (sLog2send.Action == "StartOrd")
{
try
{
// PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
Task.Run(async () =>
{
if (FullPodlUrl)
{
// se il record è apertura PODL chiamo apertura
await TrySetupPODLAsync(sLog2send.IdxPodl);
//TrySetupPODLAsync(sLog2send.IdxPodl, true, sLog2send.DtEve, DateTime.Now);
}
else
{
await TrySetupPODLAsync(0);
}
})
.GetAwaiter()
.GetResult();
}
catch (Exception ex)
{
lgError($"Errore in tryClosePODL | {Environment.NewLine}{ex.Message}");
}
}
else if (sLog2send.Action == "EndOrd")
{
// se il record è chiusura PODL chiamo chiusura... DOPO aver cercato
// PODL --> ODL
if (FullPodlUrl)
{
TryClosePODL(sLog2send.IdxPodl);
//tryClosePODL(sLog2send.IdxPodl, sLog2send.DtEve, DateTime.Now);
}
else
{
TryClosePODL(0);
}
}
}
}
// aggiorno idx inviato...
currSignLogSent.Note = $"Updated from {currSignLogRead.LastIdx}";
currSignLogSent.LastUpdate = adesso;
currSignLogSent.LastIdx = currSignLogRead.LastIdx;
#if false
var lastRec = data2send.LastOrDefault();
if (lastRec != null)
{
// salvo in B_input ultimo valore letto...
B_input = lastRec.ValInt;
}
#endif
fatto = true;
}
// alla fine aggiorno i dati inviati!
dbProxy.SyncStateUpsert(currSignLogSent);
return fatto;
}
/// <summary>
/// Esegue processing + invio dati tab SignLog
/// </summary>
/// <param name="adesso"></param>
/// <returns></returns>
private bool processSignLogTable(DateTime adesso)
{
bool fatto = false;
string tabNameIn = "SignLog";
string tabNameOut = "SignLogToMes";
// cerco info sui SignLog (ToMes e letti)
var currSignLogRead = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameIn);
// se nullo inizializzo
if (currSignLogRead == null)
{
currSignLogRead = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabNameIn,
Note = "Init"
};
}
var currSignLogSent = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameOut);
// se nullo inizializzo
if (currSignLogSent == null)
{
currSignLogSent = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabNameOut,
Note = "Init"
};
}
// verifica se ci siano dati da trasmettere (sui valori LastIdx)
if (currSignLogRead.LastIdx > currSignLogSent.LastIdx)
{
// recupero i dati dal DB...
List<MachSigLogModel> data2send = dbProxy.MachSigLogGetNew(currSignLogSent.LastIdx);
// se ho dati preparo invio
if (data2send != null && data2send.Count > 0)
{
foreach (var sLog2send in data2send)
{
string currVal = getEncodSigLog(sLog2send.DtEve, sLog2send.ValInt, counterSigIN);
// verifico non sia in veto invio iniziale...
if (queueInEnabCurr)
{
// --> accodo (valore già formattato)!
QueueIN.Enqueue(currVal);
// loggo!
lgTrace(string.Format("[QUEUE-IN] {0}", currVal));
counterSigIN++;
if (counterSigIN > 9999)
{
counterSigIN = 0;
}
}
else
{
lgDebug($"[VETO FOR QUEUE-IN] | {currVal} - MESSAGE NOT SENT | {adesso:yyyyMMdd_HHmmss}");
checkVetoQueueIn();
}
}
}
// aggiorno idx inviato...
currSignLogSent.Note = $"Updated from {currSignLogRead.LastIdx}";
currSignLogSent.LastUpdate = adesso;
currSignLogSent.LastIdx = currSignLogRead.LastIdx;
var lastRec = data2send.LastOrDefault();
if (lastRec != null)
{
// salvo in B_input ultimo valore letto...
B_input = lastRec.ValInt;
}
fatto = true;
}
// alla fine aggiorno i dati inviati!
dbProxy.SyncStateUpsert(currSignLogSent);
return fatto;
}
#endregion Private Methods
}
}