Files
mapo-core/MP.SPEC/Data/MpDataService.cs
T
Samuele Locatelli 9407b451c7 bozza ODL
2022-08-01 13:34:16 +02:00

555 lines
22 KiB
C#

using MP.Data;
using MP.Data.Conf;
using MP.Data.DatabaseModels;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
using System.Diagnostics;
using System.Text;
namespace MP.SPEC.Data
{
public class MpDataService : IDisposable
{
#region Public Constructors
public MpDataService(IConfiguration configuration, ILogger<MpDataService> logger)
{
_logger = logger;
_configuration = configuration;
// setup compoenti REDIS
redisConn = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis"));
redisDb = redisConn.GetDatabase();
// setup canali pub/sub
dataPipe = new MessagePipe(redisConn, Constants.ACT_MSE_DATA_KEY);
blinkPipe = new MessagePipe(redisConn, Constants.ACT_BLINK_KEY);
// conf DB
string connStr = _configuration.GetConnectionString("Mp.Data");
if (string.IsNullOrEmpty(connStr))
{
_logger.LogError("ConnString empty!");
}
else
{
dbController = new MP.Data.Controllers.MpSpecController(configuration);
StringBuilder sb = new StringBuilder();
sb.AppendLine($"DbController OK");
_logger.LogInformation(sb.ToString());
}
// setup conf IOB da dizionario
tryLoadIobTags();
#if false
// avvio timers...
startTimers();
#endif
}
#endregion Public Constructors
#region Public Properties
public static MP.Data.Controllers.MpSpecController dbController { get; set; } = null!;
public MessagePipe blinkPipe { get; set; } = null!;
/// <summary>
/// Dizionario dei tag configurati per IOB
/// </summary>
public Dictionary<string, List<TagData>> currTagConf { get; set; } = new Dictionary<string, List<TagData>>();
public MessagePipe dataPipe { get; set; } = null!;
#endregion Public Properties
#region Public Methods
public async Task<List<ListValues>> AnagStatiComm()
{
int maxAgeConfig = 5;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
List<ListValues>? result = new List<ListValues>();
// cerco in redis...
RedisValue rawData = await redisDb.StringGetAsync(redisStatoCom);
if (!string.IsNullOrEmpty($"{rawData}"))
{
result = JsonConvert.DeserializeObject<List<ListValues>>($"{rawData}");
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagStatiComm Read from REDIS: {ts.TotalMilliseconds}ms");
}
else
{
result = await Task.FromResult(dbController.AnagStatiComm());
// serializzp e salvo...
rawData = JsonConvert.SerializeObject(result);
await redisDb.StringSetAsync(redisStatoCom, rawData, TimeSpan.FromMinutes(maxAgeConfig));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagStatiComm Read from DB: {ts.TotalMilliseconds}ms");
}
if (result == null)
{
result = new List<ListValues>();
}
return result;
}
public async Task<List<ListValues>> AnagTipoArtLV()
{
int maxAgeConfig = 5;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
List<ListValues>? result = new List<ListValues>();
// cerco in redis...
RedisValue rawData = await redisDb.StringGetAsync(redisTipoArt);
if (!string.IsNullOrEmpty($"{rawData}"))
{
result = JsonConvert.DeserializeObject<List<ListValues>>($"{rawData}");
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagTipoArtLV Read from REDIS: {ts.TotalMilliseconds}ms");
}
else
{
result = await Task.FromResult(dbController.AnagTipoArtLV());
// serializzp e salvo...
rawData = JsonConvert.SerializeObject(result);
await redisDb.StringSetAsync(redisTipoArt, rawData, TimeSpan.FromMinutes(maxAgeConfig));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagTipoArtLV Read from DB: {ts.TotalMilliseconds}ms");
}
if (result == null)
{
result = new List<ListValues>();
}
return result;
}
/// <summary>
/// Eliminazione record selezionato
/// </summary>
/// <param name="currRec"></param>
/// <returns></returns>
public async Task<bool> ArticoliDeleteRecord(AnagArticoli currRec)
{
return await dbController.ArticoliDeleteRecord(currRec);
}
/// <summary>
/// Restitusice elenco articoli cercati
/// </summary>
/// <param name="numRecord"></param>
/// <param name="searchVal"></param>
/// <returns></returns>
public async Task<List<AnagArticoli>> ArticoliGetSearch(int numRecord, string azienda, string searchVal)
{
return await Task.FromResult(dbController.ArticoliGetSearch(numRecord, azienda, searchVal));
}
/// <summary>
/// Aggiornamento record selezionato
/// </summary>
/// <param name="currRec"></param>
/// <returns></returns>
public async Task<bool> ArticoliUpdateRecord(AnagArticoli currRec)
{
return await dbController.ArticoliUpdateRecord(currRec);
}
/// <summary>
/// Elenco ODL filtrati x stato, articolo, KeyRich (che contiene stato)
/// </summary>
/// <param name="inCorso">Stato ODL: true=in corso/completato</param>
/// <param name="codArt">Cod articolo</param>
/// <param name="keyRichPart">KeyRich (parziale) da cercare (es cod stato x yacht)</param>
/// <returns></returns>
public async Task<List<ODLModel>> ListODLFilt(bool inCorso, string codArt, string keyRichPart)
{
return await Task.FromResult(dbController.ListODLFilt(inCorso, codArt, keyRichPart));
}
/// <summary>
/// Elenco PODL non avviati filtrati x articolo, KeyRich (che contiene stato)
/// </summary>
/// <param name="codArt">Cod articolo</param>
/// <param name="keyRichPart">KeyRich (parziale) da cercare (es cod stato x yacht)</param>
/// <returns></returns>
public async Task<List<PODLModel>> ListPODLFilt(string codArt, string keyRichPart)
{
return await Task.FromResult(dbController.ListPODLFilt(codArt, keyRichPart));
}
/// <summary>
/// Verifica se sia possiubile cancellare articolo dato suo CodArt cercando su redis o su
/// tab veto da DB
/// </summary>
/// <param name="CodArt"></param>
/// <returns></returns>
public bool ArticoloDelEnabled(object CodArt)
{
bool answ = false;
string codArticolo = $"{CodArt}";
int cacheCheckArtUsato = 1;
int.TryParse(_configuration.GetValue<string>("ServerConf:cacheCheckArtUsato"), out cacheCheckArtUsato);
TimeSpan TTLCache = TimeSpan.FromMinutes(cacheCheckArtUsato);
// cerco in cache redis...
string redKeyArtUsed = $"{Utils.redKeyArtUsed}:{codArticolo}";
string redKeyTabCheckArt = Utils.redKeyTabCheckArt;
string rawData = redisDb.StringGet(redKeyArtUsed);
if (!string.IsNullOrEmpty(rawData))
{
bool.TryParse(rawData, out answ);
}
else
{
// controllo non sia stato mai prodotto sennò non posso cancellare...
try
{
// cerco in cache se ci sia la tabella con gli articoli impiegati...
string rawTable = redisDb.StringGet(redKeyTabCheckArt);
List<string> artList = new List<string>();
if (!string.IsNullOrEmpty(rawTable))
{
artList = JsonConvert.DeserializeObject<List<string>>(rawTable);
}
// rileggo...
if (artList == null || artList.Count == 0)
{
artList = new List<string>();
var tabArticoli = dbController.ArticoliGetUsed();
var codList = tabArticoli.Select(x => x.CodArticolo);
foreach (string cod in codList)
{
artList.Add(cod);
}
// SE fosse vuoto aggiungo comunque il cado "ND"...
if (artList.Count == 0)
{
artList.Add("ND");
}
// salvo
rawTable = JsonConvert.SerializeObject(artList);
redisDb.StringSet(redKeyTabCheckArt, rawTable, TTLCache);
}
// cerco nella tabella: se ci fosse --> disabilitato delete
bool usato = false;
if (artList != null && artList.Count > 0)
{
usato = artList.Contains(codArticolo);
}
answ = !usato;
redisDb.StringSet(redKeyArtUsed, $"{answ}", TTLCache);
}
catch
{ }
}
return answ;
}
public async Task<List<ConfigModel>> ConfigGetAll()
{
int maxAgeConfig = 5;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
List<ConfigModel>? result = new List<ConfigModel>();
// cerco in redis...
RedisValue rawData = await redisDb.StringGetAsync(redisConfKey);
if (!string.IsNullOrEmpty($"{rawData}"))
{
result = JsonConvert.DeserializeObject<List<ConfigModel>>($"{rawData}");
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"ConfigGetAll Read from REDIS: {ts.TotalMilliseconds}ms");
}
else
{
result = await Task.FromResult(dbController.ConfigGetAll());
// serializzp e salvo...
rawData = JsonConvert.SerializeObject(result);
await redisDb.StringSetAsync(redisConfKey, rawData, TimeSpan.FromMinutes(maxAgeConfig));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"ConfigGetAll Read from DB: {ts.TotalMilliseconds}ms");
}
if (result == null)
{
result = new List<ConfigModel>();
}
return result;
}
/// <summary>
/// Reset dati cache config
/// </summary>
/// <returns></returns>
public async Task ConfigResetCache()
{
await redisDb.StringSetAsync(redisConfKey, "");
}
/// <summary>
/// Update chiave config
/// </summary>
/// <returns></returns>
public async Task<bool> ConfigUpdate(ConfigModel updRec)
{
return await Task.FromResult(dbController.ConfigUpdate(updRec));
}
public void Dispose()
{
// Clear database controller
dbController.Dispose();
}
/// <summary>
/// Richiesta attivazione --&gt; sposto avanti 1 minuto il periodo limite x fast running
/// </summary>
public void doActivate()
{
fastLimit = DateTime.Now.AddMinutes(1);
}
/// <summary> Restitusice elenco aziende <returns></returns>
public Task<List<AnagGruppi>> ElencoAziende()
{
return Task.FromResult(dbController.AnagGruppiAziende());
}
public Task<List<LinkMenu>> ElencoLink()
{
return Task.FromResult(dbController.ElencoLink());
}
/// <summary>
/// Elenco setup dei tag conf correnti
/// </summary>
/// <returns></returns>
public Task<Dictionary<string, List<TagData>>> getAllTags()
{
return Task.FromResult(currTagConf);
}
/// <summary>
/// restituisce il valore da REDIS associato al tag richeisto
/// </summary>
/// <param name="redKey">Chiave in cui cercare il valore</param>
/// <returns></returns>
public string getTagConf(string redKey)
{
string outVal = "";
// cerco in REDIS la conf x l'IOB
var rawData = redisDb.StringGet(redKey);
if (!string.IsNullOrEmpty(rawData))
{
outVal = $"{rawData}";
}
return outVal;
}
public Task<List<Macchine>> MacchineGetAll()
{
return Task.FromResult(dbController.MacchineGetAll());
}
public async Task<List<MappaStatoExpl>> MseGetAll()
{
int maxAge = 2000;
int.TryParse(_configuration.GetValue<string>("ServerConf:maxAge"), out maxAge);
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
List<MappaStatoExpl>? result = new List<MappaStatoExpl>();
// cerco in redis...
RedisValue rawData = await redisDb.StringGetAsync(redisMseKey);
if (!string.IsNullOrEmpty($"{rawData}"))
{
result = JsonConvert.DeserializeObject<List<MappaStatoExpl>>($"{rawData}");
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"MseGetAll | Read from REDIS: {ts.TotalMilliseconds}ms");
}
else
{
result = await Task.FromResult(dbController.MseGetAll(maxAge));
// serializzp e salvo...
rawData = JsonConvert.SerializeObject(result);
await redisDb.StringSetAsync(redisMseKey, rawData, TimeSpan.FromMilliseconds(maxAge));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"MseGetAll | Read from DB: {ts.TotalMilliseconds}ms");
}
if (result == null)
{
result = new List<MappaStatoExpl>();
}
return result;
}
#endregion Public Methods
#region Private Fields
private static IConfiguration _configuration = null!;
private static ILogger<MpDataService> _logger = null!;
private static System.Timers.Timer fastTimer = new System.Timers.Timer(4000);
private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Limite in formato data-ora per inviare dati rapidamente (incrementato come now + 1 min
/// ad ogni chiamata client)
/// </summary>
private DateTime fastLimit = DateTime.Now;
private int fastRefreshMs = 1000;
private string redisConfKey = "MP:MON:Cache:Config";
/// <summary>
/// Oggetto per connessione a REDIS
/// </summary>
private ConnectionMultiplexer redisConn = null!;
//ISubscriber sub = redis.GetSubscriber();
/// <summary>
/// Oggetto DB redis da impiegare x chiamate R/W
/// </summary>
private IDatabase redisDb = null!;
private string redisMseKey = "MP:MON:Cache:MSE";
private string redisStatoCom = "MP:MON:Cache:StatoCom";
private string redisTipoArt = "MP:MON:Cache:TipoArt";
#endregion Private Fields
#region Private Methods
private void ElapsedFastTimer(object? source, System.Timers.ElapsedEventArgs e)
{
var pUpd = Task.Run(async () =>
{
// secondi pari --> blink, secondi dispari --> ricarica
DateTime adesso = DateTime.Now;
int resto = 0;
Math.DivRem(adesso.Second, 2, out resto);
if (resto == 0)
{
// invio in channel blink il segnale
blinkPipe.sendMessage("true");
Log.Debug("Elapsed Fast Timer Blink");
}
else
{
// invio in channel blink segnale false
blinkPipe.sendMessage("false");
// rileggo dati...
var newData = await MseGetAll();
// invio tramite la pipe...
dataPipe.sendMessage(JsonConvert.SerializeObject(newData));
Log.Debug("Elapsed Fast Timer reload");
}
});
pUpd.Wait();
}
private void startTimers()
{
fastTimer = new System.Timers.Timer(fastRefreshMs);
fastTimer.Elapsed += ElapsedFastTimer;
fastTimer.Enabled = true;
fastTimer.Start();
}
/// <summary>
/// Prova a caricare da file la conf degli IOB se presente
/// </summary>
private void tryLoadIobTags()
{
Dictionary<string, List<TagData>> currConf = new Dictionary<string, List<TagData>>();
string strExeFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
if (!string.IsNullOrEmpty(strExeFilePath))
{
string? strWorkPath = Path.GetDirectoryName(strExeFilePath);
if (!string.IsNullOrEmpty(strWorkPath))
{
string filePath = $"{strWorkPath}/Conf/iobTagsConf.json";
if (File.Exists(filePath))
{
string rawData = File.ReadAllText(filePath);
if (!string.IsNullOrEmpty(rawData))
{
var fileConfData = JsonConvert.DeserializeObject<IobTags>(rawData);
if (fileConfData != null)
{
// effettuo esplosione conf SE contenesse il valore "***" = tutti
// gli IOB
if (fileConfData.IobSetup.ContainsKey("***"))
{
// recupero elenco macchine...
var elencoMacc = dbController.MacchineGetAll();
// x ogni macchina creo le righe standard da conf...
var baseConf = fileConfData.IobSetup.Where(x => x.Key == "***").FirstOrDefault();
foreach (var item in elencoMacc)
{
if (!string.IsNullOrEmpty(item.IdxMacchina))
{
// converto i valori x la macchina corrente... clono in
// nuovo oggetto
var specVal = baseConf.Value.Select(i => i.Clone()).ToList();
// sostituisco segnaposto
foreach (var singleVal in specVal)
{
singleVal.TagLocation = singleVal.TagLocation.Replace("***", item.IdxMacchina);
}
// ora aggiungo eventuali valori in override...
if (fileConfData.IobSetup.ContainsKey(item.IdxMacchina))
{
var otConf = fileConfData.IobSetup.Where(x => x.Key == item.IdxMacchina).FirstOrDefault();
//verifico x ogni valore other...
foreach (var otTag in otConf.Value)
{
var ovrTag = specVal.Where(x => x.ColNum == otTag.ColNum && x.RowNum == otTag.RowNum).FirstOrDefault();
// se contiene --> sovrascrivo
if (ovrTag != null)
{
//ovrTag = otTag.Clone();
specVal.Remove(ovrTag);
specVal.Add(otTag.Clone());
}
// se non contiene --> aggiungo
else
{
specVal.Add(otTag);
}
}
}
currConf.Add(item.IdxMacchina, specVal);
}
}
}
// altrimenti copio ed ho finito
else
{
currConf = fileConfData.IobSetup;
}
}
}
}
if (currConf != null)
{
currTagConf = currConf;
}
}
}
}
#endregion Private Methods
}
}