Files
mapo-core/MP.IOC/refactor_migration.md
T
2026-06-15 11:23:08 +02:00

15 KiB

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:

// 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:

  • processEvListJsonAsyncProcessEventListJsonAsync in IocService
  • processFLogJsonAsyncProcessFluxLogJsonAsync in IocService (complesso — usa MachineParamListAsync + UpsertCurrObjItemsAsync)
  • processULogJsonAsyncProcessUserLogJsonAsync in IocService

10.4. Rimuovere MpDataService da Program.cs

// 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.csMpDataService 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):

public IocService(IConfiguration config, IConnectionMultiplexer redis, 
                  IFusionCache cache, IIocRepository repo, 
                  IServiceScopeFactory scopeFactory)

Da MP.Data.Services.BaseServ:

public BaseServ(IConfiguration configuration, IFusionCache cache, IConnectionMultiplexer redConn)

La gerarchia sarà: IocService estende BaseServ e il constructor chiama base(configuration, cache, redConn).