- inizio gestione display folder ODL
- browse directory locale x documenti
- da verificare metodo refresh modulo browse
This commit is contained in:
Samuele Locatelli
2024-10-15 10:12:50 +02:00
parent 7b5c7afd54
commit 9007a4df85
14 changed files with 473 additions and 21 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ namespace MP_TAB3.Components
public string MacIobConf(string kReq)
{
string answ = "";
string answ = "-";
if (MachineData.ContainsKey(kReq))
{
answ = MachineData[kReq];
+53
View File
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MP.Data
{
public class MeasureUtils
{
#region Public Fields
public static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
#endregion Public Fields
#region Public Methods
/// <summary>
/// Calcola dimensione file automaticamwente secondo dimensione
/// </summary>
/// <param name="value"></param>
/// <param name="decimalPlaces"></param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static string SizeSuffix(Int64 value, int decimalPlaces = 1)
{
if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
if (value < 0) { return "-" + SizeSuffix(-value, decimalPlaces); }
if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }
// mag is 0 for bytes, 1 for KB, 2, for MB, etc.
int mag = (int)Math.Log(value, 1024);
// 1L << (mag * 10) == 2 ^ (10 * mag) [i.e. the number of bytes in the unit
// corresponding to mag]
decimal adjustedSize = (decimal)value / (1L << (mag * 10));
// make adjustment when the value is large enough that it would round up to 1000 or more
if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
{
mag += 1;
adjustedSize /= 1024;
}
return string.Format("{0:n" + decimalPlaces + "} {1}",
adjustedSize,
SizeSuffixes[mag]);
}
#endregion Public Methods
}
}
+34
View File
@@ -0,0 +1,34 @@
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Name</th>
<th class="text-end">Type</th>
<th class="text-end">Size</th>
</tr>
</thead>
<tbody>
@if (ListFiles == null || ListFiles.Count == 0)
{
<tr>
<td colspan="3">
<div class="alert alert-info text-center fs-5">Attenzione: nessun file trovato per ODL richiesto.</div>
</td>
</tr>
}
else
{
@foreach (var record in ListFiles)
{
<tr>
<td>
<a href="@FileLink(record.Name)" target="_blank" class="" title="Download File">@record.Name</a>
</td>
<td class="text-end">@record.Extension</td>
<td class="text-end">@CalcSize(record.Length)</td>
</tr>
}
}
</tbody>
</table>
+66
View File
@@ -0,0 +1,66 @@
using Microsoft.AspNetCore.Components;
using MP.Data;
namespace MP.SPEC.Components
{
public partial class FolderBrowser
{
#region Public Properties
[Parameter]
public string LogicalPath { get; set; } = "";
#endregion Public Properties
#region Protected Properties
[Inject]
protected IConfiguration ConfMan { get; set; } = null!;
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Restituisce size calcolata
/// </summary>
/// <param name="origSize"></param>
/// <returns></returns>
protected string CalcSize(long origSize)
{
return MeasureUtils.SizeSuffix(origSize, 1);
}
protected string FileLink(string fName)
{
return $"RET_DATA/{LogicalPath}/{fName}";
}
/// <summary>
/// Eseguo browsing directory...
/// </summary>
/// <returns></returns>
protected override void OnParametersSet()
{
// calcolo phisical path...
string BasePathOdlReturn = ConfMan.GetValue<string>("ServerConf:BasePathOdlReturn") ?? ConfMan.GetValue<string>("OptConf:BasePathOdlReturn") ?? "";
PhysicalPath = Path.Combine(BasePathOdlReturn, LogicalPath);
// controllo esista
if (Directory.Exists(PhysicalPath))
{
// recupero come DirectoryInfo x avere tutte le informazioni su nome, tipo, size...
DirectoryInfo dirInfo = new DirectoryInfo(PhysicalPath);
ListFiles = dirInfo.GetFiles().ToList();
}
}
#endregion Protected Methods
#region Private Properties
private List<FileInfo> ListFiles { get; set; } = new List<FileInfo>();
private string PhysicalPath { get; set; } = "";
#endregion Private Properties
}
}
+56 -9
View File
@@ -15,7 +15,7 @@ else
@if (currRecord != null && !showStats && isCurrOdl)
{
<div class="col-6 col-lg-8">
@if(enableForceSync)
@if (enableForceSync)
{
<button @onclick="() => forceSyncDb()" class="btn btn-success btn-sm">Forza sync &rarr; macchina <i class="bi bi-fast-forward-circle"></i></button>
}
@@ -25,7 +25,7 @@ else
}
</div>
<div class="col-6 col-lg-4 text-end">
@if(enableStopODL)
@if (enableStopODL)
{
<button @onclick="() => chiudiOdl()" class="btn btn-danger btn-sm">Registra chiusura ODL <i class="far fa-stop-circle"></i></button>
}
@@ -61,7 +61,16 @@ else
<td>
@if (isCurrOdl)
{
<button class="btn btn-primary btn-sm" @onclick="() => selRecord(record)"><i class="fa-solid fa-magnifying-glass"></i></button>
<div>
<button class="btn btn-primary btn-sm" @onclick="() => selRecord(record)" title="Select Record"><i class="fa-solid fa-magnifying-glass"></i></button>
</div>
@if (HasFolderMan(record.IdxMacchina))
{
<div class="mt-1">
<button class="btn btn-info btn-sm" @onclick="() => selBrowseRecord(record)" title="Show Folder" data-bs-toggle="modal" data-bs-target="#odlDirModal"><i class="fa-regular fa-folder-open"></i></button>
@* <a href="@OdlLink(record.IdxOdl)" target="_blank" class="btn btn-info btn-sm py-1" title="Show Folder"><i class="fa-regular fa-folder-open"></i></a> *@
</div>
}
}
else
{
@@ -147,10 +156,12 @@ else
</td>
<td>
<div>
<b>@record.DurataMinuti</b>
</div>
<div>
<button class="btn btn-sm btn-primary py-0" type="button" @onclick="() => selectStatRecord(record)" data-bs-toggle="modal" data-bs-target="#myModal" title="Mostra statistiche"><i class="fa-solid fa-chart-pie"></i></button>
<button class="btn btn-sm btn-primary py-1 px-2" type="button" @onclick="() => selectStatRecord(record)" data-bs-toggle="modal" data-bs-target="#statsModal" title="Mostra statistiche">
<div>
<b>@record.DurataMinuti</b>
</div>
<i class="fa-solid fa-chart-pie"></i>
</button>
</div>
<!-- Modal -->
</td>
@@ -159,8 +170,7 @@ else
</tbody>
</table>
<div class="modal fade" id="myModal" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal fade" id="statsModal" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content">
<div class="modal-header bg-primary col-12">
@@ -306,6 +316,43 @@ else
</div>
</div>
</div>
<div class="modal fade" id="odlDirModal" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content">
<div class="modal-header bg-primary col-12">
@if (browseRecord != null)
{
<div class="col-3">
<div class="modal-title fs-2" id="staticBackdropLabel"><b>ODL @($"{browseRecord.IdxOdl.ToString(padCodXdl)}")</b></div>
<div class="small text-light">PODL @($"{getPodl(browseRecord.IdxOdl).ToString(padCodXdl)}")</div>
</div>
<div class="col-6 fs-5">
<b>@browseRecord.CodArticolo</b>
<div class="small textConsensed text-light">
@(@browseRecord.ArticoloNav != null ? @browseRecord.ArticoloNav.DescArticolo : "N.A.")
</div>
</div>
<div class="col-2 fs-5">
<b>@browseRecord.IdxMacchina</b>
<div class="small textConsensed text-light">
@(@browseRecord.MachineNav != null ? @browseRecord.MachineNav.Descrizione : "N.A.")
</div>
</div>
}
<div class="col-1 text-end">
<button type="button" class="btn btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body col-12">
@if (browseRecord != null && showBrowse)
{
<FolderBrowser LogicalPath="@($"ODL{browseRecord.IdxOdl.ToString(padCodXdl)}")"></FolderBrowser>
}
</div>
</div>
</div>
</div>
</div>
</div>
}
+59
View File
@@ -76,6 +76,8 @@ namespace MP.SPEC.Components
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
protected Dictionary<string, bool> MachHasFolderLut { get; set; } = new Dictionary<string, bool>();
[Inject]
protected MpDataService MDService { get; set; } = null!;
@@ -146,6 +148,32 @@ namespace MP.SPEC.Components
return answ;
}
/// <summary>
/// Determina se abbia gestione folder dati in ritorno
/// </summary>
/// <param name="idxMacc"></param>
/// <returns></returns>
protected bool HasFolderMan(string idxMacc)
{
bool answ = true;
// cerco nella LUT
if (MachHasFolderLut.ContainsKey(idxMacc))
{
answ = MachHasFolderLut[idxMacc];
}
// se non trovo cerco nella cache...
else
{
var rawVal = MDService.MachIobConfVal(idxMacc, KeyFolderMan);
if (rawVal != null)
{
bool.TryParse((string)rawVal, out answ);
MachHasFolderLut.Add(idxMacc, answ);
}
}
return answ;
}
protected override async Task OnInitializedAsync()
{
ListStati = await MDService.AnagStatiComm();
@@ -189,6 +217,7 @@ namespace MP.SPEC.Components
protected async Task selectStatRecord(ODLExpModel? currRec)
{
showBrowse = false;
showStats = true;
await Task.Delay(1);
statRecord = currRec;
@@ -202,6 +231,24 @@ namespace MP.SPEC.Components
ListOdlStats = null;
}
}
protected async Task selBrowseRecord(ODLExpModel? currRec)
{
showBrowse = true;
showStats = false;
await Task.Delay(1);
browseRecord = currRec;
#if false
if (currRec != null)
{
await reloadStatsData(currRec);
}
else
{
showBrowse = false;
ListOdlStats = null;
}
#endif
}
protected async Task selRecord(ODLExpModel? currRec)
{
@@ -237,6 +284,16 @@ namespace MP.SPEC.Components
private ODLExpModel? currRecord = null;
/// <summary>
/// Chiave gestione folder (hard coded, da IOB OptPar)
/// </summary>
private string KeyFolderMan = "OP_ODL_FOLDER";
protected string OdlLink(int idxOdl)
{
return $"RET_DATA/ODL{idxOdl:00000000}";
}
private List<StatODLModel>? ListOdlStats;
private List<StatODLModel>? ListOdlStatsNetto;
@@ -248,6 +305,7 @@ namespace MP.SPEC.Components
private List<ODLExpModel>? SearchRecords;
private ODLExpModel? statRecord = null;
private ODLExpModel? browseRecord = null;
#endregion Private Fields
@@ -340,6 +398,7 @@ namespace MP.SPEC.Components
private DateTime selDtFine { get; set; } = DateTime.Now;
private bool showStats { get; set; } = false;
private bool showBrowse { get; set; } = false;
private int totalCount
{
+175 -3
View File
@@ -1,10 +1,12 @@
using EgwCoreLib.Utils;
using Microsoft.Extensions.Options;
using MP.Data;
using MP.Data.Conf;
using MP.Data.DatabaseModels;
using MP.Data.DTO;
using MP.Data.MgModels;
using MP.Data.Objects;
using MP.Data.Services;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
@@ -45,6 +47,9 @@ namespace MP.SPEC.Data
_logger.LogInformation("DbController OK");
}
// conf x lettura dati da area REDIS di MP-IO
MpIoNS = _configuration.GetValue<string>("ServerConf:MpIoNS");
// conf mongo...
connStr = _configuration.GetConnectionString("mdbConnString");
if (string.IsNullOrEmpty(connStr))
@@ -60,6 +65,15 @@ namespace MP.SPEC.Data
#endregion Public Constructors
#region Public Events
/// <summary>
/// Evento richiesta rilettura dati pagina (x refresh pagine aperte)
/// </summary>
public event EventHandler ReloadRequest = delegate { };
#endregion Public Events
#region Public Properties
public static MP.Data.Controllers.MpSpecController dbController { get; set; } = null!;
@@ -443,7 +457,7 @@ namespace MP.SPEC.Data
return answ;
}
/// <summary>
/// Update chiave config
/// </summary>
@@ -868,6 +882,42 @@ namespace MP.SPEC.Data
return mongoController.InitRecipe(confPath, idxPODL, CalcArgs);
}
/// <summary>
/// Recupero info IOB x TAB (da info registrate IOB-WIN--&gt; MP-IO)
/// </summary>
/// <param name="IdxMacchina"></param>
/// <returns></returns>
public async Task<IOB_data> IobInfo(string IdxMacchina)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
IOB_data? result = new IOB_data();
// cerco in redis...
string currKey = redHashMpIO($"hM2IOB:{IdxMacchina}");
RedisValue rawData = await redisDb.StringGetAsync(currKey);
//if (!string.IsNullOrEmpty($"{rawData}"))
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<IOB_data>($"{rawData}");
source = "REDIS";
}
else
{
Log.Error($"Errore: non trovato valore <IOB_data> valido in REDIS | key: {currKey}");
Log.Info($"REDIS | conf: {redisConn.Configuration}");
Log.Info($" --> Valore trovato:{Environment.NewLine}{rawData}");
}
if (result == null)
{
result = new IOB_data();
Log.Debug($"Init valore default <IOB_data> | IdxMacchina: {IdxMacchina}");
}
sw.Stop();
Log.Debug($"IobInfo per {IdxMacchina} | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// </summary>
/// <param name="IdxOdl">id odl da cercare</param>
@@ -1047,6 +1097,69 @@ namespace MP.SPEC.Data
return result;
}
/// <summary>
/// Recupero info Machine-IOB x TAB (da info registrate IOB-WIN --&gt; MP-IO)
/// </summary>
/// <param name="IdxMacchina"></param>
/// <returns></returns>
public Dictionary<string, string> MachIobConf(string IdxMacchina)
{
string source = "NA";
Stopwatch sw = new Stopwatch();
sw.Start();
Dictionary<string, string> result = new Dictionary<string, string>();
// cerco in redis...
string currKey = redHashMpIO($"IOB:{IdxMacchina}:MachIobConf");
try
{
result = redisDb
.HashGetAll(currKey)
.ToDictionary(x => $"{x.Name}", x => $"{x.Value}");
source = "REDIS";
}
catch (Exception exc)
{
Log.Error($"Errore in MachIobConf{Environment.NewLine}{exc}");
}
if (result == null)
{
result = new Dictionary<string, string>();
Log.Debug($"Init valore default MachIobConf | IdxMacchina: {IdxMacchina}");
}
sw.Stop();
Log.Debug($"MachIobConf per {IdxMacchina} | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Recupero singolo recordo info Machine-IOB x TAB (da info registrate IOB-WIN --&gt; MP-IO)
/// </summary>
/// <param name="IdxMacchina"></param>
/// <returns></returns>
public string MachIobConfVal(string IdxMacchina, string Key)
{
string answ = "";
var currList = MachIobConf(IdxMacchina);
if (currList.ContainsKey(Key))
{
answ = currList[Key];
}
return answ;
}
/// <summary>
/// Invio notifica rilettura (con parametro)
/// </summary>
/// <param name="message"></param>
public void NotifyReloadRequest(string message)
{
if (ReloadRequest != null)
{
// messaggio
ReloadEventArgs rea = new ReloadEventArgs(message);
ReloadRequest.Invoke(this, rea);
}
}
/// <summary>
/// Elenco ODL dato batch selezionato
/// </summary>
@@ -1634,6 +1747,23 @@ namespace MP.SPEC.Data
return answ;
}
/// <summary>
/// Reset della cache IO post operazioni come setup ODL...
/// </summary>
/// <param name="baseMem">Indirizzo base da cui rimuovere memoria cache</param>
/// <returns></returns>
public async Task<bool> ResetIoCache(string baseMem)
{
// patterna a partire da cache IO...
RedisValue pattern = new RedisValue($"{MpIoNS}:*");
if (!string.IsNullOrEmpty(baseMem))
{
pattern = new RedisValue($"{MpIoNS}:{baseMem}:*");
}
bool answ = await ExecFlushRedisPattern(pattern);
return answ;
}
/// <summary>
/// Statistiche ODL calcolate (da stored stp_STAT_ODL)
/// </summary>
@@ -1860,10 +1990,9 @@ namespace MP.SPEC.Data
#region Private Fields
private static IConfiguration _configuration = null!;
private static ILogger<MpDataService> _logger = null!;
private static Logger Log = LogManager.GetCurrentClassLogger();
private string MpIoNS = "";
/// <summary>
/// Oggetto vocabolario x uso continuo traduzione
@@ -1893,6 +2022,34 @@ namespace MP.SPEC.Data
#region Private Methods
/// <summary>
/// Esegue flush memoria redis dato pattern
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
private async Task<bool> ExecFlushRedisPattern(RedisValue pattern)
{
bool answ = false;
var listEndpoints = redisConn.GetEndPoints();
foreach (var endPoint in listEndpoints)
{
//var server = redisConnAdmin.GetServer(listEndpoints[0]);
var server = redisConn.GetServer(endPoint);
if (server != null)
{
var keyList = server.Keys(redisDb.Database, pattern);
foreach (var item in keyList)
{
await redisDb.KeyDeleteAsync(item);
}
answ = true;
}
}
// notifico update ai client in ascolto x reset cache
NotifyReloadRequest($"FlushRedisCache | {pattern}");
return answ;
}
private async Task<bool> POdlFlushCache()
{
bool answ = false;
@@ -1908,6 +2065,21 @@ namespace MP.SPEC.Data
return answ;
}
private string redHashMpIO(string keyName)
{
string result = keyName;
try
{
result = $"{MpIoNS}:{keyName}".Replace("\\", "_");
}
catch (Exception exc)
{
Log.Error($"Errore in redHashMpIO{Environment.NewLine}{exc}");
}
return result;
}
private async Task resetCacheArticoli()
{
RedisValue pattern = new RedisValue($"{Utils.redisArtByDossier}:*");
+1 -1
View File
@@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>MP.SPEC</RootNamespace>
<Version>6.16.2410.1411</Version>
<Version>6.16.2410.1509</Version>
<UserSecretsId>1800a78a-6ff1-40f9-b490-87fb8bfc1394</UserSecretsId>
</PropertyGroup>
+19 -2
View File
@@ -3,6 +3,7 @@ using Blazored.SessionStorage;
using Microsoft.AspNetCore.Authentication.Negotiate;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.FileProviders;
using MP.SPEC.Components;
using MP.SPEC.Data;
using MP.SPEC.Services;
@@ -15,7 +16,7 @@ var builder = WebApplication.CreateBuilder(args);
/*--------------------
* Note migrazione startup.cs --> program.cs:
*
* - https://stackoverflow.com/questions/69722872/asp-net-core-6-how-to-access-configuration-during-startup
* - https://stackoverflow.com/questions/69722872/asp-net-core-6-how-to-access-ConfMan-during-startup
* - https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60?view=aspnetcore-5.0&tabs=visual-studio#where-do-i-put-state-that-was-stored-as-fields-in-my-program-or-startup-class
*
* */
@@ -32,7 +33,7 @@ ConfigurationManager configuration = builder.Configuration;
// REDIS setup
logger.Info("Setup REDIS");
string connStringRedis = configuration.GetConnectionString("Redis");
//string connStringRedis = configuration.GetConnectionString("RedisAdmin");
//string connStringRedis = ConfMan.GetConnectionString("RedisAdmin");
string redisSrvAddr = connStringRedis.Substring(0, connStringRedis.IndexOf(":"));
// avvio oggetto shared x redis...
var redisMultiplexer = ConnectionMultiplexer.Connect(connStringRedis);
@@ -77,6 +78,22 @@ app.UseHttpsRedirection();
app.UseStaticFiles();
// gestione static files: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-8.0
string BasePathOdlReturn = configuration.GetValue<string>("ServerConf:BasePathOdlReturn") ?? configuration.GetValue<string>("OptConf:BasePathOdlReturn") ?? "";
if (!string.IsNullOrEmpty(BasePathOdlReturn))
{
// verifico esista folder
if (Directory.Exists(BasePathOdlReturn))
{
// gestione cartella x file ritornati x ODL
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(BasePathOdlReturn),
RequestPath = "/RET_DATA",
});
}
}
app.UseRouting();
app.UseAuthentication();
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo MAPOSPEC </i>
<h4>Versione: 6.16.2410.1411</h4>
<h4>Versione: 6.16.2410.1509</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
6.16.2410.1411
6.16.2410.1509
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>6.16.2410.1411</version>
<version>6.16.2410.1509</version>
<url>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/MP.SPEC.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+3 -1
View File
@@ -19,6 +19,8 @@
"maxAge": "2000",
"cacheCheckArtUsato": 2,
"redisLongTimeCache": 15,
"MpIoBaseUrl": "http://localhost/MP/IO/"
"MpIoBaseUrl": "http://localhost/MP/IO/",
"MpIoNS": "MoonPro:SQL2016DEV:MoonPro",
"BasePathOdlReturn": "\\\\iis01\\W$\\Files\\ODL"
}
}
+3 -1
View File
@@ -61,6 +61,8 @@
"maxAge": "2000",
"cacheCheckArtUsato": "2",
"redisLongTimeCache": "15",
"MpIoBaseUrl": "http://localhost:20967/"
"MpIoBaseUrl": "http://localhost:20967/",
"MpIoNS": "MoonPro:SQL2016DEV:MoonPro",
"BasePathOdlReturn": "\\\\iis01\\W$\\Files\\ODL"
}
}