307 lines
12 KiB
C#
307 lines
12 KiB
C#
using EgwCoreLib.Lux.Data.DbModel.Production;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Configuration;
|
|
using NLog;
|
|
using StackExchange.Redis;
|
|
using System.Data;
|
|
|
|
namespace EgwCoreLib.Lux.Data.Controllers
|
|
{
|
|
internal class LuxController
|
|
{
|
|
|
|
#region Internal Methods
|
|
|
|
|
|
#if true
|
|
/// <summary>
|
|
/// Assegnazione in blocco degli item agli ODL corrispondenti
|
|
/// </summary>
|
|
/// <param name="dbList"></param>
|
|
/// <param name="dictParts"></param>
|
|
/// <returns></returns>
|
|
internal async Task<int> ProdItem2ODL_AssignAsync(List<ProductionODLModel> dbList, Dictionary<(int phaseId, int resId, string machine, int index), List<string>> dictParts)
|
|
{
|
|
int totalCreated = 0;
|
|
using (DataLayerContext dbCtx = new DataLayerContext())
|
|
{
|
|
// 1. Recuperiamo tutti i ProdBatchID coinvolti per fare una sola query
|
|
List<int> batchIds = dbList.Select(o => o.ProdBatchID).Distinct().ToList();
|
|
|
|
if (batchIds != null && batchIds.Count > 0)
|
|
{
|
|
// 2. Carichiamo in memoria i ProdItem necessari (solo ID e Tag per risparmiare RAM)
|
|
var itemsList = await dbCtx.DbSetProdItem
|
|
.Where(x => batchIds.Contains(x.ProdBatchID ?? 0) && x.ProdItemTag != null && x.ProdItemTag != "")
|
|
.Select(x => new { x.ProdItemID, x.ProdItemTag })
|
|
.ToListAsync();
|
|
|
|
// 1. Usiamo il "!" (null-forgiving operator) dopo x.ProdItemTag
|
|
// perché il filtro .Where sopra garantisce che non sia null.
|
|
var itemLookup = itemsList
|
|
.GroupBy(x => x.ProdItemTag!)
|
|
.ToDictionary(
|
|
g => g.Key,
|
|
g => g.First().ProdItemID,
|
|
StringComparer.OrdinalIgnoreCase
|
|
);
|
|
|
|
using var transaction = await dbCtx.Database.BeginTransactionAsync();
|
|
try
|
|
{
|
|
var relationsToInsert = new List<ProductionItem2ODLModel>();
|
|
|
|
foreach (var odl in dbList)
|
|
{
|
|
var key = (odl.PhaseID ?? 0, odl.ResourceID ?? 0, odl.ProdPlantCod, odl.Index);
|
|
|
|
if (dictParts.TryGetValue(key, out List<string> tagList))
|
|
{
|
|
foreach (var tag in tagList)
|
|
{
|
|
// 3. Cerchiamo l'ID corrispondente al tag nel nostro lookup locale
|
|
if (itemLookup.TryGetValue(tag, out int realItemId))
|
|
{
|
|
relationsToInsert.Add(new ProductionItem2ODLModel
|
|
{
|
|
ProdODLID = odl.ProdODLID,
|
|
ProdItemID = realItemId,
|
|
DtAssign = DateTime.Now
|
|
});
|
|
}
|
|
else
|
|
{
|
|
//Log.Warning($"Tag {tag} non trovato nel database per i batch selezionati.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (relationsToInsert.Any())
|
|
{
|
|
await dbCtx.DbSetProdItem2ODL.AddRangeAsync(relationsToInsert);
|
|
totalCreated = relationsToInsert.Count;
|
|
await dbCtx.SaveChangesAsync();
|
|
}
|
|
|
|
await transaction.CommitAsync();
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
await transaction.RollbackAsync();
|
|
Log.Error($"Errore nel salvataggio relazioni ODL-Parts: {exc.Message}");
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
return totalCreated;
|
|
}
|
|
#endif
|
|
|
|
#if false
|
|
/// <summary>
|
|
/// Elenco da DB delle stats aggregate dato periodo inizio/fine
|
|
/// </summary>
|
|
/// <param name="dtStart"></param>
|
|
/// <param name="dtEnd"></param>
|
|
/// <returns></returns>
|
|
internal async Task<List<StatsAggregatedModel>> StatsAggrGetAsync(DateTime dtStart, DateTime dtEnd)
|
|
{
|
|
List<StatsAggregatedModel> answ = new List<StatsAggregatedModel>();
|
|
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
|
|
using (DataLayerContext dbCtx = new DataLayerContext())
|
|
{
|
|
// recupero ed ordino per data-ora
|
|
answ = await dbCtx
|
|
.DbSetStatsAggr
|
|
.Where(x => x.Hour >= dtStart && x.Hour <= dtEnd)
|
|
.AsNoTracking()
|
|
.OrderBy(x => x.Hour)
|
|
.ToListAsync();
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Range periodo per chiamate aggregate
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal async Task<Utils.DtUtils.Periodo> StatsAggrRangeAsync()
|
|
{
|
|
Utils.DtUtils.Periodo answ = new Utils.DtUtils.Periodo(Utils.DtUtils.PeriodSet.Today);
|
|
using (DataLayerContext dbCtx = new DataLayerContext())
|
|
{
|
|
var query = dbCtx.DbSetStatsAggr.AsQueryable();
|
|
|
|
var minHour = await query.MinAsync(x => x.Hour);
|
|
var maxHour = await query.MaxAsync(x => x.Hour);
|
|
answ.Inizio = minHour;
|
|
answ.Fine = maxHour;
|
|
return answ;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esegue insert statistiche aggregate sul DB
|
|
/// </summary>
|
|
/// <param name="listRecords">Elenco dei record da inserire</param>
|
|
/// <param name="removeOld">Se true preventivamente elimina record nel periodo richiesto</param>
|
|
/// <returns></returns>
|
|
internal async Task<long> StatsAggrUpsertAsync(List<StatsAggregatedModel> listRecords, bool removeOld)
|
|
{
|
|
int answ = 0;
|
|
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
|
|
using (DataLayerContext dbCtx = new DataLayerContext())
|
|
{
|
|
// in primis se richiesto calcolo range periodo e svuoto...
|
|
if (removeOld)
|
|
{
|
|
var firstRec = listRecords.OrderBy(x => x.Hour).FirstOrDefault();
|
|
var lastRec = listRecords.OrderByDescending(x => x.Hour).FirstOrDefault();
|
|
|
|
if (firstRec != null && lastRec != null)
|
|
{
|
|
DateTime startDate = firstRec.Hour;
|
|
DateTime endDate = lastRec.Hour;
|
|
// uso direttamente ExecuteDelete
|
|
await dbCtx
|
|
.DbSetStatsAggr
|
|
.Where(x => x.Hour >= startDate && x.Hour <= endDate)
|
|
.ExecuteDeleteAsync();
|
|
}
|
|
}
|
|
|
|
// ora preparo inserimento massivo
|
|
await dbCtx
|
|
.DbSetStatsAggr
|
|
.AddRangeAsync(listRecords);
|
|
|
|
// salvo!
|
|
answ = await dbCtx.SaveChangesAsync();
|
|
|
|
// libero memoria del changeTracker
|
|
dbCtx.ChangeTracker.Clear();
|
|
}
|
|
return answ;
|
|
}
|
|
#endif
|
|
#if false
|
|
|
|
/// <summary>
|
|
/// Recupera dati stats di dettaglio dato filtro envir/tipo (opzionali) e periodo
|
|
/// </summary>
|
|
/// <param name="dtStart"></param>
|
|
/// <param name="dtEnd"></param>
|
|
/// <param name="sEnvir"></param>
|
|
/// <param name="sType"></param>
|
|
/// <returns></returns>
|
|
internal async Task<List<StatsDetailModel>> StatsDetailModelGetAsync(DateTime dtStart, DateTime dtEnd, string sEnvir = "", string sType = "")
|
|
{
|
|
List<StatsDetailModel> answ = new List<StatsDetailModel>();
|
|
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
|
|
using (DataLayerContext dbCtx = new DataLayerContext())
|
|
{
|
|
// recupero ed ordino per data-ora
|
|
var query = dbCtx.DbSetStatsDet
|
|
.Where(x => x.Hour >= dtStart && x.Hour <= dtEnd);
|
|
|
|
if (!string.IsNullOrEmpty(sEnvir))
|
|
query = query.Where(x => x.Environment == sEnvir);
|
|
|
|
if (!string.IsNullOrEmpty(sType))
|
|
query = query.Where(x => x.Type == sType);
|
|
|
|
answ = await query
|
|
.AsNoTracking()
|
|
.OrderBy(x => x.Hour)
|
|
.ThenBy(x => x.Environment)
|
|
.ThenBy(x => x.Type)
|
|
.ToListAsync();
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Range periodo x chiamate detail eventualmente filtrate
|
|
/// </summary>
|
|
/// <param name="sEnvir"></param>
|
|
/// <param name="sType"></param>
|
|
/// <returns></returns>
|
|
internal async Task<Utils.DtUtils.Periodo> StatsDetailModelRangeAsync(string sEnvir, string sType)
|
|
{
|
|
Utils.DtUtils.Periodo answ = new Utils.DtUtils.Periodo(Utils.DtUtils.PeriodSet.Today);
|
|
using (DataLayerContext dbCtx = new DataLayerContext())
|
|
{
|
|
var query = dbCtx.DbSetStatsDet.AsQueryable();
|
|
|
|
if (!string.IsNullOrEmpty(sEnvir))
|
|
query = query.Where(x => x.Environment == sEnvir);
|
|
|
|
if (!string.IsNullOrEmpty(sType))
|
|
query = query.Where(x => x.Type == sType);
|
|
|
|
var minHour = await query.MinAsync(x => x.Hour);
|
|
var maxHour = await query.MaxAsync(x => x.Hour);
|
|
answ.Inizio = minHour;
|
|
answ.Fine = maxHour;
|
|
return answ;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esegue insert statistiche di dettaglio sul DB
|
|
/// </summary>
|
|
/// <param name="listRecords">Elenco dei record da inserire</param>
|
|
/// <param name="removeOld">Se true preventivamente elimina record nel periodo richiesto</param>
|
|
/// <returns></returns>
|
|
internal async Task<long> StatsDetailModelUpsertAsync(List<StatsDetailModel> listRecords, bool removeOld)
|
|
{
|
|
int answ = 0;
|
|
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
|
|
using (DataLayerContext dbCtx = new DataLayerContext())
|
|
{
|
|
// in primis se richiesto calcolo range periodo e svuoto...
|
|
if (removeOld)
|
|
{
|
|
var firstRec = listRecords.OrderBy(x => x.Hour).FirstOrDefault();
|
|
var lastRec = listRecords.OrderByDescending(x => x.Hour).FirstOrDefault();
|
|
|
|
if (firstRec != null && lastRec != null)
|
|
{
|
|
DateTime startDate = firstRec.Hour;
|
|
DateTime endDate = lastRec.Hour;
|
|
// uso direttamente ExecuteDelete
|
|
await dbCtx
|
|
.DbSetStatsDet
|
|
.Where(x => x.Hour >= startDate && x.Hour <= endDate)
|
|
.ExecuteDeleteAsync();
|
|
}
|
|
}
|
|
|
|
// ora preparo inserimento massivo
|
|
await dbCtx
|
|
.DbSetStatsDet
|
|
.AddRangeAsync(listRecords);
|
|
|
|
// salvo!
|
|
answ = await dbCtx.SaveChangesAsync();
|
|
|
|
// libero memoria del changeTracker
|
|
dbCtx.ChangeTracker.Clear();
|
|
}
|
|
return answ;
|
|
}
|
|
#endif
|
|
|
|
|
|
#endregion Internal Methods
|
|
|
|
#region Private Fields
|
|
|
|
private static IConfiguration _configuration;
|
|
|
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
|
|
|
#endregion Private Fields
|
|
}
|
|
} |