using EgwMultiEngineManager; using Lux.Data; using Lux.Data.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using StackExchange.Redis; using System.Diagnostics; using System.Runtime; namespace Lux.API.Controllers { [Route("api/[controller]")] [ApiController] public class WindowController : ControllerBase { #region Public Constructors public WindowController(IConfiguration config, ILogger logger, ImageCacheService imgServ, ExecProcessManager? EgwProcManager = null) { _logger = logger; _config = config; // setup compoenti REDIS redisConn = ConnectionMultiplexer.Connect(_config.GetConnectionString("Redis") ?? "localhost"); redisDb = redisConn.GetDatabase(); // json serializer... FIX errore loop circolare https://www.ryadel.com/en/jsonserializationexception-self-referencing-loop-detected-error-fix-entity-framework-asp-net-core/ JSSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; // init classe sottoscrizione PubSub CHannel messages REDIS messageDisp.Subscribe(ChannelName("", true), (channel, message) => { SaveCalcData(channel, message); }); // verifico se usare engine enableEgwEng = _config.GetValue("ServerConf:EgwEngineEnab"); #if false if (enableEgwEng && EgwProcManager != null) { ProcessMan = EgwProcManager; ProcessMan.m_AnswerReceived += ProcessMan_m_AnswerReceived; } #endif ImgService = imgServ; } /// /// Demorichiesta jwd x fare test richiesta calcolo /// private string demoJwd = "{\r\n \"ProfilePath\": \"Profilo78\",\r\n \"AreaList\": [\r\n {\r\n \"Shape\": \"RECTANGLE\",\r\n \"DimensionList\": [\r\n {\r\n \"nIndex\": 1,\r\n \"sName\": \"Width\",\r\n \"dValue\": 1200.0\r\n },\r\n {\r\n \"nIndex\": 2,\r\n \"sName\": \"Height\",\r\n \"dValue\": 1500.0\r\n }\r\n ],\r\n \"JointList\": [\r\n {\r\n \"nIndex\": 1,\r\n \"JointType\": \"FULL_V\"\r\n },\r\n {\r\n \"nIndex\": 2,\r\n \"JointType\": \"FULL_V\"\r\n },\r\n {\r\n \"nIndex\": 3,\r\n \"JointType\": \"FULL_V\"\r\n },\r\n {\r\n \"nIndex\": 4,\r\n \"JointType\": \"FULL_V\"\r\n }\r\n ],\r\n \"BottomRail\": false,\r\n \"BottomRailQty\": 0,\r\n \"AreaList\": [\r\n {\r\n \"bIsSashVertical\": true,\r\n \"SashList\": [\r\n {\r\n \"OpeningType\": \"TURNONLY_LEFT\",\r\n \"bHasHandle\": false,\r\n \"dDimension\": 50.0\r\n },\r\n {\r\n \"OpeningType\": \"TILTTURN_RIGHT\",\r\n \"bHasHandle\": true,\r\n \"dDimension\": 50.0\r\n }\r\n ],\r\n \"SashType\": \"NULL\",\r\n \"JointList\": [\r\n {\r\n \"nIndex\": 1,\r\n \"JointType\": \"FULL_V\"\r\n },\r\n {\r\n \"nIndex\": 2,\r\n \"JointType\": \"FULL_V\"\r\n },\r\n {\r\n \"nIndex\": 3,\r\n \"JointType\": \"FULL_V\"\r\n },\r\n {\r\n \"nIndex\": 4,\r\n \"JointType\": \"FULL_V\"\r\n }\r\n ],\r\n \"Hardware\": \"000000\",\r\n \"AreaList\": [\r\n {\r\n \"AreaList\": [\r\n {\r\n \"FillType\": \"GLASS\",\r\n \"AreaList\": [],\r\n \"AreaType\": \"FILL\"\r\n }\r\n ],\r\n \"AreaType\": \"SPLITTED\"\r\n },\r\n {\r\n \"AreaList\": [\r\n {\r\n \"FillType\": \"GLASS\",\r\n \"AreaList\": [],\r\n \"AreaType\": \"FILL\"\r\n }\r\n ],\r\n \"AreaType\": \"SPLITTED\"\r\n }\r\n ],\r\n \"AreaType\": \"SASH\"\r\n }\r\n ],\r\n \"AreaType\": \"FRAME\"\r\n }\r\n ]\r\n}"; /// /// Salva risultato calcolo da broadcast channel REDIS /// /// /// private void SaveCalcData(RedisChannel channel, RedisValue message) { string rawData = $"{message}"; if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2) { // provo a deserializzare try { var retData = JsonConvert.DeserializeObject(rawData); if (retData != null) { // verifico nId di risposta x salvare correttamente ProcessMan_m_AnswerReceived(retData); } } catch (Exception exc) { _logger.LogError($"Errore in fase decodifica messaggio da REDIS Channel{Environment.NewLine}{exc}"); } } } /// /// Wrapper per invio messaggio richieste calcolo via REDIS /// /// /// private void sendMessage(string notifyChannel, string message) { RedisChannel pubNotifyChannel = new RedisChannel(notifyChannel, RedisChannel.PatternMode.Literal); messageDisp.Publish(pubNotifyChannel, message); } /// /// Nome del channel sottoscritto per ritorno calcoli /// /// /// private string ChannelName(string servId, bool isOut) { return isOut ? $"EgwEngineOutput" : $"EgwEngineInput"; // se implementassimo multi-elaborazione calcoli ogni esecutore ha un suo channel //return $"EgwEngineOutput_{servId}"; } /// /// Oggetto subscriber x pubblicazione/sottoscrizione canali REDIS /// private ISubscriber _currSub { get; set; } = null!; /// /// Message Dispatcher: oggetto comunicazione pub/sub via REDIS channels corrente /// protected ISubscriber messageDisp { get { ISubscriber answ; // se già valorizzato uso oggetto private... if (_currSub != null) { answ = _currSub; } else { // sottoscrizione al dispatcher messaggi answ = redisConn.GetSubscriber(); _currSub = answ; } // restituisco oggetto DB return answ; } } /// /// conf speciale serializzatore JSON /// protected JsonSerializerSettings? JSSettings; /// /// Oggetto per connessione a REDIS /// protected ConnectionMultiplexer redisConn = null!; /// /// Oggetto DB redis da impiegare x chiamate R/W /// protected IDatabase redisDb = null!; #endregion Public Constructors #region Public Methods /// /// Chiamata POST: riceve Json in formato JWD serializzato, lo ripete come risposta /// PUT: api/calc/svg/00000000-0000-0000-0000-000000000000 /// /// id oggetto /// [HttpPost("calc/{id}")] public async Task> calc(string id, [FromBody] string currJwd) { Stopwatch sw = new Stopwatch(); sw.Start(); lastSvg = ""; string svgContent = ""; // se messaggio vuoto --> uso default! currJwd = string.IsNullOrEmpty(currJwd) ? demoJwd : currJwd; // ...se ricevo percorso --> leggo jwd/svg cablato if (!string.IsNullOrEmpty(currJwd)) { if (true || (enableEgwEng && ProcessMan != null)) { Dictionary DictExec = new Dictionary(); DictExec.Add("Mode", "1"); DictExec.Add("Jwd", currJwd); int nId = 1; ProcessArgs currArgs = new ProcessArgs(nId, DictExec); sendMessage(ChannelName("", false), currArgs.sProcessArgs); svgContent = "DONE"; #if false bool done = ProcessMan.ArgumentsEnqueue(currArgs); waitResult = true; int numWait = 200; while (numWait > 0 && waitResult) { numWait--; await Task.Delay(waitDelay); } #endif } #if false // se ho risultato mostro... if (!string.IsNullOrEmpty(lastSvg)) { svgContent = lastSvg; // salvo! ImgService.SaveSvg(id, svgContent); } else { svgContent = "EMPTY"; } #endif } sw.Stop(); _logger.LogInformation($"svgString | {sw.Elapsed.TotalMilliseconds:N3} ms"); return Ok(svgContent); } /// /// Chiamata POST: riceve Json in formato JWD serializzato, lo ripete come risposta /// PUT: api/calc/svg/00000000-0000-0000-0000-000000000000 /// /// id oggetto /// [HttpGet("svg/{id}")] public async Task> svg(string id) { Stopwatch sw = new Stopwatch(); sw.Start(); string svgContent = ""; // ...se ricevo percorso --> leggo jwd/svg cablato if (!string.IsNullOrEmpty(id)) { svgContent = ImgService.LoadSvg(id); } sw.Stop(); _logger.LogInformation($"svgString | {sw.Elapsed.TotalMilliseconds:N3} ms"); return Ok(svgContent); } #endregion Public Methods #region Private Fields private readonly ILogger _logger; private IConfiguration _config; private bool enableEgwEng = true; //private Dictionary listSvg = new Dictionary(); private string lastSvg = ""; private int waitDelay = 10; private bool waitResult = false; #endregion Private Fields #region Private Properties private ImageCacheService ImgService { get; set; } /// /// Esecutore di processi generico /// private ExecProcessManager? ProcessMan { get; set; } #endregion Private Properties #region Private Methods /// /// Restituisce una risposta all'esecuzione /// /// private void ProcessMan_m_AnswerReceived(ProcessArgsResult result) { // verifico che sia la mia richeista con id immagine... FARE!!! // salvo il risultato... if (result.Args != null && result.Args.Count > 0) { if (result.Args.ContainsKey("Svg")) { // salvo SVG string newSvg = result.Args["Svg"]; // salvo nel dizionario lastSvg = newSvg; waitResult = false; } } } #endregion Private Methods } }