Fix reset cache anche di InsEnabled da reload

This commit is contained in:
Samuele Locatelli
2026-06-15 11:23:08 +02:00
parent 3dfb917123
commit 3ee34c0300
9 changed files with 409 additions and 7 deletions
+2 -2
View File
@@ -152,7 +152,6 @@ namespace MP.Data.Services.IOC
// 3. Conversione efficiente da RedisValue a string (evita l'interpolazione $"{val}")
string? sRedisVal = val;
return IsStringTrue(sRedisVal);
#if false
var rKey = MP.Data.Utils.RedKeyDatiMacc(idxMacchina, MpIoNS);
var val = await _redisDb.HashGetAsync(rKey, "insEnabled");
@@ -170,7 +169,8 @@ namespace MP.Data.Services.IOC
return !string.IsNullOrEmpty(sVal) && (sVal == "1" || sVal.ToLower() == "true");
#endif
},
expiration: GetRandTOut(30),
expiration: GetRandTOut(60),
//expiration: GetRandTOut(30),
tagList: ["IOC_IobInsEnab", cKey, idxMacchina]
);
}
@@ -63,6 +63,13 @@ namespace MP.Data.Services.Utils
/// <returns></returns>
Task ResetCache();
/// <summary>
/// Forza il reset della cache REDIS x il pattern richiesto
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
Task ResetCache(string pattern);
/// <summary>
/// Inserisce o aggiorna in batch le statistiche di dettaglio nel database.
/// Opzionalmente elimina i record precedenti nel periodo specificato.
+9 -1
View File
@@ -22,7 +22,7 @@ namespace MP.Data.Services.Utils
IConnectionMultiplexer redis,
IFusionCache cache,
IStatsDetailRepository repo
) : base(config,cache, redis)
) : base(config, cache, redis)
{
_className = "StatsDetail";
_repo = repo;
@@ -134,6 +134,14 @@ namespace MP.Data.Services.Utils
await ClearCacheAsync($"{_redisBaseKey}:*");
}
/// <inheritdoc />
public async Task ResetCache(string pattern)
{
// tolgo eventuali ":" finali
pattern = pattern.EndsWith(":") ? pattern.Substring(0, pattern.Length - 1) : pattern;
await ClearCacheAsync($"{pattern}:*");
}
/// <inheritdoc />
public async Task<int> UpsertManyAsync(List<StatsDetailModel> listRecords, bool removeOld)
{
@@ -14,6 +14,7 @@ namespace MP.IOC.Components.Pages
protected override async Task OnInitializedAsync()
{
MpIoNS = Config.GetValue<string>("ServerConf:MpIoNS") ?? "MP";
await ReloadData();
}
@@ -105,6 +106,9 @@ namespace MP.IOC.Components.Pages
[Inject]
private IIocService IocService { get; set; } = null!;
[Inject]
private IConfiguration Config { get; set; } = null!;
#endregion Private Properties
#region Private Methods
@@ -114,8 +118,14 @@ namespace MP.IOC.Components.Pages
return currStatSel != null && currStatSel.Title == curKey ? "active" : "";
}
private string MpIoNS = "";
private async Task DoForceReload()
{
// svuoto cache dati IoNS...
var dtMaccKey = Utils.RedKeyDatiMacc("", MpIoNS);
await SDetService.ResetCache(dtMaccKey);
// resto delle cache
await SDetService.ResetCache();
await IocService.ClearFusionCache();
DoReset();
+1 -1
View File
@@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>8.16.2606.1312</Version>
<Version>8.16.2606.1511</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo MP-IOC </i>
<h4>Versione: 8.16.2606.1312</h4>
<h4>Versione: 8.16.2606.1511</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
8.16.2606.1312
8.16.2606.1511
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>8.16.2606.1312</version>
<version>8.16.2606.1511</version>
<url>https://nexus.steamware.net/repository/SWS/MP-IOC/stable/LAST/MP.IOC.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/MP-IOC/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+377
View File
@@ -0,0 +1,377 @@
# Piano Migrazione MpDataService → IIocService
> **Obiettivo**: Standardizzare l'accesso dati su `IIocService` (con FusionCache) e pensionare `MpDataService` come service layer principale di `MP.IOC`.
>
> **Strategia**: Ogni metodo `MpDataService` usato dal controller viene migrato in `IocService`. La cache FusionCache avvolge le chiamate Redis/DB con le stesse durate e tag esistenti. I controller usano solo `IIocService`.
---
## Architettura Target
```
IOBController
└── IIocService (scoped)
├── FusionCache (L1 Memory + L2 Redis + L3 DB via tags)
├── StackExchange.Redis (IDatabase) ← sorgente "live" per dati esterni
└── IIocRepository (scoped) ← DB access
```
**Regola chiave**: Redis diretto (scritto da IOB-WIN, altri programmi) resta accessibile via `IDatabase _redisDb` in `IocService`. FusionCache ha una durata equivalente per validare/invalidare coerentemente.
---
## Stato Attuale
### Minimal APIs — OK
I 3 endpoint in `IobEndpoints.cs` sono stati puliti e ora contengono solo:
- `GET api/IOB` / `api/IOB/alive` — health check
- `GET api/IOB/enabled/{id}` — usa `await iocService.IobInsEnabAsync(id)`
I metodi `setCounter` e `getCurrODL` sono stati rimossi (troppo semplici, nel controller bastano).
### Controller — Misto
| Usa DService | Usa IOCService |
|---|---|
| ~37 metodi | 5 metodi (`getCounterTCRec`, `getCurrODL`, `setCounter`, `input`, `getTask2Exe` in parte) |
### MpDataService
- Registrato come **singleton** in `Program.cs:106`
- Contiene ~80 metodi, molti con accesso Redis diretto senza FusionCache
- `IocService` è **scoped** e usa già FusionCache
### IocService
- Eredita da `MP.Data.Services.BaseServ` (contiene `GetOrFetchAsync<T>`)
- 12 metodi pubblici nell'interface
- Usa FusionCache per i metodi più critici (`IobInsEnabAsync`, `GetCurrOdlAsync`, `StatoProdMacchinaAsync`)
---
## Piano di Migrazione — Fasi
### Fase 0: Preparazione (`IocService` base)
**File**: `IIocService.cs`, `IocService.cs`
0.1. `IocService` deve ereditare da `MP.Data.Services.BaseServ` (attuale `IocService` non lo fa) — per accedere a `GetOrFetchAsync<T>` condiviso
0.2. Aggiunta di `IFusionCache _cache` nel constructor (già presente in `BaseServ`)
0.3. Verifica: `redisLongTimeCache` e `redisShortTimeCache` usati da `IocService` per le durate FusionCache
---
### Fase 1: Metodi KeepAlive + Task Management
**Priorità**: Alta — usati da quasi tutti gli altri metodi come precursori.
| MpDataService | → IocService | NOTE |
|---|---|---|
| `ScriviKeepAliveAsync` | ✓ già duplicato in `IocService` | Unificare, aggiungere FusionCache per la check key |
| `AddOptPar4Machine` | → `AddOptPar4Machine` | Redis hash, cache per tag machine |
| `AddTask4Machine` | → `AddTask4Machine` | Redis hash + backup, cache per tag machine |
| `mOptParMacchina` | → `GetOptParMacchina` | Redis hash read |
| `mTaskMacchina` / `mTaskMacchinaAsync` | → `GetTaskMacchina` | Redis hash read |
| `RemTask2ExeMacchinaAsync` | → `RemoveTask2ExeAsync` | Redis hash write + delete |
| `mSavedTaskMacchina` | → `GetSavedTasksMacchina` | Redis hash read |
| `AddCheckTask4Machine` | → `AddCheckTask4Machine` | Redis hash read/write |
| `AddTask4MacListAsync` | → `AddTaskListAsync` | Redis hash batch write |
**Durata cache**: `redisShortTimeCache` (2 min + jitter) per task hash (dati volatile)
**Tag cache**: `Task:{idxMacchina}`, `OptPar:{idxMacchina}`, `SavedTask:{idxMacchina}`
---
### Fase 2: Metodi Process (INPUT, FLOG, ULOG)
**Priorità**: Altissima — il cuore del sistema, alto traffico.
| MpDataService | → IocService | NOTE |
|---|---|---|
| `ProcessInputAsync` | → `ProcessInputAsync` | GIÀ in `IocService` ✓ |
| `ProcessFluxLogAsync` | → `ProcessFluxLogAsync` | DB insert + Redis write |
| `ProcessUserLogAsync` | → `ProcessUserLogAsync` | DB insert multi-flux |
| `CheckMicroStatoAsync` | → `CheckMicroStatoAsync` | DB upsert + transizione |
| `scriviRigaEventoAsync` | → `WriteEventRecordAsync` | DB insert |
| `saveSigLogAsync` | → `SaveSignalLogAsync` | DB insert |
| `GetSrvDtEvent` | → `GetServerDateTime` | Pure logic — no cache |
| `ParseEventTime` | → `ParseEventTime` | GIÀ in `IocService` ✓ |
| `preProcInput` | → `PreProcessInput` | Pure logic — no cache |
| `ValidateInputParams` / `ValidateinputParams` | → `ValidateInputParams` | Pure logic — no cache |
**Durata cache**: Nessuna (scrittura). Tag invalidazione per `MachineData:{idxMacchina}` sui dati che influenzano.
**Tag cache**: `Input:{idxMacchina}`, `FluxLog:{idxMacchina}`, `UserLog:{idxMacchina}`, `MicroStato:{idxMacchina}`
---
### Fase 3: Metodi ODL/PODL
**Priorità**: Alta — letture frequenti durante produzione.
| MpDataService | → IocService | NOTE |
|---|---|---|
| `GetCurrOdlAsync` | → `GetCurrOdlAsync` | GIÀ in `IocService` ✓ |
| `GetLastOdlAsync` | → `GetLastOdlAsync` | Usare `GetOrFetchAsync<T>` |
| `OdlCurrByMaccAsync` | → `GetCurrentOdlAsync` | Usare `GetOrFetchAsync<T>` |
| `AutoStartOdlAsync` | → `AutoStartOdlAsync` | Complesso — Redis + DB + eventi |
| `FixDailyDossierAsync` | → `FixDailyDossierAsync` | Chiamata batch |
| `OdlAutoDayGenAsync` | → `GenerateDailyOdlAsync` | DB stored procedure |
| `OdlAutoDayGenFullAsync` | → `GenerateDailyOdlFullAsync` | DB stored procedure |
| `POdlGetByKey` | → `GetPodlByKeyAsync` | Usare `GetOrFetchAsync<T>` |
| `POdlGetByMaccArtAsync` | → `GetPodlByMachineAsync` | Usare `GetOrFetchAsync<T>` |
**Durata cache**: `redisLongTimeCache` (5 min) per ODL/PODL
**Tag cache**: `Odl:{idxMacchina}`, `Podl:{idxMacchina}`
---
### Fase 4: Metodi Contapezzi
**Priorità**: Alta — traffico continuo.
| MpDataService | → IocService | NOTE |
|---|---|---|
| `pzCounter` | → `GetPzCounterAsync` | Redis StringGet (lettura live) |
| `PzCounterTcAsync` | → `PzCounterTcAsync` | GIÀ in `IocService` ✓ |
| `saveCaricoPezzi` | → `SavePezziCaricoAsync` | Redis StringSet + DB insert |
| `SaveCounterAsync` | → `SaveCounterAsync` | GIÀ in `IocService` ✓ |
**Durata cache**: `redisShortTimeCache` per counter (dati volatile scritti da IOB esterni)
**Tag cache**: `PzCounter:{idxMacchina}`
---
### Fase 5: Metodi Parametri Macchina
**Priorità**: Alta — metodo più chiamato (ogni richiesta legge i parametri).
| MpDataService | → IocService | NOTE |
|---|---|---|
| `MachineParamListAsync` | → `GetMachineParamsAsync` | **CRITICO** — Redis read, aggiungere FusionCache |
| `MachineParamListPendingWriteAsync` | → `GetPendingWriteParamsAsync` | Wrapper su GetMachineParamsAsync |
| `MachineParamListSetAsync` | → `SetMachineParamsAsync` | Redis StringSet |
| `MachineParamUpsertAsync` | → `UpsertMachineParamsAsync` | Redis read + write batch |
| `UpsertCurrObjItemsAsync` | → `UpsertMachineParamsAsync` | Duplicate — unificare con UpsertMachineParamsAsync |
**Durata cache**: `redisShortTimeCache` (2 min) per parametri macchina — dati scritti da IOB esterni ogni pochi secondi
**Tag cache**: `MachineParams:{idxMacchina}`
---
### Fase 6: Metodi Anagrafica e Lookup
**Priorità**: Media — dati relativamente statici.
| MpDataService | → IocService | NOTE |
|---|---|---|
| `AnagStatiGetAllAsync` | → `GetAllStatiAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
| `ArticoliGetLastByMaccAsync` | → `GetLastArticlesByMachineAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
| `DossierLastByMachAsync` | → `GetLastDossiersByMachineAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
| `ListValuesFilt` | → `GetListValuesFilteredAsync` | Redis read (duplicare logica FusionCache) |
| `DecNumArtGetFiltAsync` | → `GetDecNumArticoliAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
| `MacchineGetFilt` | → `GetMachinesFilteredAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
| `Macchine2SlaveGetAllAsync` | → `GetMachineSlaveMapAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
| `MacchineRecipeArchive` | → `GetMachineRecipeArchivePathAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
| `ConfigGetAllAsync` | → `GetAllConfigAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
| `ConfFluxMach` | → `GetConfFluxByMachineAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
| `GetArtNumAsync` | → `GetArtNumByCodeAsync` | Interno — wrapper a DecNumArtGetFiltAsync |
| `MseGetAllAsync` | → `GetMachineStateExplAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
**Durata cache**: `redisLongTimeCache` (5 min + jitter)
**Tag cache**: `AnagStati`, `Articoli:{idxMacchina}`, `Dossier:{idxMacchina}`, `Config`, `MSE`, `Macchine`
---
### Fase 7: Metodi Configurazione IOB + Redis Diretto
**Priorità**: Bassa — configurazione manuale, basso traffico.
| MpDataService | → IocService | NOTE |
|---|---|---|
| `SetIobMemMap` | → `SetIobMemMapAsync` | Redis StringSet |
| `SetIobConfYamlAsync` | → `SetIobConfYamlAsync` | Redis StringSet |
| `SaveMachineIobConf` | → `SaveMachineIobConfAsync` | Redis HashSet |
| `SaveMachine2Iob` | → `SaveMachine2IobAsync` | Redis StringSet |
| `SaveDataItemsAsync` | → `SaveDataItemsAsync` | Mongo o DB |
| `GetCurrObjItems` | → `GetMachineParamsAsync` | **Duplicate** — usare Fase 5 |
---
### Fase 8: Metodi Logging e Monitoring
**Priorità**: Bassa — writing-only, nessun cache.
| MpDataService | → IocService | NOTE |
|---|---|---|
| `AlarmInsertAsync` | → `InsertAlarmLogAsync` | DB insert |
| `RemRebootLogAddAsync` | → `AddRebootLogAsync` | DB insert + Redis flush |
| `WriteEventRecordAsync` (scriviRigaEventoAsync) | → `WriteEventRecordAsync` | DB insert |
| `FluxLogSaveSnapshotAsync` | → `SaveFluxSnapshotAsync` | DB stored procedure |
| `DossierLastByMachResetAsync` | → `ResetDossierCacheAsync` | Redis delete |
| `FluxLogFirstByMachAsync` | → `GetFirstFluxLogsAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
---
### Fase 9: Metodi Helper e Internal
**Priorità**: Bassa — usati internamente.
| MpDataService | → IocService | NOTE |
|---|---|---|
| `mDatiMacchineAsync` | → `GetMachineDataAsync` | Redis HashGetAll + reset |
| `ResetDatiMacchinaAsync` | → `ResetMachineDataAsync` | Redis transaction batch |
| `StateMachInByKeyAsync` | → `GetStateMachineIngressiAsync` | Redis Hash |
| `resetMSMIAsync` | → `ResetMultiSMIAsync` | Redis Hash write |
| `resetSMIAsync` | → `ResetSMIAsync` | Redis Hash write |
| `ValoreSmiAsync` | → `GetSMIValueAsync` | Redis HashField get |
| `isMulti` | → `IsMultiMachine` | Pure logic |
| `ListMasterAsync` / `ListSlaveAsync` | → `GetMasterListAsync` / `GetSlaveListAsync` | Redis cache |
| `getRandTOut` | → `GetRandomTimeout` | Shared in BaseServ |
| `RedisCountKey`, `RedisDelKey`, `RedisFlushPatternAsync` | → `FlushKeysByPatternAsync` | Admin only |
| `RedisGetHashDictAsync`, `RedisSetHashDictAsync` | → `RedisGet/HashDictAsync` | Wrapper utilities |
---
## Fase 10: Pulizia Controller
### 10.1. Sostituire `DService` con `IOCService` in `IOBController`
Cambiare constructor e tutte le chiamate:
```csharp
// PRIMA
public IOBController(IConfiguration configuration, MpDataService DataService, IIocService IService)
{
DService = DataService;
IOCService = IService;
}
// DOPO
public IOBController(IIocService service)
{
_service = service;
}
```
Poi sostituire ogni `DService.Xxx(...)` con `_service.Xxx(...)`.
### 10.2. Rimuovere `MpDataService` dal controller
- Rimuovere la property `DService`
- Rimuovere il parametro dal constructor
- Unificare le chiamate miste (alcuni metodi fanno `DService.ScriviKeepAliveAsync` + `IOCService.Xxx`)
### 10.3. Spostare i private method in `IocService`
I metodi `processEvListJsonAsync`, `processFLogJsonAsync`, `processULogJsonAsync` contengono logica di business:
- `processEvListJsonAsync``ProcessEventListJsonAsync` in `IocService`
- `processFLogJsonAsync``ProcessFluxLogJsonAsync` in `IocService` (complesso — usa `MachineParamListAsync` + `UpsertCurrObjItemsAsync`)
- `processULogJsonAsync``ProcessUserLogJsonAsync` in `IocService`
### 10.4. Rimuovere `MpDataService` da `Program.cs`
```csharp
// RIMUOVERE:
// builder.Services.AddSingleton<MpDataService>();
```
---
## Riepilogo Metodi da Migrire
| Categoria | Metodi da aggiungere a IocService |
|---|---|
| KeepAlive + Task | ~9 metodi |
| Process (INPUT/FLOG/ULOG) | ~9 metodi |
| ODL/PODL | ~8 metodi |
| Contapezzi | ~3 metodi |
| Parametri Macchina | ~4 metodi |
| Anagrafica/Lookup | ~11 metodi |
| Config IOB | ~5 metodi |
| Logging | ~6 metodi |
| Helper/Internal | ~10 metodi |
| **TOTALE** | **~65 metodi** |
Di questi, ~15 sono già in `IocService` o `BaseServ`. Quindi **~50 nuovi metodi** da implementare.
---
## Criteri di Validazione
1. **Build**`./build_all_par.ps1 --agent` senza errori
2. **Functional parity** — ogni endpoint del controller restituisce lo stesso output di prima
3. **Cache behavior** — i metodi migrati registrano `data.source` come TRACE/DEBUG
4. **No `MpDataService` references** nel controller
5. **`Program.cs`** — `MpDataService` rimosso dalla DI registration
---
## Sequenza di Sviluppo Consigliata
```
Fase 1 → Fase 4: Blocchi "hot" (keepalive, task, counter)
Fase 5: Parametri macchina (blocco più critico per perf)
Fase 2: Process (INPUT/FLOG/ULOG - cuore dell'app)
Fase 3: ODL/PODL
Fase 6: Anagrafica (dati statici, meno rischioso)
Fase 7-9: Config + Logging + Helper
Fase 10: Cleanup finale
```
Ogni fase va build-ata e testata prima di procedere alla successiva.
---
## Note Tecniche
### Caching su Redis diretto
Per i dati scritti da programmi esterni (IOB-WIN, ecc.), il pattern è:
```
1. Lettura: _redisDb.StringGet/HashGet (dati "live")
2. Scrittura: programmi esterni scrivono in Redis direttamente
3. FusionCache: durata pari a redisShortTimeCache o redisLongTimeCache
- Il backplane Redis invalida L1 negli altri worker
- Se un programma esterno scrive, la prossima lettura dal controller fa cache-miss
```
Questo è coerente con l'architettura attuale: il controller legge Redis (sorgente di verità) e FusionCache avvolge per ridurre i round-trip.
### Tagging Strategy
```
MachineData:{idxMacchina} — dati macchina generali (ResetDatiMacchina)
MachineParams:{idxMacchina} — parametri macchina (MachineParamListAsync)
Task:{idxMacchina} — task da eseguire
PzCounter:{idxMacchina} — contapezzi
Odl:{idxMacchina} — ODL correnti
Podl:{idxMacchina} — PODL
Input:{idxMacchina} — log input
FluxLog:{idxMacchina} — log flux
UserLog:{idxMacchina} — log user
```
### Transizioni di Scope
`MpDataService` è singleton → `IIocService` è scoped. Questo va bene per le minimal APIs (che iniettano `IIocService` correttamente). Per il controller, dopo la rimozione di `MpDataService`, il controller diventerà scoped di default (già lo è per `[ApiController]`).
### IocService constructor
Attuale (da `MP.Data.Services.IOC.IocService`):
```csharp
public IocService(IConfiguration config, IConnectionMultiplexer redis,
IFusionCache cache, IIocRepository repo,
IServiceScopeFactory scopeFactory)
```
Da `MP.Data.Services.BaseServ`:
```csharp
public BaseServ(IConfiguration configuration, IFusionCache cache, IConnectionMultiplexer redConn)
```
La gerarchia sarà: `IocService` estende `BaseServ` e il constructor chiama `base(configuration, cache, redConn)`.