diff --git a/MP.Data/Controllers/MpIocController.cs b/MP.Data/Controllers/MpIocController.cs index 4a9cf45a..76753997 100644 --- a/MP.Data/Controllers/MpIocController.cs +++ b/MP.Data/Controllers/MpIocController.cs @@ -372,6 +372,60 @@ namespace MP.Data.Controllers return await dbCtx.SaveChangesAsync() > 0; } + + + /// + /// Aggiunta record MicroStato + EventList + /// + /// + /// + /// + public async Task EvListMicroStatoInsertAsync(MicroStatoMacchinaModel newRecMsm, EventListModel newRecEv) + { + // eseguo in transazione... + await using var dbCtx = new MoonProContext(_configuration); + await using var tx = await dbCtx.Database.BeginTransactionAsync(); + + try + { + // inizio con record microstato... + var actRec = await dbCtx + .DbSetMicroStatoMacc + .FindAsync(newRecMsm.IdxMacchina); + //.SingleOrDefaultAsync(); + if (actRec == null) + { + dbCtx.DbSetMicroStatoMacc.Add(newRecMsm); + } + else + { + actRec.IdxMicroStato = newRecMsm.IdxMicroStato; + actRec.InizioStato = newRecMsm.InizioStato; + actRec.Value = newRecMsm.Value; + + // Update() allega l'entità e segna tutti i campi come Modified + dbCtx.DbSetMicroStatoMacc.Update(actRec); +#if false + dbCtx.Entry(actRec).State = EntityState.Modified; +#endif + } + + // ora record EVList + dbCtx.DbSetEvList.Add(newRecEv); + + // EF Core 8 raggruppa automaticamente INSERT/UPDATE in un batch + var rowsAffected = await dbCtx.SaveChangesAsync(); + await tx.CommitAsync(); + + return rowsAffected > 0; + } + catch + { + await tx.RollbackAsync(); + throw; + } + } + /// /// Chiamata x stored recupero FluxLog x macchina (first) /// diff --git a/MP.IOC/Controllers/BenchController.cs b/MP.IOC/Controllers/BenchController.cs index e8b5d3c8..357ee5cb 100644 --- a/MP.IOC/Controllers/BenchController.cs +++ b/MP.IOC/Controllers/BenchController.cs @@ -131,7 +131,7 @@ namespace MP.IOC.Controllers } // recupero singolo valore (stringa) x chiave - outVal = DService.ValoreSMI(idxFamIn, idxMicroStato, valIOB); + outVal = await DService.ValoreSmiAsync(idxFamIn, idxMicroStato, valIOB); // mostro output answ += $"idxFamIN: {idxFamIn} | idxMS: {idxMicroStato} | valIOB: {valIOB} | out: {outVal}"; } diff --git a/MP.IOC/Data/MpDataService.cs b/MP.IOC/Data/MpDataService.cs index ee1733c8..d41e1cff 100644 --- a/MP.IOC/Data/MpDataService.cs +++ b/MP.IOC/Data/MpDataService.cs @@ -156,9 +156,9 @@ namespace MP.IOC.Data try { Dictionary savedTask = mSavedTaskMacchina(idxMacchina); - // cerco valore saved + // cerco valOut saved string savedVal = savedTask[taskKey.ToString()]; - // se ho un valore != "" --> rimetto in coda di invio... + // se ho un valOut != "" --> rimetto in coda di invio... if (!string.IsNullOrEmpty(savedVal) && (savedVal != taskVal)) { // leggo tName attuali... @@ -342,8 +342,8 @@ namespace MP.IOC.Data /// Idx macchina /// area memoria /// indice memoria - /// valore status - /// valore decodificato + /// valOut status + /// valOut decodificato /// public async Task AlarmInsertAsync(DateTime dtRif, string idxMacchina, string memAddress, int memIndex, int statusVal, string valDecoded) { @@ -449,7 +449,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = Utils.redisArtByDossier; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -497,7 +497,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisArtList}:Last:{idxMacc}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -535,7 +535,7 @@ namespace MP.IOC.Data string readType = "DB"; string sKey = string.IsNullOrEmpty(searchVal) ? "***" : searchVal; string currKey = $"{Utils.redisArtList}:{azienda}:{sKey}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -947,7 +947,7 @@ namespace MP.IOC.Data /// controlla se da il segnale di "microstato" deriva un evento da generare - modalità OFFLINE /// /// idx macchina - /// valore ingresso + /// valOut ingresso /// data-ora evento (server) /// sequenza dati inviati /// dati macchina in cache (opzionale, se null fa lookup) @@ -960,8 +960,11 @@ namespace MP.IOC.Data // processing inputComandoMapo answ = new inputComandoMapo(); - // verifico se esista la macchina altrimenti la creo... REDIS compliant - await verificaIdxMacchinaAsync(idxMacchina); + // SE no trovassi verifico se esista la macchina altrimenti la creo... REDIS compliant + if (!datiMacc.ContainsKey(idxMacchina)) + { + await verificaIdxMacchinaAsync(idxMacchina); + } // continuo processing... string CodArticolo = datiMacc["CodArticolo"]; @@ -980,34 +983,31 @@ namespace MP.IOC.Data int next_idxMS = 0; try { - valINT = int.Parse(valore, System.Globalization.NumberStyles.HexNumber); - try + valINT = int.Parse(valore, NumberStyles.HexNumber); + int idxFamIn = Convert.ToInt32(datiMacc["IdxFamIn"]); + int idxMicroStato = Convert.ToInt32(datiMacc["IdxMicroStato"]); + int valIOB = Convert.ToInt32(valINT); + next_idxMS = idxMicroStato; +#if false + // verifico esistenza tab SMI... + var fiHASH = Utils.GetHashSMI(idxFamIn); + bool trovato = await RedisKeyPresentAsync(fiHASH); + if (!trovato) { - int idxFamIn = Convert.ToInt32(datiMacc["IdxFamIn"]); - int idxMicroStato = Convert.ToInt32(datiMacc["IdxMicroStato"]); - int valIOB = Convert.ToInt32(valINT); - next_idxMS = idxMicroStato; - // verifico esistenza tab SMI... - var fiHASH = Utils.GetHashSMI(idxFamIn); - bool trovato = await RedisKeyPresentAsync(fiHASH); - if (!trovato) - { - // ricarico tabella (salvando in redis x ricerca successiva)! - KeyValuePair[] valori = await StateMachInByKeyAsync(idxFamIn); - } - string todoSMI = ""; - // recupero singolo valore (stringa) x chiave - todoSMI = valoreSMI(idxFamIn, idxMicroStato, valIOB); - // solo se ho trovato un risultato nella tab SMI della famiglia macchina... - if (todoSMI != "") - { - // splitto e salvo valori OUT... - string[] valori = todoSMI.Split('_'); - idxTipoEv = Convert.ToInt32(valori[0]); - next_idxMS = Convert.ToInt32(valori[1]); - } + // ricarico tabella (salvando in redis x ricerca successiva)! + KeyValuePair[] valori = await StateMachInByKeyAsync(idxFamIn); + } +#endif + // recupero singolo valOut (stringa) x chiave + string todoSMI = await ValoreSmiAsync(idxFamIn, idxMicroStato, valIOB); + // solo se ho trovato un risultato nella tab SMI della famiglia macchina... + if (!string.IsNullOrEmpty(todoSMI) && todoSMI.Contains("_")) + { + // splitto e salvo valori OUT... + string[] valori = todoSMI.Split('_'); + idxTipoEv = Convert.ToInt32(valori[0]); + next_idxMS = Convert.ToInt32(valori[1]); } - catch { } } catch (Exception exc) { @@ -1015,38 +1015,35 @@ namespace MP.IOC.Data } // effettuo update vari SU DB!!! - MicroStatoMacchinaModel newRec = new MicroStatoMacchinaModel() + MicroStatoMacchinaModel newRecMsm = new MicroStatoMacchinaModel() { IdxMacchina = idxMacchina, IdxMicroStato = next_idxMS, InizioStato = dtEve, Value = valore }; - await IocDbController.MicroStatoMacchinaUpsertAsync(newRec); if (idxTipoEv > 0) { - try + // preparo record + string valEsteso = string.Format("[{0}] {1}", contatore.PadLeft(3, '0'), valore); + // creo evento + EventListModel newRecEv = new EventListModel() { - // preparo record - string valEsteso = string.Format("[{0}] {1}", contatore.PadLeft(3, '0'), valore); - // creo evento - EventListModel newRecEv = new EventListModel() - { - CodArticolo = CodArticolo, - IdxMacchina = idxMacchina, - IdxTipo = idxTipoEv, - InizioStato = dtEve, - MatrOpr = 0, - pallet = "-", - Value = valEsteso - }; - // salva e processa - answ = await scriviRigaEventoAsync(newRecEv); - } - catch (Exception exc) - { - Log.Error($"[ChkMiSt_8a] - Eccezione:{Environment.NewLine}{exc}"); - } + CodArticolo = CodArticolo, + IdxMacchina = idxMacchina, + IdxTipo = idxTipoEv, + InizioStato = dtEve, + MatrOpr = 0, + pallet = "-", + Value = valEsteso + }; + // salva e processa evento + microstato + answ = await scriviRigaEventoAsync(newRecMsm, newRecEv); + } + else + { + // solo microstato + await IocDbController.MicroStatoMacchinaUpsertAsync(newRecMsm); } return answ; } @@ -1112,7 +1109,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisBaseAddr}:TabDatiMacchine:ALL"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -1141,7 +1138,7 @@ namespace MP.IOC.Data List result = new(); string tag = string.IsNullOrEmpty(codArt) ? "ALL" : codArt; string currKey = $"{Utils.redisDecNumArtKey}:{tag}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = await redisDb.StringGetAsync(currKey); if (rawData.HasValue) { @@ -1234,7 +1231,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisDossByMac}:{IdxMacchina}:{CodArticolo}:{DtStart:yyyyMMddHHmm}:{DtEnd:yyyyMMddHHmm}:{MaxRec}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = await redisDb.StringGetAsync(currKey); if (rawData.HasValue) { @@ -1293,7 +1290,7 @@ namespace MP.IOC.Data } /// - /// Update valore dossier + /// Update valOut dossier /// /// /// @@ -1325,7 +1322,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisAnagGruppi}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -1443,7 +1440,7 @@ namespace MP.IOC.Data DateTime dtEnd = dtFrom; // max 10 dossier alla volta (se non configurato diversamente) int maxAdd = 1; - string confVal = await tryGetConfig("IO_numDossMaxCreate"); + string confVal = await tryGetConfigAsync("IO_numDossMaxCreate"); if (!string.IsNullOrEmpty(confVal)) { int.TryParse(confVal, out maxAdd); @@ -1536,7 +1533,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisFluxLogFilt}:{IdxMacchina}:{CodFlux}:{MaxRec}:{DtMax:yyyyMMddHHmm}:{DtMin:yyyyMMddHHmm}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -1550,7 +1547,7 @@ namespace MP.IOC.Data rawData = JsonConvert.SerializeObject(result); if (string.IsNullOrEmpty(canCacheParametri)) { - canCacheParametri = await tryGetConfig("SPEC_ParametriEnableRedisCache"); + canCacheParametri = await tryGetConfigAsync("SPEC_ParametriEnableRedisCache"); } if (canCacheParametri != "false") { @@ -1568,7 +1565,7 @@ namespace MP.IOC.Data } /// - /// Restituisce il valore dell'ODL corrente (ODL deve esserci per gestione contapezzi, senza + /// Restituisce il valOut dell'ODL corrente (ODL deve esserci per gestione contapezzi, senza /// ODL NO invio/gestione ODL) /// /// @@ -1577,7 +1574,7 @@ namespace MP.IOC.Data { string result = ""; string currKey = $"{Utils.redisOdlCurrByMac}:{idxMacchina}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -1594,7 +1591,7 @@ namespace MP.IOC.Data } /// - /// Restituisce il valore dell'ODL corrente (ODL deve esserci per gestione contapezzi, senza + /// Restituisce il valOut dell'ODL corrente (ODL deve esserci per gestione contapezzi, senza /// ODL NO invio/gestione ODL) /// /// @@ -1620,7 +1617,7 @@ namespace MP.IOC.Data } /// - /// Restituisce il valore dell'ultimo ODL + /// Restituisce il valOut dell'ultimo ODL /// /// /// @@ -1628,7 +1625,7 @@ namespace MP.IOC.Data { ODLExpModel result = new ODLExpModel(); string currKey = $"{Utils.redisOdlLastByMac}:{idxMacchina}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -1720,7 +1717,7 @@ namespace MP.IOC.Data } /// - /// Restituisce il valore booleano se la macchina sia abilitata all'input + /// Restituisce il valOut booleano se la macchina sia abilitata all'input /// /// /// @@ -1740,7 +1737,7 @@ namespace MP.IOC.Data } /// - /// Restituisce il valore booleano se la macchina sia master + /// Restituisce il valOut booleano se la macchina sia master /// /// /// @@ -1765,7 +1762,7 @@ namespace MP.IOC.Data } /// - /// Restituisce il valore booleano se la macchina sia abilitata all'inserimento COMPLETO nel + /// Restituisce il valOut booleano se la macchina sia abilitata all'inserimento COMPLETO nel /// Signal Log /// /// @@ -1807,7 +1804,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisGiacenzaList}:{IdxOdl}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -1865,7 +1862,7 @@ namespace MP.IOC.Data { List? result = new List(); string currKey = $"{Utils.redisBaseAddr}:M2STab"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = await redisDb.StringGetAsync(currKey); if (rawData.HasValue) { @@ -1908,7 +1905,7 @@ namespace MP.IOC.Data string readType = "DB"; string keyGrp = codGruppo != "*" ? codGruppo : "ALL"; string currKey = $"{Utils.redisMacList}:{keyGrp}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -1944,7 +1941,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisMacRecipePath}:{idxMacchina}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -1979,7 +1976,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisMacRecipeConf}:{idxMacchina}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -2015,7 +2012,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisMacByFlux}:{dtStart:yyyyMMddHHmm}:{dtEnd:yyyyMMddHHmm}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -2151,11 +2148,11 @@ namespace MP.IOC.Data Log.Info($"upsertCurrObjItems | idxMacchina: {idxMacchina} | {innovations.Count} innovations"); // leggo i valori attuali... List actValues = await MachineParamListAsync(idxMacchina); - // per ogni valore passatomi faccio insert o update rispetto elenco valori correnti + // per ogni valOut passatomi faccio insert o update rispetto elenco valori correnti // in REDIS foreach (var item in actValues) { - // cerco nelle innovazioni SE CI SIA il valore... + // cerco nelle innovazioni SE CI SIA il valOut... var trovato = innovations.Find(obj => obj.uid == item.uid); // se non trovato nelle innovazioni... if (trovato == null) @@ -2391,7 +2388,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = Utils.redisOdlByBatch; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -2474,7 +2471,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisOdlList}:Current:{IdxMacchina}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = await redisDb.StringGetAsync(currKey); if (rawData.HasValue) { @@ -2521,7 +2518,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisOdlCurrByMac}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -2586,7 +2583,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisOdlList}:{inCorso}:{codArt}:{keyRichPart}:{Reparto}:{IdxMacchina}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -2622,7 +2619,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisFluxByMac}:{IdxMacchina}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -2688,7 +2685,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisPOdlByPOdl}:{idxPODL}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -2768,7 +2765,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisPOdlByOdl}:{idxODL}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -2818,7 +2815,7 @@ namespace MP.IOC.Data stopWatch.Start(); string readType = "DB"; string currKey = $"{Utils.redisPOdlList}:{codGruppo}:{idxMacchina}:{keyRichPart}:{lanciato}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}"; - // cerco in redis dato valore sel macchina... + // cerco in redis dato valOut sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { @@ -2881,7 +2878,7 @@ namespace MP.IOC.Data if (string.IsNullOrEmpty(idxMacchina) || string.IsNullOrEmpty(valore)) { - string errore = "processFluxLog | Errore: parametri macchina/valore vuoti"; + string errore = "processFluxLog | Errore: parametri macchina/valOut vuoti"; Log.Error(errore); answ = errore; } @@ -2896,7 +2893,7 @@ namespace MP.IOC.Data Cnt = contatore }; await IocDbController.FluxLogInsertAsync(newRec); - // 2022.06.06 salvo su redis il valore ULTIMO del flux x recupero rapido ultimo valore + // 2022.06.06 salvo su redis il valOut ULTIMO del flux x recupero rapido ultimo valOut var currKey = Utils.RedKeyLastFLog(idxMacchina, flux, MpIoNS); // 10 min cache max... await redisDb.StringSetAsync(currKey, valore, TimeSpan.FromMinutes(10)); @@ -2907,6 +2904,89 @@ namespace MP.IOC.Data return answ; } + /// + /// Validazione preliminare valori input + /// + /// + /// + /// + /// + /// + private bool ValidateinputParams(string idxMacchina, string valore, string dtEve, string dtCurr) + { + bool isValid = false; + // preparo stringa valori correnti + string currVals = $"idxMacchina: {idxMacchina} | valOut: {valore} | dtEve: {dtEve} | dtCurr:{dtCurr}"; + if (dtEve == null || dtCurr == null) + { + Log.Warn($"procInput: null found | {currVals}"); + } + else if (dtEve.Length < 17 || dtCurr.Length < 17) + { + Log.Info($"procInput: invalid data | {currVals}"); + } + else if (string.IsNullOrEmpty(idxMacchina)) + { + Log.Info($"procInput: missing IdxMacchina | {currVals}"); + } + else if (string.IsNullOrEmpty(valore)) + { + Log.Info($"procInput: missing valOut | {currVals}"); + } + else + { + isValid = true; + } + return isValid; + } + + /// + /// Calcola dataora evento da info dt ricevute + /// + /// + /// + /// + private DateTime ParseEventTime(string dtEve, string dtCurr) + { + DateTime dataOraEvento = DateTime.Now; + + // fix formato dataora in ingresso + string stdEve = dtFormStd(dtEve); + string stdCurr = dtFormStd(dtCurr); + + // 2. Se le stringhe normalizzate coincidono, skip dei calcoli + if (stdEve != stdCurr) + { + // 3. Parsing sicuro + if (DateTime.TryParseExact(stdEve, dtFormat, ciProvider, DateTimeStyles.None, out DateTime dtEvento) && + DateTime.TryParseExact(stdCurr, dtFormat, ciProvider, DateTimeStyles.None, out DateTime dtCorrente)) + { + TimeSpan diff = dtCorrente - dtEvento; + double ms = Math.Abs(diff.TotalMilliseconds); + + // 4. Classificazione delta con switch expression (più leggibile e performante) + string deltaClass = ms switch + { + <= 10 => "0-10 ms", + <= 100 => "10-100 ms", + <= 1000 => "100-1000 ms", + <= 10000 => "1-10 sec", + _ => "> 10 sec" + }; + + Log.Debug($"Correzione delta {deltaClass}"); + + // 5. Correzione data/ora server: sottrao lo scarto calcolato + dataOraEvento = dataOraEvento.Subtract(diff); + } + else + { + Log.Error($"Errore parsing date: {stdEve} | {stdCurr}"); + } + } + return dataOraEvento; + } + /// /// Processa input da IOB eventualmente registrando i segnali inviati /// @@ -2919,118 +2999,55 @@ namespace MP.IOC.Data public async Task ProcessInputAsync(string idxMacchina, string valore, string dtEve, string dtCurr, string contatore) { string answ = ""; - // 2018.10.26 controllo dtEve e dtCurr - if (dtEve == null || dtCurr == null) + if (ValidateinputParams(idxMacchina, valore, dtEve, dtCurr)) { - Log.Warn(string.Format("procInput: valori nulli date: idxMacchina: {0} | valore: {1} | dtEve: {2} | dtCurr:{3}", idxMacchina, valore, dtEve, dtCurr)); - } - else if (dtEve.Length < 17 || dtCurr.Length < 17) - { - Log.Info(string.Format("procInput: valori data non corretti: idxMacchina: {0} | valore: {1} | dtEve: {2} | dtCurr:{3}", idxMacchina, valore, dtEve, dtCurr)); - } - else - { - DateTime dataOraEvento = DateTime.Now; + DateTime dataOraEvento = ParseEventTime(dtEve, dtCurr); - // fix formato dataora in ingresso - string stdEve = dtFormStd(dtEve); - string stdCurr = dtFormStd(dtCurr); - - // 2. Se le stringhe normalizzate coincidono, skip dei calcoli - if (stdEve != stdCurr) + // se abilitato registro evento sul DB + if (await IobSLogEnabAsync(idxMacchina)) { - // 3. Parsing sicuro - if (DateTime.TryParseExact(stdEve, dtFormat, ciProvider, DateTimeStyles.None, out DateTime dtEvento) && - DateTime.TryParseExact(stdCurr, dtFormat, ciProvider, DateTimeStyles.None, out DateTime dtCorrente)) + int cntVal = 0; + int.TryParse(contatore, out cntVal); + await saveSigLogAsync(idxMacchina, valore, dataOraEvento, cntVal); + } + // continuo col resto + try + { + // scrivo keep alive!!! (se necessario, altrimenti è in cache...) + await ScriviKeepAliveAsync(idxMacchina, DateTime.Now); + // Cache dati macchina per evitare lookup ridondanti + Dictionary datiMacc = await mDatiMacchineAsync(idxMacchina); + // verifico se sia una macchina MULTI.... + if (isMulti(idxMacchina, datiMacc)) { - TimeSpan diff = dtCorrente - dtEvento; - double ms = Math.Abs(diff.TotalMilliseconds); - - // 4. Classificazione delta con switch expression (più leggibile e performante) - string deltaClass = ms switch + // inizio preprocessing + string newVal = ""; + // processo OGNI macchina a stati dell'impianto... (KEY:IdxMacchina / IdxMacchina#qualcosa, Val = IdxFamIn) + foreach (var item in await mTabMSMIAsync(idxMacchina)) { - <= 10 => "0-10 ms", - <= 100 => "10-100 ms", - <= 1000 => "100-1000 ms", - <= 10000 => "1-10 sec", - _ => "> 10 sec" - }; - - Log.Debug($"Correzione delta {deltaClass}"); - - // 5. Correzione data/ora server: sottrao lo scarto calcolato - dataOraEvento = dataOraEvento.Subtract(diff); + newVal = preProcInput(item.Key, valore, datiMacc); + // ora processo e salvo il valOut del microstato... + // INTERNAMENTE gestisce i casi DB/REDIS secondo necessità + await CheckMicroStatoAsync(item.Key, newVal, dataOraEvento, contatore, datiMacc); + } } else { - Log.Error($"Errore parsing date: {stdEve} | {stdCurr}"); + // ora processo e salvo il valOut del microstato... INTERNAMENTE + // gestisce i casi DB/REDIS secondo necessità + await CheckMicroStatoAsync(idxMacchina, valore, dataOraEvento, contatore, datiMacc); } + // forzo RESET dati macchina... + await ResetDatiMacchinaAsync(idxMacchina); + // registro in risposta che è andato tutto bene... + answ = "OK"; } - // inizio processing vero e proprio INPUT... - if (string.IsNullOrEmpty(idxMacchina)) + catch (Exception exc) { - string errore = "Errore: missing IdxMacchina"; - Log.Warn(errore); + string errore = $"Errore: {Environment.NewLine}{exc}"; + Log.Error(errore); answ = errore; } - else - { - if (string.IsNullOrEmpty(valore)) - { - string errore = "Errore: missing valore"; - Log.Warn(errore); - answ = errore; - } - else - { - // se abilitato registro evento sul DB - if (await IobSLogEnabAsync(idxMacchina)) - { - int cntVal = 0; - int.TryParse(contatore, out cntVal); - await saveSigLogAsync(idxMacchina, valore, dataOraEvento, cntVal); - } - // continuo col resto - try - { - // scrivo keep alive!!! (se necessario, altrimenti è in cache...) - await ScriviKeepAliveAsync(idxMacchina, DateTime.Now); - // Cache dati macchina per evitare lookup ridondanti - Dictionary datiMacc = await mDatiMacchineAsync(idxMacchina); - // verifico se sia una macchina MULTI ed in tal caso calcolo i - // SUB-systems e CHIAMERO' alla fine pure loro.... - if (isMulti(idxMacchina, datiMacc)) - { - // inizio preprocessing - string newVal = ""; - // processo OGNI macchina a stati dell'impianto... (KEY:IdxMacchina / IdxMacchina#qualcosa, Val = IdxFamIn) - foreach (var item in await mTabMSMIAsync(idxMacchina)) - { - newVal = preProcInput(item.Key, valore, datiMacc); - // ora processo e salvo il valore del microstato... - // INTERNAMENTE gestisce i casi DB/REDIS secondo necessità - await CheckMicroStatoAsync(item.Key, newVal, dataOraEvento, contatore, datiMacc); - } - } - else - { - // ora processo e salvo il valore del microstato... INTERNAMENTE - // gestisce i casi DB/REDIS secondo necessità - await CheckMicroStatoAsync(idxMacchina, valore, dataOraEvento, contatore, datiMacc); - } - // forzo RESET dati macchina... - await ResetDatiMacchinaAsync(idxMacchina); - // registro in risposta che è andato tutto bene... - answ = "OK"; - } - catch (Exception exc) - { - string errore = $"Errore: {Environment.NewLine}{exc}"; - Log.Error(errore); - answ = errore; - } - } - } } return answ; } @@ -3040,7 +3057,7 @@ namespace MP.IOC.Data /// /// Macchina /// Flusso: DI/RC/RC - /// valore = note/valString + /// valOut = note/valString /// data evento /// data corrente /// contatore invio @@ -3386,9 +3403,9 @@ namespace MP.IOC.Data return result; } - public string RedisGetHashField(RedisKey key, string hashField) + public async Task RedisGetHashFieldAsync(RedisKey key, string hashField) { - return redisDb.HashGet(key, hashField).ToString(); + return await redisDb.HashGetAsync(key, hashField); } public bool RedisKeyPresent(RedisKey key) @@ -3456,7 +3473,7 @@ namespace MP.IOC.Data doClean = true; } - string confVal = await tryGetConfig("IO_NumReboot2Keep"); + string confVal = await tryGetConfigAsync("IO_NumReboot2Keep"); int num2keep = int.TryParse(confVal, out int n) ? n : 5; // insert del record + pulizia bool fatto = await IocDbController.RemRebootLogAddAndCleanAsync(newRec, doClean, num2keep); @@ -3591,7 +3608,7 @@ namespace MP.IOC.Data i++; } // verifico il timeout (default 60 sec...) - var sTOutSmi = await tryGetConfig("TmOut.MSMI"); + var sTOutSmi = await tryGetConfigAsync("TmOut.MSMI"); int tOut = 60; int.TryParse(sTOutSmi, out tOut); tOut = tOut <= 60 ? 60 : tOut; @@ -3822,7 +3839,7 @@ namespace MP.IOC.Data /// salva il segnale di "microstato" (segnale) ASYNC /// /// idx macchina - /// valore ingresso + /// valOut ingresso /// data-ora evento (server) /// contatore sequenza dati inviati /// @@ -3847,16 +3864,14 @@ namespace MP.IOC.Data /// public async Task ScriviKeepAliveAsync(string IdxMacchina, DateTime oraMacchina) { - string nomeVar = string.Format("KeepAlive:{0}", IdxMacchina); // cerco se ho keep alive in redis, - DateTime adesso = DateTime.Now; - var currKey = Utils.RedKeyHash(nomeVar); + var currKey = Utils.RedKeyHash($"KeepAlive:{IdxMacchina}"); bool keyPresent = await RedisKeyPresentAsync(currKey); // se NON presente salvo in REDIS con TTL 10 sec e sul DB... if (!keyPresent) { - await redisDb.StringSetAsync(currKey, adesso.ToString("s"), TimeSpan.FromSeconds(20)); - Log.Trace($"Scrittura keep alive! IdxMacchina: {IdxMacchina}"); + DateTime adesso = DateTime.Now; + await redisDb.StringSetAsync(currKey, adesso.ToString("s"), TimeSpan.FromSeconds(30)); // effettuo scrittura sul DB await IocDbController.KeepAliveUpsertAsync(IdxMacchina, DateTime.Now, oraMacchina); } @@ -3906,7 +3921,7 @@ namespace MP.IOC.Data { // hard coded dimensione vettore DatiMacchine KeyValuePair[] answ = new KeyValuePair[1]; - // iniziualizzo con un valore... 0/0 + // iniziualizzo con un valOut... 0/0 answ[0] = new KeyValuePair("0", "0"); // ORA recupero da memoria redis... try @@ -3936,9 +3951,9 @@ namespace MP.IOC.Data } /// - /// restituisce il valore da REDIS associato al tag richeisto + /// restituisce il valOut da REDIS associato al tag richeisto /// - /// Chiave in cui cercare il valore + /// Chiave in cui cercare il valOut /// public string TagConfGetKey(string redKey) { @@ -4005,7 +4020,7 @@ namespace MP.IOC.Data fluxLogList.Add(editFL); } - // serializzo nuovamente valore + // serializzo nuovamente valOut DossierFluxLogDTO? result = new DossierFluxLogDTO(); var ODLflux = result.ODL.ToList(); foreach (var item in fluxLogList) @@ -4038,10 +4053,10 @@ namespace MP.IOC.Data Log.Info($"upsertCurrObjItems | idxMacchina: {idxMacchina} | {innovations.Count} innovations"); // leggo i valori attuali... List actValues = MachineParamList(idxMacchina); - // per ogni valore passatomi faccio insert o update rispetto elenco valori correnti in REDIS + // per ogni valOut passatomi faccio insert o update rispetto elenco valori correnti in REDIS foreach (var item in actValues) { - // cerco nelle innovazioni SE CI SIA il valore... + // cerco nelle innovazioni SE CI SIA il valOut... var trovato = innovations.Find(obj => obj.uid == item.uid); // se non trovato nelle innovazioni... if (trovato == null) @@ -4074,11 +4089,23 @@ namespace MP.IOC.Data /// /// /// - public string ValoreSMI(int idxFamIn, int idxMicroStato, int valoreIn) + public async Task ValoreSmiAsync(int idxFamIn, int idxMicroStato, int valoreIn) { + string valOut = ""; var currHash = Utils.GetHashSMI(idxFamIn); string field = $"{idxMicroStato}_{valoreIn}"; - return RedisGetHashField(currHash, field); + var searchVal = await RedisGetHashFieldAsync(currHash, field); + if (!searchVal.HasValue) + { + // ricarico tabella (salvando in redis x ricerca successiva)! + var valori = await StateMachInByKeyAsync(idxFamIn); + if (valori.Any(x => x.Key == field)) + { + searchVal = valori.FirstOrDefault(x => x.Key == field).Value; + } + } + valOut = $"{searchVal}"; + return valOut; } /// @@ -4378,7 +4405,7 @@ namespace MP.IOC.Data } /// - /// Helper standardizzazione valore dataora ricevuto da IOB remoti + /// Helper standardizzazione valOut dataora ricevuto da IOB remoti /// /// /// @@ -4467,7 +4494,7 @@ namespace MP.IOC.Data } /// - /// Restituisce il valore booleano se la macchina sia di tipo MULTI (con più state machine x INGRESSI) + /// Restituisce il valOut booleano se la macchina sia di tipo MULTI (con più state machine x INGRESSI) /// usando dati macchina in cache per evitare lookup ridondanti /// /// @@ -4543,7 +4570,7 @@ namespace MP.IOC.Data } /// - /// Calcola l'effettivo valore da passare alla macchina a stati INGRESSI usando dati macchina in cache + /// Calcola l'effettivo valOut da passare alla macchina a stati INGRESSI usando dati macchina in cache /// /// /// @@ -4568,7 +4595,7 @@ namespace MP.IOC.Data // non usato (x ora) int.TryParse(datiMacc.ContainsKey("NumBit") ? datiMacc["NumBit"] : "0", out NumBit); - // recupero valore + // recupero valOut valINT = int.Parse(valore, NumberStyles.HexNumber); // filtro newValInt = MP.Core.Utils.bMaskInt(valINT, BitFilt); @@ -4626,7 +4653,8 @@ namespace MP.IOC.Data double numSecCache = redisLongTimeCache; // converto in formato dizionario... if (dbResult != null) - { // salvo 1:1 i valori... STATO + { + // salvo 1:1 i valori... STATO result.Add("IdxMicroStato", $"{dbResult.IdxMicroStato}"); result.Add("IdxStato", $"{dbResult.IdxStato}"); result.Add("CodArticolo", $"{dbResult.CodArticolo}"); @@ -4653,9 +4681,14 @@ namespace MP.IOC.Data result.Add("IdxFamMacc", $"{dbResult.IdxFamiglia}"); result.Add("simplePallet", $"{dbResult.SimplePallet}"); result.Add("palletChange", $"{dbResult.PalletChange}"); - // durata cache in secondi dal valore insEnabled... + // durata cache in secondi dal valOut insEnabled... //double numSecCache = ((result["insEnabled"].ToLower() == "true") ? redisShortTimeCache : redisLongTimeCache); numSecCache = dbResult.InsEnabled ? redisShortTimeCache : redisLongTimeCache; + } + else + { + + } // dati master/slave string isMaster = (await ListMasterAsync()).Contains(idxMacc) ? "1" : "0"; @@ -4708,8 +4741,8 @@ namespace MP.IOC.Data i++; } // verifico il timeout (default 60 sec...) - var sTOutSmi = tryGetConfig("TmOut.SMI").Result; - int tOut = 60; + int tOut = 300; + var sTOutSmi = await tryGetConfigAsync("TmOut.SMI"); if (!string.IsNullOrEmpty(sTOutSmi)) { int.TryParse(sTOutSmi, out tOut); @@ -4773,6 +4806,35 @@ namespace MP.IOC.Data return answ; } + /// + /// Scrive una riga evento + una riga microstato insieme, ed effettua verifica necessità cambio stato + /// + /// Record MicroStatoMacchina + /// record EventList + /// + private async Task scriviRigaEventoAsync(MicroStatoMacchinaModel newRecMsm, EventListModel newRecEv) + { + bool inserito = false; + try + { + // inserisco evento + inserito = await IocDbController.EvListMicroStatoInsertAsync(newRecMsm, newRecEv); + // faccio controllo per eventuale cambio stato da tab transizioni... + await CheckCambiaStatoBatchAsync(tipoInputEvento.hw, newRecEv.IdxMacchina, newRecEv.InizioStato ?? DateTime.Now, newRecEv.IdxTipo, newRecEv.CodArticolo, newRecEv.Value, newRecEv.MatrOpr, newRecEv.pallet); + } + catch (Exception exc) + { + Log.Error($"Errore in scriviRigaEvento | IdxMacchina {newRecEv.IdxMacchina} | IdxTipo {newRecEv.IdxTipo} | codArticolo {newRecEv.CodArticolo} | Value {newRecEv.Value} | MatrOpr {newRecEv.MatrOpr} | Pallet {newRecEv.pallet} | dTime {newRecEv.InizioStato}{Environment.NewLine}{exc}"); + } + // formatto output + inputComandoMapo answ = new inputComandoMapo(); + answ.outValue = inserito.ToString(); + answ.needStatusRefresh = true; + return answ; + } + + + /// /// Scrive una riga di evento manuale (barcode) nel db + check cambio stato DiarioDiBordo /// @@ -4833,11 +4895,11 @@ namespace MP.IOC.Data } /// - /// Restituisce valore della stringa (SE disponibile) + /// Restituisce valOut della stringa (SE disponibile) /// /// /// - private async Task tryGetConfig(string keyName) + private async Task tryGetConfigAsync(string keyName) { string answ = ""; // preselezione valori @@ -4850,8 +4912,9 @@ namespace MP.IOC.Data return answ; } +#if false /// - /// Restituisce il valore SPECIFICATO per la state machine ingressi + /// Restituisce il valOut SPECIFICATO per la state machine ingressi /// value: iTipoEv_nState (IdxTipoEv da trasmettere + New MICRO-STATE) /// /// @@ -4862,8 +4925,9 @@ namespace MP.IOC.Data { var currHash = Utils.GetHashSMI(idxFamIn); string field = string.Format("{0}_{1}", idxMicroStato, valoreIn); - return RedisGetHashField(currHash, field); - } + return RedisGetHashFieldAsync(currHash, field); + } +#endif /// /// cerca codice in anagrafica macchine ed eventualmente inserisce nuova macchina