using AppData; using Newtonsoft.Json; using NKC_SDK; using NLog; using SteamWare; using System; using System.Diagnostics; using System.IO; using System.Web.Http; namespace NKC_WF.Controllers { public class BatchProcController : ApiController { #region Public Methods /// /// Restituisce SE C'E' la richiesta di elaborazione BATCH corrente /// /// // GET: api/BatchProc [HttpGet] public batchRequest Get() { batchRequest answ = new batchRequest(); string redKey = ""; string redVal = ""; // in primis: controllo su redis SE HO una richiesta CURRENT di processing... string envNum = ComLib.currBatchReq; // se c'è carico "la busta" come ID if (!string.IsNullOrEmpty(envNum)) { // cerco in REDIS se ci sia l'item richiesto redKey = $"{ComLib.redOutPath}:{envNum}"; // leggo da redis la stringa... redVal = memLayer.ML.getRSV(redKey); // PROVO a deserializzare... try { answ = JsonConvert.DeserializeObject(redVal); // aggiungo LA SUA ENV NUM!!!! answ.EnvNum = envNum; } catch (Exception exc) { Log.Instance.Error($"EXCEPTION api/BatchProc | get{Environment.NewLine}{exc}"); } } // altrimenti NULL! else { answ = null; } return answ; } /// /// Restituisce UNA SPECIFICA richiesta di elaborazione BATCH /// /// // GET: api/BatchProc/5 [HttpGet] public batchRequest Get(int id) { batchRequest answ = new batchRequest(); // in primis: controllo su redis SE HO una richiista CURRENT di processing... string redKey = $"{ComLib.redOutPath}:{id}"; string redVal = memLayer.ML.getRSV(redKey); // se c'è carico "la busta" come ID if (!string.IsNullOrEmpty(redVal)) { // PROVO a deserializzare... try { answ = JsonConvert.DeserializeObject(redVal); } catch (Exception exc) { Log.Instance.Error($"EXCEPTION api/BatchProc | get(id) {id}{Environment.NewLine}{exc}"); } } return answ; } /// /// Processa una chiamata POST per l'invio in blocco Batch /// POST: api/BatchProc /// /// ID dell'IOB /// // POST: api/BatchProc [HttpPost] public string Post() { string logIdKey = "BatchProcController.Post()"; string answ = ""; bool resetOk = false; string envNum = ""; bool isSplitReq = false; bool isValidation = false; bool isTesting = false; // questa classe è derivata da Controller.Response... x cui recupero lo stream in altro modo... string content = ""; System.Web.HttpContext.Current.Request.InputStream.Position = 0; using (var reader = new StreamReader(System.Web.HttpContext.Current.Request.InputStream, System.Text.Encoding.UTF8, true, 4096, true)) { content = reader.ReadToEnd(); } //Rest System.Web.HttpContext.Current.Request.InputStream.Position = 0; baseNestAnsw batchProcAnsw = new baseNestAnsw(); // procedo a deserializzare in blocco l'oggetto... try { // DEBUG: salvo su redis x fare DEBUG string redKey = $"{ComLib.redNestAnsw}:LAST_CALL"; memLayer.ML.setRSV(redKey, content); Log.Instance.Info($"{logIdKey} | Post() data received | length: {content.Length}"); // deserializzo batchProcAnsw = JsonConvert.DeserializeObject(content); // per prima cosa salvo richiesta e la resetto se corrisponde... envNum = ComLib.currBatchReq; if (envNum == batchProcAnsw.EnvNum) { resetOk = ComLib.resetBatchReq(); Log.Instance.Info($"{logIdKey} | resetBatchReq() DONE | envNum: {envNum}"); } else { Log.Instance.Info($"{logIdKey} | resetBatchReq() NOT executed | envNum: {envNum} | batchProcAnsw.EnvNum: {batchProcAnsw.EnvNum}"); } // procedura idempotente: elimino eventuali dati della "busta" precedente... DLMan.taEL.deteteByParent("", batchProcAnsw.EnvNum); // se ho errori inizio a salvarli... if (batchProcAnsw.ErrorList != null && batchProcAnsw.ErrorList.Count > 0) { // ora insert foreach (var item in batchProcAnsw.ErrorList) { // salvo log errore... DLMan.taEL.insertQuery(DateTime.Now, item.ErrType, $"{batchProcAnsw.EnvNum}", $"{item.Uid}", $"{item.Description}"); } } // se non nullo... if (batchProcAnsw != null) { Log.Instance.Info($"{logIdKey} | OrderType: {batchProcAnsw.OrderType} | ProcType: {batchProcAnsw.ProcType}"); /************************************************* * IN BASE al tipo di risposta saprò se * - è BatchReq / OfflineOrder * - è stima iniziale o NEST dettagliato * - si tratta di una stima di validazione ITEM ... * * 2021.07.21: aggiunto controllo coerenza risposta: accetto solo se sono in stato di richiesta (se ho annullato in precedenza scarto la risposta) * *************************************************/ if (batchProcAnsw.OrderType == oType.BatchRequest) { Log.Instance.Info($"{logIdKey} | Processing oType.BatchRequest"); // stima "classica" if (batchProcAnsw.ProcType == 1) { Log.Instance.Info($"{logIdKey} | Processing ProcType == 1"); BatchStatus bStatus = BatchStatus.Imported; // deserializzo come BatchreqIniziale (stima) nestReplyBatchInitial rispStima = JsonConvert.DeserializeObject(content); // idempotenza: elimino errori costruiti con altri step... DLMan.taEL.deteteByParent("", $"B.{rispStima.BatchID}"); // verifica preliminare che il batch possa accettare la risposta var currBatchStatus = ComLib.BStatus(rispStima.BatchID); if (currBatchStatus != BatchStatus.EstimationRequested) { string message = $"E.BR.1 Impossibile processing della risposta da EgtNest | ProcType: {batchProcAnsw.ProcType} | BatchID: {rispStima.BatchID} | stato da DB: {currBatchStatus} | stato richiesto: {BatchStatus.EstimationRequested}"; Log.Instance.Error($"{logIdKey} | {message}"); // registro KO answ = "KO"; } else { // 2020.01.16 salvo su mongoDb la risposta... ComLib.man.saveEstAnsw(rispStima); // recupero info sul batch / KIT specifico x capire se sia di tipo "validation" var tabOrd = DLMan.taOL.getByBatch(rispStima.BatchID); if (tabOrd.Count > 0) { isValidation = tabOrd[0].OrdType == "V"; isTesting = tabOrd[0].OrdType == "T"; } // calcolo status del batch... switch (rispStima.ProcessStatus) { case procStatus.waiting: case procStatus.running: bStatus = BatchStatus.EstimationRequested; break; case procStatus.completed: if (isValidation || isTesting) { bool pdfOk = true; // se richiesto CheckPDF if (memLayer.ML.CRB("checkPdfPathTV")) { // verifico PDF, se NON OK --> errore string pdfPath = ""; foreach (var item in rispStima.PartList) { pdfOk = pdfOk && ComLib.checkPdfExistAccessible(item, out pdfPath); if (!pdfOk) { // codice è B.xxx dove xxx = BatchID DLMan.taEL.insertQuery(DateTime.Now, "Check PDF path", $"B.{rispStima.BatchID}", $"{rispStima.BatchID}.{item.PartExtCode}", $"Error: PDF file not found: {pdfPath}"); } } } // se non OK --> registro errore... if (!pdfOk) { bStatus = BatchStatus.PartKo; } // verifico se il tempo di procesisng stimato sia > minimo... else if (rispStima.EstimatedWorktime > memLayer.ML.CRI("minValidEstSec")) { bStatus = BatchStatus.PartOk; } else { bStatus = BatchStatus.PartKo; } } else { bStatus = BatchStatus.EstimationDone; } break; case procStatus.accepted: bStatus = BatchStatus.Approved; break; case procStatus.refused: if (isValidation || isTesting) { bStatus = BatchStatus.PartKo; } else { bStatus = BatchStatus.Discarded; } break; case procStatus.error: if (isValidation || isTesting) { bStatus = BatchStatus.PartKo; } else { bStatus = BatchStatus.ErrorsOnEstim; } break; case procStatus.aborted: default: if (isValidation || isTesting) { bStatus = BatchStatus.PartKo; } else { bStatus = BatchStatus.Imported; } break; } Log.Instance.Info($"{logIdKey} | Batch Status calculated | BatchID: {rispStima.BatchID} | bStatus {bStatus}"); // SALVO info riguardo al batch running DLMan.taBL.updateStatus(rispStima.BatchID, (int)bStatus, rispStima.EnvNum, (decimal)rispStima.EstimatedWorktime / 60); // salvo update elenco ITEMS ComLib.updatePartsFromNesting(rispStima.PartList); // aggiorno cadPath x items che non abbiano valorizzato... string dxfFolder = memLayer.ML.CRS("drawingFolder"); if (isTesting) { dxfFolder = memLayer.ML.CRS("fileTestFolder"); } string cadBasePath = $"{memLayer.ML.CRS("nestBasePath")}{dxfFolder}/"; DLMan.taIL.updateCadPath(cadBasePath, rispStima.BatchID, false); // verifico IN CASO di validazione andata a buon fine --> valorizzo tabella! if (bStatus > BatchStatus.Errors) { if (isValidation) { // recupero ordine da batch if (tabOrd.Count > 0) { DLMan.taIV.upsertQuery(tabOrd[0].OrderExtCode, rispStima.BatchID, (int)bStatus, rispStima.EstimatedWorktime); } } else if (isTesting) { // recupero ordine da batch if (tabOrd.Count > 0) { DLMan.taFV.upsertQuery(tabOrd[0].OrderExtCode, rispStima.BatchID, (int)bStatus, rispStima.EstimatedWorktime); } } } // registro OK answ = "OK"; } } // stima "extended" x splitting else if (batchProcAnsw.ProcType == 3) { Log.Instance.Info($"{logIdKey} | Processing ProcType == 3"); BatchStatus bStatus = BatchStatus.Imported; // deserializzo come BatchreqIniziale (stima) nestReplyBatchExtEst rispStima = JsonConvert.DeserializeObject(content); // verifica preliminare che il batch possa accettare la risposta var currBatchStatus = ComLib.BStatus(rispStima.BatchID); if (currBatchStatus != BatchStatus.EstimationRequested) { string message = $"E.BR.2 Impossibile processing della risposta da EgtNest | ProcType: {batchProcAnsw.ProcType} | BatchID: {rispStima.BatchID} | stato da DB: {currBatchStatus} | stato richiesto: {BatchStatus.EstimationRequested}"; Log.Instance.Error($"{logIdKey} | {message}"); // registro KO answ = "KO"; } else { // salvo su mongoDb la risposta... ComLib.man.saveExtEstAnsw(rispStima); // recupero info sul batch / KIT specifico x capire se sia di tipo "validation" var tabOrd = DLMan.taOL.getByBatch(rispStima.BatchID); if (tabOrd.Count > 0) { isValidation = tabOrd[0].OrdType == "V"; isTesting = tabOrd[0].OrdType == "T"; } // calcolo status del batch... switch (rispStima.ProcessStatus) { case procStatus.waiting: case procStatus.running: bStatus = BatchStatus.EstimationRequested; break; case procStatus.completed: if (isValidation || isTesting) { bool pdfOk = true; // se richiesto CheckPDF if (memLayer.ML.CRB("checkPdfPathTV")) { // verifico PDF, se NON OK --> errore string pdfPath = ""; foreach (var item in rispStima.PartList) { pdfOk = pdfOk && ComLib.checkPdfExistAccessible(item, out pdfPath); if (!pdfOk) { // codice è B.xxx dove xxx = BatchID DLMan.taEL.insertQuery(DateTime.Now, "Check PDF path", $"B.{rispStima.BatchID}", $"{rispStima.BatchID}.{item.PartExtCode}", $"Error: PDF file not found: {pdfPath}"); } } } // se non OK --> registro errore... if (!pdfOk) { bStatus = BatchStatus.PartKo; } // verifico se il tempo di procesisng stimato sia > minimo... else if (rispStima.EstimatedWorktime > memLayer.ML.CRI("minValidEstSec")) { bStatus = BatchStatus.PartOk; } else { bStatus = BatchStatus.PartKo; } } else { bStatus = BatchStatus.EstimationDone; } break; case procStatus.accepted: bStatus = BatchStatus.Approved; break; case procStatus.refused: if (isValidation || isTesting) { bStatus = BatchStatus.PartKo; } else { bStatus = BatchStatus.Discarded; } break; case procStatus.error: if (isValidation || isTesting) { bStatus = BatchStatus.PartKo; } else { bStatus = BatchStatus.ErrorsOnEstim; } break; case procStatus.aborted: default: if (isValidation || isTesting) { bStatus = BatchStatus.PartKo; } else { bStatus = BatchStatus.Imported; } break; } Log.Instance.Info($"{logIdKey} | Batch Status calculated | BatchID: {rispStima.BatchID} | bStatus {bStatus}"); // SALVO info riguardo al batch running DLMan.taBL.updateStatus(rispStima.BatchID, (int)bStatus, rispStima.EnvNum, (decimal)rispStima.EstimatedWorktime / 60); // salvo update elenco ITEMS if (rispStima.PartList != null && rispStima.PartList.Count > 0) { ComLib.updatePartsFromNesting(rispStima.PartList); } // aggiorno la risposta dei tempi di esecuzione (+ NUM cart/part) // degli ordini x permettere aggiustamenti if (rispStima.EstOrderList != null && rispStima.EstOrderList.Count > 0) { ComLib.updateExtEstimFromNesting(rispStima.BatchID, rispStima.EstOrderList); } // aggiorno cadPath x items che non abbiano valorizzato... string dxfFolder = memLayer.ML.CRS("drawingFolder"); if (isTesting) { dxfFolder = memLayer.ML.CRS("fileTestFolder"); } string cadBasePath = $"{memLayer.ML.CRS("nestBasePath")}{dxfFolder}/"; DLMan.taIL.updateCadPath(cadBasePath, rispStima.BatchID, false); // registro OK answ = "OK"; } } // nesting else if (batchProcAnsw.ProcType == 2) { Log.Instance.Info($"{logIdKey} | Processing ProcType == 2 | NESTING"); // deserializzo come BatchreqFinale nestReplyBatchFinal rispNest = JsonConvert.DeserializeObject(content); // verifica preliminare che il batch possa accettare la risposta var currBatchStatus = ComLib.BStatus(rispNest.BatchID); //if (!(currBatchStatus == BatchStatus.NestRequested || currBatchStatus == BatchStatus.NestDone)) if (currBatchStatus != BatchStatus.NestRequested) { string message = $"E.BR.3 Impossibile processing della risposta da EgtNest | ProcType: {batchProcAnsw.ProcType} | BatchID: {rispNest.BatchID} | stato da DB: {currBatchStatus} | stato richiesto: {BatchStatus.NestRequested}"; Log.Instance.Error($"{logIdKey} | {message}"); // registro KO answ = "KO"; } else { Log.Instance.Info($"{logIdKey} | Response status OK | ProcType: {batchProcAnsw.ProcType} | BatchID: {rispNest.BatchID} | stato da DB: {currBatchStatus} | stato richiesto: {BatchStatus.NestRequested}"); // 2020.01.16 salvo su mongoDb la risposta... ComLib.man.saveNestAnsw(rispNest); // calcolo status del batch... BatchStatus bStatus = BatchStatus.EstimationDone; switch (rispNest.ProcessStatus) { case procStatus.waiting: case procStatus.running: bStatus = BatchStatus.NestRequested; break; case procStatus.completed: bStatus = BatchStatus.NestDone; break; case procStatus.accepted: bStatus = BatchStatus.Approved; break; case procStatus.refused: bStatus = BatchStatus.Discarded; break; case procStatus.error: bStatus = BatchStatus.ErrorsOnNesting; break; case procStatus.aborted: default: bStatus = BatchStatus.EstimationDone; break; } Log.Instance.Info($"{logIdKey} | Batch Status calculated | BatchID: {rispNest.BatchID} | bStatus {bStatus} | stato da DB: {currBatchStatus} | rispNest.ProcessStatus: {rispNest.ProcessStatus}"); // aggiorno il resto SOLO SE status == completo... if (rispNest.ProcessStatus == procStatus.completed || rispNest.ProcessStatus == procStatus.error) { // resetto le precedenti elaborazioni: elimino dati child MA NON il batch... DLMan.taBL.resetTree(rispNest.BatchID); // SALVO info riguardo al batch completato DLMan.taBL.updateStatus(rispNest.BatchID, (int)bStatus, rispNest.EnvNum, (decimal)rispNest.EstimatedWorktime / 60); Log.Instance.Info($"{logIdKey} | BL.updateStatus | BatchID: {rispNest.BatchID} | bStatus: {bStatus} | EnvNum: {rispNest.EnvNum} | EstimatedWorktime: {rispNest.EstimatedWorktime}"); // salvo info riguardo ai vari Bunk / Sheets / Items... if (rispNest.BunkList != null && rispNest.BunkList.Count > 0) { ComLib.updateBunksFromNesting(rispNest.BatchID, rispNest.BunkList); } // salvo info x CART & BINS previsti if (rispNest.BinList != null && rispNest.BinList.Count > 0) { ComLib.updateBinsFromNesting(rispNest.BatchID, rispNest.BinList); } if (rispNest.CartList != null && rispNest.CartList.Count > 0) { ComLib.updateCartsFromNesting(rispNest.BatchID, rispNest.CartList); } // isSplitReq = ComLib.checkSendBatchSplit(rispNest.BatchID); // effettuo calcolo statistiche Stopwatch stopWatchLap = new Stopwatch(); stopWatchLap.Start(); ComLib.man.updateSheetStatsByBatch(rispNest.BatchID); stopWatchLap.Stop(); Log.Instance.Info($"{logIdKey} | Batch Stat Calculation after NEST answ | BatchID: {rispNest.BatchID} | elapsed {stopWatchLap.Elapsed.TotalMilliseconds} ms"); } // registro OK answ = "OK"; } } } else if (batchProcAnsw.OrderType == oType.OfflineOrder) { Log.Instance.Info($"{logIdKey} | Processing oType.OfflineOrder"); // deserializzo come OfflineOrder nestReplyOffOrd rispOffOrd = JsonConvert.DeserializeObject(content); // 2020.8.18 salvo su mongoDb la risposta... ComLib.man.saveOffOrdAnsw(rispOffOrd); // serisposta non nulla... if (rispOffOrd != null) { // poiché passo valore negativo scarto batch positivi.. if (rispOffOrd.BatchID > 0) { answ = "WRONG DATA (expected negative BatchID)"; } else { // verifica PRELIMINARE se fosse in stato errore... if (rispOffOrd.ProcessStatus == procStatus.error) { // status -1 --> ERRORE!!! DLMan.taOffOL.updateStatus(Math.Abs(rispOffOrd.BatchID), -1); } else { if (string.IsNullOrEmpty(rispOffOrd.DrawingPath)) { answ = "WRONG DATA (expected DrawingPath not null)"; } else { string nestBasePath = memLayer.ML.CRS("nestBasePath").ToLower(); string servBasePath = memLayer.ML.CRS("servBasePath").ToLower(); string fixPathDraw = rispOffOrd.DrawingPath.ToLower().Replace(nestBasePath, servBasePath).Replace('/', '\\'); string fixPathCnc = rispOffOrd.CncPath.ToLower().Replace(nestBasePath, servBasePath).Replace('/', '\\'); // segno offline order come processato registrando il // disegno e segno ogni PART come lavorata da OffOrd2Item // (status 992) DLMan.taOffOL.updateDrawing(Math.Abs(rispOffOrd.BatchID), fixPathDraw, fixPathCnc); } } } } // SALVO!!! answ = "OK"; } } else { answ = "WRONG DATA (expected baseNestAnsw object)"; //Log.inf Log.Instance.Error($"{logIdKey} | {answ}"); } } catch (Exception exc) { answ = "NO"; Log.Instance.Error($"{logIdKey} | EXCEPTION api/BatchProc{Environment.NewLine}{exc}"); } // se tutto OK --> tolgo ultimo batch if (answ == "OK") { Log.Instance.Info($"{logIdKey} | Close A.01 | batchProcAnsw.EnvNum {batchProcAnsw.EnvNum}"); // se è una split req --> NON resetto... if (!isSplitReq) { Log.Instance.Info($"{logIdKey} | Close B.01"); // reset resetOk = ComLib.resetBatchReq(); Log.Instance.Info($"{logIdKey} | Effettuato reset resetBatchReq"); // se tutto ok e ci sono da validare parts --> procedo! Log.Instance.Info($"{logIdKey} | Close B.02"); if (resetOk) { Log.Instance.Info($"{logIdKey} | Close B.03"); bool PartValForCreatePng = memLayer.ML.cdvb("PartValidationForceCreatePng"); bool DxfValForceCreatePng = memLayer.ML.cdvb("DxfValidationForceCreatePng"); bool forceCreatePng = (isValidation || isTesting) && (PartValForCreatePng || DxfValForceCreatePng); bool nextValidSent = ComLib.sendFirstValidationBatch(forceCreatePng); Log.Instance.Info($"{logIdKey} | Close B.04"); } } else { #if false //// rimetto in coda la richiesta SUCCESSIVA //ComLib.currBatchReq = envNum; #endif Log.Instance.Info($"{logIdKey} | Close C.01 - requeue DENIED for {envNum}"); } } Log.Instance.Info($"{logIdKey} | Close D.01"); // restituisco risposta return answ; } #endregion Public Methods #region Protected Fields /// /// oggetto static/singleton per fare chiamate sul datalayer /// protected DataLayer DLMan = new DataLayer(); #endregion Protected Fields } }