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> AnagEventiGetAll() { string source = "DB"; Dictionary dbResult = new Dictionary(); 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>(rawData); if (tempResult == null) { dbResult = new Dictionary(); } else { dbResult = tempResult; } } else { dbResult = dbController.AnagEventiGetAll(); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new Dictionary(); } 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> AnagStatiGetAll() { string source = "DB"; Dictionary dbResult = new Dictionary(); 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>(rawData); if (tempResult == null) { dbResult = new Dictionary(); } else { dbResult = tempResult; } } else { dbResult = dbController.AnagStatiGetAll(); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new Dictionary(); } 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 AnagEventinInsert(List newEvList) { var dbResult = await dbController.AnagEventinInsert(newEvList); return dbResult; } /// /// /// /// public async Task 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> FamIngressiGetAll() { string source = "DB"; List dbResult = new List(); 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>(rawData); if (tempResult == null) { dbResult = new List(); } else { dbResult = tempResult; } } else { dbResult = dbController.FamIngressiGetAll(); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new List(); } 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> FamStatiGetAll() { string source = "DB"; List dbResult = new List(); 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>(rawData); if (tempResult == null) { dbResult = new List(); } else { dbResult = tempResult; } } else { dbResult = dbController.FamStatiGetAll(); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new List(); } 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> FilesGetAll() { Dictionary dbResult = new Dictionary(); string currKey = $"{Constants.FILES_TO_PROC}"; var rawData = await redisDb.HashGetAllAsync(currKey); foreach (var item in rawData) { var desVal = JsonConvert.DeserializeObject(item.Value!); dbResult.Add($"{item.Name}", desVal!); } return dbResult; } public async Task FilesLoadRedis(Dictionary 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 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 TranInInsert(List newTIList, int currIdxFamiglia) { var dbResult = await dbController.TranInInsert(newTIList, currIdxFamiglia); return dbResult; } #endregion Public Methods public Dictionary eventsFromDb = new Dictionary(); private bool b_rules_definition = false; List csvLines = new List(); List linesList = new List(); private int n_bits = 0; private string n_state_machine_index = ""; private int n_states = 0; private string sz_state_machine_name = ""; public List events2Add { get; set; } = new List(); public List eventsAll { get; set; } = new List(); protected string bitCsvPath { get => _configuration.GetValue("ServerConf:BitCsvPath"); } protected string procRootDir { get => _configuration.GetValue("ServerConf:ProcCsvRootPath"); } protected List States2Rules { get; set; } = new List(); protected Dictionary StatesAll { get; set; } = new Dictionary(); protected Dictionary StatesAll2 { get; set; } = new Dictionary(); protected string statiCsvPath { get => _configuration.GetValue("ServerConf:StatiCsvPath"); } private List actStates2Chk { get; set; } = new List(); private List Bits { get; set; } = new List(); private Dictionary evOk { get; set; } = new Dictionary(); private FileLinesClass linesChecked { get; set; } = new FileLinesClass(); private List nextStates2Chk { get; set; } = new List(); private List Rules { get; set; } = new List(); private Dictionary Events_to_send { get; set; } = new Dictionary(); private List States { get; set; } = new List(); private List TranInList2add { get; set; } = new List(); /// /// Verifica su DB eventi e stati /// /// /// public async Task 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; } /// /// Verifica su DB eventi e stati /// /// /// public async Task 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; } /// /// Valuta un file di ruoles x ingressi 2 eventi e restituisce esito /// /// Path file *.rul da processare /// Indica se salvare sul DB /// Indica se processare parte state (x state machine stati) /// /// public async Task EvalIn2EvRuleFile(FilesClass currFile, bool saveToDb, bool doProcState) { await Task.Delay(1); Dictionary evSt2Change = new Dictionary(); 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; } /// /// Modifica il file in oggetto /// /// /// /// Necessaria modifica stati (x state machine stati) /// public async Task> modFile(FilesClass file, bool doProc, bool doProcState) { Dictionary evSt2Change = new Dictionary(); Dictionary eventsFromDb = await AnagEventiGetAll(); Dictionary 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; } /// /// Valuta un file di ruoles x ingressi 2 Stati e restituisce esito /// /// Path file *.rul da processare /// Indica se salvare sul DB /// /// /// /// Indica se processare parte state (x state machine stati) /// public async Task EvalIn2StateRuleFile(FilesClass currFile, bool saveToDb, bool calcItself, bool calcEmptyState, Core.Enum.ORDERTYPE orderType, bool doProcState) { await Task.Delay(1); Dictionary evSt2Change = new Dictionary(); 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; } /// /// Valutazione schema macchina a stati x Ingressi --> Eventi /// /// /// protected async Task 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; } /// /// Valutazione schema macchina a stati x Ingressi --> stati /// /// /// protected async Task 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 /// /// Effettua upsert in HasList redis /// /// Chiave redis della Hashlist /// Chiave nella HashList /// Valore da salvare /// Num record nella HashList protected async Task 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(); /// /// Durata cache lunga IN SECONDI /// private int cacheTtlLong = 60 * 5; /// /// Durata cache breve IN SECONDI /// private int cacheTtlShort = 60 * 1; /// /// Oggetto per connessione a REDIS /// private IConnectionMultiplexer redisConn; /// /// Oggetto DB redis da impiegare x chiamate R/W /// private IDatabase redisDb = null!; private Random rnd = new Random(); #endregion Private Fields #region Private Properties /// /// Durata cache breve (1 min circa + perturbazione percentuale +/-10%) /// private TimeSpan FastCache { get => TimeSpan.FromSeconds(cacheTtlShort * rnd.Next(900, 1100) / 1000); } /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan LongCache { get => TimeSpan.FromSeconds(cacheTtlLong * rnd.Next(900, 1100) / 1000); } /// /// Durata cache molto breve (10 sec circa + perturbazione percentuale +/-10%) /// private TimeSpan UltraFastCache { get => TimeSpan.FromSeconds(cacheTtlShort / 6 * rnd.Next(900, 1100) / 1000); } /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan UltraLongCache { get => TimeSpan.FromSeconds(cacheTtlLong * 10 * rnd.Next(900, 1100) / 1000); } #endregion Private Properties } }