Files
lux/Lux.API/Controllers/ProdController.cs
T
Samuele Locatelli 9f621c0936 Modifica tracciato x gestione prodEstimate su righe ord
review processing estimate data
2025-12-04 12:22:31 +01:00

354 lines
14 KiB
C#

using EgwCoreLib.Lux.Core.RestPayload;
using EgwCoreLib.Lux.Data.Services;
using EgwMultiEngineManager.Data;
using Lux.API.Services;
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
public ProdController(ProdService prodService, ExternalMessageProcessor extMessProc)
{
PService = prodService;
EMProc = extMessProc;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Chiamata GET: test status alive
/// GET: api/Prod/alive
/// </summary>
/// <param name="id">id oggetto</param>
/// <returns></returns>
[HttpGet("alive")]
public async Task<IActionResult> Alive()
{
Stopwatch sw = new Stopwatch();
sw.Start();
await Task.Delay(1);
sw.Stop();
Log.Info($"Alive | {sw.Elapsed.TotalMilliseconds:N3} ms");
return Ok("OK");
}
/// <summary>
/// Chiamata GET:
/// - fornisce il job da eseguire dalla coda (SE presente)
/// GET: api/Prod/getjob/ABC012345
/// </summary>
/// <returns></returns>
[HttpGet("getjob/{id}")]
public async Task<ActionResult<QuestionDTO>> GetJob(string id)
{
var result = await PService.GetJob(id);
QuestionDTO deserRes = JsonConvert.DeserializeObject<QuestionDTO>(result);
return Ok(deserRes);
}
/// <summary>
/// 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
/// </summary>
/// <returns></returns>
[HttpGet("getnext")]
public async Task<ActionResult<QuestionDTO>> GetNext()
{
var result = await PService.GetNext();
//return Ok(result);
QuestionDTO deserRes = JsonConvert.DeserializeObject<QuestionDTO>(result);
return Ok(deserRes);
}
/// <summary>
/// Ritorno calcolo
/// </summary>
/// <param name="id">Chiave auth valida (da implementare)</param>
/// <param name="retData"></param>
/// <returns></returns>
[HttpPost("jobreturn")]
public async Task<bool> 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;
}
/// <summary>
/// Chiamata GET:
/// - fornisce il job da eseguire dalla coda (SE presente)
/// GET: api/Prod/rep-answer/ABC012345
/// </summary>
/// <returns></returns>
[HttpGet("rep-answer/{id}")]
public async Task<ActionResult<string>> ReplayAnswer(string id, int env)
{
Constants.EXECENVIRONMENTS envir = (Constants.EXECENVIRONMENTS)env;
string rawData = await EMProc.LoadRawData(id, envir);
bool saved = false;
// provo a riprcessare...
var retData = JsonConvert.DeserializeObject<AnswerDTO>(rawData);
if (retData != null)
{
saved = await EMProc.ProcessAnswer(retData);
}
return Ok(rawData);
}
#if false
/// <summary>
/// Ritorno calcolo
/// </summary>
/// <param name="id">Chiave auth valida (da implementare)</param>
/// <param name="retData"></param>
/// <returns></returns>
[HttpPost("jobreturn/{id}")]
public async Task<bool> 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<bool> 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<bool> 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
/// <summary>
/// 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
/// </summary>
/// <param name="id">id oggetto</param>
/// <returns></returns>
[HttpGet("qlen/{type}")]
public async Task<IActionResult> QueueLen(QueueType type = QueueType.waiting)
{
var result = await PService.QueueLen(type);
return Ok(result);
}
/// <summary>
/// Chiamata GET: dizionario stato richieste
/// GET: api/Prod/queue-status/
/// </summary>
/// <param name="id">id oggetto</param>
/// <returns></returns>
[HttpGet("qstatus")]
public async Task<IActionResult> QueueStatus()
{
Stopwatch sw = new Stopwatch();
sw.Start();
Dictionary<string, long> queueStatus = new Dictionary<string, long>();
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")]
/// <summary>
/// Accodamento richiesta di calcolo prod
/// </summary>
/// <param name="reqType">Tipo richiesta</param>
/// <param name="reqUid">UID (riga ordine)</param>
/// <param name="currRequest">Contenuto della richiesta come QuestionDTO</param>
/// <returns></returns>
public async Task<bool> 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<string, string> calcDict = new Dictionary<string, string>();
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;
}
/// <summary>
/// Chiamata GET:
/// - elenco delle richieste di stima da eseguire
/// - vengono registrate come "passate" al calcolo alla data-ora della richiesta
/// GET: api/Prod/estimation
/// </summary>
/// <returns></returns>
[HttpGet("estimation")]
public async Task<ActionResult<List<EstimReqPayloadDTO>>> EstimationRequestQueue()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var listReq = new List<EstimReqPayloadDTO>();
// 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<ItemTag, OrderRowUid>
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);
}
/// <summary>
/// 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
/// </summary>
/// <returns></returns>
[HttpGet("optmization")]
public async Task<ActionResult<List<EstimReqPayloadDTO>>> OptimitionRequestQueue()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var listReq = new List<EstimReqPayloadDTO>();
// 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<ItemTag, OrderRowUid>
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
}
}