Files
2024-09-20 15:00:33 +02:00

1575 lines
65 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using NLog;
using Org.BouncyCastle.Utilities;
using SMGen.Core;
using SMGen.Data.Controllers;
using SMGen.Data.Data;
using SMGen.Data.DbModels;
using StackExchange.Redis;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace SMGen.Data.Services
{
public class SMGDataService
{
#region Public Fields
public static SMGenController dbController = null!;
#endregion Public Fields
#region Public Constructors
public SMGDataService(IConfiguration configuration, IConnectionMultiplexer redisConnMult)
{
_configuration = configuration;
// Conf cache
redisConn = redisConnMult;
redisDb = this.redisConn.GetDatabase();
// json serializer... FIX errore loop circolare https://www.ryadel.com/en/jsonserializationexception-self-referencing-loop-detected-error-fix-entity-framework-asp-net-core/
JSSettings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
// cod app
CodApp = _configuration["CodApp"];
// Conf DB
string connStr = _configuration.GetConnectionString("SMGen.DB");
if (string.IsNullOrEmpty(connStr))
{
Log.Info("ConnString empty!");
}
else
{
dbController = new SMGenController(configuration);
}
// chiudo log
Log.Info("Avviata classe WebDoorCreatorService");
}
#endregion Public Constructors
#region Public Properties
public string CodApp { get; set; } = "";
#endregion Public Properties
#region Public Methods
public async Task<Dictionary<int, string>> AnagEventiGetAll()
{
string source = "DB";
Dictionary<int, string> dbResult = new Dictionary<int, string>();
try
{
// cerco da cache
string currKey = $"{Constants.rKeyEventi}";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<Dictionary<int, string>>(rawData);
if (tempResult == null)
{
dbResult = new Dictionary<int, string>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.AnagEventiGetAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (dbResult == null)
{
dbResult = new Dictionary<int, string>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagEventiGetAll | {source} in: {ts.TotalMilliseconds} ms");
}
catch (Exception exc)
{
Log.Error($"Exception during AnagEventiGetAll: {exc}{Environment.NewLine}");
}
return dbResult;
}
public async Task<Dictionary<int, string>> AnagStatiGetAll()
{
string source = "DB";
Dictionary<int, string> dbResult = new Dictionary<int, string>();
try
{
// cerco da cache
string currKey = $"{Constants.rKeyStati}";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<Dictionary<int, string>>(rawData);
if (tempResult == null)
{
dbResult = new Dictionary<int, string>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.AnagStatiGetAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (dbResult == null)
{
dbResult = new Dictionary<int, string>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagStatiGetAll | {source} in: {ts.TotalMilliseconds} ms");
}
catch (Exception exc)
{
Log.Error($"Exception during AnagStatiGetAll: {exc}{Environment.NewLine}");
}
return dbResult;
}
public async Task<bool> AnagEventinInsert(List<AnagEventiModelTemp> newEvList)
{
var dbResult = await dbController.AnagEventinInsert(newEvList);
return dbResult;
}
/// <summary>
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
public async Task<bool> ExecFlushRedisPattern(RedisValue pattern)
{
bool answ = false;
var listEndpoints = redisConn.GetEndPoints();
foreach (var endPoint in listEndpoints)
{
var server = redisConn.GetServer(endPoint);
if (server != null)
{
var keyList = server.Keys(redisDb.Database, pattern);
foreach (var item in keyList)
{
await redisDb.KeyDeleteAsync(item);
}
answ = true;
}
}
return answ;
}
public async Task<List<FamIngressiViewModel>> FamIngressiGetAll()
{
string source = "DB";
List<FamIngressiViewModel> dbResult = new List<FamIngressiViewModel>();
try
{
// cerco da cache
string currKey = $"{Constants.rKeyFamIng}";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<List<FamIngressiViewModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<FamIngressiViewModel>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.FamIngressiGetAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (dbResult == null)
{
dbResult = new List<FamIngressiViewModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"FamIngressiGetAll | {source} in: {ts.TotalMilliseconds} ms");
}
catch (Exception exc)
{
Log.Error($"Exception during FamIngressiGetAll: {exc}{Environment.NewLine}");
}
return dbResult;
}
public async Task<List<FamStatiViewModel>> FamStatiGetAll()
{
string source = "DB";
List<FamStatiViewModel> dbResult = new List<FamStatiViewModel>();
try
{
// cerco da cache
string currKey = $"{Constants.rKeyFamStati}";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<List<FamStatiViewModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<FamStatiViewModel>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.FamStatiGetAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (dbResult == null)
{
dbResult = new List<FamStatiViewModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"FamStatiGetAll | {source} in: {ts.TotalMilliseconds} ms");
}
catch (Exception exc)
{
Log.Error($"Exception during FamStatiGetAll: {exc}{Environment.NewLine}");
}
return dbResult;
}
public async Task<Dictionary<string, FilesClass>> FilesGetAll()
{
Dictionary<string, FilesClass> dbResult = new Dictionary<string, FilesClass>();
string currKey = $"{Constants.FILES_TO_PROC}";
var rawData = await redisDb.HashGetAllAsync(currKey);
foreach (var item in rawData)
{
var desVal = JsonConvert.DeserializeObject<FilesClass>(item.Value!);
dbResult.Add($"{item.Name}", desVal!);
}
return dbResult;
}
public async Task<bool> FilesLoadRedis(Dictionary<string, FilesClass> filesDict)
{
bool fatto = false;
string currKey = $"{Constants.FILES_TO_PROC}";
await ExecFlushRedisPattern(currKey);
foreach (var item in filesDict)
{
string valSer = JsonConvert.SerializeObject(item.Value, JSSettings);
await RedHashUpsert(currKey, item.Key, valSer);
}
fatto = true;
return fatto;
}
public async Task<bool> FileUpdate(string origFileName, FilesClass fileNewInfo)
{
bool fatto = false;
string currKey = $"{Constants.FILES_TO_PROC}";
//await ExecFlushRedisPattern(currKey);
string valSer = JsonConvert.SerializeObject(fileNewInfo, JSSettings);
await RedHashUpsert(currKey, origFileName, valSer);
fatto = true;
return fatto;
}
public async Task<bool> TranInInsert(List<TransizioneIngressiModelTemp> newTIList, int currIdxFamiglia)
{
var dbResult = await dbController.TranInInsert(newTIList, currIdxFamiglia);
return dbResult;
}
#endregion Public Methods
public Dictionary<int, string> eventsFromDb = new Dictionary<int, string>();
private bool b_rules_definition = false;
List<CsvClass> csvLines = new List<CsvClass>();
List<string> linesList = new List<string>();
private int n_bits = 0;
private string n_state_machine_index = "";
private int n_states = 0;
private string sz_state_machine_name = "";
public List<AnagEventiModelTemp> events2Add { get; set; } = new List<AnagEventiModelTemp>();
public List<AnagEventiModelTemp> eventsAll { get; set; } = new List<AnagEventiModelTemp>();
protected string bitCsvPath
{
get => _configuration.GetValue<string>("ServerConf:BitCsvPath");
}
protected string procRootDir
{
get => _configuration.GetValue<string>("ServerConf:ProcCsvRootPath");
}
protected List<string> States2Rules { get; set; } = new List<string>();
protected Dictionary<string, int> StatesAll { get; set; } = new Dictionary<string, int>();
protected Dictionary<string, int> StatesAll2 { get; set; } = new Dictionary<string, int>();
protected string statiCsvPath
{
get => _configuration.GetValue<string>("ServerConf:StatiCsvPath");
}
private List<string> actStates2Chk { get; set; } = new List<string>();
private List<string> Bits { get; set; } = new List<string>();
private Dictionary<string, int> evOk { get; set; } = new Dictionary<string, int>();
private FileLinesClass linesChecked { get; set; } = new FileLinesClass();
private List<string> nextStates2Chk { get; set; } = new List<string>();
private List<RuleClass> Rules { get; set; } = new List<RuleClass>();
private Dictionary<string, int> Events_to_send { get; set; } = new Dictionary<string, int>();
private List<string> States { get; set; } = new List<string>();
private List<TransizioneIngressiModelTemp> TranInList2add { get; set; } = new List<TransizioneIngressiModelTemp>();
/// <summary>
/// Verifica su DB eventi e stati
/// </summary>
/// <param name="currFile"></param>
/// <returns></returns>
public async Task<FileLinesClass> DoCheckUnusedEv(FilesClass currFile)
{
await Task.Delay(1);
States.Clear();
Bits.Clear();
Events_to_send.Clear();
Rules.Clear();
eventsAll.Clear();
evOk.Clear();
StatesAll.Clear();
string lineOk = "";
// recupero nome file x partire
string filePath = currFile.tempFileName;
FileLinesClass answ = new FileLinesClass();
string[] lines = File.ReadAllLines(filePath, Encoding.UTF8);
try
{
foreach (var line in lines)
{
lineOk = line.Trim();
if (!lineOk.StartsWith("#") && !string.IsNullOrEmpty(lineOk) && lineOk.Length >= 3)
{
if (lineOk.Contains("#"))
{
var lineSplit = lineOk.Split("#");
lineOk = lineSplit[0];
}
var sz_tokens = lineOk.Split(":");
var sz_temp = sz_tokens[0].Trim();
switch (sz_temp)
{
case "$DEFINITIONS":
b_rules_definition = false;
break;
case "$NAME":
break;
case "$IDX":
break;
case "$STATE":
if (!StatesAll.ContainsKey(sz_tokens[2].Trim().ToUpper()))
{
StatesAll.Add(sz_tokens[2].Trim().ToUpper(), int.Parse(sz_tokens[1].Trim().ToUpper()));
}
break;
case "$EVENT":
if (!Events_to_send.ContainsValue(int.Parse(sz_tokens[1].Trim().ToUpper())))
{
Events_to_send.Add(sz_tokens[2].Trim().ToUpper(), int.Parse(sz_tokens[1].Trim().ToUpper()));
}
var newEvent = new AnagEventiModelTemp()
{
IdxTipo = int.Parse(sz_tokens[1].Trim().ToUpper()),
Nome = sz_tokens[2].Trim().ToUpper()
};
break;
case "$RULES":
b_rules_definition = true;
break;
case "$DO":
b_rules_definition = false;
break;
default:
if (b_rules_definition)
{
var state = sz_temp.Trim().ToUpper();
var event_to_send = sz_tokens[1].Trim().ToUpper();
var next_state = sz_tokens[2].Trim().ToUpper();
var temp_rule = new RuleClass()
{
state = state,
event_to_send = event_to_send,
next_state = next_state
};
Rules.Add(temp_rule);
States2Rules.Add(next_state);
}
break;
}
}
else
{
//linesChecked.Add(line, true);
}
}
foreach (var item in States2Rules)
{
if (!StatesAll2.ContainsKey(item))
{
StatesAll2.Add(item, StatesAll[item]);
}
}
foreach (var ev in Events_to_send)
{
var rule2Ev = Rules.FirstOrDefault(x => x.event_to_send == ev.Key);
if (rule2Ev != null)
{
if (!evOk.ContainsKey(ev.Key) && !evOk.ContainsValue(ev.Value))
{
evOk.Add(ev.Key, ev.Value);
}
}
}
}
catch (Exception exc)
{
Log.Error($"{exc}{Environment.NewLine}");
}
answ.file = currFile.tempFileName;
answ.statesOK = StatesAll2;
answ.eventsOK = evOk;
answ.lines = lines.ToList();
return answ;
}
/// <summary>
/// Verifica su DB eventi e stati
/// </summary>
/// <param name="currFile"></param>
/// <returns></returns>
public async Task<FileLinesClass> DoCheckUnusedEvSt(FilesClass currFile)
{
await Task.Delay(1);
States.Clear();
Bits.Clear();
Events_to_send.Clear();
Rules.Clear();
eventsAll.Clear();
evOk.Clear();
StatesAll.Clear();
string lineOk = "";
// recupero nome file x partire
string filePath = currFile.tempFileName;
FileLinesClass answ = new FileLinesClass();
string[] lines = File.ReadAllLines(filePath, Encoding.UTF8);
try
{
foreach (var line in lines)
{
lineOk = line.Trim();
if (!lineOk.StartsWith("#") && !string.IsNullOrEmpty(lineOk) && lineOk.Length >= 3)
{
if (lineOk.Contains("#"))
{
var lineSplit = lineOk.Split("#");
lineOk = lineSplit[0];
}
var sz_tokens = lineOk.Split(":");
var sz_temp = sz_tokens[0].Trim();
switch (sz_temp)
{
case "$DEFINITIONS":
b_rules_definition = false;
break;
case "$NAME":
break;
case "$IDX":
break;
case "$STATE":
if (!StatesAll.ContainsKey(sz_tokens[2].Trim().ToUpper()))
{
StatesAll.Add(sz_tokens[2].Trim().ToUpper(), int.Parse(sz_tokens[1].Trim().ToUpper()));
}
break;
case "$EVENT":
if (!Events_to_send.ContainsValue(int.Parse(sz_tokens[1].Trim().ToUpper())))
{
Events_to_send.Add(sz_tokens[2].Trim().ToUpper(), int.Parse(sz_tokens[1].Trim().ToUpper()));
}
var newEvent = new AnagEventiModelTemp()
{
IdxTipo = int.Parse(sz_tokens[1].Trim().ToUpper()),
Nome = sz_tokens[2].Trim().ToUpper()
};
break;
case "$RULES":
b_rules_definition = true;
break;
case "$DO":
b_rules_definition = false;
break;
default:
if (b_rules_definition)
{
var state = sz_temp.Trim().ToUpper();
var event_to_send = sz_tokens[1].Trim().ToUpper();
var next_state = sz_tokens[2].Trim().ToUpper();
var temp_rule = new RuleClass()
{
state = state,
event_to_send = event_to_send,
next_state = next_state
};
Rules.Add(temp_rule);
States2Rules.Add(next_state);
}
break;
}
}
else
{
//linesChecked.Add(line, true);
}
}
foreach (var item in States2Rules)
{
if (!StatesAll2.ContainsKey(item))
{
StatesAll2.Add(item, StatesAll[item]);
}
}
foreach (var ev in Events_to_send)
{
var rule2Ev = Rules.FirstOrDefault(x => x.event_to_send == ev.Key);
if (rule2Ev != null)
{
if (!evOk.ContainsKey(ev.Key) && !evOk.ContainsValue(ev.Value))
{
evOk.Add(ev.Key, ev.Value);
}
}
}
}
catch (Exception exc)
{
Log.Error($"{exc}{Environment.NewLine}");
}
answ.file = currFile.tempFileName;
answ.statesOK = StatesAll2;
answ.eventsOK = evOk;
answ.lines = lines.ToList();
return answ;
}
/// <summary>
/// Valuta un file di ruoles x ingressi 2 eventi e restituisce esito
/// </summary>
/// <param name="currFile">Path file *.rul da processare</param>
/// <param name="saveToDb">Indica se salvare sul DB</param>
/// <param name="doProcState">Indica se processare parte state (x state machine stati)</param>
/// <returns></returns>
///
public async Task<FilesClass> EvalIn2EvRuleFile(FilesClass currFile, bool saveToDb, bool doProcState)
{
await Task.Delay(1);
Dictionary<string, string> evSt2Change = new Dictionary<string, string>();
sz_state_machine_name = "";
n_state_machine_index = "";
n_states = 0;
n_bits = 0;
States.Clear();
Bits.Clear();
Events_to_send.Clear();
Rules.Clear();
eventsAll.Clear();
evOk.Clear();
StatesAll.Clear();
string line = "";
// recupero nome file x partire
string filePath = currFile.tempFileName;
string errMgs = "";
eventsFromDb = await AnagEventiGetAll();
try
{
// stream lettura file
var isProcActive = true;
if (isProcActive)
{
using (StreamReader sr = new StreamReader(filePath))
{
while ((line = await sr.ReadLineAsync().ConfigureAwait(false)) != null)
{
if (!line.StartsWith("#") && !string.IsNullOrEmpty(line) && line.Length >= 3)
{
if (line.Contains("#"))
{
var lineSplit = line.Split("#");
line = lineSplit[0];
}
var sz_tokens = line.Split(":");
var sz_temp = sz_tokens[0].Trim().ToUpper();
switch (sz_temp)
{
case "$DEFINITIONS":
b_rules_definition = false;
break;
case "$NAME":
sz_state_machine_name = sz_tokens[1].Trim();
break;
case "$IDX":
n_state_machine_index = sz_tokens[1].Trim();
break;
case "$N_STATES":
n_states = int.Parse(sz_tokens[1].Trim());
break;
case "$N_BITS":
n_bits = int.Parse(sz_tokens[1].Trim());
break;
case "$BIT":
Bits.Add(sz_tokens[2].Trim().ToUpper());
break;
case "$STATE":
//States.Add(sz_tokens[2].Trim().ToUpper());
if (!StatesAll.ContainsKey(sz_tokens[2].Trim().ToUpper()))
{
StatesAll.Add(sz_tokens[2].Trim().ToUpper(), int.Parse(sz_tokens[1].Trim().ToUpper()));
}
break;
case "$EVENT":
if (!Events_to_send.ContainsValue(int.Parse(sz_tokens[1].Trim().ToUpper())))
{
Events_to_send.Add(sz_tokens[2].Trim().ToUpper(), int.Parse(sz_tokens[1].Trim().ToUpper()));
}
var newEvent = new AnagEventiModelTemp()
{
IdxTipo = int.Parse(sz_tokens[1].Trim().ToUpper()),
Nome = sz_tokens[2].Trim().ToUpper()
};
break;
case "$RULES":
b_rules_definition = true;
break;
case "$DO":
b_rules_definition = false;
var isOk = checkDirExist(procRootDir);
if (isOk)
{
var fileName = $"{currFile.origFileName.Split(".")[0]}.csv";
currFile.DLoadFileName = $"{Path.Combine(procRootDir, bitCsvPath, fileName)}";
currFile = await evalIn2Ev_transitions(currFile);
if (saveToDb)
{
//await AnagEventinInsert(events2Add);
await Task.Delay(1);
//await TranInInsert(TranInList2add, int.Parse(n_state_machine_index));
}
}
isProcActive = false;
break;
default:
if (b_rules_definition)
{
var temp_rule = new RuleClass()
{
//id_state = StatesAll[sz_temp.Trim().ToUpper()],
state = sz_temp.Trim().ToUpper(),
expression = sz_tokens[1].Trim().ToUpper(),
next_state = sz_tokens[2].Trim().ToUpper(),
//id_next_state = StatesAll[sz_temp.Trim().ToUpper()],
event_to_send = sz_tokens[3].Trim().ToUpper(),
id_event_to_send = Events_to_send[sz_tokens[3].Trim().ToUpper()]
};
Rules.Add(temp_rule);
}
break;
}
}
}
}
evSt2Change = await modFile(currFile, false, doProcState);
if (evSt2Change.Count() == 0)
{
foreach (var ev in Events_to_send)
{
var rule2Ev = Rules.FirstOrDefault(x => x.event_to_send == ev.Key);
if (rule2Ev != null)
{
if (!evOk.ContainsKey(ev.Key) && !evOk.ContainsValue(ev.Value))
{
evOk.Add(ev.Key, ev.Value);
}
}
}
foreach (var item in evOk)
{
var newEvent = new AnagEventiModelTemp()
{
IdxTipo = item.Value,
Nome = item.Key.Trim().ToUpper()
};
var listElement = events2Add.FirstOrDefault(x => x.IdxTipo == newEvent.IdxTipo);
if (listElement != null && listElement.Nome != newEvent.Nome)
{
errMgs = $"L'evento '{newEvent.IdxTipo}: {newEvent.Nome}' non può essere importato perchè diverso da '{listElement.IdxTipo}: {listElement.Nome}' nel file {currFile.origFileName}.{Environment.NewLine}\n\n";
Log.Error(errMgs);
currFile.errorMsgs.Add(errMgs);
currFile.isOk = false;
currFile.calcRunning = false;
await FileUpdate(currFile.origFileName, currFile);
}
else if (listElement == null)
{
events2Add.Add(newEvent);
}
}
}
else
{
foreach (var item in evSt2Change)
{
errMgs = $"{item.Value} {item.Key}: necessita di essere sistemato sul file originale perchè la descrizione non corrisponde al DB";
Log.Error(errMgs);
currFile.errorMsgs.Add(errMgs);
}
currFile.isOk = false;
currFile.calcRunning = false;
await FileUpdate(currFile.origFileName, currFile);
}
}
}
catch (Exception exc)
{
errMgs = $"Eccezione durante la lettura del file {currFile.origFileName} alla riga {line}: {exc}{Environment.NewLine}";
currFile.errorMsgs.Add(errMgs);
currFile.isOk = false;
currFile.calcRunning = false;
await FileUpdate(currFile.origFileName, currFile);
Log.Error(errMgs);
}
return currFile;
}
/// <summary>
/// Modifica il file in oggetto
/// </summary>
/// <param name="file"></param>
/// <param name="doProc"></param>
/// <param name="doProcState">Necessaria modifica stati (x state machine stati)</param>
/// <returns></returns>
public async Task<Dictionary<string, string>> modFile(FilesClass file, bool doProc, bool doProcState)
{
Dictionary<string, string> evSt2Change = new Dictionary<string, string>();
Dictionary<int, string> eventsFromDb = await AnagEventiGetAll();
Dictionary<int, string> statesFromDb = await AnagStatiGetAll();
string filePath = file.tempFileName;
var lines = File.ReadLines(filePath);
var fileTxt = File.ReadAllText(filePath);
string errMsg = "";
int n = 0;
Log.Debug($"{n++:00}");
foreach (var line in lines)
{
if (line != "" && line.Contains(":"))
{
if (!line.StartsWith("$EVENT") && !line.StartsWith("#") && line.StartsWith("$STATE"))
{
if (doProcState)
{
var lineSplit = line.Split(":");
if (lineSplit.Count() >= 3)
{
if (!statesFromDb.ContainsKey(int.Parse(lineSplit[1].Trim())))
{
errMsg = $"Lo stato {lineSplit[1].Trim()}: {lineSplit[2].Trim()} non è presente in AnagraficaStati sul DB";
file.errorMsgs.Add(errMsg);
file.isOk = false;
}
else if (statesFromDb[int.Parse(lineSplit[1].Trim())] != lineSplit[2].Trim())
{
fileTxt = fileTxt.Replace(lineSplit[2].Trim(), statesFromDb[int.Parse(lineSplit[1].Trim())]);
evSt2Change.Add(lineSplit[2].Trim(), lineSplit[0].Trim());
}
}
}
}
else if (line.StartsWith("$EVENT") && !line.StartsWith("#") && !line.StartsWith("$STATE"))
{
var lineSplit = line.Split(":");
if (lineSplit.Count() >= 3)
{
if (!eventsFromDb.ContainsKey(int.Parse(lineSplit[1].Trim())))
{
errMsg = $"L'evento {lineSplit[1].Trim()}: {lineSplit[2].Trim()} non è presente in AnagraficaEventi sul DB";
file.errorMsgs.Add(errMsg);
file.isOk = false;
}
else if (eventsFromDb[int.Parse(lineSplit[1].Trim())] != lineSplit[2].Trim())
{
#if false
// definisco i token con spazi prima e dopo x evitare sostituzione token "esempio" ed "esempio_01"...
string sOrig = $" {lineSplit[2].Trim()} ";
string sDest = $" {eventsFromDb[int.Parse(lineSplit[1].Trim())]} ";
fileTxt = fileTxt.Replace(sOrig, sDest);
#endif
fileTxt = fileTxt.Replace(lineSplit[2].Trim(), eventsFromDb[int.Parse(lineSplit[1].Trim())]);
evSt2Change.Add(lineSplit[2].Trim(), lineSplit[0].Trim());
}
}
}
}
}
Log.Debug($"{n++:00}");
if (doProc)
{
if (file.errorMsgs.Count() == 0)
{
var fileName = $"{file.origFileName.Split(".")[0]}.rul";
file.DLoadFileName = $"{Path.Combine(procRootDir, bitCsvPath, fileName)}";
file.isOk = true;
await FileUpdate(file.origFileName, file);
using (StreamWriter sw = new StreamWriter(file.DLoadFileName))
{
sw.Write(fileTxt);
}
}
Log.Debug($"T{n++:00}");
}
else
{
await FileUpdate(file.origFileName, file);
Log.Debug($"F{n++:00}");
}
return evSt2Change;
}
/// <summary>
/// Valuta un file di ruoles x ingressi 2 Stati e restituisce esito
/// </summary>
/// <param name="currFile">Path file *.rul da processare</param>
/// <param name="saveToDb">Indica se salvare sul DB</param>
/// <param name="calcItself"></param>
/// <param name="calcEmptyState"></param>
/// <param name="orderType"></param>
/// <param name="doProcState">Indica se processare parte state (x state machine stati)</param>
/// <returns></returns>
public async Task<FilesClass> EvalIn2StateRuleFile(FilesClass currFile, bool saveToDb, bool calcItself, bool calcEmptyState, Core.Enum.ORDERTYPE orderType, bool doProcState)
{
await Task.Delay(1);
Dictionary<string, string> evSt2Change = new Dictionary<string, string>();
sz_state_machine_name = "";
n_state_machine_index = "";
n_states = 0;
n_bits = 0;
States.Clear();
Bits.Clear();
Events_to_send.Clear();
Rules.Clear();
StatesAll.Clear();
actStates2Chk.Clear();
nextStates2Chk.Clear();
// recupero nome file x partire
string filePath = currFile.tempFileName;
string line = "";
string errMsg = "";
try
{
var isProcActive = true;
if (isProcActive)
{
// stream lettura file
using (StreamReader sr = new StreamReader(filePath))
{
while ((line = await sr.ReadLineAsync().ConfigureAwait(false)) != null)
{
line = line.Replace("\t", " ").Trim();
if (!line.StartsWith("#") && !string.IsNullOrEmpty(line) && line.Length >= 3)
{
if (line.Contains("#"))
{
var lineSplit = line.Split("#");
line = lineSplit[0];
}
//else if (line.StartsWith("\t"))
//{
//}
var sz_tokens = line.Split(":");
var sz_temp = sz_tokens[0].Trim();
switch (sz_temp)
{
case "$DEFINITIONS":
b_rules_definition = false;
break;
case "$NAME":
sz_state_machine_name = sz_tokens[1].Trim();
break;
case "$IDX":
n_state_machine_index = sz_tokens[1].Trim();
break;
case "$STATE":
if (!StatesAll.ContainsKey(sz_tokens[2].Trim().ToUpper()))
{
StatesAll.Add(sz_tokens[2].Trim().ToUpper(), int.Parse(sz_tokens[1].Trim().ToUpper()));
}
break;
case "$EVENT":
if (!Events_to_send.ContainsValue(int.Parse(sz_tokens[1].Trim().ToUpper())))
{
Events_to_send.Add(sz_tokens[2].Trim().ToUpper(), int.Parse(sz_tokens[1].Trim().ToUpper()));
}
var newEvent = new AnagEventiModelTemp()
{
IdxTipo = int.Parse(sz_tokens[1].Trim().ToUpper()),
Nome = sz_tokens[2].Trim().ToUpper()
};
break;
case "$RULES":
b_rules_definition = true;
break;
case "$DO":
b_rules_definition = false;
var isOk = checkDirExist(Path.Combine(procRootDir, statiCsvPath));
if (isOk)
{
var fileName = $"{currFile.origFileName.Split(".")[0]}.csv";
currFile.DLoadFileName = $"{Path.Combine(procRootDir, statiCsvPath, fileName)}";
currFile = await evalIn2State_transitions(currFile, calcItself, calcEmptyState, orderType);
}
break;
default:
if (b_rules_definition)
{
var state = sz_temp.Trim().ToUpper();
var event_to_send = sz_tokens[1].Trim().ToUpper();
var next_state = sz_tokens[2].Trim().ToUpper();
var temp_rule = new RuleClass()
{
state = state,
event_to_send = event_to_send,
next_state = next_state,
id_next_state = StatesAll[next_state],
id_event_to_send = Events_to_send[event_to_send]
};
Rules.Add(temp_rule);
States2Rules.Add(next_state);
if (state != "ALL_STATES" && state != "ND")
{
actStates2Chk.Add(state);
}
else if (state == "ALL_STATES" && state != "ND")
{
actStates2Chk.AddRange(StatesAll.Keys.Where(x => x != "ND"));
}
if (next_state != "ALL_STATES" && next_state != "ND")
{
foreach (var st in StatesAll)
{
if (st.Key != "ND")
{
nextStates2Chk.Add(st.Key);
}
}
}
}
break;
}
}
}
}
}
}
catch (Exception exc)
{
Log.Error($"Eccezione durante la lettura del file {currFile.origFileName} alla riga {line}: {exc}{Environment.NewLine}");
}
evSt2Change = await modFile(currFile, false, doProcState);
if (evSt2Change.Count() == 0)
{
foreach (var ev in Events_to_send)
{
var rule2Ev = Rules.FirstOrDefault(x => x.event_to_send == ev.Key);
if (rule2Ev != null)
{
if (!evOk.ContainsKey(ev.Key) && !evOk.ContainsValue(ev.Value))
{
evOk.Add(ev.Key, ev.Value);
}
}
}
foreach (var item in evOk)
{
var newEvent = new AnagEventiModelTemp()
{
IdxTipo = item.Value,
Nome = item.Key.Trim().ToUpper()
};
var listElement = events2Add.FirstOrDefault(x => x.IdxTipo == newEvent.IdxTipo);
if (listElement != null && listElement.Nome != newEvent.Nome)
{
errMsg = $"L'evento '{newEvent.IdxTipo}: {newEvent.Nome}' non può essere importato perchè diverso da '{listElement.IdxTipo}: {listElement.Nome}' nel file {currFile.origFileName}.{Environment.NewLine}";
Log.Error(errMsg);
currFile.errorMsgs.Add(errMsg);
currFile.isOk = false;
currFile.calcRunning = false;
await FileUpdate(currFile.origFileName, currFile);
//return null;
}
else if (listElement == null)
{
events2Add.Add(newEvent);
}
}
foreach (var state2Chk in actStates2Chk.Distinct())
{
var nxtSt = nextStates2Chk.FirstOrDefault(x => x == state2Chk);
if (nxtSt == null)
{
errMsg = $"Nel file {currFile.origFileName} lo stato: {state2Chk} non risulta essere presente come stato di uscita.{Environment.NewLine}";
Log.Error(errMsg);
currFile.errorMsgs.Add(errMsg);
currFile.isOk = false;
currFile.calcRunning = false;
}
}
foreach (var nxtState in nextStates2Chk.Distinct())
{
var St = actStates2Chk.FirstOrDefault(x => x == nxtState);
if (St == null)
{
errMsg = $"Nel file {currFile.origFileName} lo stato: {nxtState} non risulta essere presente come stato di entrata.{Environment.NewLine}";
Log.Error(errMsg);
currFile.errorMsgs.Add(errMsg);
currFile.isOk = false;
currFile.calcRunning = false;
}
}
}
else
{
foreach (var item in evSt2Change)
{
errMsg = $"{item.Value} {item.Key}: necessita di essere sistemato sul file originale perchè la descrizione non corrisponde al DB";
Log.Error(errMsg);
currFile.errorMsgs.Add(errMsg);
}
currFile.isOk = false;
currFile.calcRunning = false;
await FileUpdate(currFile.origFileName, currFile);
}
return currFile;
}
/// <summary>
/// Valutazione schema macchina a stati x Ingressi --> Eventi
/// </summary>
/// <param name="currFile"></param>
/// <returns></returns>
protected async Task<FilesClass> evalIn2Ev_transitions(FilesClass currFile)
{
await Task.Delay(1);
StringBuilder sb = new StringBuilder();
TranInList2add.Clear();
if (File.Exists(currFile.DLoadFileName))
{
File.Delete(currFile.DLoadFileName);
}
sb.AppendLine("IdxFamigliaIngresso;IdxMicroStato;ValoreIngresso;IdxTipoEvento;next_IdxMicroStato");
string sz_actual_state = "";
string sz_actual_bit = "";
string sz_line = "";
//int i = 0;
int n_input = 0;
//int n = 0;
int n_mask = 0;
int n_bit = 0;
bool[] b_bit = new bool[20];
bool b_invert = false;
//ciclo negli stati
try
{
for (var i = 0; i <= n_states - 1; i++)
{
// verificare qui: non dovrebbe essere vente_to_send ma stati!!!!
//sz_actual_state = Events_to_send.FirstOrDefault(x => x.Value == i).Key;
sz_actual_state = StatesAll.FirstOrDefault(x => x.Value == i).Key;
//ciclo negli ingressi
for (n_input = 0; n_input <= (Math.Pow(2, n_bits) - 1); n_input++)
{
n_mask = 1;
for (var n = 0; n <= n_bits - 1; n++)
{
b_bit[n] = Convert.ToBoolean(n_input & n_mask);
n_mask = n_mask << 1;
}
var exitFor = false;
foreach (var act_rule in Rules)
{
n_bit = -1;
if ((act_rule.state == "ALL_STATES") || (act_rule.state == sz_actual_state))
{
//DA RESETTARE A FALSE (errore da principianti...)
b_invert = false;
sz_actual_bit = act_rule.expression;
//controllo se la riga contiene l'istruzione per negare il bit
if (sz_actual_bit.Trim().StartsWith("NOT"))
{
//inverto il bit
b_invert = true;
sz_actual_bit = sz_actual_bit.Replace("NOT ", "").Trim();
}
//cerca il nome del bit e ne trova l' indice da 0
n_bit = Bits.FindIndex(x => x == sz_actual_bit);
if (n_bit == -1)
{
exitFor = true;
}
int nextState = StatesAll[act_rule.next_state];
if (!exitFor)
{
if (((!b_invert) && b_bit[n_bit]) || (b_invert && (!b_bit[n_bit])))
{
sz_line = "";
//eseguo solo se diverso dallo stato corrente
if (StatesAll[act_rule.next_state] != i)
{
if (nextState < 0)
{
nextState = 0;
}
sz_line = $"{n_state_machine_index}" +
$";" +
$"{i}" +
$";" +
$"{n_input}" +
$";" +
$"{Events_to_send[act_rule.event_to_send]}" +
$";" +
$"{nextState}";
sb.AppendLine(sz_line);
#if false
var newTranIn = new TransizioneIngressiModelTemp()
{
IdxFamigliaIngresso = int.Parse(n_state_machine_index),
IdxMicroStato = i,
ValoreIngresso = n_input,
IdxTipoEvento = Events_to_send[act_rule.event_to_send],
next_IdxMicroStato = States.IndexOf(act_rule.next_state)
};
TranInList2add.Add(newTranIn);
#endif
}
exitFor = true;
}
}
}
}
}
}
using (StreamWriter sw = new StreamWriter(currFile.DLoadFileName))
{
sw.Write(sb.ToString());
}
await Task.Delay(1);
currFile.isOk = true;
await FileUpdate(currFile.origFileName, currFile);
}
catch (Exception exc)
{
Log.Error($"Eccezione in evalIn2Ev_transitions{Environment.NewLine}{exc}");
}
return currFile;
}
/// <summary>
/// Valutazione schema macchina a stati x Ingressi --> stati
/// </summary>
/// <param name="currFile"></param>
/// <returns></returns>
protected async Task<FilesClass> evalIn2State_transitions(FilesClass currFile, bool calcItself, bool calcEmptyState, Core.Enum.ORDERTYPE orderType)
{
csvLines.Clear();
TranInList2add.Clear();
StringBuilder sb = new StringBuilder();
if (File.Exists(currFile.DLoadFileName))
{
File.Delete(currFile.DLoadFileName);
}
sb.AppendLine("IdxFamiglia;IdxStato;IdxTipo;next_IdxStato");
int sz_actual_state = 0;
string sz_line = "";
foreach (var item in States2Rules)
{
if (!StatesAll2.ContainsKey(item))
{
StatesAll2.Add(item, StatesAll[item]);
}
}
#if false
if (orderType == Core.Enum.ORDERTYPE.stateEvent)
{
Events_to_send = Events_to_send.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
Rules = Rules.OrderBy(x => x.id_event_to_send).ThenBy(x => x.id_next_state).ToList();
}
else if (orderType == Core.Enum.ORDERTYPE.eventState)
{
Events_to_send = Events_to_send.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
}
else if (orderType == Core.Enum.ORDERTYPE.eventNextState)
{
StatesAll2 = StatesAll2.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
Rules = Rules.OrderBy(x => x.id_event_to_send).ThenBy(x => x.id_next_state).ToList();
}
#endif
Events_to_send = Events_to_send.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
Rules = Rules.OrderBy(x => x.id_event_to_send).ToList();
//ciclo negli stati
try
{
foreach (var item in StatesAll2)
{
sz_actual_state = item.Value;
foreach (var act_rule in Rules)
{
var exitFor = false;
if ((act_rule.state == "ALL_STATES") || (act_rule.state == StatesAll.Where(x => x.Value == sz_actual_state).FirstOrDefault().Key))
{
//DA RESETTARE A FALSE (errore da principianti...)
int nextState = StatesAll2[act_rule.next_state];
if (!exitFor)
{
sz_line = "";
//eseguo solo se diverso dallo stato corrente
// || calcItself
if (nextState != item.Value || calcItself)
{
if (nextState < 0)
{
nextState = 0;
}
if ((nextState == 0 && calcEmptyState) || (nextState > 0))
{
//sb.AppendLine(sz_line);
var newLine = new CsvClass()
{
IdxFamiglia = n_state_machine_index,
state = item.Value,
event_to_send = Events_to_send[act_rule.event_to_send],
next_state = nextState
};
csvLines.Add(newLine);
#if false
var newTranIn = new TransizioneIngressiModelTemp()
{
IdxFamigliaIngresso = int.Parse(n_state_machine_index),
IdxMicroStato = item.Value,
IdxTipoEvento = Events_to_send[act_rule.event_to_send],
next_IdxMicroStato = nextState
};
TranInList2add.Add(newTranIn);
#endif
}
}
exitFor = true;
}
}
}
}
//await SMGDService.AnagEventinInsert(events2Add);
if (orderType == Core.Enum.ORDERTYPE.stateEvent)
{
csvLines = csvLines.OrderBy(x => x.state).ThenBy(x => x.event_to_send).ToList();
}
else if (orderType == Core.Enum.ORDERTYPE.eventState)
{
csvLines = csvLines.OrderBy(x => x.event_to_send).ThenBy(x => x.state).ToList();
}
else if (orderType == Core.Enum.ORDERTYPE.eventNextState)
{
csvLines = csvLines.OrderBy(x => x.event_to_send).ThenBy(x => x.next_state).ToList();
}
foreach (var item in csvLines)
{
sz_line = $"{item.IdxFamiglia}" +
$";" +
$"{item.state}" +
$";" +
$"{item.event_to_send}" +
$";" +
$"{item.next_state}";
sb.AppendLine(sz_line);
}
using (StreamWriter sw = new StreamWriter(currFile.DLoadFileName))
{
sw.Write(sb.ToString());
}
await Task.Delay(1);
currFile.isOk = true;
await FileUpdate(currFile.origFileName, currFile);
}
catch (Exception exc)
{
Log.Error($"Eccezione in evalIn2State_transitions{Environment.NewLine}{exc}");
}
return currFile;
}
private bool checkDirExist(string dir)
{
bool answ = Directory.Exists(dir);
if (!answ)
{
var dirCreate = Directory.CreateDirectory(dir);
answ = (dirCreate != null);
}
return answ;
}
#region Protected Methods
/// <summary>
/// Effettua upsert in HasList redis
/// </summary>
/// <param name="currKey">Chiave redis della Hashlist</param>
/// <param name="chiave">Chiave nella HashList</param>
/// <param name="valore">Valore da salvare</param>
/// <returns>Num record nella HashList</returns>
protected async Task<long> RedHashUpsert(RedisKey currKey, string chiave, string valore)
{
long numReq = 0;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
await redisDb.HashSetAsync(currKey, chiave, valore);
numReq = await redisDb.HashLengthAsync(currKey);
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"RedHashUpsert | {currKey} | in: {ts.TotalMilliseconds} ms");
return numReq;
}
#endregion Protected Methods
#region Private Fields
private static IConfiguration _configuration = null!;
private static JsonSerializerSettings? JSSettings;
private static Logger Log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Durata cache lunga IN SECONDI
/// </summary>
private int cacheTtlLong = 60 * 5;
/// <summary>
/// Durata cache breve IN SECONDI
/// </summary>
private int cacheTtlShort = 60 * 1;
/// <summary>
/// Oggetto per connessione a REDIS
/// </summary>
private IConnectionMultiplexer redisConn;
/// <summary>
/// Oggetto DB redis da impiegare x chiamate R/W
/// </summary>
private IDatabase redisDb = null!;
private Random rnd = new Random();
#endregion Private Fields
#region Private Properties
/// <summary>
/// Durata cache breve (1 min circa + perturbazione percentuale +/-10%)
/// </summary>
private TimeSpan FastCache
{
get => TimeSpan.FromSeconds(cacheTtlShort * rnd.Next(900, 1100) / 1000);
}
/// <summary>
/// Durata cache lunga (+ perturbazione percentuale +/-10%)
/// </summary>
private TimeSpan LongCache
{
get => TimeSpan.FromSeconds(cacheTtlLong * rnd.Next(900, 1100) / 1000);
}
/// <summary>
/// Durata cache molto breve (10 sec circa + perturbazione percentuale +/-10%)
/// </summary>
private TimeSpan UltraFastCache
{
get => TimeSpan.FromSeconds(cacheTtlShort / 6 * rnd.Next(900, 1100) / 1000);
}
/// <summary>
/// Durata cache lunga (+ perturbazione percentuale +/-10%)
/// </summary>
private TimeSpan UltraLongCache
{
get => TimeSpan.FromSeconds(cacheTtlLong * 10 * rnd.Next(900, 1100) / 1000);
}
#endregion Private Properties
}
}