From 7a4d5cbe2f2f827c5d1edd4feb6dbd1796bdac1f Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Wed, 15 Apr 2026 19:19:06 +0200 Subject: [PATCH] Aggiunta metodo EvListJson --- MP.Core/DTO/EvJsonPayloadDto.cs | 11 ++ MP.IOC/Controllers/IOBController.cs | 161 +++++++++++++++++++- MP.IOC/Data/MpDataService.cs | 218 +++++++++++++++++++--------- 3 files changed, 317 insertions(+), 73 deletions(-) create mode 100644 MP.Core/DTO/EvJsonPayloadDto.cs diff --git a/MP.Core/DTO/EvJsonPayloadDto.cs b/MP.Core/DTO/EvJsonPayloadDto.cs new file mode 100644 index 00000000..a4d7e8fd --- /dev/null +++ b/MP.Core/DTO/EvJsonPayloadDto.cs @@ -0,0 +1,11 @@ +namespace MP.Core.DTO +{ + public class EvJsonPayloadDto + { + #region Public Properties + + public List eventList { get; set; } = new(); + + #endregion Public Properties + } +} diff --git a/MP.IOC/Controllers/IOBController.cs b/MP.IOC/Controllers/IOBController.cs index a38edc05..4f523be7 100644 --- a/MP.IOC/Controllers/IOBController.cs +++ b/MP.IOC/Controllers/IOBController.cs @@ -103,6 +103,122 @@ namespace MP.IOC.Controllers } } + + /// + /// Processa una chiamata POST per l'invio di un array Json di oggetti input (EVENTI) + /// POST: IOB/evListJson/SIMUL_03 + /// + /// ID dell'IOB + /// + [HttpPost("evListJson/{id}")] + public async Task EvListJson(string id, [FromBody] string content = "") + { + if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID"); + string answ = "-"; + try + { + // se ho dati... + if (content != "") + { + answ = await processEvListJsonAsync(id, content); + } + else + { + Log.Error($"Errore in EvListJson - no content"); + return StatusCode(StatusCodes.Status500InternalServerError, "NO"); + } + } + catch (Exception exc) + { + Log.Error($"Errore in EvListJson{Environment.NewLine}{exc}"); + return StatusCode(StatusCodes.Status500InternalServerError, "NO"); + } + return Ok(answ); + } + +#if false + /// + /// Sistema Dossier/Snapshot giornalieri x impianto indicato, andando a generare 1 Dossier + /// giornaliero x ogni giornata dall'ultimo registrato alla data corrente + /// es: http://url_site/MP/IO/IOB/fixDailyDossier/SIMUL_03 + /// + /// + /// + public string fixDailyDossier(string id) + { + string answ = ""; + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il + // carattere "|" che poi trasformiamo ora in "#" + id = id.Replace("|", "#"); + // effettuo processing + try + { + DataLayer DataLayerObj = new DataLayer(); + // verifico se si possa processare, ovvero tab ConfFlux x macchina sia valorizzata... + var confDataMach = DataLayerObj.confFluxMach(id); + if (confDataMach.Count > 0) + { + // determino ultima data da processare (inizio oggi, a mezzanotte) + DateTime dtTo = DateTime.Today; + DateTime dtFrom = dtTo; + // determino data di partenza, prima da dossier esistenti + var listaDoss = DataLayerObj.dossierLastByMach(id); + if (listaDoss.Count > 0) + { + // primo giorno DOPO ultima registrazione + dtFrom = listaDoss.OrderByDescending(x => x).FirstOrDefault().AddDays(1); + } + else + { + // ...o da fluxLog acquisiti... + var listaFL = DataLayerObj.fluxLogFirstByMach(id); + if (listaFL.Count > 0) + { + // giorno successivo a prima registrazione + dtFrom = listaFL.OrderBy(x => x).FirstOrDefault().AddDays(1); + } + } + string caller = $"takeFlogSnapshot({id})"; + DateTime dtStart = dtFrom.Date; + DateTime dtEnd = dtFrom; + // max 10 dossier alla volta (se non configurato diversamente) + int maxAdd = memLayer.ML.CRI("IO_numDossMaxCreate"); + if (dtStart < dtTo) + { + // verifico di avere almeno 1 dossier da produrre ciclo fino ad esaurire le + // date da processare + while (dtStart < dtTo && maxAdd > 0) + { + // sistemo end + dtEnd = dtStart.AddDays(1); + // effettuo chiamata registrazione snapshot! + answ = doSaveFLSnapshot(id, dtStart, dtEnd, caller); + // incremento START... + dtStart = dtEnd; + // riduco il numero di chiamate ammesse x singolo task + maxAdd--; + } + // reset cache dossier... + DataLayerObj.dossierLastByMachReset(id); + } + else + { + answ = "NO more to add"; + } + } + else + { + answ = "NO ConfFluxData"; + } + } + catch (Exception exc) + { + logger.lg.scriviLog($"Eccezione in recupero fixDailyDossier{Environment.NewLine}{exc}", tipoLog.EXCEPTION); + } + return answ; + } +#endif + /// /// Sistema ODL giornalieri x impianto indicato, andando a generare 1 ODL giornaliero x ogni /// giornata dall'ultimo ODL aperto alla data corrente @@ -609,12 +725,12 @@ namespace MP.IOC.Controllers DateTime dataOraEvento = DateTime.Now; try { - answ = DService.processInput(id, valore, dtEve, dtCurr, cnt); + answ = await DService.ProcessInputAsync(id, valore, dtEve, dtCurr, cnt); return Ok(answ); } catch (Exception exc) { - Log.Error($"Errore in processInput{Environment.NewLine}{exc}"); + Log.Error($"Errore in ProcessInputAsync{Environment.NewLine}{exc}"); return StatusCode(StatusCodes.Status500InternalServerError, "NO"); } } @@ -1233,6 +1349,47 @@ namespace MP.IOC.Controllers return answ; } + /// + /// Processing effettivo EvListJson + /// + /// + /// + /// + private async Task processEvListJsonAsync(string idxMacc, string content) + { + string answ = ""; + int insDone = 0; + + // procedo a deserializzare in blocco l'oggetto... + EvJsonPayloadDto receivedData = JsonConvert.DeserializeObject(content) ?? new(); + + // se ho qualcosa da processare... + if (receivedData != null) + { + // per ogni valore --> processo! + try + { + foreach (var item in receivedData.eventList) + { + // formato datetime come yyyyMMddHHmmssfff -->es: 20181223180600000 + answ = await DService.ProcessInputAsync(idxMacc, item.valore, item.dtEve.ToString("yyyyMMddHHmmssfff"), item.dtCurr.ToString("yyyyMMddHHmmssfff"), item.cnt.ToString()); + insDone++; + } + // se vuoto --> OK! + if (string.IsNullOrEmpty(answ)) + { + answ = $"OK {insDone} processed"; + } + } + catch (Exception exc) + { + Log.Error($"Errore in fase invio valori inputJson{Environment.NewLine}{exc}"); + answ = "NO"; + } + } + return answ; + } + /// /// Effettua processing UserLog /// diff --git a/MP.IOC/Data/MpDataService.cs b/MP.IOC/Data/MpDataService.cs index 2fd83ab9..aa67aa96 100644 --- a/MP.IOC/Data/MpDataService.cs +++ b/MP.IOC/Data/MpDataService.cs @@ -264,66 +264,6 @@ namespace MP.IOC.Data return answ; } - /// - /// Restituisce l'anagrafica STATI per intero - /// - /// - public async Task> AnagStatiGetAllAsync() - { - List dbResult = new List(); - // cerco in redis... - var currKey = Utils.redisAnagStati; - RedisValue rawData = await redisDb.StringGetAsync(currKey); - if (rawData.HasValue) - { - dbResult = JsonConvert.DeserializeObject>($"{rawData}") ?? new(); - } - else - { - dbResult = await IocDbController.AnagStatiGetAllAsync(); - // serializzo e salvo... - rawData = JsonConvert.SerializeObject(dbResult); - await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache)); - } - return dbResult; - } - /// - /// MS max age x dato MSE - /// - private int maxAge = 2000; - /// - /// Elenco da tabella MappaStatoExplModel - /// - /// - public async Task> MseGetAllAsync(bool forceDb = false) - { - Stopwatch sw = new Stopwatch(); - string source = "DB"; - sw.Start(); - List? result = new List(); - // cerco in _redisConn... - RedisValue rawData = await redisDb.StringGetAsync(Constants.redisMseKey); - if (rawData.HasValue && !forceDb) - { - result = JsonConvert.DeserializeObject>($"{rawData}") ?? new(); - source = "REDIS"; - } - else - { - result = await IocDbController.MseGetAllAsync(maxAge); - // serializzp e salvo... - rawData = JsonConvert.SerializeObject(result); - await redisDb.StringSetAsync(Constants.redisMseKey, rawData, getRandTOut(redisShortTimeCache / 2)); - } - if (result == null) - { - result = new List(); - } - sw.Stop(); - Log.Debug($"MseGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms"); - return result; - } - public async Task> AnagStatiComm() { Stopwatch stopWatch = new Stopwatch(); @@ -355,6 +295,30 @@ namespace MP.IOC.Data return result; } + /// + /// Restituisce l'anagrafica STATI per intero + /// + /// + public async Task> AnagStatiGetAllAsync() + { + List dbResult = new List(); + // cerco in redis... + var currKey = Utils.redisAnagStati; + RedisValue rawData = await redisDb.StringGetAsync(currKey); + if (rawData.HasValue) + { + dbResult = JsonConvert.DeserializeObject>($"{rawData}") ?? new(); + } + else + { + dbResult = await IocDbController.AnagStatiGetAllAsync(); + // serializzo e salvo... + rawData = JsonConvert.SerializeObject(dbResult); + await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache)); + } + return dbResult; + } + public async Task> AnagTipoArtLV() { Stopwatch stopWatch = new Stopwatch(); @@ -562,10 +526,10 @@ namespace MP.IOC.Data /// data-ora evento (server) /// sequenza dati inviati /// - public inputComandoMapo checkMicroStato(string idxMacchina, string valore, DateTime dtEve, string contatore) + public async Task CheckMicroStatoAsync(string idxMacchina, string valore, DateTime dtEve, string contatore) { // recupero SE IMPIEGATO REDIS i valori del Dictionary della macchina... - Dictionary datiMacc = mDatiMacchine(idxMacchina); + Dictionary datiMacc = await mDatiMacchineAsync(idxMacchina); // processing inputComandoMapo answ = new inputComandoMapo(); @@ -577,7 +541,7 @@ namespace MP.IOC.Data string CodArticolo = datiMacc["CodArticolo"]; if (string.IsNullOrEmpty(CodArticolo)) { - var allDatiMacch = IocDbController.DatiMacchineGetAll(); + var allDatiMacch = await IocDbController.DatiMacchineGetAllAsync(); var recMacc = allDatiMacch.FirstOrDefault(x => x.IdxMacchina == idxMacchina); if (recMacc != null) { @@ -632,7 +596,7 @@ namespace MP.IOC.Data InizioStato = dtEve, Value = valore }; - IocDbController.MicroStatoMacchinaUpsert(newRec); + await IocDbController.MicroStatoMacchinaUpsertAsync(newRec); if (idxTipoEv > 0) { try @@ -651,7 +615,7 @@ namespace MP.IOC.Data Value = valEsteso }; // salva e processa - answ = scriviRigaEvento(newRecEv); + answ = await scriviRigaEventoAsync(newRecEv); //currTask = scriviRigaEvento(idxMacchina, idxTipoEv, codArticolo, valEsteso, 0, "-", dtEve, DateTime.Now); // forzo RESET dati macchina... ResetDatiMacchina(idxMacchina); @@ -1679,6 +1643,33 @@ namespace MP.IOC.Data return answ; } + /// + /// Restitusice elenco KVP dei campi DatiMacchine + StatoMacchine per l'impianto indicato + /// + /// + /// + public async Task> mDatiMacchineAsync(string idxMacchina) + { + // hard coded dimensione vettore DatiMacchine + Dictionary answ = new Dictionary(); + // ORA recupero da memoria redis... + try + { + var currHash = Utils.RedKeyDatiMacc(idxMacchina, MpIoNS); + answ = await RedisGetHashDictAsync(currHash); + // se è vuoto... leggo da DB e popolo! + if (answ.Count == 0) + { + answ = ResetDatiMacchina(idxMacchina); + } + } + catch (Exception exc) + { + Log.Error($"Errore in compilazione dati Macchine x Redis - idxMacchina {idxMacchina}:{Environment.NewLine}{exc}"); + } + return answ; + } + /// /// Restitusice elenco KVP dei TASK (da passare a IOB-WIN) per l'impianto indicato /// @@ -1723,6 +1714,39 @@ namespace MP.IOC.Data return answ; } + /// + /// Elenco da tabella MappaStatoExplModel + /// + /// + public async Task> MseGetAllAsync(bool forceDb = false) + { + Stopwatch sw = new Stopwatch(); + string source = "DB"; + sw.Start(); + List? result = new List(); + // cerco in _redisConn... + RedisValue rawData = await redisDb.StringGetAsync(Constants.redisMseKey); + if (rawData.HasValue && !forceDb) + { + result = JsonConvert.DeserializeObject>($"{rawData}") ?? new(); + source = "REDIS"; + } + else + { + result = await IocDbController.MseGetAllAsync(maxAge); + // serializzp e salvo... + rawData = JsonConvert.SerializeObject(result); + await redisDb.StringSetAsync(Constants.redisMseKey, rawData, getRandTOut(redisShortTimeCache / 2)); + } + if (result == null) + { + result = new List(); + } + sw.Stop(); + Log.Debug($"MseGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms"); + return result; + } + /// /// Restitusice elenco KVP /// currKey: IdxMacchina @@ -2315,7 +2339,7 @@ namespace MP.IOC.Data /// /// /// - public string processInput(string idxMacchina, string valore, string dtEve, string dtCurr, string contatore) + public async Task ProcessInputAsync(string idxMacchina, string valore, string dtEve, string dtCurr, string contatore) { string answ = ""; // 2018.10.26 controllo dtEve e dtCurr @@ -2346,7 +2370,7 @@ namespace MP.IOC.Data // se ho meno decimali x evento rispetto dtCorrente... if (dtEve.Length < dtCurr.Length) { - Log.Info($"processInput: fix valore dtEve: {dtEve} | dtCurr: {dtCurr}"); + Log.Info($"ProcessInputAsync: fix valore dtEve: {dtEve} | dtCurr: {dtCurr}"); dtEve = dtEve.PadRight(dtCurr.Length, '0'); } delta = Convert.ToInt64(dtCurr) - Convert.ToInt64(dtEve); @@ -2403,13 +2427,13 @@ namespace MP.IOC.Data { int cntVal = 0; int.TryParse(contatore, out cntVal); - saveSigLog(idxMacchina, valore, dataOraEvento, cntVal); + await saveSigLogAsync(idxMacchina, valore, dataOraEvento, cntVal); } // continuo col resto try { // scrivo keep alive!!! (se necessario, altrimenti è in cache...) - ScriviKeepAlive(idxMacchina, DateTime.Now); + await ScriviKeepAliveAsync(idxMacchina, DateTime.Now); // verifico se sia una macchina MULTI ed in tal caso calcolo i // SUB-systems e CHIAMERO' alla fine pure loro.... if (isMulti(idxMacchina)) @@ -2422,14 +2446,14 @@ namespace MP.IOC.Data newVal = preProcInput(item.Key, valore); // ora processo e salvo il valore del microstato... // INTERNAMENTE gestisce i casi DB/REDIS secondo necessità - checkMicroStato(item.Key, newVal, dataOraEvento, contatore); + await CheckMicroStatoAsync(item.Key, newVal, dataOraEvento, contatore); } } else { // ora processo e salvo il valore del microstato... INTERNAMENTE // gestisce i casi DB/REDIS secondo necessità - checkMicroStato(idxMacchina, valore, dataOraEvento, contatore); + await CheckMicroStatoAsync(idxMacchina, valore, dataOraEvento, contatore); } // registro in risposta che è andato tutto bene... answ = "OK"; @@ -3349,6 +3373,27 @@ namespace MP.IOC.Data return IocDbController.SignalLogInsert(newRec); } + /// + /// salva il segnale di "microstato" (segnale) ASYNC + /// + /// idx macchina + /// valore ingresso + /// data-ora evento (server) + /// contatore sequenza dati inviati + /// + public async Task saveSigLogAsync(string idxMacchina, string valore, DateTime dtEve, int contatore) + { + SignalLogModel newRec = new SignalLogModel() + { + IdxMacchina = idxMacchina, + DtCurr = DateTime.Now, + DtEve = dtEve, + Contatore = contatore, + Valore = valore + }; + return await IocDbController.SignalLogInsertAsync(newRec); + } + /// /// scrive un evento di keepalive sulla tabella /// @@ -3733,6 +3778,11 @@ namespace MP.IOC.Data private static Logger Log = LogManager.GetCurrentClassLogger(); + /// + /// MS max age x dato MSE + /// + private int maxAge = 2000; + private string MpIoNS = ""; /// @@ -4072,6 +4122,32 @@ namespace MP.IOC.Data return answ; } + /// + /// Scrive una riga di evento nel db + check cambio stato DiarioDiBordo + /// + /// codice macchina + /// + private async Task scriviRigaEventoAsync(EventListModel newRec) + { + bool inserito = false; + try + { + // inserisco evento + inserito = await IocDbController.EvListInsertAsync(newRec); + // faccio controllo per eventuale cambio stato da tab transizioni... + checkCambiaStatoBatch(tipoInputEvento.hw, newRec.IdxMacchina, newRec.InizioStato ?? DateTime.Now, newRec.IdxTipo, newRec.CodArticolo, newRec.Value, newRec.MatrOpr, newRec.pallet); + } + catch (Exception exc) + { + Log.Error($"Errore in scriviRigaEvento | IdxMacchina {newRec.IdxMacchina} | IdxTipo {newRec.IdxTipo} | codArticolo {newRec.CodArticolo} | Value {newRec.Value} | MatrOpr {newRec.MatrOpr} | Pallet {newRec.pallet} | dTime {newRec.InizioStato}{Environment.NewLine}{exc}"); + } + // formatto output + inputComandoMapo answ = new inputComandoMapo(); + answ.outValue = inserito.ToString(); + answ.needStatusRefresh = true; + return answ; + } + /// /// Restituisce il valore booleano se la macchina sia abilitata all'inserimento COMPLETO nel /// Signal Log