Files
gwms/GWMS.UI/Data/GWMSDataService.cs
T

636 lines
25 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Configuration;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using GWMS.Data;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using System.Diagnostics;
using NLog;
using GWMS.Data.DTO;
using GWMS.Data.DatabaseModels;
using static GWMS.Data.IobObjects;
using System.Globalization;
namespace GWMS.UI.Data
{
public class GWMSDataService
{
#region Private Fields
private static IConfiguration _configuration;
private static ILogger<GWMSDataService> _logger;
private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
private readonly IDistributedCache distributedCache;
private readonly IMemoryCache memoryCache;
/// <summary>
/// Durata assoluta massima della cache IN SECONDI
/// </summary>
private int chAbsExp = 60 * 5;
/// <summary>
/// Durata della cache IN SECONDI in modalità inattiva (non acceduta) prima di venire rimossa
/// NON estende oltre il tempo massimo di validità della cache (chAbsExp)
/// </summary>
private int chSliExp = 60 * 1;
#endregion Private Fields
#region Protected Fields
protected static string connStringBBM = "";
#endregion Protected Fields
#region Public Fields
public static GWMS.Data.Controllers.GWMSController dbController;
#endregion Public Fields
#region Public Constructors
public GWMSDataService(IConfiguration configuration, ILogger<GWMSDataService> logger, IMemoryCache memoryCache, IDistributedCache distributedCache)
{
_logger = logger;
_configuration = configuration;
// conf cache
this.memoryCache = memoryCache;
this.distributedCache = distributedCache;
// conf DB
string connStr = _configuration.GetConnectionString("GWMS.Data");
if (string.IsNullOrEmpty(connStr))
{
_logger.LogError("ConnString empty!");
}
else
{
dbController = new GWMS.Data.Controllers.GWMSController(configuration);
StringBuilder sb = new StringBuilder();
sb.AppendLine($"DbController OK");
_logger.LogInformation(sb.ToString());
}
}
#endregion Public Constructors
#region Private Methods
private DistributedCacheEntryOptions cacheOpt(bool fastCache)
{
var numSecAbsExp = fastCache ? chAbsExp : chAbsExp * 10;
var numSecSliExp = fastCache ? chSliExp : chSliExp * 10;
return new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTime.Now.AddSeconds(numSecAbsExp)).SetSlidingExpiration(TimeSpan.FromSeconds(numSecSliExp));
}
/// <summary>
/// Recupera PlantDTO e aggiorno valori attuali (se presente...)
/// ATTENZIONE: i dati sono sempre ricevuti PER SINGOLO PlantId!!!
/// </summary>
/// <param name="newItems"></param>
private async Task updateCurrDTO(List<PlantLogModel> newItems)
{
List<PlantDTO> dbResult = new List<PlantDTO>();
int PlantId = newItems.FirstOrDefault().PlantId;
string cacheKey = "DATA:PLANTS:ListDTO";
string rawData;
var redisDataList = await distributedCache.GetAsync(cacheKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<PlantDTO>>(rawData);
// ora ciclo x ogni flusso/macchina l'ultimo record
List<PlantLogModel> lastByFlux = newItems
.OrderByDescending(x => x.DtEvent)
.GroupBy(g => g.FluxType)
.Select(s => s.First())
.ToList();
// aggiorno il DTO x i valori trovati...
var currDto = dbResult.Where(x => x.PlantId == PlantId).FirstOrDefault();
// verifico SE c'è Level
var lastLev = lastByFlux.Where(x => x.FluxType == "Level").FirstOrDefault();
if (lastLev != null)
{
currDto.LevelAct = lastLev.ValNumber;
}
// verifico SE c'è MainPress
var lastMain = lastByFlux.Where(x => x.FluxType == "MainPress").FirstOrDefault();
if (lastMain != null)
{
if (currDto.PressAct.ContainsKey("Main"))
{
currDto.PressAct["Main"] = lastMain.ValNumber;
}
else
{
currDto.PressAct.Add("Main", lastMain.ValNumber);
}
}
// verifico SE c'è PressBH
var lastBH = lastByFlux.Where(x => x.FluxType == "PressBH").FirstOrDefault();
if (lastBH != null)
{
if (currDto.PressAct.ContainsKey("BH"))
{
currDto.PressAct["BH"] = lastBH.ValNumber;
}
else
{
currDto.PressAct.Add("BH", lastBH.ValNumber);
}
}
// verifico SE c'è PressBL
var lastBL = lastByFlux.Where(x => x.FluxType == "PressBL").FirstOrDefault();
if (lastBL != null)
{
if (currDto.PressAct.ContainsKey("BL"))
{
currDto.PressAct["BL"] = lastBL.ValNumber;
}
else
{
currDto.PressAct.Add("BL", lastBL.ValNumber);
}
}
// salvo DTO!
rawData = JsonConvert.SerializeObject(dbResult);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true));
}
}
#endregion Private Methods
#region Protected Methods
/// <summary>
/// Hash dati EXE TASK x la macchina specificata
/// </summary>
/// <param name="idxMacchina"></param>
/// <returns></returns>
protected static string exeTaskHash(string idxMacchina)
{
return mHash(string.Format("ExeTask:{0}", idxMacchina));
}
protected string getCacheKey(string TableName, SelectOrderData CurrFilter)
{
string answ = $"{TableName}:P_{CurrFilter.PlantId:00}:S_{CurrFilter.SupplierId:00}:T_{CurrFilter.TransporterId:00}:D_{CurrFilter.DateStart:yyyyMMddHHmm}_{CurrFilter.DateEnd:yyyyMMddHHmm}";
return answ;
}
protected async Task<List<PlantLogModel>> PlantLogGetLastByFlux(int PlantId)
{
List<PlantLogModel> lastValues = new List<PlantLogModel>();
// cerco in cache
string cacheKey = $"DATA:PLANTS:LastFlux:{PlantId}";
string rawData;
var redisDataList = await distributedCache.GetAsync(cacheKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
lastValues = JsonConvert.DeserializeObject<List<PlantLogModel>>(rawData);
}
// altrimenti DB e salvo...
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
lastValues = dbController.PlantLogGetLastByFlux(PlantId);
rawData = JsonConvert.SerializeObject(lastValues);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB + caching per PlantLogGetLastByFlux: {ts.TotalMilliseconds} ms");
}
return await Task.FromResult(lastValues);
}
#endregion Protected Methods
#region Public Methods
/// <summary>
/// Hash Redis contenente i dati MP di una specifico TYPE (es StatusMacchina, StateMachineIngressi, ...)
/// </summary>
/// <param name="dataType"></param>
/// <returns></returns>
public static string mHash(string dataType)
{
return $"DATA:{dataType}";
}
public async Task<List<GWMS.Data.DatabaseModels.ConfigModel>> ConfigGetAll()
{
//return Task.FromResult(dbController.ActionsGetAll());
List<GWMS.Data.DatabaseModels.ConfigModel> dbResult = new List<GWMS.Data.DatabaseModels.ConfigModel>();
string cacheKey = "DATA:CONFIG";
string rawData;
var redisDataList = await distributedCache.GetAsync(cacheKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<GWMS.Data.DatabaseModels.ConfigModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.GetConfig();
rawData = JsonConvert.SerializeObject(dbResult);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(false));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB + caching per ConfigGetAll: {ts.TotalMilliseconds} ms");
}
return await Task.FromResult(dbResult);
}
public PlantLogModel convertFluxToPL(int plantId, flogData origData)
{
// cerco di ottenere un val in cifre
double valDbl = 0;
NumberStyles style = NumberStyles.Number;
CultureInfo culture = CultureInfo.CreateSpecificCulture("it-IT");
double.TryParse(origData.valore.Replace(".", ","), style, culture, out valDbl);
TimeSpan delta = origData.dtCurr.Subtract(origData.dtEve);
PlantLogModel answ = new PlantLogModel()
{
FluxType = origData.flux,
ValNumber = valDbl,
ValString = origData.valore,
PlantId = plantId,
DtEvent = DateTime.Now.Subtract(delta)
};
return answ;
}
public async Task<bool> HasPlantLog()
{
return await Task.FromResult(dbController.HasPlantLog());
}
public async Task<List<GWMS.Data.DatabaseModels.ItemModel>> ItemsGetAll()
{
//return Task.FromResult(dbController.ActionsGetAll());
List<GWMS.Data.DatabaseModels.ItemModel> dbResult = new List<GWMS.Data.DatabaseModels.ItemModel>();
string cacheKey = "DATA:ITEMS";
string rawData;
var redisDataList = await distributedCache.GetAsync(cacheKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<GWMS.Data.DatabaseModels.ItemModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.GetItems();
rawData = JsonConvert.SerializeObject(dbResult);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(false));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB + caching per ItemsGetAll: {ts.TotalMilliseconds} ms");
}
return await Task.FromResult(dbResult);
}
/// <summary>
/// Restitusice elenco KVP dei TASK (da passare a IOB-WIN) per l'impianto indicato
/// </summary>
/// <param name="idxMacchina"></param>
/// <returns></returns>
public async Task<Dictionary<string, string>> mTaskMacchina(string idxMacchina)
{
// hard coded dimensione vettore DatiMacchine
Dictionary<string, string> answ = new Dictionary<string, string>();
// ORA recupero da memoria redis...
try
{
string cacheKey = exeTaskHash(idxMacchina);
string rawData;
var redisDataList = await distributedCache.GetAsync(cacheKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
answ = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawData);
}
}
catch (Exception exc)
{
Log.Info($"Errore in recupero dati EXE TASK x Redis mTaskMacchina - idxMacchina {idxMacchina}{Environment.NewLine}{exc}");
}
return answ;
}
public async Task<List<OrderModel>> OrdersGetFilt(SelectOrderData CurrFilter)
{
List<OrderModel> dbResult = new List<OrderModel>();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.GetOrdersFilt(CurrFilter.PlantId, CurrFilter.SupplierId, CurrFilter.TransporterId, CurrFilter.DateStart, CurrFilter.DateEnd);
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB per OrdersGetFilt: {ts.TotalMilliseconds} ms");
return await Task.FromResult(dbResult);
}
public void OrderUpdate(OrderModel currItem)
{
try
{
dbController.OrderUpdate(currItem);
}
catch
{
}
}
public async Task<List<PlantLogModel>> PlantLogGetFilt(int PlantId, DateTime DtMaxDate, int numRec)
{
List<PlantLogModel> dbResult = new List<PlantLogModel>();
string cacheKey = $"DATA:PLANTS:LOGS:{PlantId}:{DtMaxDate:yyyyMMddHHmm}:{numRec}";
string rawData;
var redisDataList = await distributedCache.GetAsync(cacheKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<PlantLogModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.GetPlantLog(PlantId, DtMaxDate, numRec);
rawData = JsonConvert.SerializeObject(dbResult);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB + caching per PlantLogGetFilt: {ts.TotalMilliseconds} ms");
}
return await Task.FromResult(dbResult);
}
public async Task<bool> PlantLogInsert(List<PlantLogModel> newItems)
{
bool fatto = false;
// init valori
int IntervalMin = 60;
int.TryParse(_configuration["IntervalMin"], out IntervalMin);
List<PlantLogModel> item2insert = new List<PlantLogModel>();
int PlantId = newItems.FirstOrDefault().PlantId;
// aggiorno valori ACT x DTO
await updateCurrDTO(newItems);
// recupero ultimi inseriti
List<PlantLogModel> lastValues = PlantLogGetLastByFlux(PlantId).Result;
// verifico i flussi presenti tra quelli ricevuti
List<string> fluxList = newItems
.GroupBy(g => g.FluxType)
.Select(s => s.First().FluxType)
.ToList();
foreach (var item in fluxList)
{
// cerco se c'è valore...
var lastInserted = lastValues.Where(x => x.FluxType == item).FirstOrDefault();
DateTime dateLimit = DateTime.Today.AddDays(-1);
if (lastInserted != null)
{
// per ogni flusso calcolo il valore minimo x inserimento (arrotondando a minInt)
dateLimit = dbController.DateRoundEnd(lastInserted.DtEvent, IntervalMin);
}
// cerco se ho record > valore minimo x ogni flusso ricevuto
List<PlantLogModel> insCandidates = newItems.Where(x => x.FluxType == item && x.DtEvent >= dateLimit).ToList();
while (insCandidates.Count > 0)
{
var newRec = insCandidates.First();
// il primo lo accodo da inserire
item2insert.Add(newRec);
// calcolo nuovo veto
dateLimit = dbController.DateRoundEnd(newRec.DtEvent, IntervalMin);
// ...e se ho record accodo
insCandidates = newItems.Where(x => x.FluxType == item && x.DtEvent >= dateLimit).ToList();
}
}
// se ho record da inserire...
if (item2insert.Count > 0)
{
// faccio vero insert
fatto = dbController.PlantLogInsertNew(item2insert);
// invalido i vari valori in cache
await distributedCache.RemoveAsync($"DATA:PLANTS:LastFlux:{PlantId}");
await distributedCache.RemoveAsync($"DATA:PLANTS:ListDTO");
}
// restituisco
return await Task.FromResult(fatto);
}
public async Task<List<PlantDTO>> PlantsGetAll()
{
List<PlantDTO> dbResult = new List<PlantDTO>();
string cacheKey = "DATA:PLANTS:ListDTO";
string rawData;
var redisDataList = await distributedCache.GetAsync(cacheKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<PlantDTO>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
int maxRec = 100;
int.TryParse(_configuration["MaxLogRecord"], out maxRec);
dbResult = dbController.GetPlantsDTO(maxRec);
rawData = JsonConvert.SerializeObject(dbResult);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB + caching per PlantsGetAll: {ts.TotalMilliseconds} ms");
}
return await Task.FromResult(dbResult);
}
public async Task<PlantDTO> PlantsGetByCode(string PlantCode)
{
PlantDTO answ = new PlantDTO();
var ListRecords = await PlantsGetAll();
var found = ListRecords.Where(x => x.PlantCode == PlantCode).FirstOrDefault();
if (found != null)
{
answ = found;
}
return await Task.FromResult(answ);
}
public void RebootLogInsert(RebootLogModel newItem)
{
try
{
dbController.RecordRebootLog(newItem);
}
catch
{ }
}
public async Task<bool> RegenDB(int numDays, int stepMin = 30, int maxHourRate = 800)
{
return await Task.FromResult(dbController.RegenDB(1, numDays, stepMin, maxHourRate));
}
public void rollBackEdit(object item)
{
dbController.rollBackEntity(item);
}
public async Task<List<SupplierModel>> SuppliersGetAll()
{
List<SupplierModel> dbResult = new List<SupplierModel>();
string cacheKey = "DATA:SUPPL:List";
string rawData;
var redisDataList = await distributedCache.GetAsync(cacheKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<SupplierModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.GetSuppliers();
rawData = JsonConvert.SerializeObject(dbResult);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB + caching per PlantsGetAll: {ts.TotalMilliseconds} ms");
}
return await Task.FromResult(dbResult);
}
public async Task<List<TransporterModel>> TransportersGetAll()
{
List<TransporterModel> dbResult = new List<TransporterModel>();
string cacheKey = $"DATA:TRANSP:List";
string rawData;
var redisDataList = await distributedCache.GetAsync(cacheKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<TransporterModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.GetTransporters();
rawData = JsonConvert.SerializeObject(dbResult);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB + caching per PlantsGetAll: {ts.TotalMilliseconds} ms");
}
return await Task.FromResult(dbResult);
}
public async Task<List<UserModel>> UsersGetFilt(UserLevel UsrLvl, int PlantId, int SupplierId, int TransporterId)
{
List<UserModel> dbResult = new List<UserModel>();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.GetUsersFilt(UsrLvl, PlantId, SupplierId, TransporterId);
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB per UsersGetFilt: {ts.TotalMilliseconds} ms");
return await Task.FromResult(dbResult);
}
public void UserUpdate(UserModel currItem)
{
try
{
dbController.UserUpdate(currItem);
}
catch
{
}
}
public async void WeekPlanDelete(WeekPlanModel currItem)
{
try
{
//dbController.ResetController();
dbController.WeekPlanDelete(currItem);
string cacheKey = $"DATA:WEEKPLAN:List";
var redisDataList = Encoding.UTF8.GetBytes("");
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true));
}
catch
{
}
}
public async Task<List<WeekPlanModel>> WeekPlanGet()
{
List<WeekPlanModel> dbResult = new List<WeekPlanModel>();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.GetWeekPlan();
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Info($"Effettuata lettura da DB + caching per WeekPlanGet: {ts.TotalMilliseconds} ms");
return await Task.FromResult(dbResult);
}
public async void WeekPlanUpdate(WeekPlanModel currItem)
{
try
{
//dbController.ResetController();
dbController.WeekPlanUpdate(currItem);
string cacheKey = $"DATA:WEEKPLAN:List";
var redisDataList = Encoding.UTF8.GetBytes("");
await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true));
}
catch
{
}
}
#endregion Public Methods
}
}