diff --git a/MP.Data/Controllers/MpSpecController.cs b/MP.Data/Controllers/MpSpecController.cs
index bdfc3780..9847a5ad 100644
--- a/MP.Data/Controllers/MpSpecController.cs
+++ b/MP.Data/Controllers/MpSpecController.cs
@@ -456,18 +456,18 @@ namespace MP.Data.Controllers
///
///
///
- public List ArticoliGetSearch(int numRecord, string azienda = "*", string searchVal = "")
+ public async Task> ArticoliGetSearchAsync(int numRecord, string azienda = "*", string searchVal = "")
{
List dbResult = new List();
using (var dbCtx = new MoonProContext(options))
{
- dbResult = dbCtx
- .DbSetArticoli
- .AsNoTracking()
- .Where(x => (azienda == "*" || x.Azienda.ToUpper() == azienda.ToUpper()) && (string.IsNullOrEmpty(searchVal) || x.CodArticolo.Contains(searchVal) || x.DescArticolo.Contains(searchVal) || x.Disegno.Contains(searchVal)))
- .OrderBy(x => x.CodArticolo)
- .Take(numRecord)
- .ToList();
+ dbResult = await dbCtx
+ .DbSetArticoli
+ .AsNoTracking()
+ .Where(x => (azienda == "*" || x.Azienda.ToUpper() == azienda.ToUpper()) && (string.IsNullOrEmpty(searchVal) || x.CodArticolo.Contains(searchVal) || x.DescArticolo.Contains(searchVal) || x.Disegno.Contains(searchVal)))
+ .OrderBy(x => x.CodArticolo)
+ .Take(numRecord)
+ .ToListAsync();
}
return dbResult;
}
@@ -1891,15 +1891,15 @@ namespace MP.Data.Controllers
/// Recupero Odl CORRENTI
///
///
- public List OdlGetCurrent()
+ public async Task> OdlGetCurrentAsync()
{
List dbResult = new List();
using (var dbCtx = new MoonProContext(options))
{
- dbResult = dbCtx
- .DbSetODL
- .Where(x => x.DataInizio != null && x.DataFine == null)
- .ToList();
+ dbResult = await dbCtx
+ .DbSetODL
+ .Where(x => x.DataInizio != null && x.DataFine == null)
+ .ToListAsync();
}
return dbResult;
}
diff --git a/MP.Data/Services/ListSelectDataSrv.cs b/MP.Data/Services/ListSelectDataSrv.cs
index b541ee06..2e49fc21 100644
--- a/MP.Data/Services/ListSelectDataSrv.cs
+++ b/MP.Data/Services/ListSelectDataSrv.cs
@@ -70,10 +70,7 @@ namespace MP.Data.Services
}
else
{
- result = dbController.ArticoliGetSearch(numRecord, azienda, searchVal);
-#if false
- result = await Task.FromResult(dbController.ArticoliGetSearch(numRecord, azienda, searchVal));
-#endif
+ result = await dbController.ArticoliGetSearchAsync(numRecord, azienda, searchVal);
// serializzp e salvo...
rawData = JsonConvert.SerializeObject(result);
await _redisDb.StringSetAsync(currKey, rawData, LongCache);
@@ -83,7 +80,7 @@ namespace MP.Data.Services
result = new List();
}
sw.Stop();
- Log.Debug($"ArticoliGetSearch | azienda: {azienda} | searchVal: {searchVal} | numRecord: {numRecord} | {source} | {sw.Elapsed.TotalMilliseconds}ms");
+ Log.Debug($"ArticoliGetSearchAsync | azienda: {azienda} | searchVal: {searchVal} | numRecord: {numRecord} | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
diff --git a/MP.SPEC/Components/ListDossiers.razor.cs b/MP.SPEC/Components/ListDossiers.razor.cs
index 1da69a82..f26032dd 100644
--- a/MP.SPEC/Components/ListDossiers.razor.cs
+++ b/MP.SPEC/Components/ListDossiers.razor.cs
@@ -234,7 +234,7 @@ namespace MP.SPEC.Components
ListStati = await MDService.AnagStatiComm();
selAzienda = await MDService.ConfigTryGetAsync("AZIENDA");
giacenzeConf = await MDService.ConfigTryGetAsync("SPEC_ShowGiacenze");
- ListArticoli = await MDService.ArticoliGetSearch(100000, selAzienda, "");
+ ListArticoli = await MDService.ArticoliGetSearchAsync(100000, selAzienda, "");
ListMacchine = MDService.MacchineGetFilt("*");
await ReloadData(true);
}
diff --git a/MP.SPEC/Components/ListPODL.razor.cs b/MP.SPEC/Components/ListPODL.razor.cs
index 29151abe..44897fbc 100644
--- a/MP.SPEC/Components/ListPODL.razor.cs
+++ b/MP.SPEC/Components/ListPODL.razor.cs
@@ -204,6 +204,9 @@ namespace MP.SPEC.Components
ListRecords = null;
isLoading = true;
+ var list = await MDService.OdlGetCurrentAsync();
+ _odlCurrSet = list.ToHashSet();
+
var machines = await MDService.MacchineGetFiltAsync("*");
_machinesWithConf = machines
@@ -244,7 +247,7 @@ namespace MP.SPEC.Components
{
ListRecords = null;
isLoading = true;
-
+ await UpdateOdlList();
// verifico filtro odl...
if (actFilter.ShowKit)
{
@@ -259,8 +262,7 @@ namespace MP.SPEC.Components
totalCount = SearchRecords.Count;
}
ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList();
- await Task.Delay(1);
- await InvokeAsync(() => StateHasChanged());
+ //await InvokeAsync(() => StateHasChanged());
isLoading = false;
}
@@ -356,6 +358,7 @@ namespace MP.SPEC.Components
private static Logger Log = LogManager.GetCurrentClassLogger();
private HashSet _machinesWithArch = new();
private HashSet _machinesWithConf = new();
+ private HashSet _odlCurrSet = new();
private string currRecipeArchPath = "";
///
@@ -567,18 +570,16 @@ namespace MP.SPEC.Components
///
private bool canStartOdl(string idxMacchina)
{
- // controllo se lista scaduta...
- bool answ = false;
- DateTime adesso = DateTime.Now;
- if (adesso > odlCurrExp || odlCurrList == null || odlCurrList.Count == 0)
- {
- odlCurrList = MDService.OdlGetCurrent();
- odlCurrExp = adesso.AddSeconds(2);
- }
- answ = !odlCurrList.Contains(idxMacchina);
- return answ;
+ return !_odlCurrSet.Contains(idxMacchina);
}
+ private async Task UpdateOdlList()
+ {
+ var list = await MDService.OdlGetCurrentAsync();
+ _odlCurrSet = list.ToHashSet();
+ }
+
+
///
/// Verifica se la idxMaccSel abbia associata un path x ricette (elenco)
///
diff --git a/MP.SPEC/Data/MpDataService.cs b/MP.SPEC/Data/MpDataService.cs
index 5d7405d1..79499830 100644
--- a/MP.SPEC/Data/MpDataService.cs
+++ b/MP.SPEC/Data/MpDataService.cs
@@ -1,5 +1,6 @@
using EgwCoreLib.Utils;
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Caching.Memory;
using MP.Core.Conf;
using MP.Core.DTO;
using MP.Core.Objects;
@@ -20,10 +21,11 @@ namespace MP.SPEC.Data
{
#region Public Constructors
- public MpDataService(IConfiguration configuration)
+ public MpDataService(IConfiguration configuration, IMemoryCache memoryCache)
{
- // fix oggetto configurazion
+ // salvataggio oggetti
_configuration = configuration;
+ _memoryCache = memoryCache;
// Verifica conf trace...
traceEnabled = _configuration.GetValue("Otel:EnableTracing", false);
Log.Info($"MpDataService | INIT | Trace enabled: {traceEnabled}");
@@ -298,7 +300,18 @@ namespace MP.SPEC.Data
LogTrace($"AnagKeyValGetAll Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return result;
}
+ public async Task> AnagStatiComm()
+ {
+ return await GetOrCreateCachedAsync(
+ operationName: "AnagStatiComm",
+ memKey: "ANAG_STATI_COMM_MEM",
+ redisKey: Utils.redisStatoCom,
+ memoryTtl: TimeSpan.FromMinutes(5),
+ dbFactory: () => Task.FromResult(dbController.AnagStatiComm() ?? new List())
+ );
+ }
+#if false
public async Task> AnagStatiComm()
{
using var activity = ActivitySource.StartActivity("AnagStatiComm");
@@ -327,7 +340,8 @@ namespace MP.SPEC.Data
activity?.Stop();
LogTrace($"AnagStatiComm Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return result;
- }
+ }
+#endif
public async Task> AnagTipoArtLV()
{
@@ -417,6 +431,25 @@ namespace MP.SPEC.Data
///
///
///
+ public async Task> ArticoliGetByTipoAsync(string tipo, string azienda = "*")
+ {
+ string sKey = string.IsNullOrWhiteSpace(tipo) ? "ALL" : tipo.Trim();
+
+ string redisKey = $"{Utils.redisArtList}:{azienda}:Tipo:{sKey}";
+ string memKey = $"MEM:{redisKey}";
+
+ return await GetOrCreateCachedAsync(
+ operationName: "ArticoliGetByTipoAsync",
+ memKey: memKey,
+ redisKey: redisKey,
+ // ✅ TTL lungo (dato abbastanza statico)
+ memoryTtl: TimeSpan.FromMinutes(5),
+ dbFactory: async () =>
+ await dbController.ArticoliGetByTipoAsync(tipo, azienda)
+ ?? new List()
+ );
+ }
+#if false
public async Task> ArticoliGetByTipoAsync(string tipo, string azienda = "*")
{
using var activity = ActivitySource.StartActivity("ArticoliGetByTipoAsync");
@@ -447,7 +480,8 @@ namespace MP.SPEC.Data
activity?.Stop();
LogTrace($"ArticoliGetByTipoAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return result;
- }
+ }
+#endif
///
/// Restitusice elenco articoli cercati
@@ -455,38 +489,115 @@ namespace MP.SPEC.Data
///
///
///
- public async Task> ArticoliGetSearch(int numRecord, string azienda, string searchVal)
+ public async Task> ArticoliGetSearchAsync(int numRecord, string azienda, string searchVal)
{
- using var activity = ActivitySource.StartActivity("ArticoliGetSearch");
- List? result = new List();
- string source = "DB";
- string sKey = string.IsNullOrEmpty(searchVal) ? "***" : searchVal;
- string currKey = $"{Utils.redisArtList}:{azienda}:{sKey}";
- // cerco in redis dato valore sel idxMaccSel...
- RedisValue rawData = redisDb.StringGet(currKey);
- if (rawData.HasValue)
+ string sKey = string.IsNullOrWhiteSpace(searchVal) ? "***" : searchVal.Trim();
+
+ string memKey = $"ART_SEARCH_MEM:{azienda}:{sKey}:{numRecord}";
+ string redisKey = $"{Utils.redisArtList}:{azienda}:{sKey}:{numRecord}";
+
+ return await GetOrCreateCachedAsync(
+ operationName: "ArticoliGetSearchAsync",
+ memKey: memKey,
+ redisKey: redisKey,
+ memoryTtl: TimeSpan.FromMinutes(2),
+ dbFactory: async () =>
+ await dbController.ArticoliGetSearchAsync(numRecord, azienda, searchVal)
+ ?? new List()
+ );
+ }
+
+
+ ///
+ /// Helper per gestione cache a 3 livelli: MEMORY, REDIS e DB con opzioni
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task GetOrCreateCachedAsync(string operationName, string memKey, string redisKey, TimeSpan memoryTtl, Func> dbFactory, Func? serialize = null, Func? deserialize = null)
+ {
+ using var activity = ActivitySource.StartActivity(operationName);
+
+ string source = "NA";
+ T result;
+
+ // ✅ default serializer (fallback)
+ serialize ??= (obj) => JsonConvert.SerializeObject(obj);
+ deserialize ??= (str) => JsonConvert.DeserializeObject(str)!;
+
+ // ✅ 1. MEMORY
+ if (_memoryCache.TryGetValue(memKey, out T cached))
{
- result = JsonConvert.DeserializeObject>($"{rawData}");
- source = "REDIS";
+ result = cached;
+ source = "MEMORY";
}
else
{
- result = await Task.FromResult(dbController.ArticoliGetSearch(numRecord, azienda, searchVal));
- // serializzo e salvo...
- rawData = JsonConvert.SerializeObject(result);
- redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache / 5));
- }
- if (result == null)
- {
- result = new List();
+ // ✅ 2. MISS → factory
+ result = await _memoryCache.GetOrCreateAsync(memKey, async entry =>
+ {
+ entry.AbsoluteExpirationRelativeToNow = memoryTtl;
+
+ // 👉 REDIS
+ try
+ {
+ var rawData = await redisDb.StringGetAsync(redisKey);
+
+ if (rawData.HasValue)
+ {
+ source = "REDIS";
+ return deserialize(rawData!);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogTrace($"Redis error on {operationName}: {ex.Message}");
+ }
+
+ // 👉 DB
+ source = "DB";
+
+ var dbResult = await dbFactory();
+
+ var safeResult = dbResult == null ? default! : dbResult;
+
+ try
+ {
+ await redisDb.StringSetAsync(
+ redisKey,
+ serialize(safeResult),
+ getRandTOut(redisLongTimeCache)
+ );
+ }
+ catch (Exception ex)
+ {
+ LogTrace($"Redis SET error on {operationName}: {ex.Message}");
+ }
+
+ return safeResult;
+ })!;
}
+
+ // ✅ logging e tracing centralizzati
activity?.SetTag("data.source", source);
- activity?.SetTag("result.count", result.Count);
- activity?.Stop();
- LogTrace($"ArticoliGetSearch | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
+
+ if (result is System.Collections.ICollection coll)
+ activity?.SetTag("result.count", coll.Count);
+ else
+ activity?.SetTag("result.count", result != null ? 1 : 0);
+
+ LogTrace($"{operationName} | {source} | {activity?.Duration.TotalMilliseconds}ms");
+
return result;
}
+
///
/// Aggiornamento record selezionato
///
@@ -704,7 +815,7 @@ namespace MP.SPEC.Data
activity?.SetTag("data.source", source);
activity?.Stop();
- LogTrace($"ConfigTryGetAsync | {keyName} | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
+ LogTrace($"ConfigTryGetAsync | {keyName} | {source} | {activity?.Duration.TotalMilliseconds}ms");
return value ?? "";
}
@@ -1687,7 +1798,7 @@ namespace MP.SPEC.Data
LogTrace($"MacchineGetFilt | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return result;
}
-
+ private readonly IMemoryCache _memoryCache;
///
/// Elenco di tutte le macchine filtrate x gruppo
///
@@ -1695,32 +1806,51 @@ namespace MP.SPEC.Data
///
public async Task> MacchineGetFiltAsync(string codGruppo)
{
- using var activity = ActivitySource.StartActivity("MacchineGetFilt");
- List? result = new List();
+ using var activity = ActivitySource.StartActivity("MacchineGetFiltAsync");
string source = "DB";
+
string keyGrp = codGruppo != "*" ? codGruppo : "ALL";
- string currKey = $"{Utils.redisMacList}:{keyGrp}";
- // cerco in redis dato valore sel idxMaccSel...
- RedisValue rawData = await redisDb.StringGetAsync(currKey);
+ string memKey = $"MACCHINE_MEM:{keyGrp}";
+ string redisKey = $"{Utils.redisMacList}:{keyGrp}";
+
+ // ✅ 1. MEMORY CACHE
+ if (_memoryCache.TryGetValue(memKey, out List cached))
+ {
+ source = "MEMORY";
+ activity?.SetTag("data.source", source);
+ activity?.Stop();
+ LogTrace($"MacchineGetFiltAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
+ return cached;
+ }
+
+ List result;
+
+ // ✅ 2. REDIS
+ var rawData = await redisDb.StringGetAsync(redisKey);
+
if (rawData.HasValue)
{
- result = JsonConvert.DeserializeObject>($"{rawData}");
+ result = JsonConvert.DeserializeObject>(rawData!) ?? new();
source = "REDIS";
}
else
{
+ // ✅ 3. DB
result = await dbController.MacchineGetFiltAsync(codGruppo);
- // serializzo e salvo...
- rawData = JsonConvert.SerializeObject(result);
- await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache));
- }
- if (result == null)
- {
- result = new List();
+
+ await redisDb.StringSetAsync(
+ redisKey,
+ JsonConvert.SerializeObject(result),
+ getRandTOut(redisLongTimeCache)
+ );
}
+
+ // ✅ salva in RAM (IMPORTANTISSIMO), TTL 1 minuto
+ _memoryCache.Set(memKey, result, TimeSpan.FromMinutes(1));
+
activity?.SetTag("data.source", source);
- activity?.SetTag("result.count", 1);
activity?.Stop();
+
LogTrace($"MacchineGetFiltAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return result;
}
@@ -2040,6 +2170,32 @@ namespace MP.SPEC.Data
///
///
///
+
+ public async Task> OdlGetCurrentAsync()
+ {
+ string redisKey = Utils.redisOdlCurrByMac;
+ string memKey = $"MEM:{redisKey}";
+
+ return await GetOrCreateCachedAsync(
+ operationName: "OdlGetCurrentAsync",
+ memKey: memKey,
+ redisKey: redisKey,
+ // ✅ TTL molto corto (come avevi: 3 secondi)
+ memoryTtl: TimeSpan.FromSeconds(3),
+ dbFactory: async () =>
+ {
+ var rawData = await dbController.OdlGetCurrentAsync();
+ var dbResult = rawData
+ .Select(x => x.IdxMacchina)
+ .Distinct()
+ .ToList();
+
+ return dbResult ?? new List();
+ }
+ );
+ }
+
+#if false
public List OdlGetCurrent()
{
using var activity = ActivitySource.StartActivity("OdlGetCurrent");
@@ -2068,7 +2224,8 @@ namespace MP.SPEC.Data
activity?.Stop();
LogTrace($"OdlGetCurrent | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return dbResult;
- }
+ }
+#endif
///
/// elenco TUTTI gli ODL
@@ -2479,6 +2636,31 @@ namespace MP.SPEC.Data
/// Data inizio
/// Data fine
///
+ public async Task> POdlToKitListGetFiltAsync(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate)
+ {
+ string currKey = $"{Utils.redisPOdlList}_kit:{codGruppo}:{idxMacchina}:{keyRichPart}:{lanciato}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}";
+
+ // ✅ stessa chiave per memoria (puoi anche prefissare)
+ string memKey = $"MEM:{currKey}";
+
+ return await GetOrCreateCachedAsync(
+ operationName: "POdlToKitListGetFiltAsync",
+ memKey: memKey,
+ redisKey: currKey,
+ // ✅ TTL RAM breve (coerente con redisShortTimeCache)
+ memoryTtl: TimeSpan.FromSeconds(redisShortTimeCache),
+ dbFactory: async () =>
+ await dbController.ListPODL_KitFiltAsync(
+ lanciato,
+ keyRichPart,
+ idxMacchina,
+ codGruppo,
+ startDate,
+ endDate
+ ) ?? new List()
+ );
+ }
+#if false
public async Task> POdlToKitListGetFiltAsync(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate)
{
using var activity = ActivitySource.StartActivity("POdlToKitListGetFiltAsync");
@@ -2508,7 +2690,8 @@ namespace MP.SPEC.Data
activity?.Stop();
LogTrace($"POdlToKitListGetFiltAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return result;
- }
+ }
+#endif
///
/// Chiamata salvataggio ricetta + refresh REDIS
diff --git a/MP.SPEC/MP.SPEC.csproj b/MP.SPEC/MP.SPEC.csproj
index e978a1c1..8ce4107b 100644
--- a/MP.SPEC/MP.SPEC.csproj
+++ b/MP.SPEC/MP.SPEC.csproj
@@ -5,7 +5,7 @@
enable
enable
MP.SPEC
- 8.16.2605.2617
+ 8.16.2605.2619
1800a78a-6ff1-40f9-b490-87fb8bfc1394
en
diff --git a/MP.SPEC/Pages/Articoli.razor.cs b/MP.SPEC/Pages/Articoli.razor.cs
index e3b23914..008bbc19 100644
--- a/MP.SPEC/Pages/Articoli.razor.cs
+++ b/MP.SPEC/Pages/Articoli.razor.cs
@@ -335,7 +335,7 @@ namespace MP.SPEC.Pages
private async Task ReloadData()
{
isLoading = true;
- SearchRecords = await MDService.ArticoliGetSearch(100000, selAzienda, SearchVal);
+ SearchRecords = await MDService.ArticoliGetSearchAsync(100000, selAzienda, SearchVal);
ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList();
isLoading = false;
}
diff --git a/MP.SPEC/Pages/PODL.razor.cs b/MP.SPEC/Pages/PODL.razor.cs
index ce20588b..23054a1a 100644
--- a/MP.SPEC/Pages/PODL.razor.cs
+++ b/MP.SPEC/Pages/PODL.razor.cs
@@ -1,17 +1,14 @@
#if false
using Blazored.LocalStorage;
#endif
+using EgwCoreLib.Razor;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using MP.Data.DbModels;
-using MP.SPEC.Components;
+using MP.Data.Services;
using MP.SPEC.Data;
using MP.SPEC.Services;
using NLog;
-using System.Reflection.PortableExecutable;
-using EgwCoreLib.Razor;
-using Microsoft.AspNetCore.DataProtection;
-using MP.Data.Services;
namespace MP.SPEC.Pages
{
@@ -501,9 +498,8 @@ namespace MP.SPEC.Pages
private async Task ReloadData()
{
isLoading = true;
- await Task.Delay(1);
- ListMacchine = MDService.MacchineGetFilt(selReparto);
- ListArticoli = await MDService.ArticoliGetSearch(100, currAzienda, artSearch);
+ ListMacchine = await MDService.MacchineGetFiltAsync(selReparto);
+ ListArticoli = await MDService.ArticoliGetSearchAsync(100, currAzienda, artSearch);
if (ListGruppiFase != null)
{
var firstGroup = ListGruppiFase.Where(x => x.CodGruppo == selReparto).FirstOrDefault();
diff --git a/MP.SPEC/Program.cs b/MP.SPEC/Program.cs
index b91a66a0..5b8365ff 100644
--- a/MP.SPEC/Program.cs
+++ b/MP.SPEC/Program.cs
@@ -132,9 +132,13 @@ builder.Services.AddAuthorization(options =>
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
-// redis preliminare
-builder.Services.AddSingleton(redisMultiplexer);
builder.Services.AddRazorPages();
+
+// memory + redis preliminare
+builder.Services.AddMemoryCache();
+builder.Services.AddSingleton(redisMultiplexer);
+
+// altri servizi
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
diff --git a/MP.SPEC/Resources/ChangeLog.html b/MP.SPEC/Resources/ChangeLog.html
index 323e79be..dca5987a 100644
--- a/MP.SPEC/Resources/ChangeLog.html
+++ b/MP.SPEC/Resources/ChangeLog.html
@@ -1,6 +1,6 @@
Modulo MAPOSPEC
- Versione: 8.16.2605.2617
+ Versione: 8.16.2605.2619
Note di rilascio:
-
diff --git a/MP.SPEC/Resources/VersNum.txt b/MP.SPEC/Resources/VersNum.txt
index e207479d..5ea9eaa9 100644
--- a/MP.SPEC/Resources/VersNum.txt
+++ b/MP.SPEC/Resources/VersNum.txt
@@ -1 +1 @@
-8.16.2605.2617
+8.16.2605.2619
diff --git a/MP.SPEC/Resources/manifest.xml b/MP.SPEC/Resources/manifest.xml
index 68083a5c..b079c1c6 100644
--- a/MP.SPEC/Resources/manifest.xml
+++ b/MP.SPEC/Resources/manifest.xml
@@ -1,6 +1,6 @@
-
- 8.16.2605.2617
+ 8.16.2605.2619
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