From 571ac2cea8d0ae77bd5f644b4f239e1f5cde4de6 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Fri, 20 Oct 2023 18:54:28 +0200 Subject: [PATCH] SPEC: - inizio pagina gest fluxLog e pareto eventi - ok visualizzazione ultimo mese --- MP.Data/Controllers/MpSpecController.cs | 50 +++++++- MP.Data/DTO/ParetoFluxLogDTO.cs | 11 ++ MP.Data/DatabaseModels/AnagKeyValueModel.cs | 26 ++++ MP.Data/MoonProContext.cs | 7 +- MP.Data/Utils.cs | 2 + MP.SPEC/Components/FLStatusList.razor | 40 +++++++ MP.SPEC/Components/FLStatusList.razor.cs | 126 ++++++++++++++++++++ MP.SPEC/Data/MpDataService.cs | 107 ++++++++++++++--- MP.SPEC/MP.SPEC.csproj | 5 +- MP.SPEC/Pages/FluxLogStatus.razor | 37 ++++++ MP.SPEC/Pages/FluxLogStatus.razor.cs | 107 +++++++++++++++++ MP.SPEC/Resources/ChangeLog.html | 2 +- MP.SPEC/Resources/VersNum.txt | 2 +- MP.SPEC/Resources/manifest.xml | 2 +- MP.SPEC/wwwroot/images/LogoEgw.png | Bin 0 -> 3346 bytes 15 files changed, 494 insertions(+), 30 deletions(-) create mode 100644 MP.Data/DTO/ParetoFluxLogDTO.cs create mode 100644 MP.Data/DatabaseModels/AnagKeyValueModel.cs create mode 100644 MP.SPEC/Components/FLStatusList.razor create mode 100644 MP.SPEC/Components/FLStatusList.razor.cs create mode 100644 MP.SPEC/Pages/FluxLogStatus.razor create mode 100644 MP.SPEC/Pages/FluxLogStatus.razor.cs create mode 100644 MP.SPEC/wwwroot/images/LogoEgw.png diff --git a/MP.Data/Controllers/MpSpecController.cs b/MP.Data/Controllers/MpSpecController.cs index bfdc9cf0..f00881f2 100644 --- a/MP.Data/Controllers/MpSpecController.cs +++ b/MP.Data/Controllers/MpSpecController.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using MP.Data.DatabaseModels; +using MP.Data.DTO; using NLog; using System; using System.Collections.Generic; @@ -53,10 +54,10 @@ namespace MP.Data.Controllers using (var dbCtx = new MoonProContext(_configuration)) { dbResult = dbCtx - .DbSetAnagGruppi - .AsNoTracking() - .OrderBy(x => x.CodGruppo) - .ToList(); + .DbSetAnagGruppi + .AsNoTracking() + .OrderBy(x => x.CodGruppo) + .ToList(); } return dbResult; } @@ -81,6 +82,24 @@ namespace MP.Data.Controllers return dbResult; } + /// + /// Elenco Gruppi + /// + /// + public List AnagKeyValGetAll() + { + List dbResult = new List(); + using (var dbCtx = new MoonProContext(_configuration)) + { + dbResult = dbCtx + .DbSetAKV + .AsNoTracking() + .OrderBy(x => x.nomeVar) + .ToList(); + } + return dbResult; + } + /// /// Elenco valori ammessi x Stati commessa (es Yacht Baglietto) /// @@ -737,7 +756,7 @@ namespace MP.Data.Controllers } catch (Exception exc) { - Log.Error($"Eccezione in MacchineGetFilt{Environment.NewLine}{exc}"); + Log.Error($"Eccezione in MacchineByMatrOper{Environment.NewLine}{exc}"); } return dbResult; } @@ -1067,6 +1086,27 @@ namespace MP.Data.Controllers return dbResult; } + /// + /// Elenco Gruppi + /// + /// + public List ParetoFluxLog(string idxMacchina, DateTime dtFrom, DateTime dtTo) + { + List dbResult = new List(); + using (var dbCtx = new MoonProContext(_configuration)) + { + dbResult = dbCtx + .DbSetFluxLog + .Where(x => (string.IsNullOrEmpty(idxMacchina) || x.IdxMacchina == idxMacchina) && (dtFrom <= x.dtEvento && x.dtEvento <= dtTo)) + .AsNoTracking() + .GroupBy(x => x.CodFlux) + .Select(g => new ParetoFluxLogDTO() { IdxMacchina = idxMacchina, CodFlux = g.Key, Qty = g.Count() }) + .OrderByDescending(x => x.Qty) + .ToList(); + } + return dbResult; + } + /// /// Stato prod macchina /// diff --git a/MP.Data/DTO/ParetoFluxLogDTO.cs b/MP.Data/DTO/ParetoFluxLogDTO.cs new file mode 100644 index 00000000..5367b141 --- /dev/null +++ b/MP.Data/DTO/ParetoFluxLogDTO.cs @@ -0,0 +1,11 @@ +using System; + +namespace MP.Data.DTO +{ + public class ParetoFluxLogDTO + { + public string IdxMacchina { get; set; } + public string CodFlux { get; set; } + public int Qty { get; set; } + } +} \ No newline at end of file diff --git a/MP.Data/DatabaseModels/AnagKeyValueModel.cs b/MP.Data/DatabaseModels/AnagKeyValueModel.cs new file mode 100644 index 00000000..22fbf280 --- /dev/null +++ b/MP.Data/DatabaseModels/AnagKeyValueModel.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +#nullable disable +// +// This is here so CodeMaid doesn't reorganize this document +// +namespace MP.Data.DatabaseModels +{ + [Table("AnagKeyValue")] + public partial class AnagKeyValueModel + { + #region Public Properties + + [Key] + public string nomeVar { get; set; } = ""; + public int valInt { get; set; } = 0; + public double valFloat { get; set; } = 0; + public string valString { get; set; } = ""; + public string descrizione { get; set; } = ""; + + #endregion Public Properties + } +} \ No newline at end of file diff --git a/MP.Data/MoonProContext.cs b/MP.Data/MoonProContext.cs index 403a09ab..aad4e57d 100644 --- a/MP.Data/MoonProContext.cs +++ b/MP.Data/MoonProContext.cs @@ -39,6 +39,7 @@ namespace MP.Data public virtual DbSet DbSetAlarmLog { get; set; } + public virtual DbSet DbSetAKV { get; set; } public virtual DbSet DbSetStatArticoli { get; set; } public virtual DbSet DbSetArticoli { get; set; } public virtual DbSet DbSetAnagEventi { get; set; } @@ -94,6 +95,7 @@ namespace MP.Data public virtual DbSet DbSetVSEB { get; set; } public virtual DbSet DbSetVSODL { get; set; } public virtual DbSet DbSetVSCS { get; set; } + public virtual DbSet DbSetParetoFluxLog { get; set; } #endregion Public Properties @@ -566,7 +568,10 @@ namespace MP.Data { entity.ToView("v_DD_exp"); }); - + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.IdxMacchina, e.CodFlux }); + }); OnModelCreatingPartial(modelBuilder); } diff --git a/MP.Data/Utils.cs b/MP.Data/Utils.cs index 77f04a19..4a4efbaf 100644 --- a/MP.Data/Utils.cs +++ b/MP.Data/Utils.cs @@ -230,6 +230,8 @@ namespace MP.Data public const string redisArtList = redisBaseAddr + "Cache:ArtList"; public const string redisBaseAddr = "MP:"; public const string redisConfKey = redisBaseAddr + "Cache:Config"; + public const string redisAKVKey = redisBaseAddr + "Cache:AKV"; + public const string redisParetoFLKey = redisBaseAddr + "Cache:ParetoFL"; public const string redisDossByMac = redisBaseAddr + "Cache:DossByMac"; public const string redisFluxByMac = redisBaseAddr + "Cache:FluxByMac"; public const string redisFluxLogFilt = redisBaseAddr + "Cache:FluxLogFilt"; diff --git a/MP.SPEC/Components/FLStatusList.razor b/MP.SPEC/Components/FLStatusList.razor new file mode 100644 index 00000000..9a31dad1 --- /dev/null +++ b/MP.SPEC/Components/FLStatusList.razor @@ -0,0 +1,40 @@ +@if (isProcessing) +{ + +} +else +{ + + + + + + + + + + + + @foreach (var item in ListPaged) + { + + + + + + + + } + +
MacchinaKeyQty#/gg#/h
+ @item.IdxMacchina + + @item.CodFlux + + @($"{item.Qty:N0}") + + @($"{(item.Qty / numDay):N0}") + + @($"{(item.Qty / numHour):N0}") +
+} diff --git a/MP.SPEC/Components/FLStatusList.razor.cs b/MP.SPEC/Components/FLStatusList.razor.cs new file mode 100644 index 00000000..0cc0e41b --- /dev/null +++ b/MP.SPEC/Components/FLStatusList.razor.cs @@ -0,0 +1,126 @@ +using global::Microsoft.AspNetCore.Components; +using MP.Data.DTO; +using MP.SPEC.Data; +using NLog; +using static EgwCoreLib.Utils.DtUtils; + +namespace MP.SPEC.Components +{ + public partial class FLStatusList + { + #region Public Properties + + [Parameter] + public Periodo CurrPeriodo { get; set; } = new Periodo(); + + [Parameter] + public EventCallback E_TotalCount { get; set; } + + [Parameter] + public string IdxMaccSel { get; set; } = ""; + + [Parameter] + public int NumRecPage { get; set; } = 10; + + [Parameter] + public int PageNum { get; set; } = 1; + + #endregion Public Properties + + #region Protected Properties + + protected List ListComplete { get; set; } = new List(); + protected List ListPaged { get; set; } = new List(); + + [Inject] + protected MpDataService MDataServ { get; set; } = null!; + + protected int TotalCount + { + get => totalCount; + set + { + if (totalCount != value) + { + totalCount = value; + E_TotalCount.InvokeAsync(value).ConfigureAwait(false); + } + } + } + + protected int numDay + { + get + { + var numRaw = (int)CurrPeriodo.Fine.Subtract(CurrPeriodo.Inizio).TotalDays; + int answ = numRaw > 1 ? numRaw : 1; + return answ; + } + } + protected int numHour + { + get + { + var numRaw = (int)CurrPeriodo.Fine.Subtract(CurrPeriodo.Inizio).TotalHours; + int answ = numRaw > 1 ? numRaw : 1; + return answ; + } + } + + #endregion Protected Properties + + #region Protected Methods + + protected override async Task OnParametersSetAsync() + { + await ReloadData(); + } + + /// + /// Aggiorno valori produzione alla data richiesta... + /// + /// + protected async Task ReloadData() + { + isProcessing = true; + await Task.Delay(1); + if (!string.IsNullOrEmpty(IdxMaccSel) && (!IdxMaccSel.Equals(idxMaccLast) || !CurrPeriodo.Equals(lastPeriodo))) + { + idxMaccLast = IdxMaccSel; + lastPeriodo = CurrPeriodo; + ListComplete = await MDataServ.ParetoFluxLog(IdxMaccSel, CurrPeriodo.Inizio, CurrPeriodo.Fine); + TotalCount = ListComplete.Count; + } + // esegue paginazione + UpdateTable(); + isProcessing = false; + await Task.Delay(1); + } + + protected void UpdateTable() + { + // esegue paginazione + if (TotalCount > NumRecPage) + { + ListPaged = ListComplete.Skip((PageNum - 1) * NumRecPage).Take(NumRecPage).ToList(); + } + else + { + ListPaged = ListComplete; + } + } + + #endregion Protected Methods + + #region Private Fields + + private static NLog.Logger Log = LogManager.GetCurrentClassLogger(); + private bool isProcessing = false; + private int totalCount = 0; + + private string idxMaccLast = ""; + private Periodo lastPeriodo { get; set; } = new Periodo(); + + #endregion Private Fields + } +} \ No newline at end of file diff --git a/MP.SPEC/Data/MpDataService.cs b/MP.SPEC/Data/MpDataService.cs index 9176fd47..34ce70cf 100644 --- a/MP.SPEC/Data/MpDataService.cs +++ b/MP.SPEC/Data/MpDataService.cs @@ -119,6 +119,40 @@ namespace MP.SPEC.Data return fatto; } + /// + /// Elenco Gruppi + /// + /// + public async Task> AnagKeyValGetAll() + { + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + string source = "DB"; + List? result = new List(); + // cerco in redis... + RedisValue rawData = await redisDb.StringGetAsync(Utils.redisAKVKey); + if (!string.IsNullOrEmpty($"{rawData}")) + { + result = JsonConvert.DeserializeObject>($"{rawData}"); + source = "REDIS"; + } + else + { + result = await Task.FromResult(dbController.AnagKeyValGetAll()); + // serializzo e salvo... + rawData = JsonConvert.SerializeObject(result); + await redisDb.StringSetAsync(Utils.redisConfKey, rawData, getRandTOut(redisLongTimeCache)); + } + if (result == null) + { + result = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AnagKeyValGetAll Read from {source}: {ts.TotalMilliseconds}ms"); + return result; + } + public async Task> AnagStatiComm() { Stopwatch stopWatch = new Stopwatch(); @@ -389,6 +423,25 @@ namespace MP.SPEC.Data await redisDb.StringSetAsync(Utils.redisConfKey, ""); } + /// + /// Restituisce valore della stringa (SE disponibile) + /// + /// + /// + public async Task ConfigTryGet(string keyName) + { + string answ = ""; + // preselezione valori + var configData = await ConfigGetAll(); + var currRec = configData.FirstOrDefault(x => x.Chiave == keyName); + if (currRec != null) + { + answ = currRec.Valore; + } + + return answ; + } + /// /// Update chiave config /// @@ -1123,6 +1176,41 @@ namespace MP.SPEC.Data return result; } + /// + /// Elenco Gruppi + /// + /// + public async Task> ParetoFluxLog(string idxMacchina, DateTime dtFrom, DateTime dtTo) + { + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + string source = "DB"; + List? result = new List(); + // cerco in redis... + string redKey = $"{Utils.redisParetoFLKey}:{idxMacchina}:{dtFrom:yyyyMMdd}:{dtTo:yyyyMMdd}"; + RedisValue rawData = await redisDb.StringGetAsync(redKey); + if (!string.IsNullOrEmpty($"{rawData}")) + { + result = JsonConvert.DeserializeObject>($"{rawData}"); + source = "REDIS"; + } + else + { + result = await Task.FromResult(dbController.ParetoFluxLog(idxMacchina, dtFrom, dtTo)); + // serializzo e salvo... + rawData = JsonConvert.SerializeObject(result); + await redisDb.StringSetAsync(redKey, rawData, getRandTOut(redisLongTimeCache)); + } + if (result == null) + { + result = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"ParetoFluxLog Read from {source}: {ts.TotalMilliseconds}ms"); + return result; + } + /// /// Eliminazione record selezionato /// @@ -1511,25 +1599,6 @@ namespace MP.SPEC.Data return answ; } - /// - /// Restituisce valore della stringa (SE disponibile) - /// - /// - /// - public async Task ConfigTryGet(string keyName) - { - string answ = ""; - // preselezione valori - var configData = await ConfigGetAll(); - var currRec = configData.FirstOrDefault(x => x.Chiave == keyName); - if (currRec != null) - { - answ = currRec.Valore; - } - - return answ; - } - public async Task updateDossierValue(DossierModel currDoss, FluxLogDTO editFL) { bool answ = false; diff --git a/MP.SPEC/MP.SPEC.csproj b/MP.SPEC/MP.SPEC.csproj index 5c7a8ef1..602c8c04 100644 --- a/MP.SPEC/MP.SPEC.csproj +++ b/MP.SPEC/MP.SPEC.csproj @@ -5,7 +5,7 @@ enable enable MP.SPEC - 6.16.2310.411 + 6.16.2310.2018 @@ -39,7 +39,8 @@ - + + diff --git a/MP.SPEC/Pages/FluxLogStatus.razor b/MP.SPEC/Pages/FluxLogStatus.razor new file mode 100644 index 00000000..4b36b514 --- /dev/null +++ b/MP.SPEC/Pages/FluxLogStatus.razor @@ -0,0 +1,37 @@ +@page "/FluxLogStatus" + +
+
+
+
+

FluxLogStatus

+
+
+
+ Macc + +
+
+
+ +
+
+
+
+ + +
+
\ No newline at end of file diff --git a/MP.SPEC/Pages/FluxLogStatus.razor.cs b/MP.SPEC/Pages/FluxLogStatus.razor.cs new file mode 100644 index 00000000..96a919d5 --- /dev/null +++ b/MP.SPEC/Pages/FluxLogStatus.razor.cs @@ -0,0 +1,107 @@ +using global::Microsoft.AspNetCore.Components; +using MP.SPEC.Data; +using NLog; +using System; +using static EgwCoreLib.Utils.DtUtils; + +namespace MP.SPEC.Pages +{ + public partial class FluxLogStatus + { + #region Protected Fields + + protected int numRecPage = 10; + protected int pageNum = 1; + protected int totalCount = 0; + + #endregion Protected Fields + + #region Protected Properties + + protected Dictionary ListMacchineAll { get; set; } = new Dictionary(); + + [Inject] + protected MpDataService MDataServ { get; set; } = null!; + + #endregion Protected Properties + + #region Protected Methods + + protected override async Task OnInitializedAsync() + { + await ReloadData(); + } + + protected async Task ReloadMacchine() + { + if (ListMacchineAll == null || ListMacchineAll.Count == 0) + { + var rawData = await MDataServ.MacchineGetFilt("*"); + // trasformo! + if (rawData != null) + { + ListMacchineAll = rawData.ToDictionary(x => x.IdxMacchina, x => $"{x.IdxMacchina} | {x.Nome}"); + } + } + } + + protected void SaveNumRec(int newNum) + { + if (numRecPage >= newNum) + { + numRecPage = newNum; + } + } + + protected void SavePage(int newNum) + { + if (pageNum >= newNum) + { + pageNum = newNum; + } + } + + protected async Task SetPeriodo(Periodo newPeriodo) + { + if (!CurrPeriodo.Equals(newPeriodo)) + { + CurrPeriodo = newPeriodo; + } + await Task.Delay(1); + } + + protected async Task SetTotCount(int numRec) + { + totalCount = numRec; + await Task.Delay(1); + } + + #endregion Protected Methods + + #region Private Fields + + private static NLog.Logger Log = LogManager.GetCurrentClassLogger(); + + #endregion Private Fields + + #region Private Properties + + private Periodo CurrPeriodo { get; set; } = new Periodo(); + + private string idxMaccSel { get; set; } = ""; + + #endregion Private Properties + + #region Private Methods + + private async Task ReloadData() + { + await ReloadMacchine(); + DateTime dtEnd = DateTime.Today.AddDays(1); + DateTime dtStart = dtEnd.AddMonths(-1); + CurrPeriodo = new Periodo(dtStart, dtEnd); + } + + #endregion Private Methods + } +} \ No newline at end of file diff --git a/MP.SPEC/Resources/ChangeLog.html b/MP.SPEC/Resources/ChangeLog.html index 9d10fe0a..79eb0707 100644 --- a/MP.SPEC/Resources/ChangeLog.html +++ b/MP.SPEC/Resources/ChangeLog.html @@ -1,6 +1,6 @@ Modulo MAPOSPEC -

Versione: 6.16.2310.411

+

Versione: 6.16.2310.2018


Note di rilascio:
  • diff --git a/MP.SPEC/Resources/VersNum.txt b/MP.SPEC/Resources/VersNum.txt index e1d45667..f3926b0c 100644 --- a/MP.SPEC/Resources/VersNum.txt +++ b/MP.SPEC/Resources/VersNum.txt @@ -1 +1 @@ -6.16.2310.411 +6.16.2310.2018 diff --git a/MP.SPEC/Resources/manifest.xml b/MP.SPEC/Resources/manifest.xml index e89e8d38..6782cc6c 100644 --- a/MP.SPEC/Resources/manifest.xml +++ b/MP.SPEC/Resources/manifest.xml @@ -1,6 +1,6 @@ - 6.16.2310.411 + 6.16.2310.2018 https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/MP.SPEC.zip https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/ChangeLog.html false diff --git a/MP.SPEC/wwwroot/images/LogoEgw.png b/MP.SPEC/wwwroot/images/LogoEgw.png new file mode 100644 index 0000000000000000000000000000000000000000..f65c23cac27389ef8e3029f8bc901a1e8008f1ac GIT binary patch literal 3346 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 kF}9Wy5kaU+OVjEUG`HQKyhT3