diff --git a/Varie/Thiene/RedisManager.cs b/Varie/Thiene/RedisManager.cs new file mode 100644 index 0000000..3560a46 --- /dev/null +++ b/Varie/Thiene/RedisManager.cs @@ -0,0 +1,687 @@ +using ProEdge.Model; +using ProEdge.Model.Report; +using ProEdge.Model.Report.MachineEvents; +using ProEdge.Report; +using Schneider.PLC; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using TeamDev.SDK; +using TeamDev.SDK.MVVM; + +namespace ProEdge.IOT +{ + public class RedisManager + { + private enum Status + { + EXE, + READY, + AZZERAMENTO, // Stato aggiuntivo da non inviare a Redis + RISCALDAMENTO, // Stato aggiuntivo da non inviare a Redis + SETUP, + FAIL, + POWER_OFF + } + + private static Dictionary> _mappingProperties; + private static Dictionary _nonPlcGroupsMappingTypes; + private static Dictionary> _axisToTypeMapping; + private static Dictionary> _activeTimesToTypeMapping; + private static Dictionary> _loadsToTypeMapping; + + private Dictionary[] _mappingGroups; + private Dictionary> _mappingNonPLCGroupsToPLCGroups; + private Dictionary _mappingLoads; + private Dictionary _mappingActiveTimes; + private Dictionary _mappingAxes; + + private int[] ActiveAlarms; + private int[] ActiveWarnings; + private int[] ActiveMessages; + private bool[] ActiveStatuses = new bool[Enum.GetNames(typeof(Status)).Length]; + static RedisManager() + { + _mappingProperties = new Dictionary>(); + _nonPlcGroupsMappingTypes = new Dictionary(); + _loadsToTypeMapping = new Dictionary>(); + _activeTimesToTypeMapping = new Dictionary>(); + _axisToTypeMapping = new Dictionary>(); + // PREFIX + var subgroups = "Subgroups:"; + // SUFFIXES + var distance = ":Distance"; + var feedrate = ":FeedRate"; + var activetime = ":ActiveTime"; + var repetition = ":Repetition"; + var value = ":Value"; + var load = ":Load"; + + Dictionary dict; + Dictionary loadDict; + Dictionary activeTimeDict; + Dictionary axisDict; + + // BASE MACCHINA + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.BaseMacchina.KmCingolo), $"{subgroups}01{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.BaseMacchina.VelocitaCingolo), $"{subgroups}01{feedrate}"); + dict.Add(nameof(ProEdge.Model.IOT.BaseMacchina.ActiveTimeCingolo), $"{subgroups}01{activetime}"); + _mappingProperties.Add(typeof(BaseMacchina), dict); + loadDict = new Dictionary(); + loadDict.Add(1, $"{subgroups}01{load}"); + _loadsToTypeMapping.Add(typeof(BaseMacchina), loadDict); + axisDict = new Dictionary(); + axisDict.Add(1, 2); + axisDict.Add(2, 3); + _axisToTypeMapping.Add(typeof(BaseMacchina), axisDict); + + // RETTIFICATORE 2 INVERTER + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.Rettificatore2Inverter.MetriFresaAnteriore), $"{subgroups}05{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Rettificatore2Inverter.MetriFresaPosteriore), $"{subgroups}06{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Rettificatore2Inverter.NumeroPannelloAnteriore), $"{subgroups}07{repetition}"); + dict.Add(nameof(ProEdge.Model.IOT.Rettificatore2Inverter.NumeroPannelliPosteriore), $"{subgroups}08{repetition}"); + _mappingProperties.Add(typeof(Rettificatore2Inverter), dict); + loadDict = new Dictionary(); + loadDict.Add(2, $"{subgroups}01{load}"); + loadDict.Add(8, $"{subgroups}02{load}"); + _loadsToTypeMapping.Add(typeof(Rettificatore2Inverter), loadDict); + activeTimeDict = new Dictionary(); + activeTimeDict.Add(3, $"{subgroups}03{activetime}"); + activeTimeDict.Add(4, $"{subgroups}04{activetime}"); + _activeTimesToTypeMapping.Add(typeof(Rettificatore2Inverter), activeTimeDict); + axisDict = new Dictionary(); + axisDict.Add(4, 9); + _axisToTypeMapping.Add(typeof(Rettificatore2Inverter), axisDict); + + // INCOLLAGGIO VC1000 + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.IncollaggioVC1000.NumeroPannelliLavorati), "Repetition"); + dict.Add(nameof(ProEdge.Model.IOT.IncollaggioVC1000.NumeroInterventiCesoia), $"{subgroups}04{repetition}"); + _mappingProperties.Add(typeof(IncollatoreVC1000), dict); + loadDict = new Dictionary(); + loadDict.Add(5, $"{subgroups}02{load}"); + _loadsToTypeMapping.Add(typeof(IncollatoreVC1000), loadDict); + activeTimeDict = new Dictionary(); + activeTimeDict.Add(15, $"{subgroups}03{activetime}"); + _activeTimesToTypeMapping.Add(typeof(IncollatoreVC1000), activeTimeDict); + axisDict = new Dictionary(); + axisDict.Add(4, 1); + axisDict.Add(25, 1); + _axisToTypeMapping.Add(typeof(IncollatoreVC1000), axisDict); + + // PREFUSORE + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.Prefusore.TemperaturaPrefusore), $"{subgroups}01{value}"); + _mappingProperties.Add(typeof(Prefusore), dict); + + // VASCA COLLA VC1000 + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.VascaCollaVC1000.TemperaturaVasca), $"{subgroups}01{value}"); + dict.Add(nameof(ProEdge.Model.IOT.VascaCollaVC1000.TemperaturaRullo), $"{subgroups}02{value}"); + _mappingProperties.Add(typeof(VascaCollaVC1000), dict); + loadDict = new Dictionary(); + loadDict.Add(4, $"{subgroups}04{load}"); + _loadsToTypeMapping.Add(typeof(VascaCollaVC1000), loadDict); + activeTimeDict = new Dictionary(); + activeTimeDict.Add(14, $"{subgroups}05{activetime}"); + _activeTimesToTypeMapping.Add(typeof(VascaCollaVC1000), activeTimeDict); + axisDict = new Dictionary(); + axisDict.Add(17, 2); + _axisToTypeMapping.Add(typeof(VascaCollaVC1000), axisDict); + + // AIR FUSION + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.AirFusion.TemperaturaLeister1), $"{subgroups}01{value}"); + dict.Add(nameof(ProEdge.Model.IOT.AirFusion.TemperaturaLeister2), $"{subgroups}02{value}"); + _mappingProperties.Add(typeof(AirFusion), dict); + + // LAMPADE + _nonPlcGroupsMappingTypes.Add(typeof(Lampada), typeof(IncollatoreVC1000)); + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.IncollaggioVC1000.TempoAccensioneLampade), $"ActiveTime"); + _mappingProperties.Add(typeof(Lampada), dict); + + // INTESTATORE KSEL + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.IntestatoreKSEL.MetriLamaAnteriore), $"{subgroups}04{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.IntestatoreKSEL.MetriLamaPosteriore), $"{subgroups}05{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.IntestatoreKSEL.NumeroPannelliAnteriore), $"{subgroups}06{repetition}"); + dict.Add(nameof(ProEdge.Model.IOT.IntestatoreKSEL.NumeroPannelliPosteriore), $"{subgroups}07{repetition}"); + _mappingProperties.Add(typeof(IntestatoreKSEL), dict); + loadDict = new Dictionary(); + loadDict.Add(6, $"{subgroups}01{load}"); + _loadsToTypeMapping.Add(typeof(IntestatoreKSEL), loadDict); + activeTimeDict = new Dictionary(); + activeTimeDict.Add(3, $"{subgroups}02{activetime}"); + activeTimeDict.Add(4, $"{subgroups}03{activetime}"); + _activeTimesToTypeMapping.Add(typeof(IntestatoreKSEL), activeTimeDict); + + // REFILATORE RSINV + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.RefilatoreRSINV.MetriFresaSuperiore), $"{subgroups}04{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.RefilatoreRSINV.MetriFresaInferiore), $"{subgroups}05{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.RefilatoreRSINV.NumeroPannelliSuperiore), $"{subgroups}06{repetition}"); + dict.Add(nameof(ProEdge.Model.IOT.RefilatoreRSINV.NumeroPannelliInferiore), $"{subgroups}07{repetition}"); + _mappingProperties.Add(typeof(RefilatoreRSINV), dict); + loadDict = new Dictionary(); + loadDict.Add(3, $"{subgroups}01{load}"); + _loadsToTypeMapping.Add(typeof(RefilatoreRSINV), loadDict); + activeTimeDict = new Dictionary(); + activeTimeDict.Add(6, $"{subgroups}02{activetime}"); + activeTimeDict.Add(5, $"{subgroups}03{activetime}"); + _activeTimesToTypeMapping.Add(typeof(RefilatoreRSINV), activeTimeDict); + + // SPIGOLATORE 6 ASSI + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.MetriFresaSuperiore1), $"{subgroups}09{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.MetriFresaInferiore1), $"{subgroups}05{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.MetriFresaSuperiore2), $"{subgroups}10{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.MetriFresaInferiore2), $"{subgroups}06{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.MetriFresaSuperiore3), $"{subgroups}11{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.MetriFresaInferiore3), $"{subgroups}07{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.MetriFresaSuperiore4), $"{subgroups}12{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.MetriFresaInferiore4), $"{subgroups}08{distance}"); + // TODO + //dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.NumeroPannelliSuperiore), $"{subgroups}04{repetition}"); + //dict.Add(nameof(ProEdge.Model.IOT.Spigolatore6Assi.NumeroPannelliInferiore), $"{subgroups}05{repetition}"); + _mappingProperties.Add(typeof(Spigolatore6Assi), dict); + loadDict = new Dictionary(); + loadDict.Add(9, $"{subgroups}01{load}"); + loadDict.Add(12, $"{subgroups}02{load}"); + _loadsToTypeMapping.Add(typeof(Spigolatore6Assi), loadDict); + activeTimeDict = new Dictionary(); + activeTimeDict.Add(7, $"{subgroups}03{activetime}"); + activeTimeDict.Add(8, $"{subgroups}04{activetime}"); + _activeTimesToTypeMapping.Add(typeof(Spigolatore6Assi), activeTimeDict); + axisDict = new Dictionary(); + axisDict.Add(7, 13); + axisDict.Add(8, 15); + axisDict.Add(9, 17); + axisDict.Add(10, 14); + axisDict.Add(11, 16); + axisDict.Add(12, 18); + _axisToTypeMapping.Add(typeof(Spigolatore6Assi), axisDict); + + // ROUND SK2 + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.RoundSK2.MetriFresaSuperiore), $"{subgroups}06{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.RoundSK2.MetriFresaInferiore), $"{subgroups}05{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.RoundSK2.NumeroCicliLavorazione1Superiore), $"{subgroups}09{repetition}"); + dict.Add(nameof(ProEdge.Model.IOT.RoundSK2.NumeroCicliLavorazione1Inferiore), $"{subgroups}07{repetition}"); + dict.Add(nameof(ProEdge.Model.IOT.RoundSK2.NumeroCicliLavorazione2Superiore), $"{subgroups}10{repetition}"); + dict.Add(nameof(ProEdge.Model.IOT.RoundSK2.NumeroCicliLavorazione2Inferiore), $"{subgroups}08{repetition}"); + _mappingProperties.Add(typeof(RoundSK2), dict); + loadDict = new Dictionary(); + loadDict.Add(10, $"{subgroups}01{load}"); + _loadsToTypeMapping.Add(typeof(RoundSK2), loadDict); + activeTimeDict = new Dictionary(); + activeTimeDict.Add(9, $"{subgroups}02{activetime}"); + activeTimeDict.Add(10, $"{subgroups}03{activetime}"); + _activeTimesToTypeMapping.Add(typeof(RoundSK2), activeTimeDict); + axisDict = new Dictionary(); + axisDict.Add(15, 10); + axisDict.Add(16, 11); + _axisToTypeMapping.Add(typeof(RoundSK2), axisDict); + + // RASCHIABORDO 4 ASSI + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.MetriColtelloSuperiore1), $"{subgroups}05{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.MetriColtelloInferiore1), $"{subgroups}01{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.MetriColtelloSuperiore2), $"{subgroups}06{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.MetriColtelloInferiore2), $"{subgroups}02{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.MetriColtelloSuperiore3), $"{subgroups}07{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.MetriColtelloInferiore3), $"{subgroups}03{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.MetriColtelloSuperiore4), $"{subgroups}08{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.MetriColtelloInferiore4), $"{subgroups}04{distance}"); + // TODO + //dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.NumeroPannelliSuperiore), $"{subgroups}06{distance}"); + //dict.Add(nameof(ProEdge.Model.IOT.Raschiabordo4Assi.NumeroPannelliInferiore), $"{subgroups}05{distance}"); + _mappingProperties.Add(typeof(Raschiabordo4Assi), dict); + axisDict = new Dictionary(); + axisDict.Add(19, 11); + axisDict.Add(20, 13); + axisDict.Add(22, 12); + axisDict.Add(23, 14); + _axisToTypeMapping.Add(typeof(Raschiabordo4Assi), axisDict); + + // TOUPIE + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.Toupie.MetriLavorati), $"{subgroups}03{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Toupie.NumeroPannelli), $"{subgroups}04{repetition}"); + _mappingProperties.Add(typeof(Toupie), dict); + loadDict = new Dictionary(); + loadDict.Add(7, $"{subgroups}01{load}"); + _loadsToTypeMapping.Add(typeof(Toupie), loadDict); + activeTimeDict = new Dictionary(); + activeTimeDict.Add(13, $"{subgroups}02{activetime}"); + _activeTimesToTypeMapping.Add(typeof(Toupie), activeTimeDict); + + // RASCHIACOLLA + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.Raschiacolla.MetriLavoratiSuperiore), $"{subgroups}02{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiacolla.MetriLavoratiInferiore), $"{subgroups}01{distance}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiacolla.NumeroPannelliSuperiore), $"{subgroups}04{repetition}"); + dict.Add(nameof(ProEdge.Model.IOT.Raschiacolla.NumeroPannelliInferiore), $"{subgroups}03{repetition}"); + _mappingProperties.Add(typeof(RCA), dict); + + // SPAZZOLE + dict = new Dictionary(); + dict.Add(nameof(ProEdge.Model.IOT.Spazzole.MetriLavorati), $"Distance"); + _mappingProperties.Add(typeof(SpazzoleSPK), dict); + } + + public static RedisManager Current { get; set; } = new RedisManager(); + private RedisManager() { } + + // Indica se lo scambio di dati con Redis è attivo + private bool Active { get; set; } + + public void Init(GruppoBase[] groups) + { + Active = true; + if (!Active) return; + + initMapping(groups); + + // Messaggio per terminare l'heartbeat e le comunicazioni in generale + MessageServices.Current.Subscribe("REDIS_END_COMUNICATION", (o1, o2) => { Active = false; }); + + // Ricezione stato di allarme + MessageServices.Current.Subscribe("CLIENTNOTIFY_UPDATED_STATUS_MACHINE", (o, status) => + { + Write("Machine:Power", (status.Stato > 50 && status.Stato < 54) || (status.Stato > 60 && status.Stato < 64) ? "true" : "false"); + Write("Machine:Alarm", status.Alarms ? "true" : "false"); + }); + + // resetto TUTTE le aree di competenza + redUtil.man.redFlushKey(redUtil.man.redHash("AdpVeto*")); + redUtil.man.redFlushKey(redUtil.man.redHash("AdpConf*")); + redUtil.man.redFlushKey(redUtil.man.redHash("Adp*")); + + // Imposto CONF + Write("Adp:Vers", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()); + + Write("Adp:Status", "started"); + + // Thread per l'heartbeat + new Thread(() => + { + // una volta al secondo + while (Active) + { + Write("Adp:Heartbeat", DateTime.UtcNow.ToString("yyyy-MM-dd\\THH:mm:ss.fffK")); + Thread.Sleep(1000); + } + redUtil.man.setRSV(redUtil.man.redHash("Adp:Status"), "stopped"); + }).Start(); + + // Scrittura datamodel + var datamodelPath = Path.Combine(AppContext.BaseDirectory, ConfigurationManager.AppSettings["DataModelFile"]); + if (File.Exists(datamodelPath)) + { + using (StreamReader sr = new StreamReader(new FileStream(datamodelPath, FileMode.Open, FileAccess.Read))) + { + var datamodel = xmlSanitize(sr.ReadToEnd()); + WriteConf("DataModel", datamodel); + } + } + + // imposto VETO (codici allarme da ignorare) + WriteVeto("Hmi:Condition", "000000000000|000"); + WriteVeto("Plc:Condition", "000000000000|000"); + + // Versione PLC + Write("Machine:Plc:Version", PlcStatus.PLCVersion()); + + // Versione HMI (TODO: Aggiorna con la versione del client) + System.Reflection.Assembly assembly = System.Reflection.Assembly.GetEntryAssembly(); + FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location); + Write("Machine:Hmi:Version", fvi.FileVersion); + + // Utente Collegato + Write("Machine:Hmi:User", "null"); + MessageServices.Current.Subscribe("LOGGED_IN", (o, u) => Write("Machine:Hmi:User", u.User)); + MessageServices.Current.Subscribe("LOGGED_OUT", (o, u) => Write("Machine:Hmi:User", "null")); + + // STATUS + /* + EXE=Macchina piena cingolo in moto ===> MachineWorkPieces + READY=Macchina in work e (cingolo in moto e vuota o cingolo fermo e abilitato ma mancano consensi esterni) ===> MachineWorkNoPieces/MachineStopTrackWithPieces + SETUP=Azzeramento assi in corso e/o riscaldamento colla ===> stati vari, non c'è in eventi (e se AirFusion?) + FAIL=Macchina in allarme ===> MachineInAlarm + POWER_OFF=Macchina in stop" ===> !(quelli sopra) + */ + Write("Machine:Status", "POWER_OFF"); + ActiveStatuses[(int)Status.POWER_OFF] = true; + WriteStatus(); + Action updateStatusArray = (s, b) => + { + switch (s) + { + case nameof(MachineEventTypeIds.General.MachineInAlarm): + ActiveStatuses[(int)Status.FAIL] = b; + break; + case nameof(MachineEventTypeIds.General.MachineWorkNoPieces): + case nameof(MachineEventTypeIds.General.MachineStopTrackWithPieces): + ActiveStatuses[(int)Status.READY] = b; + break; + case nameof(MachineEventTypeIds.General.MachineWorkPieces): + ActiveStatuses[(int)Status.EXE] = b; + break; + default: return; + } + WriteStatus(); + }; + MessageServices.Current.Subscribe("CLIENTNOTIFY_UPDATED_NEW_EVENT", (o, e) => updateStatusArray(e.Description, true)); + MessageServices.Current.Subscribe("CLIENTNOTIFY_UPDATED_EVENT_CLOSED", (o, e) => updateStatusArray(e.Description, false)); + var vc = groups.Where(g => g is IVascaColla).FirstOrDefault(); + if (vc != null) + MessageServices.Current.Subscribe("CLIENTNOTIFY_UPDATED_STATUS_" + vc.UniqueID, (o, s) => + { + ActiveStatuses[(int)Status.RISCALDAMENTO] = s.Stato == 3 && !s.ConsensoVascaColla; + ActiveStatuses[(int)Status.SETUP] = ActiveStatuses[(int)Status.AZZERAMENTO] || ActiveStatuses[(int)Status.RISCALDAMENTO]; + WriteStatus(); + }); + MessageServices.Current.Subscribe("CLIENTNOTIFY_UPDATED_STATUS_MANUAL", (o, s) => + { + ActiveStatuses[(int)Status.AZZERAMENTO] = s.AzzeramentoAssiInCorso; + ActiveStatuses[(int)Status.SETUP] = ActiveStatuses[(int)Status.AZZERAMENTO] || ActiveStatuses[(int)Status.RISCALDAMENTO]; + WriteStatus(); + }); + } + + private void initMapping(GruppoBase[] groups) + { + _mappingGroups = new Dictionary[groups.Max(g => g.Id)]; + _mappingNonPLCGroupsToPLCGroups = new Dictionary>(); + _mappingLoads = new Dictionary(); + _mappingActiveTimes = new Dictionary(); + _mappingAxes = new Dictionary(); + + foreach (var g in groups) + { + var gType = g.GetType(); + // Dati dei gruppi + if (_mappingProperties.ContainsKey(gType)) + _mappingGroups[g.Id] = _mappingProperties[gType]; + // Carico inverter + if (_loadsToTypeMapping.ContainsKey(gType)) + foreach (var l in _loadsToTypeMapping[gType]) + _mappingLoads[l.Key] = $"{g.Id:00}:{l.Value}"; + // Active Time mandrini + if (_activeTimesToTypeMapping.ContainsKey(gType)) + foreach (var l in _activeTimesToTypeMapping[gType]) + _mappingActiveTimes[l.Key] = $"{g.Id:00}:{l.Value}"; + // Dati assi + if (_axisToTypeMapping.ContainsKey(gType)) + foreach (var l in _axisToTypeMapping[gType]) + _mappingAxes[l.Key] = $"{g.Id:00}:Subgroups:{l.Value:00}"; + } + + // GRUPPI NON PLC + foreach (var g in groups) + { + var gType = g.GetType(); + if (_nonPlcGroupsMappingTypes.ContainsKey(gType)) + { + var plcType = _nonPlcGroupsMappingTypes[gType]; + var realGroupId = groups.Where(gr => gr.GetType() == plcType).Select(gr => gr.Id).First(); + if (!_mappingNonPLCGroupsToPLCGroups.ContainsKey(realGroupId)) + _mappingNonPLCGroupsToPLCGroups.Add(realGroupId, new List()); + _mappingNonPLCGroupsToPLCGroups[realGroupId].Add(g.Id); + } + } + } + + private void WriteAlarmsList() + { + string result = "000000000000|000"; + if (ActiveAlarms != null) + lock (ActiveAlarms) + foreach (var alm in ActiveAlarms) + result += $",000A{alm:0000}|900"; + if (ActiveWarnings != null) + lock (ActiveWarnings) + foreach (var wrn in ActiveWarnings) + result += $",000W{wrn:0000}|500"; + if (ActiveMessages != null) + lock (ActiveMessages) + foreach (var msg in ActiveMessages) + result += $",000M{msg:0000}|100"; + Write("Machine:Plc:Condition", result); + } + + private void WriteStatus() + { + Status result = Status.POWER_OFF; + if (ActiveStatuses[(int)Status.SETUP]) result = Status.SETUP; + if (ActiveStatuses[(int)Status.READY]) result = Status.READY; + if (ActiveStatuses[(int)Status.EXE]) result = Status.EXE; + if (ActiveStatuses[(int)Status.FAIL]) result = Status.FAIL; + + Write("Machine:Status", result.ToString()); + } + + private void WriteConf(string key, string value) + { + Write($"AdpConf:{key}", value); + } + + private void WriteVeto(string key, string value) + { + Write($"AdpVeto:{key}", value); + } + + + /// + /// Metodo di sanitizzazione XML + /// - elimina commenti + /// - formatta + /// + /// + /// + private string xmlSanitize(string xmlOrig) + { + string sXml = xmlOrig; + bool xmlSanitize = false; +#if DEBUG + xmlSanitize = true;// utils.CRB("xmlSanitize"); +#endif + // se richeisto faccio sanitize xml (pulizia commenti...) + if (xmlSanitize) + { + // primo step: converto stringa in dox XML + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.PreserveWhitespace = false; + xmlDoc.LoadXml(sXml); + // ora lo parso come lista eliminando i commenti + XmlNodeList list = xmlDoc.SelectNodes("//comment()"); + foreach (XmlNode node in list) + { + node.ParentNode.RemoveChild(node); + } + // fix formattazione "riscrivendo" indentazione... + StringWriter string_writer = new StringWriter(); + XmlTextWriter xml_text_writer = new XmlTextWriter(string_writer); + xml_text_writer.Formatting = Formatting.Indented; + xmlDoc.WriteTo(xml_text_writer); + sXml = string_writer.ToString(); + } + // restituisco + return sXml; + } + + public bool Write(string key, string value) + { + // TODO: se non connesso, attendi che si riconnetta + + if (Active && redUtil.connRedis.IsConnected) + { + string hashKey = redUtil.man.redHash(key); + string hashVal = string.Format(value); + redUtil.man.setRSV(hashKey, hashVal); + return true; + } + return false; + } + + // Scrive la lista di tutti i possibili allarmi/warning/messaggi con la loro traduzione nella lingua corrente, in inglese ed in italiano + public void WriteAlarms(string currentLanguage, Dictionary curr, Dictionary en, Dictionary it) + { + if (!Active) return; + + // Scrivo la lingua attuale + Write("Machine:Hmi:Language", currentLanguage); + + //var listCNC_curr = new List>(); + var listPLC_curr = new List>(); + var listHMI_curr = new List>(); + //var listCNC_it = new List>(); + var listPLC_it = new List>(); + var listHMI_it = new List>(); + //var listCNC_en = new List>(); + var listPLC_en = new List>(); + var listHMI_en = new List>(); + // imposto valori "empty" di default.... + //listCNC_curr.Add(new KeyValuePair("000A0001|900", "NONE")); // Allarmi: 900, Warning: 500, Messaggi: 100 + //listCNC_en.Add(new KeyValuePair("000000000000|000", "NONE")); + //listCNC_it.Add(new KeyValuePair("000000000000|000", "NONE")); + listHMI_curr.Add(new KeyValuePair("000000000000|000", "NONE")); + listHMI_en.Add(new KeyValuePair("000000000000|000", "NONE")); + listHMI_it.Add(new KeyValuePair("000000000000|000", "NONE")); + listPLC_curr.Add(new KeyValuePair("000000000000|000", "NONE")); + listPLC_en.Add(new KeyValuePair("000000000000|000", "NONE")); + listPLC_it.Add(new KeyValuePair("000000000000|000", "NONE")); + + foreach (var key in curr.Keys) + { + string type = key.StartsWith("SCH_A") ? "900" : key.StartsWith("SCH_W") ? "500" : key.StartsWith("SCH_M") ? "100" : null; + if (type == null) continue; + int number = -1; + if (int.TryParse(key.Substring(5), out number)) + { + string prefix = type == "900" ? "000A" : type == "500" ? "000W" : "000M"; + var almKey = $"{prefix}{number:0000}|{type}"; + listPLC_curr.Add(new KeyValuePair(almKey, !curr.ContainsKey(key) || string.IsNullOrEmpty(curr[key]) ? number.ToString() : curr[key])); + listPLC_en.Add(new KeyValuePair(almKey, !en.ContainsKey(key) || string.IsNullOrEmpty(en[key]) ? number.ToString() : en[key])); + listPLC_it.Add(new KeyValuePair(almKey, !it.ContainsKey(key) || string.IsNullOrEmpty(it[key]) ? number.ToString() : it[key])); + //listPLC_curr.Add(new KeyValuePair(almKey, curr[key])); + //listPLC_en.Add(new KeyValuePair(almKey, en[key])); + //listPLC_it.Add(new KeyValuePair(almKey, it[key])); + } + } + + // salvo vettori lingua CURR + //var hashKey = redUtil.man.redHash("AdpConf:Cnc:Condition:Curr"); + //redUtil.man.redFlushKey(hashKey); + //redUtil.man.redSaveHashList(hashKey, listCNC_curr); + var hashKey = redUtil.man.redHash("AdpConf:Hmi:Condition:Curr"); + redUtil.man.redFlushKey(hashKey); + redUtil.man.redSaveHashList(hashKey, listHMI_curr); + hashKey = redUtil.man.redHash("AdpConf:Plc:Condition:Curr"); + redUtil.man.redFlushKey(hashKey); + redUtil.man.redSaveHashList(hashKey, listPLC_curr); + // salvo vettori lingua EN + //hashKey = redUtil.man.redHash("AdpConf:Cnc:Condition:En"); + //redUtil.man.redFlushKey(hashKey); + //redUtil.man.redSaveHashList(hashKey, listCNC_en); + hashKey = redUtil.man.redHash("AdpConf:Hmi:Condition:En"); + redUtil.man.redFlushKey(hashKey); + redUtil.man.redSaveHashList(hashKey, listHMI_en); + hashKey = redUtil.man.redHash("AdpConf:Plc:Condition:En"); + redUtil.man.redFlushKey(hashKey); + redUtil.man.redSaveHashList(hashKey, listPLC_en); + // salvo vettori lingua IT + //hashKey = redUtil.man.redHash("AdpConf:Cnc:Condition:It"); + //redUtil.man.redFlushKey(hashKey); + //redUtil.man.redSaveHashList(hashKey, listCNC_it); + hashKey = redUtil.man.redHash("AdpConf:Hmi:Condition:It"); + redUtil.man.redFlushKey(hashKey); + redUtil.man.redSaveHashList(hashKey, listHMI_it); + hashKey = redUtil.man.redHash("AdpConf:Plc:Condition:It"); + redUtil.man.redFlushKey(hashKey); + redUtil.man.redSaveHashList(hashKey, listPLC_it); + + // Valori di default + Write("Machine:Hmi:Condition", "000000000000|000"); + Write("Machine:Plc:Condition", "000000000000|000"); + + // Scritto l'elenco di allarmi, comincio a ricevere le variazioni degli allarmi + MessageServices.Current.Subscribe("CLIENTNOTIFY_UPDATED_MESSAGES", (o, msgs) => { ActiveMessages = msgs.List.Select(m => m.ID).ToArray(); WriteAlarmsList(); }); + MessageServices.Current.Subscribe("CLIENTNOTIFY_UPDATED_WARNINGS", (o, wrns) => { ActiveWarnings = wrns.List.Select(m => m.ID).ToArray(); WriteAlarmsList(); }); + MessageServices.Current.Subscribe("CLIENTNOTIFY_UPDATED_ALARMS", (o, alms) => { ActiveAlarms = alms.List.Select(m => m.ID).ToArray(); WriteAlarmsList(); }); + + } + + // Dati dei gruppi + public void WriteGroupData(int idGroup, string property, string value) + { + // Lo stato di emergenza è contenuto nel gruppo Base Macchina + if (property == nameof(ProEdge.Model.IOT.BaseMacchina.Emergenza)) + Write("Machine:Emergency", value); + + if (_mappingGroups != null && _mappingGroups[idGroup] != null && _mappingGroups[idGroup].ContainsKey(property)) + { + string key = $"Machine:OperatingGroups:{idGroup:00}:{_mappingGroups[idGroup][property]}"; + Write(key, value.ToString()); + } + else if (_mappingNonPLCGroupsToPLCGroups != null && _mappingNonPLCGroupsToPLCGroups.ContainsKey(idGroup)) + { + foreach (var i in _mappingNonPLCGroupsToPLCGroups[idGroup]) + WriteGroupData(i, property, value); + } + } + + // Assorbimenti inverter + public void WriteLoad(int id, string value) + { + if (_mappingLoads != null && _mappingLoads.ContainsKey(id)) + { + Write("Machine:OperatingGroups:" + _mappingLoads[id], value.ToString()); + } + } + + // Active Time motori + public void WriteActiveTime(int id, string value) + { + if (_mappingActiveTimes != null && _mappingActiveTimes.ContainsKey(id)) + { + Write("Machine:OperatingGroups:" + _mappingActiveTimes[id], value.ToString()); + } + } + + // Dati Assi + public void WriteAxis(int id, string distance, string repetition) + { + if (_mappingAxes != null && _mappingAxes.ContainsKey(id)) + { + Write("Machine:OperatingGroups:" + _mappingAxes[id] + ":Distance", distance.ToString()); + Write("Machine:OperatingGroups:" + _mappingAxes[id] + ":Repetition", repetition.ToString()); + } + } + + public void WriteTotalActiveTime(TimeSpan value) + { + Write("Machine:ActiveTime", Math.Truncate(value.TotalHours).ToString()); + } + + public void WriteActualActiveTime(TimeSpan value) + { + Write("Machine:Hmi:ActiveTimeSession", Math.Truncate(value.TotalHours).ToString()); + } + + public void WriteActualSection(string section) + { + Write("Machine:Hmi:ActiveSection", section); + } + } +}