using EgwCoreLib.Lux.Core.RestPayload; using EgwCoreLib.Lux.Data.Services; using EgwMultiEngineManager.Data; using Lux.API.Services; using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System.Diagnostics; using static EgwCoreLib.Lux.Data.Services.ProdService; namespace Lux.API.Controllers { [Route("api/[controller]")] [ApiController] public class ProdController : ControllerBase { #region Public Constructors /// /// Costruttore metodo /// /// /// /// public ProdController(ProdService prodService, ExternalMessageProcessor extMessProc, CalcRuidService crService) { PService = prodService; EMProc = extMessProc; _calcRuidService = crService; } #endregion Public Constructors #region Public Methods /// /// Chiamata GET: test status alive /// GET: api/Prod/alive /// /// uid oggetto /// [HttpGet("alive")] public async Task Alive() { Stopwatch sw = new Stopwatch(); sw.Start(); await Task.Delay(1); sw.Stop(); Log.Info($"Alive | {sw.Elapsed.TotalMilliseconds:N3} ms"); return Ok("OK"); } /// /// Chiamata GET: /// - fornisce il job da eseguire dalla coda (SE presente) /// GET: api/Prod/getjob/ABC012345 /// /// [HttpGet("getjob/{uid}")] public async Task> GetJob(string uid) { var result = await PService.GetJob(uid); QuestionDTO? deserRes = JsonConvert.DeserializeObject(result); if (deserRes != null) { // verifico RUID code x rigenerarlo... if (deserRes.Args != null) { // elimino vecchio RUID se esistesse... if (deserRes.Args.ContainsKey("RUID")) { deserRes.Args.Remove("RUID"); } // lo aggiungo! string envir = $"{deserRes.ExecEnvironment}"; var mode = deserRes.Args["Mode"]; var sub = deserRes.Args["SubMode"]; string type = string.IsNullOrEmpty(sub) ? mode : $"{mode}-{sub}"; // creo registrazione richiesta... var ruid = await _calcRuidService.AddRequestAsync(envir, type, uid); // aggiungo RUID effettivo deserRes.Args.Add("RUID", ruid); } return Ok(deserRes); } else { return NotFound("No Data Found"); } } private readonly CalcRuidService _calcRuidService; /// /// Chiamata GET: /// - fornisce il primo job da eseguire dalla coda (SE presente) /// - viene registrato come "in corso" e spostato dalla coda richiesta /// GET: api/Prod/getnext /// /// [HttpGet("getnext")] public async Task> GetNext() { var result = await PService.GetNext(); QuestionDTO? deserRes = JsonConvert.DeserializeObject(result); if (deserRes != null) { // verifico RUID code x rigenerarlo... if (deserRes.Args != null) { // elimino vecchio RUID se esistesse... if (deserRes.Args.ContainsKey("RUID")) { deserRes.Args.Remove("RUID"); } // lo aggiungo! string envir = $"{deserRes.ExecEnvironment}"; var mode = deserRes.Args["Mode"]; var sub = deserRes.Args["SubMode"]; var uid = deserRes.Args["UID"]; string type = string.IsNullOrEmpty(sub) ? mode : $"{mode}-{sub}"; // creo registrazione richiesta... var ruid = await _calcRuidService.AddRequestAsync(envir, type, uid); // aggiungo RUID effettivo deserRes.Args.Add("RUID", ruid); } return Ok(deserRes); } else { return NotFound("No Data Available"); } } /// /// Ritorno calcolo /// /// Chiave auth valida (da implementare) /// /// [HttpPost("jobreturn")] public async Task JobReturn([FromBody] AnswerDTO retData) { bool saved = false; // se risposta valida --> processo! if (retData != null) { saved = await EMProc.ProcessAnswer(retData); // se in debug forzo save dell'ultima chiamata comunque... saved = false; // se non avessi salvato e non sapessi cosa fosse --> salvo comunque x debug... if (!saved && retData != null) { string UID = retData.Args["UID"]; var envir = retData.ExecEnvironment; // salvo in area generica... string rawData = JsonConvert.SerializeObject(retData); await EMProc.SaveRawData(UID, retData.ExecEnvironment, rawData); } } // ritorno return saved; } /// /// Chiamata GET: /// - fornisce il job da eseguire dalla coda (SE presente) /// GET: api/Prod/rep-answer/ABC012345 /// /// [HttpGet("rep-answer/{uid}")] public async Task> ReplayAnswer(string uid, int env) { Constants.EXECENVIRONMENTS envir = (Constants.EXECENVIRONMENTS)env; string rawData = await EMProc.LoadRawData(uid, envir); bool saved = false; // provo a riprcessare... var retData = JsonConvert.DeserializeObject(rawData); if (retData != null) { saved = await EMProc.ProcessAnswer(retData); } return Ok(rawData); } #if false /// /// Ritorno calcolo /// /// Chiave auth valida (da implementare) /// /// [HttpPost("jobreturn/{id}")] public async Task JobReturn(string id, [FromBody] AnswerDTO retData) { bool saved = false; // se risposta valida --> processo! if (retData != null) { saved = await EMProc.ProcessAnswer(retData); #if DEBUG // se in debug forzo save dell'ultima chiamata comunque... saved = false; #endif // se non avessi salvato e non sapessi cosa fosse --> salvo comunque x debug... if (!saved && retData != null) { string UID = retData.Args["UID"]; var envir = retData.ExecEnvironment; // salvo in area generica... string rawData = JsonConvert.SerializeObject(retData); await EMProc.SaveRawData(UID, retData.ExecEnvironment, rawData); } } // ritorno return saved; } [HttpPost("jobcreate/{id}")] public async Task JobCreate(string id, [FromBody] AnswerDTO retData) { bool saved = false; // se risposta valida --> processo! if (retData != null) { saved = await EMProc.ProcessAnswer(retData); // se in debug forzo save dell'ultima chiamata comunque... saved = false; // se non avessi salvato e non sapessi cosa fosse --> salvo comunque x debug... if (!saved && retData != null) { string UID = retData.Args["UID"]; var envir = retData.ExecEnvironment; // salvo in area generica... string rawData = JsonConvert.SerializeObject(retData); await EMProc.SaveRawData(UID, retData.ExecEnvironment, rawData); } } // ritorno return saved; } [HttpPost("jobestimate/{id}")] public async Task JobEstimate(string id, [FromBody] AnswerDTO retData) { bool saved = false; // se risposta valida --> processo! if (retData != null) { saved = await EMProc.ProcessAnswer(retData); // se in debug forzo save dell'ultima chiamata comunque... saved = false; // se non avessi salvato e non sapessi cosa fosse --> salvo comunque x debug... if (!saved && retData != null) { string UID = retData.Args["UID"]; var envir = retData.ExecEnvironment; // salvo in area generica... string rawData = JsonConvert.SerializeObject(retData); await EMProc.SaveRawData(UID, retData.ExecEnvironment, rawData); } } // ritorno return saved; } #endif /// /// Chiamata GET: num richieste in coda (se non specificato è waiting, altrimenti ) /// GET: api/Prod/queue-len/ /// GET: api/Prod/queue-len/waiting /// GET: api/Prod/queue-len/running /// /// uid oggetto /// [HttpGet("qlen/{type}")] public async Task QueueLen(QueueType type = QueueType.waiting) { var result = await PService.QueueLen(type); return Ok(result); } /// /// Chiamata GET: dizionario stato richieste /// GET: api/Prod/queue-status/ /// /// uid oggetto /// [HttpGet("qstatus")] public async Task QueueStatus() { Stopwatch sw = new Stopwatch(); sw.Start(); Dictionary queueStatus = new Dictionary(); long numWait = await PService.QueueLen(QueueType.waiting); long numRun = await PService.QueueLen(QueueType.running); // simulo status... queueStatus.Add("waiting", numWait); queueStatus.Add("running", numRun); sw.Stop(); Log.Info($"QueueStatus | {sw.Elapsed.TotalMilliseconds:N3} ms"); return Ok(queueStatus); } #endregion Public Methods #region Private Fields private static Logger Log = LogManager.GetCurrentClassLogger(); private ProdService PService; private ExternalMessageProcessor EMProc; #endregion Private Fields #if false [HttpPost("enqueue")] /// /// Accodamento richiesta di calcolo prod /// /// Tipo richiesta /// UID (riga ordine) /// Contenuto della richiesta come QuestionDTO /// public async Task EnqueueRequest(string reqType, string reqUid, CalcRequestDTO currRequest) { bool done = false; int nId = 1; // salvo su cache x successivo reinvio da currRequest QuestionDTO calcRequest = new QuestionDTO(nId, currRequest.EnvType, currRequest.DictExec); // salvo in cache contenuto della richiesta x UID string currKey = $"{redisOrderReqKey}:{reqUid.Replace("/", ":")}"; done = await _redisService.SetAsync(currKey, calcRequest.sProcessArgs); // accodo la nuova richiesta //RedisKey queueKey = (RedisKey)$"{redisBaseKey}:OrderQueue:{reqType}"; _redisService.QueuePush(queueKey, (RedisValue)reqUid); // dizionario richieste: è il serializzato dell'elenco degli UID da calcolare... var currList = await _redisService.QueueListAllAsync(queueKey); Dictionary calcDict = new Dictionary(); calcDict.Add("ReqLen", $"{calcDict.Count}"); string listReq = JsonConvert.SerializeObject(currList); calcDict.Add("ReqList", listReq); // preparo richiesta di calcolo x UID da inviare QuestionDTO chRequest = new QuestionDTO(nId, currRequest.EnvType, calcDict); // invio sul channel redis della richiesta di processing await _redisService.PublishAsync(chPub, chRequest.sProcessArgs); // ritorno return done; } /// /// Chiamata GET: /// - elenco delle richieste di stima da eseguire /// - vengono registrate come "passate" al calcolo alla data-ora della richiesta /// GET: api/Prod/estimation /// /// [HttpGet("estimation")] public async Task>> EstimationRequestQueue() { Stopwatch sw = new Stopwatch(); sw.Start(); var listReq = new List(); // vado a recuperare da REDIS elenco degli ordini NON ancora associati ad 1/+ prod // opzione 1: restituisco TUTTI ordini NON ancora eseguiti // opzione 2: restituisco dall'inizio solo max(n) non ancora eseguiti? (es primi 5 ordini) // genero elenco degli ordini e per ogni ordine aggiungo il Dict await Task.Delay(100); // opzione 1: per tutti gli ordini ritornato registro data-ora invio e tolgo dalla coda... // opzione 2: aspetto conferma dal sistema che li ha presi in carico e registro data-ora... sw.Stop(); Log.Info($"EstimationRequestQueue | {sw.Elapsed.TotalMilliseconds:N3} ms"); return Ok(listReq); } /// /// Chiamata GET: /// - elenco delle richieste di ottimizzazione/nesting da eseguire /// - vengono registrate come "passate" al calcolo alla data-ora della richiesta /// GET: api/Prod/estimation /// /// [HttpGet("optmization")] public async Task>> OptimitionRequestQueue() { Stopwatch sw = new Stopwatch(); sw.Start(); var listReq = new List(); // vado a recuperare da REDIS elenco degli ordini NON ancora associati ad 1/+ prod // opzione 1: restituisco TUTTI ordini NON ancora eseguiti // opzione 2: restituisco dall'inizio solo max(n) non ancora eseguiti? (es primi 5 ordini) // genero elenco degli ordini e per ogni ordine aggiungo il Dict await Task.Delay(100); // opzione 1: per tutti gli ordini ritornato registro data-ora invio e tolgo dalla coda... // opzione 2: aspetto conferma dal sistema che li ha presi in carico e registro data-ora... sw.Stop(); Log.Info($"EstimationRequestQueue | {sw.Elapsed.TotalMilliseconds:N3} ms"); return Ok(listReq); } #endif } }