From 0a8a0f0eb2946dcc6ad07d21be1fa50379d72966 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 10 Nov 2022 11:10:38 +0100 Subject: [PATCH] Sviluppo ForceStartPOdl --- MP-IO/Controllers/IOBController.cs | 42 +++- MapoDb/DataLayer.cs | 366 ++++++++++++++++++++++++++++- 2 files changed, 389 insertions(+), 19 deletions(-) diff --git a/MP-IO/Controllers/IOBController.cs b/MP-IO/Controllers/IOBController.cs index e785e576..89f31f7f 100644 --- a/MP-IO/Controllers/IOBController.cs +++ b/MP-IO/Controllers/IOBController.cs @@ -320,13 +320,33 @@ namespace MP_IO.Controllers /// Esito chiamata (OK/vuoto) public string forceSplitOdl(string id) { - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" id = id.Replace("|", "#"); DataLayer DataLayerObj = new DataLayer(); return DataLayerObj.AutoStartOdl(id, true, true, 100, ""); } + /// + /// Avvia PODL indicato + /// - se esistesse un ODL da altro PODL --> chiude + /// - se fosse già in essere ODL collegato --> lascia aperto + /// - se fosse chiuso ODL collegato --> duplica PODL e poi avvia nuovo ODL. + /// + /// GET: IOB/forceStartPOdl/SIMUL_03?idxPODL=123 + /// + /// + /// idx del PDL da avviare + /// Esito chiamata (OK/vuoto) + public string forceStartPOdl(string id,int idxPODL) + { + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il + // carattere "|" che poi trasformiamo ora in "#" + id = id.Replace("|", "#"); + DataLayer DataLayerObj = new DataLayer(); + return DataLayerObj.ForceStartPOdl(id, idxPODL, true); + } + /// Chiude ODL precedente ed avvia uno nuovo (duplicandolo e sitemando quantità /// RIMANENTE), e CONFERMA produzione... /// @@ -338,7 +358,7 @@ namespace MP_IO.Controllers /// Esito chiamata (OK/vuoto) public string forceSplitOdlFull(string id, bool doConfirm, bool qtyFromLast, int? roundStep, string keyRichiesta = "") { - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" id = id.Replace("|", "#"); DataLayer DataLayerObj = new DataLayer(); @@ -452,7 +472,7 @@ namespace MP_IO.Controllers /// Json contenente la riga di stato macchina public string getCurrData(string id) { - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" id = id.Replace("|", "#"); string answ = ""; @@ -531,7 +551,7 @@ namespace MP_IO.Controllers /// public string getCurrOdlRow(string id) { - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" id = id.Replace("|", "#"); string answ = ""; @@ -558,7 +578,7 @@ namespace MP_IO.Controllers /// public string getCurrOdlStart(string id) { - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" id = id.Replace("|", "#"); DateTime answ = new DateTime(DateTime.Now.Year - 1, 12, 31); @@ -589,7 +609,7 @@ namespace MP_IO.Controllers /// Json contenente lista oggetti PODL serializzati public string getCurrPODL(string id) { - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" if (!string.IsNullOrEmpty(id)) { @@ -618,7 +638,7 @@ namespace MP_IO.Controllers /// public string getCurrStatoRow(string id) { - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" id = id.Replace("|", "#"); string answ = ""; @@ -686,7 +706,7 @@ namespace MP_IO.Controllers /// public int getIdlePeriod(string id) { - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" id = id.Replace("|", "#"); int answ = 0; @@ -774,7 +794,7 @@ namespace MP_IO.Controllers public string getLastDossArt(string id) { string answ = ""; - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" if (string.IsNullOrEmpty(id)) { @@ -809,7 +829,7 @@ namespace MP_IO.Controllers public string getLastDossByMacc(string id) { string answ = ""; - // attenzione! poiché nell'URL il carattere "#" fiene filtrato ci aspettiamo il + // attenzione! poiché nell'URL il carattere "#" viene filtrato ci aspettiamo il // carattere "|" che poi trasformiamo ora in "#" if (string.IsNullOrEmpty(id)) { @@ -1283,7 +1303,7 @@ namespace MP_IO.Controllers } else { - logger.lg.scriviLog($"remTask2Exe: impossibile riconoscere il comando {taskName} come uno deitipi ammessi, NON aggiunto", tipoLog.ERROR); + logger.lg.scriviLog($"remTask2Exe: impossibile riconoscere il comando {taskName} come uno dei tipi ammessi, NON rimosso", tipoLog.ERROR); } answ = getTask2Exe(id); } diff --git a/MapoDb/DataLayer.cs b/MapoDb/DataLayer.cs index 9a68ad22..77d66bfa 100644 --- a/MapoDb/DataLayer.cs +++ b/MapoDb/DataLayer.cs @@ -1,12 +1,12 @@ using MapoSDK; using Newtonsoft.Json; -using PdfSharp.Charting; using SteamWare; using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Globalization; +using System.Linq; using System.Reflection; using System.Threading; @@ -297,7 +297,16 @@ namespace MapoDb /// public static string currOdlRowHash(string idxMacchina) { - return mHash(string.Format("CurrOdlRow:{0}", idxMacchina)); + return mHash($"CurrOdlRow:{idxMacchina}"); + } + /// + /// Hash dati PODL ROW x la macchina specificata + /// + /// + /// + public static string getPOdlRowHash(int idxPODL) + { + return mHash($"PODLRow:{idxPODL}"); } /// @@ -485,6 +494,15 @@ namespace MapoDb { return mHash($"VetoOdlMacc:{idxMacchina}"); } + /// + /// Hash x VETO force Start PODL la macchina specificata + /// + /// + /// + public static string vetoForceStartPOdlMaccHash(string idxMacchina) + { + return mHash($"VetoStartPOdlMacc:{idxMacchina}"); + } /// /// verifica se sia da reinviare un task alla macchina dall'elenco di quelli salvati (in @@ -1209,17 +1227,18 @@ namespace MapoDb { DS_ProdTempi.ODLDataTable answTab = null; string rCall = ""; + string rKey = currOdlRowHash(idxMacchina); if (memLayer.ML.CRB("IOB_RedEnab")) { saveCallRec("hasODL_row"); try { - rCall = memLayer.ML.getRSV(currOdlRowHash(idxMacchina)); + rCall = memLayer.ML.getRSV(rKey); if (!string.IsNullOrEmpty(rCall)) { answTab = JsonConvert.DeserializeObject(rCall); } - else + if (answTab == null || answTab.Count == 0) { // istanzio un NUOVO oggetto lettura MapoDb connDb = new MapoDb(); @@ -1227,7 +1246,7 @@ namespace MapoDb // salvo in redis... rCall = JsonConvert.SerializeObject(answTab); int currOdlRowCacheDur = memLayer.ML.CRI("currOdlRowCacheDur"); - memLayer.ML.setRSV(currOdlRowHash(idxMacchina), rCall, currOdlRowCacheDur); + memLayer.ML.setRSV(rKey, rCall, currOdlRowCacheDur); } } catch (Exception exc) @@ -1244,6 +1263,48 @@ namespace MapoDb return answTab; } + /// + /// Restituisce il valore dell'intera RIGA PODL data chiave (da redis o da DB se non trovata...) + /// + /// IDX della PODL + /// + public DS_ProdTempi.PromesseODLDataTable getPODLRowTab(int idxPODL) + { + DS_ProdTempi.PromesseODLDataTable answTab = null; + string rCall = ""; + string rKey = getPOdlRowHash(idxPODL); + if (memLayer.ML.CRB("IOB_RedEnab")) + { + saveCallRec("hasPODL_row"); + try + { + rCall = memLayer.ML.getRSV(rKey); + if (!string.IsNullOrEmpty(rCall)) + { + answTab = JsonConvert.DeserializeObject(rCall); + } + if (answTab == null || answTab.Count == 0) + { + answTab = taPODL.getByKey(idxPODL); + + // salvo in redis... + rCall = JsonConvert.SerializeObject(answTab); + int currOdlRowCacheDur = memLayer.ML.CRI("currOdlRowCacheDur"); + memLayer.ML.setRSV(rKey, rCall, currOdlRowCacheDur); + } + } + catch (Exception exc) + { + logger.lg.scriviLog($"Eccezione in recupero getPODLRowTab{Environment.NewLine}{exc}", tipoLog.EXCEPTION); + } + } + else + { + answTab = taPODL.getByKey(idxPODL); + } + return answTab; + } + /// /// Restituisce il valore dell'intera RIGA stato macchina corrente (da redis o da DB se non trovata...) /// @@ -1388,6 +1449,295 @@ namespace MapoDb return fatto; } + /// Effettua force Start PODL + /// [CurrProdStatus = 1] se fosse già in essere ODL collegato --> lascia aperto + /// [CurrProdStatus = 2] se esistesse un ODL da altro PODL x diverso articolo/fase --> chiude vecchio x poi far partire nuovo + /// [CurrProdStatus = 3] se fosse chiuso ODL collegato --> duplica PODL e poi avvia nuovo ODL + /// [CurrProdStatus = 4] se fosse già in essere ODL non collegato --> lascia aperto e collego + /// + /// macchina IDX PODL da + /// processare effettuare conferma qty STEP x cui + /// arrotondare la quantità prox ODL (corrente come riferimento) + public string ForceStartPOdl(string idxMacchina, int idxPODL, bool doConfirm) + { + string answ = "KO"; + int CurrProdStatus = 0; + string sIdxODL = ""; + string redKey = vetoForceStartPOdlMaccHash(idxMacchina); + // verifico NON CI SIA un veto a NUOVI split... 30 sec di default... + string rawData = memLayer.ML.getRSV(redKey); + if (string.IsNullOrEmpty(rawData)) + { + // registro VETO x altri split... 30sec + memLayer.ML.setRSV(redKey, $"Inizio FORCE START PODL: {idxPODL} | {DateTime.Now}", 30); + // calcolo la qta da gestire + int qtyConf = 0; + if (doConfirm) + { + DS_ProdTempi.stp_PzProd_getByMacchinaRow rigaProd; + try + { + rigaProd = taPzProd2conf.GetData(idxMacchina)[0]; + qtyConf = rigaProd.pezziNonConfermati; + } + catch (Exception exc) + { + logger.lg.scriviLog($"Errore recupero pezzi da confermare per la macchina {idxMacchina}{Environment.NewLine}{exc}", tipoLog.ERROR); + } + } + + // proseguo + DS_ProdTempi.ODLDataTable currODLData = null; + DS_ProdTempi.PromesseODLDataTable reqPODLData = null; + DS_ProdTempi.ODLRow OdlRow = null; + DS_ProdTempi.PromesseODLRow POdlRow = null; + // metodo principale... + try + { + sIdxODL = getCurrODL(idxMacchina); + // recupero dati da PODL + reqPODLData = getPODLRowTab(idxPODL); + // recupero ODL corrente... + currODLData = currODLRowTab(idxMacchina); + + // procedo SOLO SE ho trovato un PODL + if (reqPODLData != null && reqPODLData.Rows.Count > 0) + { + POdlRow = reqPODLData[0]; + // prima cosa: verifico se sia un PODL libero (idxODL==0) + if (reqPODLData[0].IdxODL == 0) + { + if (currODLData != null && currODLData.Count > 0) + { + OdlRow = currODLData[0]; + // verifico ODL corrente, se compatibili... + if (OdlRow.CodArticolo == POdlRow.CodArticolo && OdlRow.KeyRichiesta == POdlRow.KeyRichiesta && OdlRow.IdxMacchina == POdlRow.IdxMacchina) + { + // compatibili --> caso 4 + CurrProdStatus = 4; + } + else + { + // anche qui chiusura ed avvio + CurrProdStatus = 2; + } + } + else + { + CurrProdStatus = 2; + } + } + // altrimenti se PODL già associato + else + { + // se trovo qualcosa come ODL + if (currODLData != null && currODLData.Count > 0) + { + OdlRow = currODLData[0]; + // se è quello già associato + if ($"{reqPODLData[0].IdxODL}" == sIdxODL) + { + // non devo fare nulla + CurrProdStatus = 1; + } + else + { + // altrimenti devo clonare + CurrProdStatus = 3; + } + } + else + { + // anche in questo caso devo clonare PODL + CurrProdStatus = 3; + } + } + } + else + { + logger.lg.scriviLog($"Errore in ForceStartPOdl: non trovato PODL per idxPODL{idxPODL}", tipoLog.ERROR); + } + + // OVVIAMENTE solo se ho POdlRow... + if (POdlRow != null) + { + switch (CurrProdStatus) + { + case 1: + // --> non tocco nulla PODL/ODL + logger.lg.scriviLog($"Richiesto ForceStartPOdl, caso 1 | PODL {idxPODL} | ODL {sIdxODL} | nessun intervento", tipoLog.INFO); + break; + case 2: + // eventuale chiusura vecchio ODL + if (currODLData.Count > 0) + { + doCloseCurrODL(idxMacchina, doConfirm, qtyConf, currODLData, DateTime.Now); + } + // avvio PODL: creo nuovo ODL da promessa ed associo + taODL.inizioSetupPromessa(idxPODL, MatrOpr, POdlRow.IdxMacchina, POdlRow.TCAssegnato, POdlRow.PzPallet, POdlRow.Note); + // fix (invio ) dati ODL + Thread.Sleep(1000); + answ = saveOdlData(idxMacchina, currODLData, reqPODLData); + + break; + case 3: + // eventuale chiusura vecchio ODL + if (currODLData.Count > 0) + { + doCloseCurrODL(idxMacchina, doConfirm, qtyConf, currODLData, DateTime.Now); + } + // duplico PODL... inserisco nuovo record... + taPODL.insertQuery(POdlRow.KeyRichiesta, POdlRow.KeyBCode, true, POdlRow.CodArticolo, POdlRow.CodGruppo, POdlRow.IdxMacchina, POdlRow.NumPezzi, POdlRow.TCAssegnato, POdlRow.DueDate, POdlRow.Priorita, POdlRow.PzPallet, POdlRow.Note); + // recupero odl attivabili... + var podlList = taPODL.getByCodArt(POdlRow.CodArticolo, true); + // cerco quello che ha la stessa fase/macchina e + recente... + var lastPodl = podlList.Where(x => x.KeyRichiesta == POdlRow.KeyRichiesta && x.IdxMacchina == POdlRow.IdxMacchina).OrderByDescending(x => x.idxPromessa).FirstOrDefault(); + int newPODL = lastPodl != null ? lastPodl.idxPromessa : POdlRow.idxPromessa; + // avvio (nuovo) PODL + taODL.inizioSetupPromessa(idxPODL, MatrOpr, POdlRow.IdxMacchina, POdlRow.TCAssegnato, POdlRow.PzPallet, POdlRow.Note); + // fix (invio ) dati ODL + Thread.Sleep(1000); + answ = saveOdlData(idxMacchina, currODLData, reqPODLData); + + break; + case 4: + // --> registro in PODL e non tocco ODL + + break; + default: + break; + } + } + else + { } + } + catch (Exception exc) + { + logger.lg.scriviLog($"Eccezione in ForceStartPOdl{Environment.NewLine}{exc}", tipoLog.EXCEPTION); + } + } + else + { + logger.lg.scriviLog($"VETO ATTIVO | Richiesto ForceStartPOdl per impianto {idxMacchina} | impossibile procedere"); + } + return answ; + } + + private string saveOdlData(string idxMacchina, DS_ProdTempi.ODLDataTable currODLData, DS_ProdTempi.PromesseODLDataTable reqPODLData) + { + string answ; + // recupera ODL macchina + var newOdl = currODL(idxMacchina, true); + // attendo 1000 msec + // registro fine ODL (idxTipoEv = 1) + int idxEvento = 1; + logger.lg.scriviLog($"Invio evento ODL-forced start per macchina {idxMacchina}, evento {idxEvento}, articolo {currODLData[0].CodArticolo}", tipoLog.INFO); + var resCmd = scriviRigaEventoBarcode(idxMacchina, idxEvento, currODLData[0].CodArticolo, "ODL-FORFCE-START"); + // invio eventi setup a macchina.... + string setArtVal = $"{currODLData[0].CodArticolo}"; + string setPzCommVal = $"{reqPODLData[0].NumPezzi}"; + string setCommVal = $"ODL{newOdl}"; + if (!string.IsNullOrEmpty(reqPODLData[0].KeyRichiesta)) + { + setCommVal = $"{reqPODLData[0].KeyRichiesta} ODL{newOdl}"; + } + try + { + // invio task caricamento dati ODL + addTask4Machine(idxMacchina, taskType.setArt, setArtVal); + addTask4Machine(idxMacchina, taskType.setComm, setCommVal); + addTask4Machine(idxMacchina, taskType.setPzComm, setPzCommVal); + updateMachineParameter(idxMacchina, "setArt", setArtVal); + updateMachineParameter(idxMacchina, "setComm", setCommVal); + updateMachineParameter(idxMacchina, "setPzComm", setPzCommVal); + } + catch + { } + // chiamo refresh MSE + taMSE.forceRecalc(0, idxMacchina); + // resetto stato macchina... + memLayer.ML.redDelKey(currStatoMaccHash(idxMacchina)); + answ = "OK"; + logger.lg.scriviLog($"Effettuato reset e ricalcoli x split ODL per macchina {idxMacchina}", tipoLog.INFO); + // se è una master richiamo fix x child... + if (isMaster(idxMacchina)) + { + string ts = ""; + string outData = ""; + taODL.fixMachineSlave(idxMacchina, 30, 1); + // ciclo su ogni slave la gestione reinvio pezzi -->calcolo gli slave... + var slaveList = taM2S.getByMaster(idxMacchina); + foreach (var machine in slaveList) + { + // invio chiusura attrezzaggio + ts = string.Format("{0:yyMMdd}T{0:HHmmss.fff}Z", DateTime.Now); + outData = $"TS:{ts}|MATR:{MatrOpr}|ODL:{newOdl}"; + addTask4Machine(machine.IdxMacchinaSlave, taskType.fixStopSetup, outData); + + // invio task caricamento dati ODL + addTask4Machine(machine.IdxMacchinaSlave, taskType.setArt, setArtVal); + addTask4Machine(machine.IdxMacchinaSlave, taskType.setComm, setCommVal); + addTask4Machine(machine.IdxMacchinaSlave, taskType.setPzComm, setPzCommVal); + + updateMachineParameter(machine.IdxMacchinaSlave, "setArt", setArtVal); + updateMachineParameter(machine.IdxMacchinaSlave, "setComm", setCommVal); + updateMachineParameter(machine.IdxMacchinaSlave, "setPzComm", setPzCommVal); + } + } + + return answ; + } + + private bool doCloseCurrODL(string idxMacchina, bool doConfirm, int qtyConf, DS_ProdTempi.ODLDataTable currODLData, DateTime adesso) + { + bool fatto = false; + // registro un evento di inizio attrezzaggio (idxTipoEv = 2) + int idxEvento = 2; + logger.lg.scriviLog($"Invio evento CHIUSURA ODL per macchina {idxMacchina}, evento {idxEvento}, articolo {currODLData[0].CodArticolo}, qty confermata {qtyConf}", tipoLog.INFO); + + // chiudo ALTRO ODL + inputComandoMapo resCmd = scriviRigaEventoBarcode(idxMacchina, idxEvento, currODLData[0].CodArticolo, "ODL-CLOSE", 0, "", adesso, adesso); + if (doConfirm) + { + // attendo 100 msec + Thread.Sleep(100); + adesso = DateTime.Now; + // chiamo conferma produzione... + try + { + string chiamata = confRett ? "confermaProdMacchinaFull" : "confermaProdMacchina"; + logger.lg.scriviLog($"Chiamata a {chiamata} con parametri {currODLData[0].IdxMacchina} |{MatrOpr} | {DateTime.Now.AddDays(-10)} | {DateTime.Now} | {qtyConf} | 0 | 1 | {DateTime.Now} | false", tipoLog.INFO); + string idxMacchinaSel = currODLData[0].IdxMacchina; + if (confRett) + { + // confermo al netto dei pezzi lasciati... + fatto = confermaProdMacchinaFull(idxMacchinaSel, memLayer.ML.CRI("modoConfProd"), qtyConf, 0, 0, adesso); + } + else + { + fatto = confermaProdMacchina(idxMacchinaSel, memLayer.ML.CRI("modoConfProd"), qtyConf, 0, adesso); + } + if (!fatto) + { + logger.lg.scriviLog($"ERRORE in chiamata {chiamata}", tipoLog.ERROR); + } + } + catch (Exception exc) + { + logger.lg.scriviLog($"Eccezione in ConfermaProduzione{Environment.NewLine}{exc}", tipoLog.EXCEPTION); + } + } + // attendo 100 msec + Thread.Sleep(100); + // chiamo chiusura + MapoDbObj.taODL.forceClose(currODLData[0].IdxODL, idxMacchina); + // elimino eventuale ODL precedente... + string rKey = memLayer.ML.redHash($"ODL:{idxMacchina}"); + memLayer.ML.setRSV(rKey, ""); + fatto = true; + return fatto; + } + /// /// GET elenco parametri correnti x IOB /// @@ -1894,7 +2244,7 @@ namespace MapoDb DateTime dataOraEvento = DateTime.Now; DateTime dtEvento, dtCorrente; // controllo: se ho valori dt x evento e orario DIVERSI per acquisitore IOB calcolo - // dataOraEvento corretto + // dataOraEvento corretto if (dtEve != dtCurr) { Int64 delta = 0; @@ -1993,7 +2343,7 @@ namespace MapoDb DateTime dataOraEvento = DateTime.Now; DateTime dtEvento, dtCorrente; // controllo: se ho valori dt x evento e orario DIVERSI per acquisitore IOB calcolo - // dataOraEvento corretto + // dataOraEvento corretto if (dtEve != dtCurr) { // delta è un calcolo MOLTO approssimativo della differenza temporale... @@ -2328,7 +2678,7 @@ namespace MapoDb DateTime dataOraEvento = DateTime.Now; DateTime dtEvento, dtCorrente; // controllo: se ho valori dt x evento e orario DIVERSI per acquisitore IOB calcolo - // dataOraEvento corretto + // dataOraEvento corretto if (dtEve != dtCurr) { Int64 delta = 0;