Update pesante x gestione servizio x accesso IOC repo
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using MP.Data.DbModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using static MP.Core.Objects.Enums;
|
||||
|
||||
@@ -26,6 +27,18 @@ namespace MP.Data.Repository.IOC
|
||||
/// <returns></returns>
|
||||
Task<bool> CheckCambiaStatoBatchAsync(tipoInputEvento tipoInput, string IdxMacchina, DateTime InizioStato, int IdxTipo, string CodArt, string Value, int MatrOpr, string pallet);
|
||||
|
||||
/// <summary>
|
||||
/// Elenco da tabella Config
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<List<ConfigModel>> ConfigGetAllAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Intera tab dati macchina
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<List<DatiMacchineModel>> DatiMacchineGetAllAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Aggiunta record MicroStato + EventList
|
||||
/// </summary>
|
||||
@@ -34,6 +47,41 @@ namespace MP.Data.Repository.IOC
|
||||
/// <returns></returns>
|
||||
Task<bool> EvListMicroStatoInsertAsync(MicroStatoMacchinaModel newRecMsm, EventListModel newRecEv);
|
||||
|
||||
/// <summary>
|
||||
/// Upsert record keepalive
|
||||
/// </summary>
|
||||
/// <param name="IdxMacc"></param>
|
||||
/// <param name="OraServer"></param>
|
||||
/// <param name="OraMacc"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> KeepAliveUpsertAsync(string IdxMacc, DateTime OraServer, DateTime OraMacc);
|
||||
|
||||
/// <summary>
|
||||
/// Intera tabella relazione master/slave in machine (gestione setup master - slave)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<List<Macchine2SlaveModel>> Macchine2SlaveAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Recupera record macchina da Idx
|
||||
/// </summary>
|
||||
/// <param name="IdxMacchina"></param>
|
||||
/// <returns></returns>
|
||||
Task<MacchineModel?> MacchineGetByIdxAsync(string IdxMacchina);
|
||||
|
||||
/// <summary>
|
||||
/// Upsert Record Macchine ASYNC
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<bool> MacchineUpsertAsync(MacchineModel entity);
|
||||
|
||||
/// <summary>
|
||||
/// Elenco da tabella Macchine
|
||||
/// </summary>
|
||||
/// <param name="IdxMacc"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<MicroStatoMacchinaModel>> MicroStatoMacchinaGetByIdxMaccAsync(string IdxMacc);
|
||||
|
||||
/// <summary>
|
||||
/// Aggiornamento record Microstato macchina
|
||||
/// </summary>
|
||||
@@ -41,6 +89,33 @@ namespace MP.Data.Repository.IOC
|
||||
/// <returns></returns>
|
||||
Task<bool> MicroStatoMacchinaUpsertAsync(MicroStatoMacchinaModel newRec);
|
||||
|
||||
/// <summary>
|
||||
/// Aggiunta record SignalLog Async
|
||||
/// </summary>
|
||||
/// <param name="newRec"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> SignalLogInsertAsync(SignalLogModel newRec);
|
||||
|
||||
/// <summary>
|
||||
/// Intera tabella state machine ingressi 2 eventi
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<List<TransizioneIngressiModel>> StateMachineIngressiAsync(int idxFam);
|
||||
|
||||
/// <summary>
|
||||
/// Vista v_MSFD x singola macchina (da stored) - singolo record
|
||||
/// </summary>
|
||||
/// <param name="idxMacc"></param>
|
||||
/// <returns></returns>
|
||||
Task<VMSFDModel?> VMSFDGetByMaccAsync(string idxMacc);
|
||||
|
||||
/// <summary>
|
||||
/// Vista v_MSFD delle machine MULTI filtrato x macchina (da stored)
|
||||
/// </summary>
|
||||
/// <param name="idxMacc"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<VMSFDModel>> VMSFDGetMultiByMaccAsync(string idxMacc);
|
||||
|
||||
#endregion Public Methods
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MongoDB.Driver;
|
||||
using MP.Data.DbModels;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -99,6 +101,30 @@ namespace MP.Data.Repository.IOC
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<ConfigModel>> ConfigGetAllAsync()
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
var dbResult = await dbCtx
|
||||
.DbSetConfig
|
||||
.AsNoTracking()
|
||||
.OrderBy(x => x.Chiave)
|
||||
.ToListAsync();
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<DatiMacchineModel>> DatiMacchineGetAllAsync()
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
var dbResult = await dbCtx
|
||||
.DbSetDatiMacchine
|
||||
.AsNoTracking()
|
||||
.OrderBy(x => x.IdxMacchina)
|
||||
.ToListAsync();
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> EvListMicroStatoInsertAsync(MicroStatoMacchinaModel newRecMsm, EventListModel newRecEv)
|
||||
{
|
||||
@@ -145,10 +171,103 @@ namespace MP.Data.Repository.IOC
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> KeepAliveUpsertAsync(string IdxMacc, DateTime OraServer, DateTime OraMacc)
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
bool fatto = false;
|
||||
|
||||
var currRec = await dbCtx
|
||||
.DbSetKeepAlive
|
||||
.Where(x => x.IdxMacchina == IdxMacc)
|
||||
.FirstOrDefaultAsync();
|
||||
if (currRec != null)
|
||||
{
|
||||
currRec.DataOraServer = OraServer;
|
||||
currRec.DataOraMacchina = OraMacc;
|
||||
dbCtx.Entry(currRec).State = EntityState.Modified;
|
||||
}
|
||||
else
|
||||
{
|
||||
KeepAliveModel newRec = new KeepAliveModel()
|
||||
{
|
||||
IdxMacchina = IdxMacc,
|
||||
DataOraMacchina = OraMacc,
|
||||
DataOraServer = OraServer,
|
||||
DataOraStart = DateTime.Now
|
||||
};
|
||||
dbCtx
|
||||
.DbSetKeepAlive
|
||||
.Add(newRec);
|
||||
}
|
||||
fatto = await dbCtx.SaveChangesAsync() > 0;
|
||||
|
||||
return fatto;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<Macchine2SlaveModel>> Macchine2SlaveAsync()
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
List<Macchine2SlaveModel> dbResult = await dbCtx
|
||||
.DbSetM2S
|
||||
.AsNoTracking()
|
||||
.OrderBy(x => x.IdxMacchina)
|
||||
.ToListAsync();
|
||||
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MacchineModel?> MacchineGetByIdxAsync(string IdxMacchina)
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
MacchineModel dbResult = await dbCtx
|
||||
.DbSetMacchine
|
||||
.FirstOrDefaultAsync(x => x.IdxMacchina == IdxMacchina);
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> MacchineUpsertAsync(MacchineModel entity)
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
|
||||
// Recuperiamo l'entità tracciata dal context
|
||||
var trackedEntity = await dbCtx
|
||||
.DbSetMacchine
|
||||
.FirstOrDefaultAsync(x => x.IdxMacchina == entity.IdxMacchina);
|
||||
|
||||
if (trackedEntity != null)
|
||||
{
|
||||
// Aggiorna i valori dell'entità tracciata con quelli della nuova
|
||||
dbCtx.Entry(trackedEntity).CurrentValues.SetValues(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbCtx.DbSetMacchine.Update(entity);
|
||||
}
|
||||
return await dbCtx.SaveChangesAsync() > 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<MicroStatoMacchinaModel>> MicroStatoMacchinaGetByIdxMaccAsync(string IdxMacc)
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
List<MicroStatoMacchinaModel> dbResult = new List<MicroStatoMacchinaModel>();
|
||||
dbResult = await dbCtx
|
||||
.DbSetMicroStatoMacc
|
||||
.Where(x => x.IdxMacchina == IdxMacc)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> MicroStatoMacchinaUpsertAsync(MicroStatoMacchinaModel newRec)
|
||||
{
|
||||
bool fatto = false;
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
bool fatto = false;
|
||||
|
||||
var actRec = await dbCtx
|
||||
.DbSetMicroStatoMacc
|
||||
@@ -174,6 +293,56 @@ namespace MP.Data.Repository.IOC
|
||||
return fatto;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> SignalLogInsertAsync(SignalLogModel newRec)
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
var currRec = dbCtx
|
||||
.DbSetSignalLog
|
||||
.Add(newRec);
|
||||
return await dbCtx.SaveChangesAsync() > 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<TransizioneIngressiModel>> StateMachineIngressiAsync(int idxFam)
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
var IdxFamIn = new SqlParameter("@IdxFamigliaIngresso", idxFam);
|
||||
var dbResult = await dbCtx
|
||||
.DbSetSMI
|
||||
.FromSqlRaw("exec dbo.stp_TRI_getByIdxFamIng @IdxFamigliaIngresso", IdxFamIn)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<VMSFDModel?> VMSFDGetByMaccAsync(string idxMacc)
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
var IdxMacchina = new SqlParameter("@IdxMacchina", idxMacc);
|
||||
var dbResult = (await dbCtx
|
||||
.DbSetMSFD
|
||||
.FromSqlRaw("exec dbo.stp_MSFD_getMacc @IdxMacchina", IdxMacchina)
|
||||
.AsNoTracking()
|
||||
.ToListAsync())
|
||||
.FirstOrDefault();
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<VMSFDModel>> VMSFDGetMultiByMaccAsync(string idxMacc)
|
||||
{
|
||||
await using var dbCtx = await CreateContextAsync();
|
||||
var IdxMacchina = new SqlParameter("@IdxMacchina", idxMacc);
|
||||
var dbResult = await dbCtx
|
||||
.DbSetMSFD
|
||||
.FromSqlRaw("exec dbo.stp_MSFD_getMulti @IdxMacchina", IdxMacchina)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
@@ -38,6 +38,17 @@ namespace MP.Data.Services.IOC
|
||||
/// <returns></returns>
|
||||
Task<bool> MicroStatoMacchinaUpsertAsync(MicroStatoMacchinaModel newRec);
|
||||
|
||||
/// <summary>
|
||||
/// Processa input da IOB eventualmente registrando i segnali inviati
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina"></param>
|
||||
/// <param name="valore"></param>
|
||||
/// <param name="dtEve"></param>
|
||||
/// <param name="dtCurr"></param>
|
||||
/// <param name="contatore"></param>
|
||||
/// <returns></returns>
|
||||
Task<string> ProcessInputAsync(string idxMacchina, string valore, string dtEve, string dtCurr, string contatore);
|
||||
|
||||
#endregion Public Methods
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MP.Core.Objects;
|
||||
using MP.Data.DbModels;
|
||||
using MP.Data.Repository.IOC;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static MP.Core.Objects.Enums;
|
||||
|
||||
@@ -15,10 +22,14 @@ namespace MP.Data.Services.IOC
|
||||
public IocService(
|
||||
IConfiguration config,
|
||||
IConnectionMultiplexer redis,
|
||||
IIocRepository repo) : base(config, redis)
|
||||
IIocRepository repo,
|
||||
IServiceScopeFactory scopeFactory) : base(config, redis)
|
||||
{
|
||||
_className = "IocServ";
|
||||
int.TryParse(config.GetValue<string>("ServerConf:redisLongTimeCache"), out redisLongTimeCache);
|
||||
int.TryParse(config.GetValue<string>("ServerConf:redisShortTimeCache"), out redisShortTimeCache);
|
||||
_repo = repo;
|
||||
_scopeFactory = scopeFactory;
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
@@ -32,6 +43,37 @@ namespace MP.Data.Services.IOC
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Elenco completo config da DB
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<List<ConfigModel>> ConfigGetAllAsync()
|
||||
{
|
||||
List<ConfigModel>? result = new List<ConfigModel>();
|
||||
// cerco in redis...
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(MP.Data.Utils.redisConfKey);
|
||||
string source = "DB";
|
||||
if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<ConfigModel>>($"{rawData}");
|
||||
source = "REDIS";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await _repo.ConfigGetAllAsync();
|
||||
//result = await Task.FromResult(SpecDbController.ConfigGetAllAsync());
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await _redisDb.StringSetAsync(MP.Data.Utils.redisConfKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
Log.Debug($"ConfigGetAllAsync Read from {source}");
|
||||
if (result == null)
|
||||
{
|
||||
result = new List<ConfigModel>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> EvListMicroStatoInsertAsync(MicroStatoMacchinaModel newRecMsm, EventListModel newRecEv)
|
||||
{
|
||||
@@ -39,6 +81,66 @@ namespace MP.Data.Services.IOC
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce il valOut booleano se la macchina sia abilitata all'inserimento COMPLETO nel
|
||||
/// Signal Log
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> IobSLogEnabAsync(string idxMacchina)
|
||||
{
|
||||
bool answ = false;
|
||||
// ORA recupero da memoria redis...
|
||||
try
|
||||
{
|
||||
var currHash = MP.Data.Utils.RedKeyDatiMacc(idxMacchina, MpIoNS);
|
||||
RedisValue rawData = await _redisDb.HashGetAsync(currHash, (RedisValue)"sLogEnabled");
|
||||
// se è vuoto... leggo da DB e popolo!
|
||||
if (!rawData.HasValue)
|
||||
{
|
||||
await ResetDatiMacchinaAsync(idxMacchina);
|
||||
// riprovo
|
||||
rawData = await _redisDb.HashGetAsync(currHash, (RedisValue)"sLogEnabled");
|
||||
}
|
||||
|
||||
// provo conversione
|
||||
bool.TryParse($"{rawData}", out answ);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Log.Error($"Errore IobSLogEnabAsync | idxMacchina {idxMacchina}:{Environment.NewLine}{exc}");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Elenco completo valori Macchine 2 Slave
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<List<Macchine2SlaveModel>> Macchine2SlaveGetAllAsync()
|
||||
{
|
||||
List<Macchine2SlaveModel>? result = new List<Macchine2SlaveModel>();
|
||||
string currKey = $"{MP.Data.Utils.redisBaseAddr}:M2STab";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<Macchine2SlaveModel>>($"{rawData}");
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await _repo.Macchine2SlaveAsync();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await _redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache * 10));
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new List<Macchine2SlaveModel>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> MicroStatoMacchinaUpsertAsync(MicroStatoMacchinaModel newRec)
|
||||
{
|
||||
@@ -46,13 +148,819 @@ namespace MP.Data.Services.IOC
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> ProcessInputAsync(string idxMacchina, string valore, string dtEve, string dtCurr, string contatore)
|
||||
{
|
||||
string answ = "";
|
||||
if (ValidateinputParams(idxMacchina, valore, dtEve, dtCurr))
|
||||
{
|
||||
DateTime dataOraEvento = ParseEventTime(dtEve, dtCurr);
|
||||
|
||||
// se abilitato registro evento sul DB
|
||||
if (await IobSLogEnabAsync(idxMacchina))
|
||||
{
|
||||
int cntVal = 0;
|
||||
int.TryParse(contatore, out cntVal);
|
||||
await saveSigLogAsync(idxMacchina, valore, dataOraEvento, cntVal);
|
||||
}
|
||||
// continuo col resto
|
||||
try
|
||||
{
|
||||
// scrivo keep alive!!! (se necessario, altrimenti è in cache...)
|
||||
await ScriviKeepAliveAsync(idxMacchina, DateTime.Now);
|
||||
// Cache dati macchina per evitare lookup ridondanti
|
||||
Dictionary<string, string> datiMacc = await mDatiMacchineAsync(idxMacchina);
|
||||
// verifico se sia una macchina MULTI....
|
||||
if (isMulti(idxMacchina, datiMacc))
|
||||
{
|
||||
// inizio preprocessing
|
||||
string newVal = "";
|
||||
// processo OGNI macchina a stati dell'impianto... (KEY:IdxMacchina / IdxMacchina#qualcosa, Val = IdxFamIn)
|
||||
foreach (var item in await mTabMSMIAsync(idxMacchina))
|
||||
{
|
||||
newVal = preProcInput(item.Key, valore, datiMacc);
|
||||
// ora processo e salvo il valOut del microstato...
|
||||
// INTERNAMENTE gestisce i casi DB/REDIS secondo necessità
|
||||
await CheckMicroStatoAsync(item.Key, newVal, dataOraEvento, contatore, datiMacc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ora processo e salvo il valOut del microstato... INTERNAMENTE
|
||||
// gestisce i casi DB/REDIS secondo necessità
|
||||
await CheckMicroStatoAsync(idxMacchina, valore, dataOraEvento, contatore, datiMacc);
|
||||
}
|
||||
// forzo RESET dati macchina...
|
||||
await ResetDatiMacchinaAsync(idxMacchina);
|
||||
// registro in risposta che è andato tutto bene...
|
||||
answ = "OK";
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
string errore = $"Errore: {Environment.NewLine}{exc}";
|
||||
Log.Error(errore);
|
||||
answ = errore;
|
||||
}
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// scrive un evento di keepalive sulla tabella
|
||||
/// </summary>
|
||||
/// <param name="IdxMacchina"></param>
|
||||
/// <param name="oraMacchina"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ScriviKeepAliveAsync(string IdxMacchina, DateTime oraMacchina)
|
||||
{
|
||||
// cerco se ho keep alive in redis,
|
||||
var currKey = MP.Data.Utils.RedKeyHash($"KeepAlive:{IdxMacchina}");
|
||||
bool keyPresent = await _redisDb.KeyExistsAsync(currKey);
|
||||
// se NON presente salvo in REDIS con TTL 10 sec e sul DB...
|
||||
if (!keyPresent)
|
||||
{
|
||||
DateTime adesso = DateTime.Now;
|
||||
await _redisDb.StringSetAsync(currKey, adesso.ToString("s"), TimeSpan.FromSeconds(30));
|
||||
// effettuo scrittura sul DB
|
||||
await _repo.KeepAliveUpsertAsync(IdxMacchina, DateTime.Now, oraMacchina);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected Random rand = new Random();
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce un timeout dai minuti richiesti + tempo random 1..60 sec
|
||||
/// </summary>
|
||||
/// <param name="stdMinutes"></param>
|
||||
/// <returns></returns>
|
||||
protected TimeSpan getRandTOut(double stdMinutes)
|
||||
{
|
||||
double rndValue = stdMinutes + (double)rand.Next(1, 60) / 60;
|
||||
return TimeSpan.FromMinutes(rndValue);
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private readonly string _className;
|
||||
|
||||
private readonly IIocRepository _repo;
|
||||
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Provider CultureInfo x parse valori (es dataora)
|
||||
/// </summary>
|
||||
private CultureInfo ciProvider = CultureInfo.InvariantCulture;
|
||||
|
||||
/// <summary>
|
||||
/// Formato dataora standard x parsing
|
||||
/// </summary>
|
||||
private string dtFormat = "yyyyMMddHHmmssfff";
|
||||
|
||||
private int redisLongTimeCache = 5;
|
||||
|
||||
private int redisShortTimeCache = 2;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// controlla se da il segnale di "microstato" deriva un evento da generare - modalità OFFLINE
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina">idx macchina</param>
|
||||
/// <param name="valore">valOut ingresso</param>
|
||||
/// <param name="dtEve">data-ora evento (server)</param>
|
||||
/// <param name="contatore">sequenza dati inviati</param>
|
||||
/// <param name="datiMaccCache">dati macchina in cache (opzionale, se null fa lookup)</param>
|
||||
/// <returns></returns>
|
||||
private async Task<inputComandoMapo> CheckMicroStatoAsync(string idxMacchina, string valore, DateTime dtEve, string contatore, Dictionary<string, string>? datiMaccCache = null)
|
||||
{
|
||||
// recupero SE IMPIEGATO REDIS i valori del Dictionary della macchina...
|
||||
Dictionary<string, string> datiMacc = datiMaccCache ?? await mDatiMacchineAsync(idxMacchina);
|
||||
|
||||
// processing
|
||||
inputComandoMapo answ = new inputComandoMapo();
|
||||
|
||||
// SE no trovassi verifico se esista la macchina altrimenti la creo... REDIS compliant
|
||||
if (!datiMacc.ContainsKey(idxMacchina))
|
||||
{
|
||||
await verificaIdxMacchinaAsync(idxMacchina);
|
||||
}
|
||||
|
||||
// continuo processing...
|
||||
string CodArticolo = datiMacc["CodArticolo"];
|
||||
if (string.IsNullOrEmpty(CodArticolo))
|
||||
{
|
||||
var allDatiMacch = await _repo.DatiMacchineGetAllAsync();
|
||||
var recMacc = allDatiMacch.FirstOrDefault(x => x.IdxMacchina == idxMacchina);
|
||||
if (recMacc != null)
|
||||
{
|
||||
CodArticolo = recMacc.CodArticoloA;
|
||||
}
|
||||
}
|
||||
// preparo gestione val ingresso
|
||||
int? valINT = 0;
|
||||
int idxTipoEv = 0;
|
||||
int next_idxMS = 0;
|
||||
try
|
||||
{
|
||||
valINT = int.Parse(valore, NumberStyles.HexNumber);
|
||||
int idxFamIn = Convert.ToInt32(datiMacc["IdxFamIn"]);
|
||||
int idxMicroStato = Convert.ToInt32(datiMacc["IdxMicroStato"]);
|
||||
int valIOB = Convert.ToInt32(valINT);
|
||||
next_idxMS = idxMicroStato;
|
||||
// recupero singolo valOut (stringa) x chiave
|
||||
string todoSMI = await ValoreSmiAsync(idxFamIn, idxMicroStato, valIOB);
|
||||
// solo se ho trovato un risultato nella tab SMI della famiglia macchina...
|
||||
if (!string.IsNullOrEmpty(todoSMI) && todoSMI.Contains("_"))
|
||||
{
|
||||
// splitto e salvo valori OUT...
|
||||
string[] valori = todoSMI.Split('_');
|
||||
idxTipoEv = Convert.ToInt32(valori[0]);
|
||||
next_idxMS = Convert.ToInt32(valori[1]);
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Log.Error($"[ChkMiSt_5a] - - Eccezione in recupero riga Trans ingressi | idxMacchina {idxMacchina} | valINT {valINT}:{Environment.NewLine}{exc}", Environment.NewLine, exc, valINT, idxMacchina);
|
||||
}
|
||||
|
||||
// effettuo update vari SU DB!!!
|
||||
MicroStatoMacchinaModel newRecMsm = new MicroStatoMacchinaModel()
|
||||
{
|
||||
IdxMacchina = idxMacchina,
|
||||
IdxMicroStato = next_idxMS,
|
||||
InizioStato = dtEve,
|
||||
Value = valore
|
||||
};
|
||||
if (idxTipoEv > 0)
|
||||
{
|
||||
// preparo record
|
||||
string valEsteso = string.Format("[{0}] {1}", contatore.PadLeft(3, '0'), valore);
|
||||
// creo evento
|
||||
EventListModel newRecEv = new EventListModel()
|
||||
{
|
||||
CodArticolo = CodArticolo,
|
||||
IdxMacchina = idxMacchina,
|
||||
IdxTipo = idxTipoEv,
|
||||
InizioStato = dtEve,
|
||||
MatrOpr = 0,
|
||||
pallet = "-",
|
||||
Value = valEsteso
|
||||
};
|
||||
// salva e processa evento + microstato
|
||||
answ = await scriviRigaEventoAsync(newRecMsm, newRecEv);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _repo.MicroStatoMacchinaUpsertAsync(newRecMsm);
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper standardizzazione valOut dataora ricevuto da IOB remoti
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
private string dtFormStd(string? s)
|
||||
{
|
||||
return s?.Length > 17 ? s[..17] : s?.PadRight(17, '0') ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce il valOut booleano se la macchina sia di tipo MULTI (con più state machine x INGRESSI)
|
||||
/// usando dati macchina in cache per evitare lookup ridondanti
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina"></param>
|
||||
/// <param name="datiMacc"></param>
|
||||
/// <returns></returns>
|
||||
private bool isMulti(string idxMacchina, Dictionary<string, string> datiMacc)
|
||||
{
|
||||
bool answ = false;
|
||||
try
|
||||
{
|
||||
answ = Convert.ToBoolean(datiMacc.ContainsKey("Multi") && datiMacc["Multi"] == "1");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Log.Error($"Eccezione in isMulti{Environment.NewLine}{exc}");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
private async Task<HashSet<string>> ListMasterAsync()
|
||||
{
|
||||
HashSet<string> result = new();
|
||||
string currKey = $"{MP.Data.Utils.redisBaseAddr}:ListMaster";
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<HashSet<string>>($"{rawData}") ?? new();
|
||||
}
|
||||
else
|
||||
{
|
||||
var fullList = await Macchine2SlaveGetAllAsync();
|
||||
result = fullList.Select(x => x.IdxMacchina).ToHashSet<string>();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await _redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache * 10));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<HashSet<string>> ListSlaveAsync()
|
||||
{
|
||||
HashSet<string> result = new();
|
||||
string currKey = $"{MP.Data.Utils.redisBaseAddr}:ListSlave";
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<HashSet<string>>($"{rawData}") ?? new();
|
||||
}
|
||||
else
|
||||
{
|
||||
var fullList = await Macchine2SlaveGetAllAsync();
|
||||
result = fullList.Select(x => x.IdxMacchinaSlave).Distinct().ToHashSet<string>();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await _redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache * 10));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restitusice elenco KVP dei campi DatiMacchine + StatoMacchine per l'impianto indicato
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<Dictionary<string, string>> mDatiMacchineAsync(string idxMacchina)
|
||||
{
|
||||
// hard coded dimensione vettore DatiMacchine
|
||||
Dictionary<string, string> answ = new Dictionary<string, string>();
|
||||
// ORA recupero da memoria redis...
|
||||
try
|
||||
{
|
||||
var currHash = MP.Data.Utils.RedKeyDatiMacc(idxMacchina, MpIoNS);
|
||||
answ = await RedisGetHashDictAsync(currHash);
|
||||
// se è vuoto... leggo da DB e popolo!
|
||||
if (answ.Count == 0)
|
||||
{
|
||||
answ = await ResetDatiMacchinaAsync(idxMacchina);
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Log.Error($"Errore in compilazione dati Macchine x Redis - idxMacchina {idxMacchina}:{Environment.NewLine}{exc}");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restitusice elenco KVP (async) per evitare blocchi quando chiamato da metodi asincroni
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<KeyValuePair<string, string>[]> mTabMSMIAsync(string idxMacchina)
|
||||
{
|
||||
KeyValuePair<string, string>[] answ = new KeyValuePair<string, string>[1];
|
||||
answ[0] = new KeyValuePair<string, string>("0", "0");
|
||||
try
|
||||
{
|
||||
var currHash = MP.Data.Utils.RedKeyMsmi(idxMacchina);
|
||||
answ = await RedisGetHashAsync(currHash);
|
||||
if (answ == null || answ.Length == 0)
|
||||
{
|
||||
answ = await resetMSMIAsync(idxMacchina);
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Log.Error($"Errore in compilazione Tabella Multi State Machine Ingressi x Redis:{Environment.NewLine}{exc}");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calcola dataora evento da info dt ricevute
|
||||
/// </summary>
|
||||
/// <param name="dtEve"></param>
|
||||
/// <param name="dtCurr"></param>
|
||||
/// <returns></returns>
|
||||
private DateTime ParseEventTime(string dtEve, string dtCurr)
|
||||
{
|
||||
DateTime dataOraEvento = DateTime.Now;
|
||||
|
||||
// fix formato dataora in ingresso
|
||||
string stdEve = dtFormStd(dtEve);
|
||||
string stdCurr = dtFormStd(dtCurr);
|
||||
|
||||
// 2. Se le stringhe normalizzate coincidono, skip dei calcoli
|
||||
if (stdEve != stdCurr)
|
||||
{
|
||||
// 3. Parsing sicuro
|
||||
if (DateTime.TryParseExact(stdEve, dtFormat, ciProvider, DateTimeStyles.None, out DateTime dtEvento) &&
|
||||
DateTime.TryParseExact(stdCurr, dtFormat, ciProvider, DateTimeStyles.None, out DateTime dtCorrente))
|
||||
{
|
||||
TimeSpan diff = dtCorrente - dtEvento;
|
||||
double ms = Math.Abs(diff.TotalMilliseconds);
|
||||
|
||||
// 4. Classificazione delta con switch expression (più leggibile e performante)
|
||||
string deltaClass = ms switch
|
||||
{
|
||||
<= 10 => "0-10 ms",
|
||||
<= 100 => "10-100 ms",
|
||||
<= 1000 => "100-1000 ms",
|
||||
<= 10000 => "1-10 sec",
|
||||
_ => "> 10 sec"
|
||||
};
|
||||
|
||||
Log.Debug($"Correzione delta {deltaClass}");
|
||||
|
||||
// 5. Correzione data/ora server: sottrao lo scarto calcolato
|
||||
dataOraEvento = dataOraEvento.Subtract(diff);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"Errore parsing date: {stdEve} | {stdCurr}");
|
||||
}
|
||||
}
|
||||
return dataOraEvento;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calcola l'effettivo valOut da passare alla macchina a stati INGRESSI usando dati macchina in cache
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina"></param>
|
||||
/// <param name="valore"></param>
|
||||
/// <param name="datiMacc"></param>
|
||||
/// <returns></returns>
|
||||
private string preProcInput(string idxMacchina, string valore, Dictionary<string, string> datiMacc)
|
||||
{
|
||||
string newVal = "";
|
||||
try
|
||||
{
|
||||
// variabili
|
||||
int valINT = 0;
|
||||
int BitFilt = 0;
|
||||
int BSR = 0;
|
||||
bool ExplodeBit = false;
|
||||
int NumBit = 0;
|
||||
int newValInt = 0;
|
||||
// recupero parametri dalla cache...
|
||||
int.TryParse(datiMacc.ContainsKey("BitFilt") ? datiMacc["BitFilt"] : "0", out BitFilt);
|
||||
int.TryParse(datiMacc.ContainsKey("BSR") ? datiMacc["BSR"] : "0", out BSR);
|
||||
Boolean.TryParse(datiMacc.ContainsKey("ExplodeBit") ? datiMacc["ExplodeBit"] : "false", out ExplodeBit);
|
||||
// non usato (x ora)
|
||||
int.TryParse(datiMacc.ContainsKey("NumBit") ? datiMacc["NumBit"] : "0", out NumBit);
|
||||
|
||||
// recupero valOut
|
||||
valINT = int.Parse(valore, NumberStyles.HexNumber);
|
||||
// filtro
|
||||
newValInt = MP.Core.Utils.bMaskInt(valINT, BitFilt);
|
||||
// effettuo eventuale BitShiftRight
|
||||
if (BSR > 0)
|
||||
{
|
||||
newValInt = newValInt >> BSR;
|
||||
}
|
||||
// effettuo eventuale esplosione in BIT esclusivi
|
||||
if (ExplodeBit)
|
||||
{
|
||||
newValInt = Convert.ToInt32(1 << newValInt);
|
||||
}
|
||||
// riconverto a STRING HEX!!!
|
||||
newVal = newValInt.ToString("X");
|
||||
}
|
||||
catch
|
||||
{
|
||||
newVal = valore;
|
||||
}
|
||||
|
||||
return newVal;
|
||||
}
|
||||
|
||||
private async Task<KeyValuePair<string, string>[]> RedisGetHashAsync(RedisKey redKey)
|
||||
{
|
||||
HashEntry[] rawData = await _redisDb.HashGetAllAsync(redKey);
|
||||
var result = rawData.Where(x => !x.Name.IsNull).Select(x => new KeyValuePair<string, string>($"{x.Name}", $"{x.Value}")).ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<Dictionary<string, string>> RedisGetHashDictAsync(RedisKey hashKey)
|
||||
{
|
||||
HashEntry[] rawData = await _redisDb.HashGetAllAsync(hashKey);
|
||||
var result = rawData
|
||||
.Where(x => !x.Name.IsNull)
|
||||
.ToDictionary(x => x.Name.ToString(), x => x.Value.ToString());
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task RedisSetHashAsync(RedisKey redKey, KeyValuePair<string, string>[] valori, double expireSeconds = -1.0)
|
||||
{
|
||||
HashEntry[] redHash = valori.Select(x => new HashEntry(x.Key, x.Value)).ToArray();
|
||||
await _redisDb.HashSetAsync(redKey, redHash);
|
||||
if (expireSeconds > 0.0)
|
||||
{
|
||||
await _redisDb.KeyExpireAsync(redKey, DateTime.Now.AddSeconds(expireSeconds));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restitusice elenco KVP dei campi DatiMacchine + StatoMacchine per l'impianto indicato
|
||||
/// </summary>
|
||||
/// <param name="idxMacc"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<Dictionary<string, string>> ResetDatiMacchinaAsync(string idxMacc)
|
||||
{
|
||||
Dictionary<string, string> result = new Dictionary<string, string>();
|
||||
VMSFDModel? dbResult = null;
|
||||
dbResult = await _repo.VMSFDGetByMaccAsync(idxMacc);
|
||||
if (dbResult == null) return new Dictionary<string, string>();
|
||||
|
||||
double numSecCache = redisLongTimeCache;
|
||||
// converto in formato dizionario...
|
||||
if (dbResult != null)
|
||||
{
|
||||
// salvo 1:1 i valori... STATO
|
||||
result.Add("IdxMicroStato", $"{dbResult.IdxMicroStato}");
|
||||
result.Add("IdxStato", $"{dbResult.IdxStato}");
|
||||
result.Add("CodArticolo", $"{dbResult.CodArticolo}");
|
||||
result.Add("insEnabled", $"{dbResult.InsEnabled}");
|
||||
result.Add("sLogEnabled", $"{dbResult.SLogEnabled}");
|
||||
result.Add("pallet", $"{dbResult.Pallet}");
|
||||
result.Add("CodArticolo_A", $"{dbResult.CodArticoloA}");
|
||||
result.Add("CodArticolo_B", $"{dbResult.CodArticoloB}");
|
||||
result.Add("TempoCicloBase", $"{dbResult.TempoCicloBase}");
|
||||
result.Add("PzPalletProd", $"{dbResult.PzPalletProd}");
|
||||
result.Add("MatrOpr", $"{dbResult.MatrOpr}");
|
||||
result.Add("lastVal", $"{dbResult.LastVal}");
|
||||
result.Add("TCBase", $"{dbResult.TempoCicloBase}");
|
||||
|
||||
//...e SETUP
|
||||
result.Add("CodMacc", $"{dbResult.Codmacchina}");
|
||||
result.Add("IdxFamIn", $"{dbResult.IdxFamigliaIngresso}");
|
||||
result.Add("Multi", $"{dbResult.Multi}");
|
||||
result.Add("BitFilt", $"{dbResult.BitFilt}");
|
||||
result.Add("MaxVal", $"{dbResult.MaxVal}");
|
||||
result.Add("BSR", $"{dbResult.Bsr}");
|
||||
result.Add("ExplodeBit", $"{dbResult.ExplodeBit}");
|
||||
result.Add("NumBit", $"{dbResult.NumBit}");
|
||||
result.Add("IdxFamMacc", $"{dbResult.IdxFamiglia}");
|
||||
result.Add("simplePallet", $"{dbResult.SimplePallet}");
|
||||
result.Add("palletChange", $"{dbResult.PalletChange}");
|
||||
// durata cache in secondi dal valOut insEnabled...
|
||||
//double numSecCache = ((result["insEnabled"].ToLower() == "true") ? redisShortTimeCache : redisLongTimeCache);
|
||||
numSecCache = dbResult.InsEnabled ? redisShortTimeCache : redisLongTimeCache;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
// dati master/slave
|
||||
string isMaster = (await ListMasterAsync()).Contains(idxMacc) ? "1" : "0";
|
||||
string isSlave = (await ListSlaveAsync()).Contains(idxMacc) ? "1" : "0";
|
||||
result.Add("Master", isMaster);
|
||||
result.Add("Slave", isSlave);
|
||||
|
||||
// Processing redis in transazoine...
|
||||
var redKey = MP.Data.Utils.RedKeyDatiMacc(idxMacc, MpIoNS);
|
||||
|
||||
// variazione con redis transaction...
|
||||
var transaction = _redisDb.CreateTransaction();
|
||||
// 1. Eliminiamo la chiave (rimuove i vecchi campi)
|
||||
_ = transaction.KeyDeleteAsync(redKey);
|
||||
// 2. Prepariamo gli HashEntry per il batch (più efficiente di un Dictionary)
|
||||
HashEntry[] entries = result.Select(kvp => new HashEntry(kvp.Key, kvp.Value)).ToArray();
|
||||
// 3. Inseriamo i nuovi valori
|
||||
_ = transaction.HashSetAsync(redKey, entries);
|
||||
// 4. Impostiamo l'expiration in un unico colpo
|
||||
_ = transaction.KeyExpireAsync(redKey, TimeSpan.FromSeconds(numSecCache));
|
||||
// Eseguiamo tutto in un unico viaggio verso Redis
|
||||
bool success = await transaction.ExecuteAsync();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resetta (rileggendo) i dati della State Machine multi ingressi nel formato
|
||||
/// currKey: IdxMacchina
|
||||
/// value: IdxFamigliaIngresso
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<KeyValuePair<string, string>[]> resetMSMIAsync(string idxMacchina)
|
||||
{
|
||||
var currHash = MP.Data.Utils.RedKeyMsmi(idxMacchina);
|
||||
// recupero records
|
||||
var tabMSMI = await _repo.VMSFDGetMultiByMaccAsync(idxMacchina);
|
||||
|
||||
KeyValuePair<string, string>[] answ = new KeyValuePair<string, string>[tabMSMI.Count];
|
||||
// salvo tutti i valori StateMachineIngressi...
|
||||
int i = 0;
|
||||
foreach (var item in tabMSMI)
|
||||
{
|
||||
answ[i] = new KeyValuePair<string, string>(item.IdxMacchina, item.IdxFamigliaIngresso.ToString());
|
||||
i++;
|
||||
}
|
||||
// verifico il timeout (default 60 sec...)
|
||||
var sTOutSmi = await tryGetConfigAsync("TmOut.MSMI");
|
||||
int tOut = 60;
|
||||
int.TryParse(sTOutSmi, out tOut);
|
||||
tOut = tOut <= 60 ? 60 : tOut;
|
||||
// salvo in redis!
|
||||
await RedisSetHashAsync(currHash, answ, tOut);
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resetta (rileggendo) i dati della State Machine ingressi nel formato
|
||||
/// currKey: cState_nVal (current MICRO-STATE + "_" + new Value)
|
||||
/// value: iTipoEv_nState (IdxTipoEv da trasmettere + New MICRO-STATE)
|
||||
/// </summary>
|
||||
/// <param name="idxFamIn"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<KeyValuePair<string, string>[]> resetSMIAsync(int idxFamIn)
|
||||
{
|
||||
var currHash = MP.Data.Utils.GetHashSMI(idxFamIn);
|
||||
// leggo da DB...
|
||||
var tabSMI = await _repo.StateMachineIngressiAsync(idxFamIn);
|
||||
|
||||
KeyValuePair<string, string>[] answ = new KeyValuePair<string, string>[tabSMI.Count];
|
||||
// salvo tutti i valori StateMachineIngressi...
|
||||
int i = 0;
|
||||
string key = "";
|
||||
string val = "";
|
||||
foreach (var item in tabSMI)
|
||||
{
|
||||
key = string.Format("{0}_{1}", item.IdxMicroStato, item.ValoreIngresso);
|
||||
val = string.Format("{0}_{1}", item.IdxTipoEvento, item.NextIdxMicroStato);
|
||||
answ[i] = new KeyValuePair<string, string>(key, val);
|
||||
i++;
|
||||
}
|
||||
// verifico il timeout (default 60 sec...)
|
||||
int tOut = 300;
|
||||
var sTOutSmi = await tryGetConfigAsync("TmOut.SMI");
|
||||
if (!string.IsNullOrEmpty(sTOutSmi))
|
||||
{
|
||||
int.TryParse(sTOutSmi, out tOut);
|
||||
}
|
||||
// salvo in redis!
|
||||
await RedisSetHashAsync(currHash, answ, tOut);
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// salva il segnale di "microstato" (segnale) ASYNC
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina">idx macchina</param>
|
||||
/// <param name="valore">valOut ingresso</param>
|
||||
/// <param name="dtEve">data-ora evento (server)</param>
|
||||
/// <param name="contatore">contatore sequenza dati inviati</param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> saveSigLogAsync(string idxMacchina, string valore, DateTime dtEve, int contatore)
|
||||
{
|
||||
SignalLogModel newRec = new SignalLogModel()
|
||||
{
|
||||
IdxMacchina = idxMacchina,
|
||||
DtCurr = DateTime.Now,
|
||||
DtEve = dtEve,
|
||||
Contatore = contatore,
|
||||
Valore = valore
|
||||
};
|
||||
return await _repo.SignalLogInsertAsync(newRec);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scrive una riga evento + una riga microstato insieme, ed effettua verifica necessità cambio stato
|
||||
/// </summary>
|
||||
/// <param name="newRecMsm">Record MicroStatoMacchina</param>
|
||||
/// <param name="newRecEv">record EventList</param>
|
||||
/// <returns></returns>
|
||||
private async Task<inputComandoMapo> scriviRigaEventoAsync(MicroStatoMacchinaModel newRecMsm, EventListModel newRecEv)
|
||||
{
|
||||
bool inserito = false;
|
||||
|
||||
// inserisco evento
|
||||
inserito = await _repo.EvListMicroStatoInsertAsync(newRecMsm, newRecEv);
|
||||
// faccio controllo per eventuale cambio stato da tab transizioni...
|
||||
|
||||
await _repo.CheckCambiaStatoBatchAsync(tipoInputEvento.hw, newRecEv.IdxMacchina, newRecEv.InizioStato ?? DateTime.Now, newRecEv.IdxTipo, newRecEv.CodArticolo, newRecEv.Value, newRecEv.MatrOpr, newRecEv.pallet);
|
||||
|
||||
// formatto output
|
||||
inputComandoMapo answ = new inputComandoMapo();
|
||||
answ.outValue = inserito.ToString();
|
||||
answ.needStatusRefresh = true;
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restitusice elenco KVP dei campi della State Machine ingressi nel formato
|
||||
/// currKey: cState_nVal (current MICRO-STATE + "_" + new Value)
|
||||
/// value: iTipoEv_nState (IdxTipoEv da trasmettere + New MICRO-STATE
|
||||
/// </summary>
|
||||
/// <param name="idxFamIn"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<KeyValuePair<string, string>[]> StateMachInByKeyAsync(int idxFamIn)
|
||||
{
|
||||
// hard coded dimensione vettore DatiMacchine
|
||||
KeyValuePair<string, string>[] answ = new KeyValuePair<string, string>[1];
|
||||
// iniziualizzo con un valOut... 0/0
|
||||
answ[0] = new KeyValuePair<string, string>("0", "0");
|
||||
// ORA recupero da memoria redis...
|
||||
try
|
||||
{
|
||||
var currHash = MP.Data.Utils.GetHashSMI(idxFamIn);
|
||||
answ = await RedisGetHashAsync(currHash);
|
||||
// se è vuoto... leggo da DB e popolo!
|
||||
if (answ.Length == 0)
|
||||
{
|
||||
answ = await resetSMIAsync(idxFamIn);
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Log.Error($"Errore in compilazione State Machine Ingressi x Redis:{Environment.NewLine}{exc}");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce valOut della stringa (SE disponibile)
|
||||
/// </summary>
|
||||
/// <param name="keyName"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> tryGetConfigAsync(string keyName)
|
||||
{
|
||||
string answ = "";
|
||||
// preselezione valori
|
||||
var configData = await ConfigGetAllAsync();
|
||||
var currRec = configData.FirstOrDefault(x => x.Chiave == keyName);
|
||||
if (currRec != null)
|
||||
{
|
||||
answ = currRec.Valore;
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validazione preliminare valori input
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina"></param>
|
||||
/// <param name="valore"></param>
|
||||
/// <param name="dtEve"></param>
|
||||
/// <param name="dtCurr"></param>
|
||||
/// <returns></returns>
|
||||
private bool ValidateinputParams(string idxMacchina, string valore, string dtEve, string dtCurr)
|
||||
{
|
||||
bool isValid = false;
|
||||
// preparo stringa valori correnti
|
||||
string currVals = $"idxMacchina: {idxMacchina} | valOut: {valore} | dtEve: {dtEve} | dtCurr:{dtCurr}";
|
||||
if (dtEve == null || dtCurr == null)
|
||||
{
|
||||
Log.Warn($"procInput: null found | {currVals}");
|
||||
}
|
||||
else if (dtEve.Length < 17 || dtCurr.Length < 17)
|
||||
{
|
||||
Log.Info($"procInput: invalid data | {currVals}");
|
||||
}
|
||||
else if (string.IsNullOrEmpty(idxMacchina))
|
||||
{
|
||||
Log.Info($"procInput: missing IdxMacchina | {currVals}");
|
||||
}
|
||||
else if (string.IsNullOrEmpty(valore))
|
||||
{
|
||||
Log.Info($"procInput: missing valOut | {currVals}");
|
||||
}
|
||||
else
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce il valore SPECIFICATO per la state machine ingressi
|
||||
/// value: iTipoEv_nState (IdxTipoEv da trasmettere + New MICRO-STATE)
|
||||
/// </summary>
|
||||
/// <param name="idxFamIn"></param>
|
||||
/// <param name="idxMicroStato"></param>
|
||||
/// <param name="valoreIn"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> ValoreSmiAsync(int idxFamIn, int idxMicroStato, int valoreIn)
|
||||
{
|
||||
string valOut = "";
|
||||
var currHash = MP.Data.Utils.GetHashSMI(idxFamIn);
|
||||
string field = $"{idxMicroStato}_{valoreIn}";
|
||||
var searchVal = await _redisDb.HashGetAsync(currHash, field);
|
||||
if (!searchVal.HasValue)
|
||||
{
|
||||
// ricarico tabella (salvando in redis x ricerca successiva)!
|
||||
var valori = await StateMachInByKeyAsync(idxFamIn);
|
||||
if (valori.Any(x => x.Key == field))
|
||||
{
|
||||
searchVal = valori.FirstOrDefault(x => x.Key == field).Value;
|
||||
}
|
||||
}
|
||||
valOut = $"{searchVal}";
|
||||
return valOut;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// cerca codice in anagrafica macchine ed eventualmente inserisce nuova macchina
|
||||
/// </summary>
|
||||
/// <param name="IdxMacchina"></param>
|
||||
private async Task verificaIdxMacchinaAsync(string IdxMacchina)
|
||||
{
|
||||
// esecuzione in REDIS...cerco status macchina...
|
||||
if ((await mDatiMacchineAsync(IdxMacchina)).Count == 0)
|
||||
{
|
||||
// verifico se esiste su DB
|
||||
var dbRec = await _repo.MacchineGetByIdxAsync(IdxMacchina);
|
||||
if (dbRec == null)
|
||||
{
|
||||
MacchineModel newRec = new MacchineModel()
|
||||
{
|
||||
IdxMacchina = IdxMacchina,
|
||||
CodMacchina = "0000",
|
||||
Nome = IdxMacchina,
|
||||
Descrizione = "Macchina non codificata",
|
||||
Note = "-",
|
||||
locazione = "",
|
||||
RecipeArchivePath = "",
|
||||
RecipePath = ""
|
||||
};
|
||||
await _repo.MacchineUpsertAsync(newRec);
|
||||
|
||||
// verifico ci sia un microstato macchina...
|
||||
var recMSM = await _repo.MicroStatoMacchinaGetByIdxMaccAsync(IdxMacchina);
|
||||
if (recMSM.Count == 0)
|
||||
{
|
||||
// inserisco nuovo stato...
|
||||
MicroStatoMacchinaModel msRec = new MicroStatoMacchinaModel()
|
||||
{
|
||||
IdxMacchina = IdxMacchina,
|
||||
IdxMicroStato = 0,
|
||||
InizioStato = DateTime.Now,
|
||||
Value = "00"
|
||||
};
|
||||
await _repo.MicroStatoMacchinaUpsertAsync(msRec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using MP.Core.DTO;
|
||||
using MP.Core.Objects;
|
||||
using MP.Data.DbModels;
|
||||
using MP.Data.Services.IOC;
|
||||
using MP.IOC.Data;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
@@ -16,17 +17,18 @@ namespace MP.IOC.Controllers
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
public IOBController(IConfiguration configuration, MpDataService DataService)
|
||||
public IOBController(IConfiguration configuration, MpDataService DataService, IIocService IService)
|
||||
{
|
||||
_configuration = configuration;
|
||||
DService = DataService;
|
||||
IOCService = IService;
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// SALVA x macchina KVP parametro/valore:
|
||||
/// GET: api/IOB/addOptPar/SIMUL_03?pName=PZREQ&pValue=1000
|
||||
/// </summary> <param name="id"></param> <param name="pName"></param> <param name="pValue"></param>
|
||||
@@ -327,15 +329,6 @@ namespace MP.IOC.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chiude ODL precedente ed avvia uno nuovo (duplicandolo e sitemando quantità RIMANENTE),
|
||||
/// e CONFERMA produzione...
|
||||
///
|
||||
/// GET: IOB/forceSplitOdl/SIMUL_03
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns>Esito chiamata (OK/vuoto)</returns>
|
||||
|
||||
[HttpGet("forceSplitOdl/{id}")]
|
||||
public async Task<IActionResult> ForceSplitOdl(string id)
|
||||
{
|
||||
@@ -358,6 +351,14 @@ namespace MP.IOC.Controllers
|
||||
return Ok(answ);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chiude ODL precedente ed avvia uno nuovo (duplicandolo e sitemando quantità RIMANENTE),
|
||||
/// e CONFERMA produzione...
|
||||
///
|
||||
/// GET: IOB/forceSplitOdl/SIMUL_03
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns>Esito chiamata (OK/vuoto)</returns>
|
||||
/// <summary>
|
||||
/// Recupera ArtNum dato CodXdl (per impianti che accettano solo INT in scrittura):
|
||||
/// GET: IOB/getArtNum/SIMUL_03?CodXdl=ABC123
|
||||
@@ -880,7 +881,8 @@ namespace MP.IOC.Controllers
|
||||
DateTime dataOraEvento = DateTime.Now;
|
||||
try
|
||||
{
|
||||
answ = await DService.ProcessInputAsync(id, valore, dtEve, dtCurr, cnt);
|
||||
answ = await IOCService.ProcessInputAsync(id, valore, dtEve, dtCurr, cnt);
|
||||
//answ = await DService.ProcessInputAsync(id, valore, dtEve, dtCurr, cnt);
|
||||
return Ok(answ);
|
||||
}
|
||||
catch (Exception exc)
|
||||
@@ -1447,8 +1449,8 @@ namespace MP.IOC.Controllers
|
||||
#region Private Fields
|
||||
|
||||
private static IConfiguration _configuration = null!;
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private IIocService IOCService;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Version>8.16.2604.3016</Version>
|
||||
<Version>8.16.2604.3017</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MP-IOC </i>
|
||||
<h4>Versione: 8.16.2604.3016</h4>
|
||||
<h4>Versione: 8.16.2604.3017</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2604.3016
|
||||
8.16.2604.3017
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2604.3016</version>
|
||||
<version>8.16.2604.3017</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>
|
||||
|
||||
Reference in New Issue
Block a user