Files
lux/EgwCoreLib.Lux.Data/Services/BaseServ.cs
T
2026-03-07 09:26:09 +01:00

384 lines
14 KiB
C#

using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace EgwCoreLib.Lux.Data.Services
{
/// <summary>
/// Classe base per i servizi che fornisce funzionalità comuni come
/// - connessione a Redis
/// - configurazione
/// - gestione dei messaggi
/// - strategie di caching.
///
/// Questa classe agisce come modello per altri servizi derivati.
/// </summary>
public class BaseServ
{
#region Public Constructors
/// <summary>
/// Inizializza una nuova istanza della classe BaseServ.
/// Configura la connessione Redis, carica le impostazioni di configurazione e inizializza il serializzatore JSON.
/// </summary>
/// <param name="Configuration">Oggetto di configurazione per recuperare le impostazioni dell'applicazione.</param>
/// <param name="RedisConn">Multiplexer di connessione Redis per operazioni sul database.</param>
public BaseServ(IConfiguration Configuration, IConnectionMultiplexer RedisConn)
{
_config = Configuration;
redisConn = RedisConn;
redisDb = redisConn.GetDatabase();
// setup tracing
// Verifica conf trace...
traceEnabled = _config.GetValue<bool>("Otel:EnableTracing", false);
// receving channel name setup
chBom = _config.GetValue<string>("ServerConf:ChannelBom") ?? "lux:bom";
chHwList = _config.GetValue<string>("ServerConf:ChannelHwList") ?? "lux:hw:list";
chHwOpt = _config.GetValue<string>("ServerConf:ChannelHwOpt") ?? "lux:hw:opt";
chPng = _config.GetValue<string>("ServerConf:ChannelPng") ?? "lux:png:img";
chProfElem = _config.GetValue<string>("ServerConf:ChannelProfElem") ?? "lux:prof:elem";
chProfList = _config.GetValue<string>("ServerConf:ChannelProfList") ?? "lux:prof:list";
chProd = _config.GetValue<string>("ServerConf:ChannelProd") ?? "lux:prod";
chShape = _config.GetValue<string>("ServerConf:ChannelShape") ?? "lux:shape:curr";
chSvg = _config.GetValue<string>("ServerConf:ChannelSvg") ?? "lux:svg:img";
chUpdate = _config.GetValue<string>("ServerConf:ChannelUpdate") ?? "lux:update";
// Appends ":*" to the channels to enable wildcard subscription for dynamic events
fixRecChannel(ref chBom);
fixRecChannel(ref chHwList);
fixRecChannel(ref chHwOpt);
fixRecChannel(ref chPng);
fixRecChannel(ref chProfElem);
fixRecChannel(ref chProfList);
fixRecChannel(ref chProd);
fixRecChannel(ref chShape);
fixRecChannel(ref chSvg);
fixRecChannel(ref chUpdate);
#if false
if (!chPng.EndsWith(":*"))
{
chPng += ":*";
}
if (!chSvg.EndsWith(":*"))
{
chSvg += ":*";
}
if (!chBom.EndsWith(":*"))
{
chBom += ":*";
}
if (!chUpdate.EndsWith(":*"))
{
chUpdate += ":*";
}
if (!chShape.EndsWith(":*"))
{
chShape += ":*";
}
if (!chHwList.EndsWith(":*"))
{
chHwList += ":*";
}
if (!chHwOpt.EndsWith(":*"))
{
chHwOpt += ":*";
}
if (!chProfList.EndsWith(":*"))
{
chProfList += ":*";
}
#endif
// Configurazione serializzatore JSON per risolvere errore di loop circolare
JSSettings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
// Configurazione pipe dei messaggi
PipeBom = new MessagePipe(redisConn, chBom);
PipeHwList = new MessagePipe(redisConn, chHwList);
PipeHwOpt = new MessagePipe(redisConn, chHwOpt);
PipePng = new MessagePipe(redisConn, chPng);
PipeProd = new MessagePipe(redisConn, chProd);
PipeProfElement = new MessagePipe(redisConn, chProfElem);
PipeProfList = new MessagePipe(redisConn, chProfList);
PipeShape = new MessagePipe(redisConn, chShape);
PipeSvg = new MessagePipe(redisConn, chSvg);
PipeUpdate = new MessagePipe(redisConn, chUpdate);
}
#endregion Public Constructors
#region Public Properties
/// <summary>
/// Pipe dei messaggi per la comunicazione riguardo update calcolo BOM
/// </summary>
public MessagePipe PipeBom { get; set; } = null!;
/// <summary>
/// Pipe dei messaggi per ritorno HwList da Engine di calcolo verso interfaccia utente.
/// I messaggi vengono inviati sul canale Redis definito da ChannelHwList.
/// </summary>
public MessagePipe PipeHwList { get; set; } = null!;
/// <summary>
/// Pipe dei messaggi per ritorno HwOptions calcolate da Engine di calcolo verso interfaccia utente.
/// I messaggi vengono inviati sul canale Redis definito da ChannelHwOpt.
/// </summary>
public MessagePipe PipeHwOpt { get; set; } = null!;
/// <summary>
/// Pipe dei messaggi per ritorno PNG calcolati da Engine di calcolo verso interfaccia utente.
/// I messaggi vengono inviati sul canale Redis definito da ChannelPng.
/// </summary>
public MessagePipe PipePng { get; set; } = null!;
/// <summary>
/// Canale informazioni relativo ad attività relative alla gesitone PROD:
/// - carico macchine
/// - scheduling
/// </summary>
public MessagePipe PipeProd { get; set; } = null!;
/// <summary>
/// Pipe dei messaggi per ritorno info elementi del profile
/// </summary>
public MessagePipe PipeProfElement { get; set; } = null!;
/// <summary>
/// Pipe dei messaggi per ritorno ProfileList calcolate da Engine di calcolo verso interfaccia utente.
/// I messaggi vengono inviati sul canale Redis definito da ChannelProfList.
/// </summary>
public MessagePipe PipeProfList { get; set; } = null!;
/// <summary>
/// Pipe dei messaggi per ritorno Shape calcolate da Engine di calcolo verso interfaccia utente.
/// I messaggi vengono inviati sul canale Redis definito da ChannelShape.
/// </summary>
public MessagePipe PipeShape { get; set; } = null!;
/// <summary>
/// Pipe dei messaggi per ritorno SVG calcolati da Engine di calcolo verso interfaccia utente.
/// I messaggi vengono inviati sul canale Redis definito da ChannelSvg.
/// </summary>
public MessagePipe PipeSvg { get; set; } = null!;
/// <summary>
/// Pipe dei messaggi per la comunicazione riguardo update generico UI
/// </summary>
public MessagePipe PipeUpdate { get; set; } = null!;
#endregion Public Properties
/// <summary>
/// Helper avvio attività per la funzione tracciata
/// </summary>
/// <param name="methodName"></param>
/// <returns></returns>
protected static Activity? StartActivity([CallerMemberName] string? methodName = null)
{
var activity = ActivitySource.StartActivity(methodName ?? "UNDEF");
activity?.SetTag("host.name", Environment.MachineName);
return activity;
}
#region Protected Fields
/// <summary>
/// Oggetto per collezione dati Activity (span in Uptrace)
/// </summary>
protected static readonly ActivitySource ActivitySource = new ActivitySource("Lux.DATA");
/// <summary>
/// Oggetto di configurazione statico per accedere alle impostazioni dell'applicazione (es. stringhe di connessione).
/// Condiviso tra tutte le istanze di BaseServ.
/// </summary>
protected static IConfiguration _config = null!;
/// <summary>
/// Oggetto logger utilizzato per registrare eventi e errori a livello di classe.
/// Utile per il monitoraggio del comportamento dell'applicazione e la risoluzione di problemi.
/// </summary>
protected static Logger Log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Impostazioni del serializzatore JSON utilizzato per gestire oggetti con riferimenti circolari
/// (es. oggetti che si fanno riferimento reciprocamente).
/// </summary>
protected JsonSerializerSettings? JSSettings;
/// <summary>
/// Oggetto per la connessione a Redis utilizzato per operazioni di lettura/scrittura.
/// </summary>
protected IConnectionMultiplexer redisConn = null!;
/// <summary>
/// Database Redis utilizzato per le operazioni di lettura/scrittura
/// nb: ottenuto tramite _redisConn.GetDatabase()
/// </summary>
protected IDatabase redisDb = null!;
#endregion Protected Fields
#region Protected Properties
/// <summary>
/// Durata della cache breve (circa 1 minuto + variazione del +/-10%)
/// </summary>
protected TimeSpan FastCache
{
get => TimeSpan.FromSeconds(cacheTtlShort * rnd.Next(900, 1100) / 1000);
}
/// <summary>
/// Durata della cache lunga (+ variazione del +/-10%)
/// </summary>
protected TimeSpan LongCache
{
get => TimeSpan.FromSeconds(cacheTtlLong * rnd.Next(900, 1100) / 1000);
}
/// <summary>
/// Durata della cache molto breve (circa 10 secondi + variazione del +/-10%)
/// </summary>
protected TimeSpan UltraFastCache
{
get => TimeSpan.FromSeconds(cacheTtlShort / 6 * rnd.Next(900, 1100) / 1000);
}
/// <summary>
/// Durata della cache molto lunga (+ variazione del +/-10%)
/// </summary>
protected TimeSpan UltraLongCache
{
get => TimeSpan.FromSeconds(cacheTtlLong * 10 * rnd.Next(900, 1100) / 1000);
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Helper trace messaggio log (SE abilitato)
/// </summary>
/// <param name="traceMsg"></param>
protected void LogTrace(string traceMsg, NLog.LogLevel? reqLevel = null, [CallerMemberName] string? methodName = null)
{
if (!traceEnabled)
return;
reqLevel ??= NLog.LogLevel.Debug;
// Loggo!
Log.Log(reqLevel, $"{methodName} | {traceMsg}");
}
#endregion Protected Methods
#region Private Fields
/// <summary>
/// Durata della cache lunga in secondi (predefinito: 5 minuti)
/// Utilizzato nella proprietà LongCache per definire quanto a lungo i dati devono essere memorizzati in cache.
/// </summary>
private int cacheTtlLong = 60 * 5;
/// <summary>
/// Durata della cache breve in secondi (predefinito: 1 minuto)
/// Utilizzato nelle proprietà FastCache e UltraFastCache per definire la durata della cache breve.
/// </summary>
private int cacheTtlShort = 60 * 1;
/// <summary>
/// Redis channel for BOM related info
/// </summary>
private string chBom = "";
/// <summary>
/// Canale ritorno Hw List
/// </summary>
private string chHwList = "";
/// <summary>
/// Canale ritorno Hw Options
/// </summary>
private string chHwOpt = "";
/// <summary>
/// Nome del canale Redis utilizzato per l'invio/ricezione di messaggi relativi a img png.
/// Predefinito a "png:img" con suffisso ":*".
/// </summary>
private string chPng = "";
/// <summary>
/// Canale ritorno info relative ad attività PROD
/// </summary>
private string chProd = "";
/// <summary>
/// Canale ritorno Profile Elements
/// </summary>
private string chProfElem = "";
/// <summary>
/// Canale ritorno Profile List
/// </summary>
private string chProfList = "";
/// <summary>
/// Canale ritorno shape calcolate
/// </summary>
private string chShape = "";
/// <summary>
/// Nome del canale Redis utilizzato per l'invio/ricezione di messaggi relativi a img svg.
/// Predefinito a "svg:img" con suffisso ":*".
/// </summary>
private string chSvg = "";
/// <summary>
/// Nome del canale Redis utilizzato per l'invio/ricezione di messaggi di update
/// </summary>
private string chUpdate = "";
/// <summary>
/// Generatore di numeri casuali utilizzato per introdurre variabilità dinamica nelle durate della cache
/// (simula variazioni reali nella freschezza dei dati e nei tempi di scadenza).
/// </summary>
private Random rnd = new Random();
/// <summary>
/// Abilitazione operazioni tracing generiche
/// </summary>
private bool traceEnabled = false;
#endregion Private Fields
#region Private Methods
/// <summary>
/// Fix Receive Channel (ricerca "like")
/// </summary>
/// <param name="currCh"></param>
private void fixRecChannel(ref string currCh)
{
#if false
if (!currCh.EndsWith(":*"))
{
//currCh += ":*";
}
#endif
if (!currCh.EndsWith("*"))
{
currCh += "*";
}
}
#endregion Private Methods
}
}