This commit is contained in:
zaccaria.majid
2023-10-02 16:30:42 +02:00
9 changed files with 816 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
<div class="card shadow" style="width: 22rem">
<div class="card-header @CardCss">
<h3 class="card-title">
@if (DoPlaceholder)
{
<div class="placeholder-glow">
<span class="placeholder col-6"></span>
</div>
}
else
{
@Title
}
</h3>
</div>
<div class="card-body">
@if (DoPlaceholder)
{
<div class="placeholder-glow">
<span class="placeholder col-7"></span>
<span class="placeholder col-4"></span>
<span class="placeholder col-4"></span>
<span class="placeholder col-7"></span>
</div>
}
else
{
<p class="card-text">@ChildContent</p>
}
</div>
<div class="card-footer">
@if (DoPlaceholder)
{
<div class="placeholder-glow">
<button class="btn btn-sm btn-dark disabled placeholder col-2"></button>
</div>
}
else
{
<button class="btn btn-sm btn-dark" @onclick="OnYes">Yes!</button>
}
</div>
</div>
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public string? Title { get; set; }
[Parameter]
public string CardCss { get; set; } = "";
[Parameter]
public bool DoPlaceholder { get; set; } = false;
private void OnYes()
{
Console.WriteLine($"{Title} | 'Yes' button selected.");
}
}
+304
View File
@@ -0,0 +1,304 @@
@if (RecMSE == null)
{
<div class="card text-white mapBlock shadow bg-secondary p-0 m-0">
<div class="card-body">
<div class="placeholder-glow">
<span class="placeholder col-7"></span>
<span class="placeholder col-4"></span>
<span class="placeholder col-4"></span>
<span class="placeholder col-7"></span>
</div>
</div>
</div>
}
else
{
@if (FullMode)
{
<div class="card mapBlock shadow @RecMSE.Semaforo p-0 m-0" @onclick="ShowDetail">
<img src="@ImgUrlMacc(RecMSE.Url)" class="card-img-top" alt="@RecMSE.CodMacchina">
<div class="m-1 bg-black">
<div class="card-img-overlay p-0 d-flex flex-row-reverse align-items-start rCTop">
<div class="labelTopDx px-2 w-100">
<div class="row">
<div class="col-3 text-start">
Art
</div>
<div class="col-9 text-end fw-bold">
@RecMSE.CodArticolo
</div>
</div>
<div class="row small">
<div class="col-6 text-start">
@($"PODL{RecMSE.IdxPOdl:00000000}")
</div>
<div class="col-6 text-end">
@($"ODL{RecMSE.IdxOdl:00000000}")
</div>
</div>
</div>
</div>
<div class="card bg-black text-light p-0 rCBot">
<div class="row justify-content-center mx-0">
<div class="col-12 px-1">
<h4 class="text-start mb-0 fw-bold">
@RecMSE.Nome
</h4>
</div>
<div class="col-12 p-1 @RecMSE.Semaforo">
<div class="d-flex justify-content-between">
<div class="p-0 mr-auto">
<b>@RecMSE.DescrizioneStato</b>
</div>
<div class="p-0">
@(formatDurata(RecMSE.Durata))
</div>
</div>
</div>
<div class="col-12 p-0 text-nowrap" style="font-size: 1.2em; line-height: 1.0em;">
<div class="row px-1 fw-bold" style="font-size:0.85em;">
<div class="col pe-0">
<small class="text-success">Confermati</small>
</div>
<div class="col text-center px-0">
<small class="text-warning">Prodotti</small>
</div>
<div class="col text-end ps-0">
<small class="text-light">Ordinati</small>
</div>
</div>
<div class="row px-1">
<div class="col pe-0 text-success">
@($"{RecMSE.PezziConf:N0}")
</div>
<div class="col text-center text-warning px-0">
@($"{RecMSE.PezziProd:N0}")
</div>
<div class="col text-end ps-0">
@($"{RecMSE.NumPezzi:N0}")
</div>
</div>
<div class="row">
<div class="col m-1">
<ProdAdvDispl CountData="@CurrCount"></ProdAdvDispl>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
else
{
<div class="card mapBlock shadow @RecMSE.Semaforo p-0 m-0">
<div class="m-1 bg-black">
<div class="card-img-overlay p-0 d-flex flex-row-reverse align-items-start rCTop">
<div class="labelTopDx px-2 w-100">
<div class="row">
<div class="col-3 text-start">
Art
</div>
<div class="col-9 text-end fw-bold">
@RecMSE.CodArticolo
</div>
</div>
<div class="row small">
<div class="col-6 text-start">
@($"PODL{RecMSE.IdxPOdl:00000000}")
</div>
<div class="col-6 text-end">
@($"ODL{RecMSE.IdxOdl:00000000}")
</div>
</div>
</div>
</div>
<div class="card bg-black text-light p-0 rCBot">
<div class="row justify-content-center mx-0">
<div class="col-12 px-1">
<h4 class="text-start mb-0 fw-bold">
@RecMSE.Nome
</h4>
</div>
<div class="col-12 p-1 @RecMSE.Semaforo">
<div class="d-flex justify-content-between">
<div class="p-0 mr-auto">
<b>@RecMSE.DescrizioneStato</b>
</div>
<div class="p-0">
@(formatDurata(RecMSE.Durata))
</div>
</div>
</div>
<div class="col-12 p-0 text-nowrap" style="font-size: 1.2em; line-height: 1.0em;">
<div class="row px-1 fw-bold" style="font-size:0.85em;">
<div class="col pe-0">
<small class="text-success">Confermati</small>
</div>
<div class="col text-center px-0">
<small class="text-warning">Prodotti</small>
</div>
<div class="col text-end ps-0">
<small class="text-light">Ordinati</small>
</div>
</div>
<div class="row px-1">
<div class="col pe-0 text-success">
@($"{RecMSE.PezziConf:N0}")
</div>
<div class="col text-center text-warning px-0">
@($"{RecMSE.PezziProd:N0}")
</div>
<div class="col text-end ps-0">
@($"{RecMSE.NumPezzi:N0}")
</div>
</div>
<div class="row">
<div class="col m-1">
<ProdAdvDispl CountData="@CurrCount"></ProdAdvDispl>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
}
@code {
[Parameter]
public MappaStatoExpl? RecMSE { get; set; } = null;
[Parameter]
public int keepAliveMin { get; set; } = 5;
[Parameter]
public bool FullMode { get; set; } = true;
[Inject]
protected NavigationManager NavMan { get; set; } = null!;
protected int kaFactor = 60 / 2;
protected override void OnInitialized()
{
// se configurata uso cartella virtuale... altrimenti cartella processo
imgBasePath = Environment.CurrentDirectory;// @"C:\Steamware\macchine";
}
private string imgBasePath = "";
protected ProdAdvDispl.ProdCounter CurrCount
{
get
{
ProdAdvDispl.ProdCounter answ = new ProdAdvDispl.ProdCounter()
{
numPzConf = (int)RecMSE.PezziConf,
numPzOrd= (int)RecMSE.NumPezzi,
numPzProd= (int)RecMSE.PezziProd
};
return answ;
}
}
protected async Task ShowDetail()
{
NavMan.NavigateTo($"machine-detail/?IdxMacc={RecMSE!.IdxMacchina}");
}
/// <summary>
/// url completo immagine
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public string ImgUrlMacc(string url)
{
string answ = "";
if (string.IsNullOrEmpty(url.ToString()))
{
url = "Steamware.png";
}
// verifico esistenza macchina SMALL...
answ = $"images/macchine/small/{url}";
string fullPath = Path.Combine(imgBasePath, "wwwroot", answ);
if (!File.Exists(fullPath))
{
// se non ci fosse cerco immagine fullsize
answ = $"images/macchine/{url}";
fullPath = Path.Combine(imgBasePath, "wwwroot", answ);
}
// altrimenti metto default Steamware
if (!File.Exists(fullPath))
{
answ = "images/macchine/Steamware.png";
}
return answ;
}
/// <summary>
/// formatta la durata in minuti in un modo "human readable" gg/ore/min
/// </summary>
/// <param name="minuti">minuti</param>
/// <returns></returns>
public static string formatDurata(double? minuti)
{
string answ = "??";
if (minuti != null)
{
DateTime tempo = new DateTime();
tempo = tempo.AddMinutes((double)minuti);
if (minuti < 60)
{
answ = $"{tempo.Minute:00}:{tempo.Second:00}";
}
else if (minuti < 1440)
{
answ = $"{tempo.Hour}h {tempo.Minute}m";
}
else
{
answ = $"{tempo.DayOfYear}g {tempo.Hour}h";
}
}
return answ;
}
private string cssComStatus(string semaforo, DateTime? lastUpdateN)
{
DateTime lastUpdate = lastUpdateN.HasValue ? (DateTime)lastUpdateN : DateTime.Now.AddHours(-1);
string answ = cssStatus(semaforo);
if (DateTime.Now.Subtract(lastUpdate).TotalSeconds > (keepAliveMin * kaFactor))
{
answ = $"{baseCss}Ro";
// blink se secondo pari...
DateTime adesso = DateTime.Now;
int resto = 0;
Math.DivRem(adesso.Second, 2, out resto);
if (resto == 0)
{
answ += "_b";
}
}
return answ;
}
private string cssStatus(string codSemaforo)
{
// se vuoto --> mostra nero!
if (string.IsNullOrEmpty(codSemaforo))
{
codSemaforo = "sNe";
}
string codColore = codSemaforo.Substring(1, 2);
string answ = $"{baseCss}{codColore}";
return answ;
}
protected string baseCss = "sem";
}
+121
View File
@@ -0,0 +1,121 @@
<div class="progress">
<div runat="server" id="divGreen" class="progress-bar bg-success" style="width: @(NumGreen)%">
@($"{NumGreen}%")
</div>
<div runat="server" id="divYellow" class="progress-bar bg-warning text-dark" style="width: @(NumYellow)%">
@($"{NumYellow}%")
</div>
<div runat="server" id="divGray" class="progress-bar bg-secondary" style="width: @(NumGray)%">
@($"{NumGray}%")
</div>
<div runat="server" id="divBlue" class="progress-bar bg-primary" style="width: @(NumBlue)%">
@($"{NumBlue}%")
</div>
</div>
<asp:HiddenField runat="server" ID="hfProdCount" />
@code {
/// <summary>
/// Conteggio dati produzione da mostrare
/// </summary>
[Parameter]
public ProdCounter CountData { get; set; } = null!;
/// <summary>
/// Numero pezzi CONFERMATI
/// </summary>
protected int NumPzConf { get; set; } = 0;
/// <summary>
/// Numero pezzi ORDINATI
/// </summary>
protected int NumPzOrd { get; set; } = 0;
/// <summary>
/// Numero pezzi PRODOTTI
/// </summary>
protected int NumPzProd { get; set; } = 0;
/// <summary>
/// Numero pezzi per display BLU
/// </summary>
protected int NumBlue { get; set; } = 0;
/// <summary>
/// Numero pezzi per display GRIGIO
/// </summary>
protected int NumGray { get; set; } = 40;
/// <summary>
/// Numero pezzi per display VERDE
/// </summary>
protected int NumGreen { get; set; } = 20;
/// <summary>
/// Numero pezzi per display GIALLO
/// </summary>
protected int NumYellow { get; set; } = 30;
protected override void OnParametersSet()
{
decimal denom = CountData.numPzProd > CountData.numPzOrd ? (decimal)CountData.numPzProd / 100 : (decimal)CountData.numPzOrd / 100;
denom = denom == 0 ? 1 : denom;
// calcolo se sono nel caso prod < ordinati o se sono andato OVER
if (CountData.numPzProd < CountData.numPzOrd)
{
nGreen = (int)Math.Floor((decimal)CountData.numPzConf / denom);
nYellow = (int)Math.Floor((decimal)(CountData.numPzProd - CountData.numPzConf) / denom);
nGray = 100 - (nGreen + nYellow);
nBlue = 0;
}
else
{
// devo verificare SE ne ho confermati meno che ordinati o meno...
if (CountData.numPzConf < CountData.numPzOrd)
{
nGreen = (int)Math.Floor((decimal)CountData.numPzConf / denom);
}
else
{
nGreen = (int)Math.Floor((decimal)CountData.numPzOrd / denom);
}
nBlue = 100 - nGreen;
nYellow = 0;
nGray = 0;
}
// disegno!
NumGreen = nGreen;
NumYellow = nYellow;
NumGray = nGray;
NumBlue = nBlue;
// base.OnParametersSet();
}
protected int nBlue = 0;
protected int nGray = 0;
protected int nGreen = 0;
protected int nYellow = 0;
/// <summary>
/// Classe gesitone conteggi produzione
/// </summary>
public class ProdCounter
{
/// <summary>
/// NUmero pezzi CONFERMATI
/// </summary>
public int numPzConf { get; set; } = 0;
/// <summary>
/// Numero pezzi ORDINATI
/// </summary>
public int numPzOrd { get; set; } = 0;
/// <summary>
/// Numero pezzi PRODOTTI
/// </summary>
public int numPzProd { get; set; } = 0;
}
}
+58
View File
@@ -0,0 +1,58 @@
@page "/CardDemo"
@if (CardTitles == null || CardTitles.Count == 0)
{
<div class="row">
<div class="col-12 my-2 alert alert-secondary fs-3">
waiting on browser
</div>
@for (int i = 0; i < 9; i++)
{
<div class="col-4 my-2">
<CardTest Title="Loading" DoPlaceholder="true" CardCss="bg-secondary">
</CardTest>
</div>
}
</div>
}
else
{
<div class="row">
<div class="col-12 my-2 alert alert-success fs-3">
loaded on browser
</div>
@foreach (var item in CardTitles)
{
<div class="col-4 my-2">
<CardTest Title="@item">
<div>
Demo content page for <b>@item</b>
</div>
<div>
This is an example Blazor component!
</div>
</CardTest>
</div>
}
</div>
}
@code {
protected List<string>? CardTitles { get; set; }
protected override async Task OnInitializedAsync()
{
CardTitles = null;
await Task.Delay(300);
CardTitles = new List<string>();
await InvokeAsync(StateHasChanged);
await Task.Delay(300);
for (int i = 0; i < 9; i++)
{
CardTitles.Add($"Card {i + 1:00}");
}
await Task.Delay(10);
}
}
+7
View File
@@ -0,0 +1,7 @@
@page "/hw-sw-info"
<h3>HwSwInfo</h3>
@code {
}
+8
View File
@@ -0,0 +1,8 @@
@page "/logout"
<h3>logout</h3>
@code {
}
+34
View File
@@ -0,0 +1,34 @@
@page "/machine-detail"
@using MP.Data.Services;
@inject MessageService MsgServ
<h1>Machine Detail</h1>
<h2><b>@IdxMacc</b></h2>
@if (CurrMSE == null)
{
<MachineBlock FullMode="false"></MachineBlock>
}
else
{
<MachineBlock RecMSE="CurrMSE" FullMode="false"></MachineBlock>
}
@code {
[Parameter]
[SupplyParameterFromQuery(Name = "IdxMacc")]
public string? IdxMacc { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// recupero MSE macchina....
if (!string.IsNullOrEmpty(IdxMacc))
{
CurrMSE = await MsgServ.GetMachineMse(IdxMacc);
}
//await InvokeAsync(StateHasChanged);
// return base.OnAfterRenderAsync(firstRender);
}
protected MappaStatoExpl? CurrMSE { get; set; } = null;
}
+25
View File
@@ -0,0 +1,25 @@
@page "/"
@page "/home"
@page "/status-map"
<div class="row">
@if (ListMSE == null || ListMSE.Count == 0)
{
@for (int i = 0; i < 10; i++)
{
<div class="col-6 col-sm-6 col-md-4 col-lg-3 col-xl-2 mb-2 px-1 bloccoMacc">
<MachineBlock></MachineBlock>
</div>
}
}
else
{
@foreach (var item in ListMSE)
{
<div class="col-6 col-sm-6 col-md-4 col-lg-3 col-xl-2 mb-2 px-1 bloccoMacc">
<MachineBlock RecMSE="@item" FullMode="true"></MachineBlock>
</div>
}
}
</div>
+197
View File
@@ -0,0 +1,197 @@
using global::Microsoft.AspNetCore.Components;
using MP.Data.Conf;
using MP.Data.DatabaseModels;
using MP.Data.Services;
using NLog;
namespace MP_TAB_SERV.Pages
{
public partial class StatusMap
{
#region Protected Fields
protected bool doAnimate = true;
protected int keepAliveMin = 1;
protected int maxCol = 6;
protected string showArt = "";
protected int slowRefreshSec = 300;
#endregion Protected Fields
#region Protected Properties
[Inject]
protected StatusData MDataService { get; set; } = null!;
[Inject]
protected MessageService MsgServ { get; set; } = null!;
protected int slowRefreshMs
{
get
{
// tempo variabile tra +/- 10% del target
int answ = rnd.Next(900, 1100) * slowRefreshSec;
return answ;
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Recupera il valore e se trovato aggiorna
/// </summary>
/// <param name="chiave">Valore da cercare</param>
/// <param name="varObj">String in cui salvare il valore se trovato</param>
/// <returns></returns>
protected bool getConfVal(string chiave, ref string varObj)
{
bool answ = false;
if (CurrConfig != null && CurrConfig.Count > 0)
{
// sistemo i parametri opzionali...
ConfigModel? risultato = CurrConfig.FirstOrDefault(x => x.Chiave == chiave);
if (risultato != null)
{
varObj = risultato.Valore;
answ = !string.IsNullOrEmpty(risultato.Valore);
}
}
return answ;
}
/// <summary>
/// Recupera il valore e se trovato aggiorna
/// </summary>
/// <param name="chiave">Valore da cercare</param>
/// <param name="varObj">Int in cui salvare il valore se trovato</param>
/// <returns></returns>
protected bool getConfValInt(string chiave, ref int varObj)
{
bool answ = false;
if (CurrConfig != null && CurrConfig.Count > 0)
{
// sistemo i parametri opzionali...
ConfigModel? risultato = CurrConfig.FirstOrDefault(x => x.Chiave == chiave);
if (risultato != null)
{
answ = int.TryParse(risultato.Valore, out varObj);
}
}
return answ;
}
/// <summary>
/// Recupera da conf eventuale setup tag dell'IOB indicato
/// </summary>
/// <param name="codIob"></param>
/// <returns></returns>
protected List<TagData>? getIobTag(string codIob)
{
List<TagData>? answ = null;
if (MDataService.currTagConf != null)
{
// cerco x chiave IOB...
if (MDataService.currTagConf.ContainsKey(codIob))
{
answ = MDataService.currTagConf[codIob];
}
}
return answ;
}
/// <summary>
/// Recupera da redis (in una chiamata soltanto) tutti i valori richiesti e compone un
/// dizionario x ottimizzare visualizzazione
/// </summary>
/// <param name="codIob"></param>
/// <returns></returns>
protected Dictionary<string, string> getTagVal(string codIob)
{
Dictionary<string, string> answ = new Dictionary<string, string>();
// recupero conf tags...
var currTags = getIobTag(codIob);
if (currTags != null && currTags.Count > 0)
{
// FIXME TODO !!!! FARE !!!! - da verificare
answ = currTags.ToDictionary(x => x.TagLocation, x => MDataService.getTagConf(x.TagLocation));
}
return answ;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (ListMSE != null)
{
// salvo in LocalStorage...
await MsgServ.SaveMse(ListMSE);
}
}
protected override async Task OnInitializedAsync()
{
ListMSE = null;
await setupConf();
//await InvokeAsync(StateHasChanged);
//await Task.Delay(500);
await ReloadData();
}
#endregion Protected Methods
#region Private Fields
private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
private static System.Timers.Timer slowTimer = new System.Timers.Timer(300000);
private List<ConfigModel>? CurrConfig = null;
private Random rnd = new Random();
#endregion Private Fields
#region Private Properties
private List<MappaStatoExpl>? ListMSE { get; set; } = null;
#endregion Private Properties
#region Private Methods
private async Task ReloadData()
{
ListMSE = await MDataService.MseGetAll();
//try
//{
// // salvo in LocalStorage...
// await MsgServ.SaveMse(CurrListMSE);
//}
//catch (Exception exc)
//{
// Log.Error($"Eccezione in ReloadData{Environment.NewLine}{exc}");
//}
}
private async Task setupConf()
{
CurrConfig = await MDataService.ConfigGetAll();
if (CurrConfig != null && CurrConfig.Count > 0)
{
// sistemo i parametri opzionali...
getConfValInt("keepAliveMin", ref keepAliveMin);
getConfValInt("MON_maxCol", ref maxCol);
int intDoAnim = 0;
getConfValInt("doAnimate", ref intDoAnim);
doAnimate = intDoAnim == 1;
getConfValInt("pageRefreshSec", ref slowRefreshSec);
getConfVal("sART", ref showArt);
Log.Info($"setupConf | Effettuato setup parametri | keepAlive: {keepAliveMin} | MaxCol: {maxCol} | doAnimate: {doAnimate} | slowRefreshSec: {slowRefreshSec}");
}
}
#endregion Private Methods
}
}