Update pagina test scratch con comunicazione IN/OUT verso API calcolo

This commit is contained in:
Samuele Locatelli
2025-08-04 17:31:41 +02:00
parent 3e9cbccf48
commit 7cdcf74d1b
18 changed files with 372 additions and 73 deletions
+29
View File
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//// <Auto-Generated>
//// This is here so CodeMaid doesn't reorganize this document
//// </Auto-Generated>
namespace EgwCoreLib.Lux.Core
{
/// <summary>
/// Configurazione costanti applicative
/// </summary>
public class Constants
{
// dati conf REDIS Cache
public static readonly string BASE_HASH = "LUX";
#if false
public static readonly string BASE_PATH = Directory.GetCurrentDirectory();
public static readonly string CALC_REQ_DONE = $"{BASE_HASH}:CalcRequests:Completed";
// REDIS Channels messaggi x QueueMan (verso UI/srv)
public static readonly string CALC_REQ_QUEUE = $"CalcRequest";
#endif
public static readonly string CALC_DONE_QUEUE = $"CalcCompleted";
}
}
+28
View File
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Core
{
public class PubSubEventArgs : EventArgs
{
#region Public Constructors
public PubSubEventArgs(string uid, string messaggio)
{
this.msgUid = uid;
this.newMessage = messaggio;
}
#endregion Public Constructors
#region Public Properties
public string newMessage { get; set; } = "";
public string msgUid { get; set; } = "";
#endregion Public Properties
}
}
@@ -24,7 +24,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
public List<CustomerModel> CustomersGetAll()
{
List<CustomerModel> dbResult = new List<CustomerModel>();
//using (DataLayerContext dbCtx = new DataLayerContext(_configuration))
//using (DataLayerContext dbCtx = new DataLayerContext(configuration))
using (DataLayerContext dbCtx = new DataLayerContext())
{
try
@@ -48,7 +48,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
public List<DealerModel> DealersGetAll()
{
List<DealerModel> dbResult = new List<DealerModel>();
//using (DataLayerContext dbCtx = new DataLayerContext(_configuration))
//using (DataLayerContext dbCtx = new DataLayerContext(configuration))
using (DataLayerContext dbCtx = new DataLayerContext())
{
try
@@ -68,7 +68,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
public List<ItemModel> ItemGetAll()
{
List<ItemModel> dbResult = new List<ItemModel>();
//using (DataLayerContext dbCtx = new DataLayerContext(_configuration))
//using (DataLayerContext dbCtx = new DataLayerContext(configuration))
using (DataLayerContext dbCtx = new DataLayerContext())
{
try
@@ -88,7 +88,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
public List<ItemModel> ItemGetSearch(string term)
{
List<ItemModel> dbResult = new List<ItemModel>();
//using (DataLayerContext dbCtx = new DataLayerContext(_configuration))
//using (DataLayerContext dbCtx = new DataLayerContext(configuration))
using (DataLayerContext dbCtx = new DataLayerContext())
{
try
@@ -109,7 +109,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
public bool ItemUpsert(ItemModel newRec)
{
bool answ = false;
//using (DataLayerContext dbCtx = new DataLayerContext(_configuration))
//using (DataLayerContext dbCtx = new DataLayerContext(configuration))
using (DataLayerContext dbCtx = new DataLayerContext())
{
try
@@ -153,7 +153,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
public List<OfferModel> OfferGetAll()
{
List<OfferModel> dbResult = new List<OfferModel>();
//using (DataLayerContext dbCtx = new DataLayerContext(_configuration))
//using (DataLayerContext dbCtx = new DataLayerContext(configuration))
using (DataLayerContext dbCtx = new DataLayerContext())
{
try
@@ -180,7 +180,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
public List<OfferRowModel> OfferRowGetByOffer(int OfferID)
{
List<OfferRowModel> dbResult = new List<OfferRowModel>();
//using (DataLayerContext dbCtx = new DataLayerContext(_configuration))
//using (DataLayerContext dbCtx = new DataLayerContext(configuration))
using (DataLayerContext dbCtx = new DataLayerContext())
{
try
+24 -7
View File
@@ -1,5 +1,6 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.VisualBasic;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
@@ -16,34 +17,50 @@ namespace EgwCoreLib.Lux.Data.Services
{
#region Public Constructors
public BaseServ(IConfiguration configuration)
public BaseServ(IConfiguration Configuration, IConnectionMultiplexer RedisConn)
{
_configuration = configuration;
configuration = Configuration;
// setup componenti REDIS
redisConn = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis")??"localhost");
#if false
redisConn = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis") ?? "localhost");
#endif
redisConn = RedisConn;
redisDb = redisConn.GetDatabase();
svgChannel = configuration.GetValue<string>("ServerConf:SvgChannel") ?? "svg:img";
// aggiungo ricerca generica ":*" al channel...
if (!svgChannel.EndsWith(":*"))
{
svgChannel += ":*";
}
// 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
};
// recupero conf speciali
// conf message pipe
CalcDonePipe = new MessagePipe(redisConn, svgChannel);
}
#endregion Public Constructors
#region Protected Fields
protected static IConfiguration _configuration = null!;
private string svgChannel = "";
protected static IConfiguration configuration = null!;
protected JsonSerializerSettings? JSSettings;
/// <summary>
/// Message pipe esecuzione elaborazione EgwCalc >> UI
/// </summary>
public MessagePipe CalcDonePipe { get; set; } = null!;
/// <summary>
/// Oggetto per connessione a REDIS
/// </summary>
protected ConnectionMultiplexer redisConn = null!;
protected IConnectionMultiplexer redisConn = null!;
//ISubscriber sub = redis.GetSubscriber();
/// <summary>
@@ -18,10 +18,10 @@ namespace EgwCoreLib.Lux.Data.Services
{
#region Public Constructors
public DataLayerServices(IConfiguration configuration) : base(configuration)
public DataLayerServices(IConfiguration configuration, IConnectionMultiplexer RedisConn) : base(configuration, RedisConn)
{
// conf DB
string connStr = _configuration.GetConnectionString("Lux.All");
string connStr = BaseServ.configuration.GetConnectionString("Lux.All") ?? "";
if (string.IsNullOrEmpty(connStr))
{
Log.Error("ConnString empty!");
@@ -15,7 +15,7 @@ namespace EgwCoreLib.Lux.Data.Services
{
#region Public Constructors
public ImageCacheService(IConfiguration config, IConfiguration configuration, IRedisService redisService)
public ImageCacheService(IConfiguration config, IRedisService redisService)
{
_config = config;
_redisService = redisService;
+154
View File
@@ -0,0 +1,154 @@
using EgwCoreLib.Lux.Core;
using NLog;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.Services
{
public class MessagePipe : IDisposable
{
#region Public Constructors
public MessagePipe(IConnectionMultiplexer redisConn, string channelName, bool enableLog = false)
{
_channel = channelName;
rChannel = new RedisChannel(_channel, RedisChannel.PatternMode.Pattern);
this.redisConn = redisConn;
redisDb = this.redisConn.GetDatabase();
redisSub = this.redisConn.GetSubscriber();
this.enableLog = enableLog;
// aggiungo sottoscrittore
setupSubscriber();
}
#endregion Public Constructors
#region Public Events
public event EventHandler EA_NewMessage = delegate { };
#endregion Public Events
#region Public Methods
public void Dispose()
{
redisDb = null;
redisSub = null;
}
/// <summary>
/// Invio messaggio sul canale + salvataggio in cache REDIS
/// </summary>
/// <param name="memKey">Chiave REDIS x salvare valore</param>
/// <param name="message">Messaggio serializzato da inviare</param>
public bool saveAndSendMessage(string memKey, string message)
{
bool answ = false;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
// invio notifica tramite il canale richiesto
answ = sendMessage(message);
if (redisDb != null)
{
redisDb.StringSetAsync(memKey, message);
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
if (numSent.ContainsKey(memKey))
{
numSent[memKey]++;
}
else
{
numSent.Add(memKey, 1);
}
if (enableLog || numSent[memKey] > 30)
{
Log.Info($"saveAndSendMessage| mKey {memKey} x {numSent[memKey]} | {message.Length} size | {ts.TotalMilliseconds} ms");
numSent[memKey] = 0;
}
return answ;
}
/// <summary>
/// Invio messaggio sul canale
/// </summary>
/// <param name="newMess"></param>
/// <returns></returns>
public bool sendMessage(string newMess)
{
bool answ = false;
if (!string.IsNullOrEmpty(_channel))
{
var numCli = redisSub.Publish(rChannel, newMess);
answ = numCli > 0;
}
return answ;
}
#endregion Public Methods
#region Protected Fields
protected static Logger Log = LogManager.GetCurrentClassLogger();
#endregion Protected Fields
#region Private Fields
/// <summary>
/// Nome Canale associato al gestore pipeline messaggi
/// </summary>
private string _channel = "";
private bool enableLog = false;
private Dictionary<string, int> numSent = new Dictionary<string, int>();
/// <summary>
/// Channel di comunicazione REDIS
/// </summary>
private RedisChannel rChannel;
private IConnectionMultiplexer redisConn;
private IDatabase redisDb;
private ISubscriber redisSub;
#endregion Private Fields
#region Private Methods
private void setupSubscriber()
{
redisSub.Subscribe(rChannel, (channel, message) =>
{
if (enableLog)
{
Log.Trace($"req setup ch {channel} | {message}");
}
// messaggio
PubSubEventArgs mea = new PubSubEventArgs($"{channel}", $"{message}");
// se qualcuno ascolta sollevo evento nuovo valore...
if (EA_NewMessage != null)
{
EA_NewMessage(this, mea);
}
});
if (enableLog)
{
Log.Info($"Subscribed {_channel}");
}
}
#endregion Private Methods
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>0.9.2508.0412</Version>
<Version>0.9.2508.0416</Version>
</PropertyGroup>
<ItemGroup>
+13 -9
View File
@@ -1,23 +1,27 @@
@page "/scratch"
<h3>Scratch Test</h3>
<div class="row">
<div class="col-4">
<div class="form-floating mb-3">
<div class="col-6">
<div class="form-floating">
<input type="text" class="form-control" placeholder="UID Finestra" @bind="@windowUid">
<label for="floatingInput">UID Finestra</label>
</div>
<div class="form-floating mb-3">
<textarea class="form-control small" style="min-height: 20rem;">@demoJwd</textarea>
<div class="form-floating my-2">
<textarea class="form-control small" style="min-height: 30rem;" @bind="@demoJwd"></textarea>
<label for="floatingInput">JWD demo</label>
</div>
<button class="btn btn-primary" @onclick="() => SendCalc()">Req Calc</button>
<div class="d-flex justify-content-between">
<div class="px-0">
<button class="btn btn-primary" @onclick="() => SendCalc()">Req Calc</button>
</div>
<div class="px-0">
<button class="btn btn-primary" @onclick="Reset">Reset</button>
</div>
</div>
</div>
<div class="col-8">
<div class="col-6">
@outSvg
</div>
</div>
+84 -41
View File
@@ -1,66 +1,109 @@
using EgwCoreLib.Lux.Core;
using EgwCoreLib.Lux.Data;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
namespace Lux.UI.Components.Pages
{
public partial class Scratch
public partial class Scratch : IDisposable
{
private string windowUid = "TestWindow";
#region Public Methods
public void Dispose()
{
DLService.CalcDonePipe.EA_NewMessage -= CalcDonePipe_EA_NewMessage;
}
#endregion Public Methods
#region Protected Properties
[Inject]
protected IConfiguration Config { get; set; } = null!;
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
[Inject]
protected ImageCacheService ICService { get; set; } = null!;
/// <summary>
/// Generazione componente SVG da mostrare
/// </summary>
protected MarkupString outSvg
{
get
{
// aggiunta gestione classe svg per posizionamento con costraints
var newSvg = currSvg.Replace("<svg", "<svg class=\"responsive-svg\"");
return (MarkupString)newSvg;
}
}
#endregion Protected Properties
#region Protected Methods
protected override void OnInitialized()
{
apiUrl = Config.GetValue<string>("ServerConf:Prog.ApiUrl") ?? "";
calcTag = Config.GetValue<string>("ServerConf:ImageCalcTag") ?? "";
subChannel = Config.GetValue<string>("ServerConf:SvgChannel") ?? "";
DLService.CalcDonePipe.EA_NewMessage += CalcDonePipe_EA_NewMessage;
}
protected void Reset()
{
currSvg = "";
}
protected async Task SendCalc()
{
// chiamo la chiamata POST alla API, che manda la richiesta via REDIS
await ICService.CallRestPost(apiUrl, $"{calcTag}/{windowUid}", demoJwd);
}
#endregion Protected Methods
#region Private Fields
private string apiUrl = "";
private string calcTag = "";
private string currSvg = "";
/// <summary>
/// Demorichiesta jwd x fare test richiesta calcolo
/// </summary>
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}";
private string subChannel = "";
private string windowUid = "TestWindow";
[Inject]
protected ImageCacheService ICService { get; set; } = null!;
#endregion Private Fields
#if false
[Inject]
protected IRedisService RedServ { get; set; } = null!;
#endif
[Inject]
protected IConfiguration Config { get; set; } = null!;
#region Private Methods
private string apiUrl = "";
private string calcTag = "";
private string subChannel = "";
private string currChannel = "";
protected override Task OnInitializedAsync()
private async void CalcDonePipe_EA_NewMessage(object? sender, EventArgs e)
{
apiUrl = Config.GetValue<string>("ServerConf:Prog.ApiUrl") ?? "";
calcTag = Config.GetValue<string>("ServerConf:ImageCalcTag") ?? "";
subChannel = Config.GetValue<string>("ServerConf:SvgChannel") ?? "";
RedServ.Subscribe($"{subChannel}:*", (ch, msg) =>
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly SVG da mostrare
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
saveSvg($"{ch}", $"{msg}");
});
return Task.CompletedTask;
}
private void saveSvg(string channel, string newSvg)
{
// se è la mia immagine
if (channel.EndsWith(windowUid) || true)
{
currSvg = newSvg;
if (currArgs.msgUid.Equals($"{subChannel}:{windowUid}"))
{
currSvg = currArgs.newMessage;
}
await InvokeAsync(StateHasChanged);
}
await Task.Delay(1);
}
protected async Task SendCalc()
{
await ICService.CallRestPost(apiUrl, $"{calcTag}/{windowUid}", demoJwd);
}
protected MarkupString outSvg { get; set; } = (MarkupString)"";
private string currSvg = "";
#endregion Private Methods
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>aspnet-Lux.UI-a758c101-a2f4-4e38-977d-1c4887dbbd50</UserSecretsId>
<Version>0.9.2508.0412</Version>
<Version>0.9.2508.0417</Version>
</PropertyGroup>
<ItemGroup>
+3
View File
@@ -64,6 +64,9 @@ builder.Services.AddSingleton<RedisSubscriptionManager>();
// Aggiunta servizi specifici
builder.Services.AddSingleton<DataLayerServices>();
builder.Services.AddSingleton<ImageCacheService>();
#if false
builder.Services.AddSingleton<MessagePipe>();
#endif
var app = builder.Build();
+9
View File
@@ -215,6 +215,15 @@ a,
.blazor-error-boundary::after {
content: "An error has occurred.";
}
/* gestione SVG responsive */
.responsive-svg {
/* SVG scala a fit del container */
width: 100%;
/* Altezza massima in rem (caratteri) */
height: 40rem;
/* Removes extra space below SVG */
display: block;
}
/*------------------------------------------------------------------
[ Shortcuts / .shortcuts ]
*/
+12
View File
@@ -193,6 +193,18 @@ a, .btn-link {
}
/* gestione SVG responsive */
.responsive-svg {
/* SVG scala a fit del container */
width: 100%;
/* Altezza massima in rem (caratteri) */
height: 40rem;
/* Removes extra space below SVG */
display: block;
}
/*------------------------------------------------------------------
[ Shortcuts / .shortcuts ]
*/
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>LUX - Web Windows MES</i>
<h4>Versione: 0.9.2508.0412</h4>
<h4>Versione: 0.9.2508.0417</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
0.9.2508.0412
0.9.2508.0417
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>0.9.2508.0412</version>
<version>0.9.2508.0417</version>
<url>http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html</changelog>
<mandatory>false</mandatory>