diff --git a/Thermo.Active.Config/Config/IOConfig.xml b/Thermo.Active.Config/Config/IOConfig.xml new file mode 100644 index 00000000..6d641346 --- /dev/null +++ b/Thermo.Active.Config/Config/IOConfig.xml @@ -0,0 +1,229 @@ + + + + 0 + DI + 0 + 0 + 301 + 404 + A32.3 + + + 1 + DI + 0 + 1 + 301 + 405 + A32.5 + true + + + 2 + DI + 0 + 2 + 301 + 406 + A32.7 + + + 3 + DI + 0 + 3 + 301 + 407 + A32.8 + + + 4 + DI + 0 + 4 + 301 + 408 + A32.1 + + + 5 + AI + 0 + 5 + 301 + 409 + A32.2 + + + 6 + DI + 0 + 6 + 301 + 210 + A32.4 + + + 7 + DI + 0 + 7 + 301 + 211 + A32.6 + + + 0 + DO + 0 + 0 + 301 + 404 + B43.3 + + + 1 + DO + 0 + 1 + 301 + 405 + B43.5 + + + 2 + DO + 0 + 2 + 301 + 406 + B43.7 + + + 3 + DO + 0 + 3 + 301 + 407 + B43.8 + + + 4 + DO + 0 + 4 + 301 + 408 + B43.1 + + + 21 + DO + 1 + 5 + 301 + 409 + B43.2 + + + 22 + DO + 1 + 6 + 301 + 210 + B43.4 + + + 23 + DO + 1 + 7 + 301 + 211 + B43.6 + + + 0 + AI + 0 + 0 + 511 + 614 + C43.23 + + + 1 + AI + 0 + 1 + 511 + 615 + C43.25 + + + 16 + AI + 2 + 0 + 501 + 604 + C43.3 + + + 17 + AI + 2 + 1 + 501 + 605 + C43.5 + + + 0 + AO + 0 + 0 + 700 + 800 + D43.13 + + + 1 + AO + 0 + 1 + 701 + 801 + D43.14 + + + 2 + AO + 0 + 2 + 702 + 802 + D43.15 + + + 16 + AO + 3 + 0 + 701 + 804 + D43.3 + + + 31 + AO + 4 + 15 + 701 + 805 + D43.5 + + \ No newline at end of file diff --git a/Thermo.Active.Config/Config/IOConfigValidator.xsd b/Thermo.Active.Config/Config/IOConfigValidator.xsd new file mode 100644 index 00000000..2f1a68ab --- /dev/null +++ b/Thermo.Active.Config/Config/IOConfigValidator.xsd @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Thermo.Active.Config/Config/serverConfig.xml b/Thermo.Active.Config/Config/serverConfig.xml index 6301f517..43d120f2 100644 --- a/Thermo.Active.Config/Config/serverConfig.xml +++ b/Thermo.Active.Config/Config/serverConfig.xml @@ -4,7 +4,7 @@ S7NET false - 192.168.0.1 + 192.168.0.102 102 Thermo 2020 C:\CMS\Recipes\ @@ -119,6 +119,8 @@ + + diff --git a/Thermo.Active.Config/ServerConfig.cs b/Thermo.Active.Config/ServerConfig.cs index 21598824..e1b1b2e2 100644 --- a/Thermo.Active.Config/ServerConfig.cs +++ b/Thermo.Active.Config/ServerConfig.cs @@ -61,6 +61,7 @@ namespace Thermo.Active.Config public static List ThermoProdConfig; public static List RecipeConfig; public static List ModBlockConfig; + public static List IOConfig; public static List RiskResistConfig; public static List RiskChannelConfig; public static List RiskBoardConfig; diff --git a/Thermo.Active.Config/ServerConfigController.cs b/Thermo.Active.Config/ServerConfigController.cs index 8ee9f64a..de8b6946 100644 --- a/Thermo.Active.Config/ServerConfigController.cs +++ b/Thermo.Active.Config/ServerConfigController.cs @@ -326,6 +326,7 @@ namespace Thermo.Active.Config ReadThermoProdConfig(); ReadRecipeConfig(); ReadModBlockConfig(); + ReadIOConfig(); ReadRiskConfig(); ReadAxesConfig(); ReadCMSConnectConfig(); @@ -499,6 +500,30 @@ namespace Thermo.Active.Config .ToList(); } + /// + /// IO config setup from file + /// + private static void ReadIOConfig() + { + XDocument xmlConfigFile = GetXmlHandlerWithValidator(IO_CONFIG_SCHEMA_PATH, IO_CONFIG_PATH); + // Read head config from XML file + IOConfig = xmlConfigFile + .Root + .Elements() + .Select(x => new IOConfigModel() + { + Id = Convert.ToInt16(x.Element("id").Value), + Category = GetTActIO_Type(x.Element("category").Value), + Bank = x.Element("bank") != null ? x.Element("bank").Value : "0", + Position = x.Element("position") != null ? x.Element("position").Value : "0", + Page = x.Element("page") != null ? x.Element("page").Value : "", + Wire = x.Element("wire") != null ? x.Element("wire").Value : "", + Profinet = x.Element("profinet") != null ? x.Element("profinet").Value : "", + DisableForce = x.Element("disableForce") != null ? Convert.ToBoolean(x.Element("disableForce").Value): false + }) + .ToList(); + } + private static void ReadNcSoftKeys() { XDocument xmlConfigFile = GetXmlHandlerWithValidator(NC_SOFTKEYS_CONFIG_SCHEMA_PATH, NC_SOFTKEYS_CONFIG_PATH); diff --git a/Thermo.Active.Config/Thermo.Active.Config.csproj b/Thermo.Active.Config/Thermo.Active.Config.csproj index db77d72e..99d8c390 100644 --- a/Thermo.Active.Config/Thermo.Active.Config.csproj +++ b/Thermo.Active.Config/Thermo.Active.Config.csproj @@ -65,6 +65,9 @@ + + PreserveNewest + PreserveNewest @@ -215,6 +218,10 @@ Designer Always + + Designer + Always + Always diff --git a/Thermo.Active.Core/ThreadsFunctions.cs b/Thermo.Active.Core/ThreadsFunctions.cs index 211a283c..57fa3178 100644 --- a/Thermo.Active.Core/ThreadsFunctions.cs +++ b/Thermo.Active.Core/ThreadsFunctions.cs @@ -30,6 +30,7 @@ using static Thermo.Active.Utils.ExceptionManager; using System.Windows; using System.Drawing; using System.Configuration; +using Thermo.Active.Model.DTOModels.ThIO; public static class ThreadsFunctions { @@ -412,6 +413,55 @@ public static class ThreadsFunctions ncAdapter.Dispose(); } } + /// + /// Lettura valorichannelsIO + /// + public static void ReadChannelsIoData() + { + NcAdapter ncAdapter = new NcAdapter(); + Stopwatch sw = new Stopwatch(); + + try + { + // Try connection + CmsError libraryError = ncAdapter.Connect(); + if (libraryError.errorCode != 0) + ManageLibraryError(libraryError); + + while (true) + { + + sw.Restart(); + + if (ncAdapter.numericalControl.NC_IsConnected()) + { + // Get Data from config and PLC + libraryError = ncAdapter.ReadValIO(out DTOChannelsIOVal currChannelsIoVal); + if (libraryError.errorCode != 0) + ManageLibraryError(libraryError); + else + // Send through signalR + MessageServices.Current.Publish(SEND_CHANNELS_IO_DATA, null, currChannelsIoVal); + } + else + RestoreConnection(); + + sw.Stop(); + + //Update thread timer + UpdateStat(MethodBase.GetCurrentMethod().Name, sw.ElapsedMilliseconds); + + // verifico se periodo rapido o veloce... + int timerAct = RedisController.FastIoSample ? samplMsec("channelsIoFast") : samplMsec("channelsIoSlow"); + // Wait + Thread.Sleep(CalcSleepTime(timerAct, (int)sw.ElapsedMilliseconds)); + } + } + catch (ThreadAbortException) + { + ncAdapter.Dispose(); + } + } public static void ReadEnabledFunctionality() { diff --git a/Thermo.Active.Core/ThreadsHandler.cs b/Thermo.Active.Core/ThreadsHandler.cs index 1727e073..0c608eb8 100644 --- a/Thermo.Active.Core/ThreadsHandler.cs +++ b/Thermo.Active.Core/ThreadsHandler.cs @@ -36,6 +36,7 @@ namespace Thermo.Active.Core ThreadsFunctions.ReadScadaData, ThreadsFunctions.ReadMComandsData, ThreadsFunctions.ReadAxisInfoData, + ThreadsFunctions.ReadChannelsIoData, ThreadsFunctions.ReadM154Data // levare? }; diff --git a/Thermo.Active.Database/Controllers/RedisController.cs b/Thermo.Active.Database/Controllers/RedisController.cs index 1143bef6..9bd42ef6 100644 --- a/Thermo.Active.Database/Controllers/RedisController.cs +++ b/Thermo.Active.Database/Controllers/RedisController.cs @@ -19,7 +19,7 @@ namespace Thermo.Active.Database.Controllers private const string machinePowerPath = "Machine:Power"; private const string machineAlarmPath = "Machine:Alarm"; private const string machineEmergencyPath = "Machine:Emergency"; - private const string alarmsPath = "Machine:Plc:Condition"; + private const string alarmsPath = "Machine:Plc:Condition"; private const string processStatusPath = "Machine:Cnc:CncProcesses:%NN%:Status"; private const string processModePath = "Machine:Cnc:CncProcesses:%NN%:Mode"; private const string processFeedOverridePath = "Machine:Cnc:CncProcesses:%NN%:FeedOverride"; @@ -27,7 +27,7 @@ namespace Thermo.Active.Database.Controllers private const string processSpeedOverridePath = "Machine:Cnc:CncProcesses:%NN%:SpeedOverride"; private const string datamodelPath = "AdpConf:DataModel"; - private const string currentActiveVersionPath = "Machine:Hmi:Version"; + private const string currentActiveVersionPath = "Machine:Hmi:Version"; private const string machineAxisPosition = "Machine:Axes:%NN%:CurrentPos"; private const string machineAxisSpeed = "Machine:Axes:%NN%:FeedRate"; private const string machineAxisLoad = "Machine:Axes:%NN%:Load"; @@ -70,7 +70,7 @@ namespace Thermo.Active.Database.Controllers string redisHash = redUtil.man.redHash(redisAlmEn); return redUtil.man.redSaveHashDict(redisHash, alarms); } - + public static bool WriteDatamodel(string datamodel) { @@ -79,7 +79,7 @@ namespace Thermo.Active.Database.Controllers } public static bool WriteDefaultEntry(Dictionary entries) { - foreach (KeyValuePair entry in entries) + foreach (KeyValuePair entry in entries) { if (!redUtil.man.setRSV(redUtil.man.redHash(entry.Key), entry.Value)) return false; @@ -109,7 +109,7 @@ namespace Thermo.Active.Database.Controllers string redisHash = redUtil.man.redHash(machineStatusPath); return redUtil.man.setRSV(redisHash, status.ToString()); } - + public static bool WriteCurrentProcessStatus(uint ProductionProcess, string status) { string redisHash = redUtil.man.redHash(processStatusPath).Replace("%NN%", ProductionProcess.ToString("00")); @@ -131,21 +131,21 @@ namespace Thermo.Active.Database.Controllers if (!redUtil.man.setRSV(redisHash, "100")) return false; redisHash = redUtil.man.redHash(processSpeedOverridePath).Replace("%NN%", ProductionProcess.ToString("00")); - if(!redUtil.man.setRSV(redisHash, "100")) + if (!redUtil.man.setRSV(redisHash, "100")) return false; return true; } - + public static bool WriteCurrentMachinePowerPath(bool status) { string redisHash = redUtil.man.redHash(machinePowerPath); - if(status) + if (status) return redUtil.man.setRSV(redisHash, "true"); else return redUtil.man.setRSV(redisHash, "false"); } - + public static bool WriteCurrentMachineAlarmPath(bool status) { string redisHash = redUtil.man.redHash(machineAlarmPath); @@ -154,7 +154,7 @@ namespace Thermo.Active.Database.Controllers else return redUtil.man.setRSV(redisHash, "false"); } - + public static bool WriteCurrentMachineEmergencyPath(bool status) { string redisHash = redUtil.man.redHash(machineEmergencyPath); @@ -183,14 +183,41 @@ namespace Thermo.Active.Database.Controllers redUtil.man.ListPush(redisHash, msg); return true; } + public static string ReadValue(string msgKey) + { + string redisHash = redUtil.man.redHash(msgKey); + return redUtil.man.getRSV(redisHash); + } + public static bool WriteValue(string msgKey, string msg, int ttlSec) + { + string redisHash = redUtil.man.redHash(msgKey); + redUtil.man.setRSV(redisHash, msg, ttlSec); + return true; + } + + public static bool FastIoSample + { + get + { + var currVal = ReadValue("FastIoSample"); + bool answ = !string.IsNullOrEmpty(currVal); + return answ; + } + set + { + // TTL: se true 5 min, altrimenti 1 sec + int ttlSec = value ? 60 * 5 : 1; + WriteValue("FastIoSample", "Active", ttlSec); + } + } public static bool WriteCurrentAxisStatus(Dictionary axis) { - foreach(KeyValuePair asse in axis) + foreach (KeyValuePair asse in axis) { string redisHash = redUtil.man.redHash(machineAxisName).Replace("%NN%", asse.Value.ID.ToString("00")); - if(redUtil.man.getRSV(redisHash) != null) + if (redUtil.man.getRSV(redisHash) != null) { redisHash = redUtil.man.redHash(machineAxisPosition).Replace("%NN%", asse.Value.ID.ToString("00")); if (!redUtil.man.setRSV(redisHash, asse.Value.position.ToString())) @@ -203,7 +230,7 @@ namespace Thermo.Active.Database.Controllers redisHash = redUtil.man.redHash(machineAxisLoad).Replace("%NN%", asse.Value.ID.ToString("00")); if (!redUtil.man.setRSV(redisHash, asse.Value.load.ToString())) return false; - } + } } return true; } diff --git a/Thermo.Active.Model/ConfigModels/IOConfigModel.cs b/Thermo.Active.Model/ConfigModels/IOConfigModel.cs new file mode 100644 index 00000000..2cfc288c --- /dev/null +++ b/Thermo.Active.Model/ConfigModels/IOConfigModel.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using static Thermo.Active.Model.Constants; + +namespace Thermo.Active.Model.ConfigModels +{ + public class IOConfigModel + { + public TACT_IO_TYPE Category { get; set; } + public int Id { get; set; } =0; + public string Bank { get; set; } = "0"; + public string Position { get; set; } = "0"; + public string Page { get; set; } = ""; + public string Wire { get; set; } = ""; + public string Profinet { get; set; } = ""; + public bool DisableForce { get; set; } = false; + public string Label + { + get + { + string answ = $"LBL_IO_{Category}_{Id}"; + return answ; + } + } + } + +} diff --git a/Thermo.Active.Model/Constants.cs b/Thermo.Active.Model/Constants.cs index b69607df..fc041bc4 100644 --- a/Thermo.Active.Model/Constants.cs +++ b/Thermo.Active.Model/Constants.cs @@ -98,6 +98,26 @@ namespace Thermo.Active.Model Options } + public enum TACT_IO_TYPE + { + ND = 0, + /// + /// Digital IN + /// + DI, + /// + /// Digital OUT + /// + DO, + /// + /// Analog IN + /// + AI, + /// + /// Analog OUT + /// + AO + } public enum TACT_MBLOCK_TYPE { ND = 0, @@ -303,6 +323,9 @@ namespace Thermo.Active.Model public const string MODBLOCK_CONFIG_SCHEMA_PATH = RESOURCE_DIRECTORY + "moduleBlockConfigValidator.xsd"; public const string MODBLOCK_CONFIG_PATH = CONFIG_DIRECTORY + "moduleBlockConfig.xml"; + public const string IO_CONFIG_SCHEMA_PATH = RESOURCE_DIRECTORY + "IOConfigValidator.xsd"; + public const string IO_CONFIG_PATH = CONFIG_DIRECTORY + "IOConfig.xml"; + public const string RISK_CONFIG_SCHEMA_PATH = RESOURCE_DIRECTORY + "risk2007Validator.xsd"; public const string RISK_CONFIG_PATH = CONFIG_DIRECTORY + "risk2007.xml"; @@ -354,6 +377,7 @@ namespace Thermo.Active.Model public const string SEND_NC_SOFTKEYS_DATA = "SEND_NC_SOFTKEYS_DATA"; public const string SEND_HEADS_DATA = "SEND_HEADS_DATA"; public const string SEND_AXIS_INFO = "SEND_AXIS_INFO"; + public const string SEND_CHANNELS_IO_DATA = "SEND_CHANNELS_IO_DATA"; public const string SEND_ACTIVE_PROGRAM_DATA = "SEND_ACTIVE_PROGRAM_DATA"; public const string SEND_QUEUE_DATA = "SEND_QUEUE_DATA"; public const string SEND_M155_DATA = "SEND_M155_DATA"; diff --git a/Thermo.Active.Model/DTOModels/ThIO/DTOChannelsIO.cs b/Thermo.Active.Model/DTOModels/ThIO/DTOChannelsIO.cs new file mode 100644 index 00000000..5f7efb57 --- /dev/null +++ b/Thermo.Active.Model/DTOModels/ThIO/DTOChannelsIO.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Thermo.Active.Model.DTOModels.ThIO +{ + public class DTOChannelsIO + { + public List DI { get; set; } = new List(); + public List DO { get; set; } = new List(); + public List AI { get; set; } = new List(); + public List AO { get; set; } = new List(); + } + public class DigitalIN : IoItemConf + { + public bool Value { get; set; } = false; + + public override bool Equals(object obj) + { + if (!(obj is DigitalIN item)) + return false; + + if (Value != item.Value) + return false; + + return base.Equals((IoItemConf)obj); + } + /// + /// Hash gen + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + public class AnalogIN : IoItemConf + { + public int Value { get; set; } = 0; + + public override bool Equals(object obj) + { + if (!(obj is AnalogIN item)) + return false; + + if (Value != item.Value) + return false; + + return base.Equals((IoItemConf)obj); + } + /// + /// Hash gen + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + public class DigitalOUT : DigitalIN + { + public bool ForceEnabled { get; set; } = true; + public bool ForceZero { get; set; } = false; + public bool ForceOne { get; set; } = false; + public bool IsForced { get; set; } = false; + + public override bool Equals(object obj) + { + if (!(obj is DigitalOUT item)) + return false; + + if (ForceEnabled != item.ForceEnabled) + return false; + if (ForceZero != item.ForceZero) + return false; + if (ForceOne != item.ForceOne) + return false; + if (IsForced != item.IsForced) + return false; + + return base.Equals((DigitalIN)obj); + } + /// + /// Hash gen + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + public class AnalogOUT : AnalogIN + { + public bool ForceEnabled { get; set; } = true; + public int ForcedValue { get; set; } = 0; + public bool IsForced { get; set; } = false; + + public override bool Equals(object obj) + { + if (!(obj is AnalogOUT item)) + return false; + + if (ForceEnabled != item.ForceEnabled) + return false; + if (ForcedValue != item.ForcedValue) + return false; + if (IsForced != item.IsForced) + return false; + + return base.Equals((AnalogIN)obj); + } + /// + /// Hash gen + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + +} diff --git a/Thermo.Active.Model/DTOModels/ThIO/DTOChannelsIOVal.cs b/Thermo.Active.Model/DTOModels/ThIO/DTOChannelsIOVal.cs new file mode 100644 index 00000000..d327aefe --- /dev/null +++ b/Thermo.Active.Model/DTOModels/ThIO/DTOChannelsIOVal.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Thermo.Active.Model.DTOModels.ThIO +{ + public class DTOChannelsIOVal + { + public List DI { get; set; } = new List(); + public List DO { get; set; } = new List(); + public List AI { get; set; } = new List(); + public List AO { get; set; } = new List(); + } + public class DigInVal: IoItem + { + public bool Value { get; set; } = false; + + public override bool Equals(object obj) + { + if (!(obj is DigitalIN item)) + return false; + + if (Value != item.Value) + return false; + + return base.Equals((IoItemConf)obj); + } + /// + /// Hash gen + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + public class AnalInVal: IoItem + { + public int Value { get; set; } = 0; + + public override bool Equals(object obj) + { + if (!(obj is AnalogIN item)) + return false; + + if (Value != item.Value) + return false; + + return base.Equals((IoItemConf)obj); + } + /// + /// Hash gen + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + public class DigOutVal : DigInVal + { + public bool ForceEnabled { get; set; } = true; + public bool ForceZero { get; set; } = false; + public bool ForceOne { get; set; } = false; + public bool IsForced { get; set; } = false; + + public override bool Equals(object obj) + { + if (!(obj is DigitalOUT item)) + return false; + + if (ForceEnabled != item.ForceEnabled) + return false; + if (ForceZero != item.ForceZero) + return false; + if (ForceOne != item.ForceOne) + return false; + if (IsForced != item.IsForced) + return false; + + return base.Equals((DigitalIN)obj); + } + /// + /// Hash gen + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + public class AnalOutVal : AnalInVal + { + public bool ForceEnabled { get; set; } = true; + public int ForcedValue { get; set; } = 0; + public bool IsForced { get; set; } = false; + + public override bool Equals(object obj) + { + if (!(obj is AnalogOUT item)) + return false; + + if (ForceEnabled != item.ForceEnabled) + return false; + if (ForcedValue != item.ForcedValue) + return false; + if (IsForced != item.IsForced) + return false; + + return base.Equals((AnalogIN)obj); + } + /// + /// Hash gen + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + +} diff --git a/Thermo.Active.Model/DTOModels/ThIO/DTOChannelsSetup.cs b/Thermo.Active.Model/DTOModels/ThIO/DTOChannelsSetup.cs new file mode 100644 index 00000000..8c26c8b9 --- /dev/null +++ b/Thermo.Active.Model/DTOModels/ThIO/DTOChannelsSetup.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Thermo.Active.Model.DTOModels.ThIO +{ + public class DTOChannelsSetup + { + public List DI { get; set; } = new List(); + public List DO { get; set; } = new List(); + public List AI { get; set; } = new List(); + public List AO { get; set; } = new List(); + } +} diff --git a/Thermo.Active.Model/DTOModels/ThIO/DTOCycleLog.cs b/Thermo.Active.Model/DTOModels/ThIO/DTOCycleLog.cs new file mode 100644 index 00000000..9cbc2db6 --- /dev/null +++ b/Thermo.Active.Model/DTOModels/ThIO/DTOCycleLog.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Thermo.Active.Model.DTOModels.ThIO +{ + public class DTOCycleLog + { + public List events { get; set; } = new List(); + } + + public class CycleEvent + { + public DateTime dtEvent { get; set; } + + public int code { get; set; } = 0; + } +} diff --git a/Thermo.Active.Model/DTOModels/ThIO/IoItemConf.cs b/Thermo.Active.Model/DTOModels/ThIO/IoItemConf.cs new file mode 100644 index 00000000..6e4e2b6c --- /dev/null +++ b/Thermo.Active.Model/DTOModels/ThIO/IoItemConf.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Thermo.Active.Model.DTOModels.ThIO +{ + public class IoItem + { + public int Id { get; set; } = 0; + } + public class IoItemDigi : IoItem + { + public bool value { get; set; } = false; + } + public class IoItemAnal : IoItem + { + public int value { get; set; } = 0; + } + public class IoItemConf : IoItem + { + public string Bank { get; set; } = "0"; + public string Position { get; set; } = "0"; + public string Page { get; set; } = "0"; + public string Wire { get; set; } = "0"; + public string Profinet { get; set; } = "0"; + public string Label { get; set; } = "LBL"; + public bool Visible { get; set; } = true; + + + public override bool Equals(object obj) + { + if (!(obj is IoItemConf item)) + return false; + + if (Id != item.Id) + return false; + if (Bank != item.Bank) + return false; + if (Position != item.Position) + return false; + if (Page != item.Page) + return false; + if (Wire != Wire) + return false; + if (Profinet != item.Profinet) + return false; + if (Label != item.Label) + return false; + if (Visible != item.Visible) + return false; + + return true; + } + /// + /// Hash gen + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } +} diff --git a/Thermo.Active.Model/Thermo.Active.Model.csproj b/Thermo.Active.Model/Thermo.Active.Model.csproj index 53f0a8d6..9c1f8e90 100644 --- a/Thermo.Active.Model/Thermo.Active.Model.csproj +++ b/Thermo.Active.Model/Thermo.Active.Model.csproj @@ -66,6 +66,7 @@ + @@ -112,6 +113,11 @@ + + + + + diff --git a/Thermo.Active.NC/NcAdapter.cs b/Thermo.Active.NC/NcAdapter.cs index 4cdbb667..e484a923 100644 --- a/Thermo.Active.NC/NcAdapter.cs +++ b/Thermo.Active.NC/NcAdapter.cs @@ -14,6 +14,7 @@ using Thermo.Active.Model.DTOModels.AlarmModels; using Thermo.Active.Model.DTOModels.MaintenanceModels; using Thermo.Active.Model.DTOModels.Scada; using Thermo.Active.Model.DTOModels.ThAxes; +using Thermo.Active.Model.DTOModels.ThIO; using Thermo.Active.Model.DTOModels.ThModules; using Thermo.Active.Model.DTOModels.ThProd; using Thermo.Active.Model.DTOModels.ThRecipe; @@ -1456,6 +1457,134 @@ namespace Thermo.Active.NC return libraryError; } + + /// + /// Legge dal PLC elenco di eventi LOG del ciclo e li presenta + /// + /// Oggetto elenco elementi LOG registrati da macchina + /// + public CmsError GetCycleLog(out Dictionary machineLog) + { + CmsError libraryError = NO_ERROR; + machineLog = new Dictionary(); + + if (false) + { + // recupero l'oggetto dall'NC + + // effettuo traduzione +#if false + // overview di base: ultima salvata... + var err2fix = new Dictionary(); + + // leggo la ricetta dal PLC! + var currRecipe = new Dictionary(); + libraryError = ReadFullRecipe(out currRecipe); + if (libraryError.IsError()) + return libraryError; + + // leggo l'intero array delle DB... QUI FAKE sulle DB configurate... + List recipeConfig = RecipeConfig.Select(x => new DTORecipeConfigModel() + { + Id = x.Id, + ScaleFactor = x.ScaleFactor, + NumDec = x.NumDec, + Category = x.Category.ToString(), + SubCategory_1 = x.SubCategory_1, + SubCategory_2 = x.SubCategory_2, + Name = x.Name, + Description = x.Description, + Format = x.Format, + Label = $"{x.Category}_{x.SubCategory_1}_{x.SubCategory_2}_{x.Name}".Replace("__", "_").Replace("__", "_").ToLower(), + EnumVal = x.EnumVal + }).ToList(); + + RecipeCatStatus currStatus = RecipeCatStatus.Unchanged; + + // da conf ricetta --> se ci sono li leggo da li... + if (NcFileAdapter.RecipeLiveData.RecipeOverview != null) + { + currOverview = NcFileAdapter.RecipeLiveData.RecipeOverview; + } + + // verifico eventualmente se mancasse qualcosa... + bool changed = false; + foreach (var item in recipeConfig) + { + if (!currOverview.ContainsKey(getRecipeSection(item.Category))) + { + currOverview.Add(getRecipeSection(item.Category), RecipeCatStatus.Unchanged); + changed = true; + } + } + + // ricerco SE co fossero errori --> reset come changedOK + foreach (var item in currOverview) + { + if (item.Value == RecipeCatStatus.HasError) + err2fix.Add(item.Key, RecipeCatStatus.ChangedOk); + } + foreach (var item in err2fix) + { + currOverview[item.Key] = item.Value; + changed = true; + } + + // se cambiato --> salvo in live data... + if (changed) + { + NcFileAdapter.RecipeLiveData.RecipeOverview = currOverview; + } + + // ORA percorro conf ricetta x cercare eventuali ERRORI...... + foreach (var item in recipeConfig) + { + currStatus = currOverview[getRecipeSection(item.Category)]; + + // se lo stato è errore --> esco... + if (currStatus == RecipeCatStatus.HasError) + { + continue; + } + // altrimenti controllo + else + { + // se in errore AND visibile --> registro... + bool checkCondition = false; + checkCondition = (currRecipe[item.Label].Status.HasError); + // 2020.07.29 - controllo condizione secondo status debug/release... + if (checkCondition) + { + currOverview[getRecipeSection(item.Category)] = RecipeCatStatus.HasError; + } + } + } +#endif + } + // altrimenti genero FAKE data + else + { + int eventVal = 0; + DateTime eventDate = DateTime.Now; + // genero random eventi da 1..maxEvent + Random rndGen = new Random(); + int maxEvent = 16; + int maxDelay = 30000; + for (int i = 0; i < 1024; i++) + { + // calcolo nuovo evento + eventVal = rndGen.Next(maxEvent); + // calcolo tempo anticipato + eventDate = eventDate.AddMilliseconds(-rndGen.Next(maxDelay)); + //salvo + machineLog.Add(eventDate, eventVal); + } + } + + // restituisco cod errore se trovato + return libraryError; + } + /// /// Legge tutti i parametri della ricetta e calcolo la overview dei vari steps /// @@ -2038,6 +2167,154 @@ namespace Thermo.Active.NC } return libraryError; } + /// + /// Restituisce intero set dati IO Channels (conf + valori) + /// + /// + /// + public CmsError ReadFullIO(out DTOChannelsIO currChannelsIO) + { + CmsError libraryError = NO_ERROR; + currChannelsIO = new DTOChannelsIO(); + + // read and return channel IO data + if (NcConfig.NcVendor == NC_VENDOR.S7NET) + { + // lettura da PLC + Dictionary currModBlock = new Dictionary(); + ThermoModels.ChanIOVis currThermoIOVis = new ThermoModels.ChanIOVis(); + ThermoModels.ChanIOVal currThermoIOVal = new ThermoModels.ChanIOVal(); + ThermoModels.ChanIOFor currThermoIOFor = new ThermoModels.ChanIOFor(); + ThermoModels.ChanIOValFor currThermoIOValFor = new ThermoModels.ChanIOValFor(); + libraryError = numericalControl.PLC_RIOChannelsConf(ref currThermoIOVis); + libraryError = numericalControl.PLC_RIOChannelsVal(ref currThermoIOVal, ref currThermoIOFor, ref currThermoIOValFor); + + // setup da config + List listDI = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.DI).Select(x => new DigitalIN() + { + Id = x.Id, + Bank = x.Bank, + Position = x.Position, + //Page = x.Category.ToString(), + Page = x.Page, + Wire = x.Wire, + Profinet = x.Profinet, + Label = x.Label.Replace("__", "_").Replace("__", "_").ToLower(), + Visible = currThermoIOVis.DI.ContainsKey(x.Id)? currThermoIOVis.DI[x.Id]:false, + Value = currThermoIOVal.DI.ContainsKey(x.Id) ? currThermoIOVal.DI[x.Id] : false + }).ToList(); + List listDO = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.DO).Select(x => new DigitalOUT() + { + Id = x.Id, + Bank = x.Bank, + Position = x.Position, + //Page = x.Category.ToString(), + Page = x.Page, + Wire = x.Wire, + Profinet = x.Profinet, + Label = x.Label.Replace("__", "_").Replace("__", "_").ToLower(), + ForceEnabled=!x.DisableForce, + Visible = currThermoIOVis.DO.ContainsKey(x.Id) ? currThermoIOVis.DO[x.Id] : false, + Value = currThermoIOVal.DO.ContainsKey(x.Id) ? currThermoIOVal.DO[x.Id] : false, + IsForced = currThermoIOFor.DO.ContainsKey(x.Id) ? currThermoIOFor.DO[x.Id] : false, + ForceOne = currThermoIOValFor.DO.ContainsKey(x.Id) ? currThermoIOValFor.DO[x.Id] : false, + ForceZero = currThermoIOValFor.DO.ContainsKey(x.Id) ? !currThermoIOValFor.DO[x.Id] : false + }).ToList(); + List listAI = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.AI).Select(x => new AnalogIN() + { + Id = x.Id, + Bank = x.Bank, + Position = x.Position, + //Page = x.Category.ToString(), + Page = x.Page, + Wire = x.Wire, + Profinet = x.Profinet, + Label = x.Label.Replace("__", "_").Replace("__", "_").ToLower(), + Visible = currThermoIOVis.AI.ContainsKey(x.Id) ? currThermoIOVis.AI[x.Id] : false, + Value = currThermoIOVal.AI.ContainsKey(x.Id) ? currThermoIOVal.AI[x.Id] : 0 + }).ToList(); + List listAO = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.AO).Select(x => new AnalogOUT() + { + Id = x.Id, + Bank = x.Bank, + Position = x.Position, + //Page = x.Category.ToString(), + Page = x.Page, + Wire = x.Wire, + Profinet = x.Profinet, + Label = x.Label.Replace("__", "_").Replace("__", "_").ToLower(), + ForceEnabled = !x.DisableForce, + Visible = currThermoIOVis.AO.ContainsKey(x.Id) ? currThermoIOVis.AO[x.Id] : false, + Value = currThermoIOVal.AO.ContainsKey(x.Id) ? currThermoIOVal.AO[x.Id] : 0, + IsForced = currThermoIOFor.AO.ContainsKey(x.Id) ? currThermoIOFor.AO[x.Id] : false, + ForcedValue = currThermoIOValFor.AO.ContainsKey(x.Id) ? currThermoIOValFor.AO[x.Id] : 0 + }).ToList(); + + // assegno! + currChannelsIO.DI = listDI; + currChannelsIO.DO = listDO; + currChannelsIO.AI = listAI; + currChannelsIO.AO = listAO; + } + return libraryError; + } + /// + /// Restitusice SOLO VALORI IO Channels + /// + /// + /// + public CmsError ReadValIO(out DTOChannelsIOVal currChannelsIoVal) + { + CmsError libraryError = NO_ERROR; + currChannelsIoVal = new DTOChannelsIOVal(); + // read and return channel IO data + if (NcConfig.NcVendor == NC_VENDOR.S7NET) + { + // lettura da PLC + Dictionary currModBlock = new Dictionary(); + ThermoModels.ChanIOVal currThermoIOVal = new ThermoModels.ChanIOVal(); + ThermoModels.ChanIOFor currThermoIOFor = new ThermoModels.ChanIOFor(); + ThermoModels.ChanIOValFor currThermoIOValFor = new ThermoModels.ChanIOValFor(); + libraryError = numericalControl.PLC_RIOChannelsVal(ref currThermoIOVal, ref currThermoIOFor, ref currThermoIOValFor); + + // setup da config + List listDI = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.DI).Select(x => new DigInVal() + { + Id = x.Id, + Value = currThermoIOVal.DI.ContainsKey(x.Id) ? currThermoIOVal.DI[x.Id] : false + }).ToList(); + List listDO = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.DO).Select(x => new DigOutVal() + { + Id = x.Id, + ForceEnabled = !x.DisableForce, + Value = currThermoIOVal.DO.ContainsKey(x.Id) ? currThermoIOVal.DO[x.Id] : false, + IsForced = currThermoIOFor.DO.ContainsKey(x.Id) ? currThermoIOFor.DO[x.Id] : false, + ForceOne = currThermoIOValFor.DO.ContainsKey(x.Id) ? currThermoIOValFor.DO[x.Id] : false, + ForceZero = currThermoIOValFor.DO.ContainsKey(x.Id) ? !currThermoIOValFor.DO[x.Id] : false + }).ToList(); + List listAI = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.AI).Select(x => new AnalInVal() + { + Id = x.Id, + Value = currThermoIOVal.AI.ContainsKey(x.Id) ? currThermoIOVal.AI[x.Id] : 0 + }).ToList(); + List listAO = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.AO).Select(x => new AnalOutVal() + { + Id = x.Id, + ForceEnabled = !x.DisableForce, + Value = currThermoIOVal.AO.ContainsKey(x.Id) ? currThermoIOVal.AO[x.Id] : 0, + IsForced = currThermoIOFor.AO.ContainsKey(x.Id) ? currThermoIOFor.AO[x.Id] : false, + ForcedValue = currThermoIOValFor.AO.ContainsKey(x.Id) ? currThermoIOValFor.AO[x.Id] : 0 + }).ToList(); + + // assegno! + currChannelsIoVal.DI = listDI; + currChannelsIoVal.DO = listDO; + currChannelsIoVal.AI = listAI; + currChannelsIoVal.AO = listAO; + } + return libraryError; + + } /// /// Legge tutti i parametri della ricetta @@ -3061,7 +3338,7 @@ namespace Thermo.Active.NC /// /// Oggetto parametri da aggiornare (from HMI) /// num max parametri da scrivere singolarmente - /// delay in scriottura multi parametri singoli + /// delay in scrittura multi parametri singoli /// public CmsError WriteRecipeParametersToPLC(Dictionary updtRecipe, int nMaxParamWrite, int delayParamWrite) { @@ -3091,7 +3368,7 @@ namespace Thermo.Active.NC /// /// Oggetto parametri da aggiornare (from HMI) /// num max parametri da scrivere singolarmente - /// delay in scriottura multi parametri singoli + /// delay in scrittura multi parametri singoli /// public CmsError WriteRecipeParams(Dictionary updtRecipe, int nMaxParamWrite, int delayParamWrite) { @@ -3100,6 +3377,125 @@ namespace Thermo.Active.NC return libraryError; } + + /// + /// ChannelsID: write DO to PLC (values + setForce) + /// + /// Oggetto parametri da aggiornare (from HMI) + /// + public CmsError Write_IO_DO_ToPLC(Dictionary newValues) + { + // solo x S7... + if (NcConfig.NcVendor == NC_VENDOR.S7NET) + { + // scrivo! + CmsError libraryError = numericalControl.PLC_W_IO_DO_Val(newValues); + if (libraryError.IsError()) + return libraryError; + } + else + { + return FUNCTION_NOT_ALLOWED_ERROR; + } + return NO_ERROR; + } + /// + /// ChannelsID: write AO to PLC (values + setForce) + /// + /// Oggetto parametri da aggiornare (from HMI) + /// + public CmsError Write_IO_AO_ToPLC(Dictionary newValues) + { + // solo x S7... + if (NcConfig.NcVendor == NC_VENDOR.S7NET) + { + // scrivo! + CmsError libraryError = numericalControl.PLC_W_IO_AO_Val(newValues); + if (libraryError.IsError()) + return libraryError; + } + else + { + return FUNCTION_NOT_ALLOWED_ERROR; + } + return NO_ERROR; + } + /// + /// ChannelsID: Write RESET (not forced) for DO to PLC (setForce = false) + /// + /// Oggetto parametri da aggiornare (from HMI) + /// + public CmsError WriteReset_IO_DO_ToPLC(List channels) + { + // solo x S7... + if (NcConfig.NcVendor == NC_VENDOR.S7NET) + { + Dictionary newForced = new Dictionary(); + foreach (var item in channels) + { + newForced.Add(item, false); + } + // scrivo! + CmsError libraryError = numericalControl.PLC_W_IO_DO_Reset(newForced); + if (libraryError.IsError()) + return libraryError; + } + else + { + return FUNCTION_NOT_ALLOWED_ERROR; + } + return NO_ERROR; + } + /// + /// ChannelsID: Write RESET (not forced) for AO to PLC (setForce = false) + /// + /// Oggetto parametri da aggiornare (from HMI) + /// + public CmsError WriteReset_IO_AO_ToPLC(List channels) + { + // solo x S7... + if (NcConfig.NcVendor == NC_VENDOR.S7NET) + { + Dictionary newForced = new Dictionary(); + foreach (var item in channels) + { + newForced.Add(item, false); + } + // scrivo! + CmsError libraryError = numericalControl.PLC_W_IO_AO_Reset(newForced); + if (libraryError.IsError()) + return libraryError; + } + else + { + return FUNCTION_NOT_ALLOWED_ERROR; + } + return NO_ERROR; + } + /// + /// ChannelsID: Write RESET (not forced) for DO + AO to PLC (setForce = false) + /// + /// Oggetto parametri da aggiornare (from HMI) + /// num max parametri da scrivere singolarmente + /// delay in scrittura multi parametri singoli + /// + public CmsError WriteReset_IO_ALL_ToPLC() + { + // solo x S7... + if (NcConfig.NcVendor == NC_VENDOR.S7NET) + { + // scrivo! + CmsError libraryError = numericalControl.PLC_W_IO_ResetAll(); + if (libraryError.IsError()) + return libraryError; + } + else + { + return FUNCTION_NOT_ALLOWED_ERROR; + } + return NO_ERROR; + } + /// /// Scrive le softkey star /// diff --git a/Thermo.Active.Utils/supportFunctions.cs b/Thermo.Active.Utils/supportFunctions.cs index 3cd93d2f..faab6cce 100644 --- a/Thermo.Active.Utils/supportFunctions.cs +++ b/Thermo.Active.Utils/supportFunctions.cs @@ -100,6 +100,21 @@ namespace Thermo.Active.Utils return answ; } /// + /// Conversion string --> TACT_IO_TYPE + /// + /// + /// + public static TACT_IO_TYPE GetTActIO_Type(string strValue) + { + TACT_IO_TYPE answ = TACT_IO_TYPE.ND; + try + { + answ = (TACT_IO_TYPE)Enum.Parse(typeof(TACT_IO_TYPE), strValue); + } + catch { } + return answ; + } + /// /// Conversion string --> TACT_MBLOCK_SECTION /// /// diff --git a/Thermo.Active/App_Start/SwaggerConfig.cs b/Thermo.Active/App_Start/SwaggerConfig.cs index fb0fe40a..704b5f02 100644 --- a/Thermo.Active/App_Start/SwaggerConfig.cs +++ b/Thermo.Active/App_Start/SwaggerConfig.cs @@ -1,6 +1,9 @@ using Swashbuckle.Application; using Swashbuckle.Swagger; +using System; using System.Collections.Generic; +using System.IO; +using System.Reflection; using System.Web.Http; using System.Web.Http.Description; @@ -33,129 +36,132 @@ namespace Thermo.Active // c.SingleApiVersion("v1", "ThermoActive"); - // If you want the output Swagger docs to be indented properly, enable the "PrettyPrint" option. - // - //c.PrettyPrint(); + // If you want the output Swagger docs to be indented properly, enable the "PrettyPrint" option. + // + //c.PrettyPrint(); - // If your API has multiple versions, use "MultipleApiVersions" instead of "SingleApiVersion". - // In this case, you must provide a lambda that tells Swashbuckle which actions should be - // included in the docs for a given API version. Like "SingleApiVersion", each call to "Version" - // returns an "Info" builder so you can provide additional metadata per API version. - // - //c.MultipleApiVersions( - // (apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion), - // (vc) => - // { - // vc.Version("v2", "Swashbuckle Dummy API V2"); - // vc.Version("v1", "Swashbuckle Dummy API V1"); - // }); + // If your API has multiple versions, use "MultipleApiVersions" instead of "SingleApiVersion". + // In this case, you must provide a lambda that tells Swashbuckle which actions should be + // included in the docs for a given API version. Like "SingleApiVersion", each call to "Version" + // returns an "Info" builder so you can provide additional metadata per API version. + // + //c.MultipleApiVersions( + // (apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion), + // (vc) => + // { + // vc.Version("v2", "Swashbuckle Dummy API V2"); + // vc.Version("v1", "Swashbuckle Dummy API V1"); + // }); - // You can use "BasicAuth", "ApiKey" or "OAuth2" options to describe security schemes for the API. - // See https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md for more details. - // NOTE: These only define the schemes and need to be coupled with a corresponding "security" property - // at the document or operation level to indicate which schemes are required for an operation. To do this, - // you'll need to implement a custom IDocumentFilter and/or IOperationFilter to set these properties - // according to your specific authorization implementation - // - //c.BasicAuth("basic") - // .Description("Basic HTTP Authentication"); - // - // NOTE: You must also configure 'EnableApiKeySupport' below in the SwaggerUI section - //c.ApiKey("apiKey") - // .Description("API Key Authentication") - // .Name("apiKey") - // .In("header"); - // - //c.OAuth2("oauth2") - // .Description("OAuth2 Implicit Grant") - // .Flow("implicit") - // .AuthorizationUrl("http://localhost:9000/token") - // .TokenUrl("http://localhost:9000/token") - // .Scopes(scopes => - // { - // scopes.Add("read", "Read access to protected resources"); - // scopes.Add("write", "Write access to protected resources"); - // }); + // You can use "BasicAuth", "ApiKey" or "OAuth2" options to describe security schemes for the API. + // See https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md for more details. + // NOTE: These only define the schemes and need to be coupled with a corresponding "security" property + // at the document or operation level to indicate which schemes are required for an operation. To do this, + // you'll need to implement a custom IDocumentFilter and/or IOperationFilter to set these properties + // according to your specific authorization implementation + // + //c.BasicAuth("basic") + // .Description("Basic HTTP Authentication"); + // + // NOTE: You must also configure 'EnableApiKeySupport' below in the SwaggerUI section + //c.ApiKey("apiKey") + // .Description("API Key Authentication") + // .Name("apiKey") + // .In("header"); + // + //c.OAuth2("oauth2") + // .Description("OAuth2 Implicit Grant") + // .Flow("implicit") + // .AuthorizationUrl("http://localhost:9000/token") + // .TokenUrl("http://localhost:9000/token") + // .Scopes(scopes => + // { + // scopes.Add("read", "Read access to protected resources"); + // scopes.Add("write", "Write access to protected resources"); + // }); - // Set this flag to omit descriptions for any actions decorated with the Obsolete attribute - //c.IgnoreObsoleteActions(); + // Set this flag to omit descriptions for any actions decorated with the Obsolete attribute + //c.IgnoreObsoleteActions(); - // Each operation be assigned one or more tags which are then used by consumers for various reasons. - // For example, the swagger-ui groups operations according to the first tag of each operation. - // By default, this will be controller name but you can use the "GroupActionsBy" option to - // override with any value. - // - //c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString()); + // Each operation be assigned one or more tags which are then used by consumers for various reasons. + // For example, the swagger-ui groups operations according to the first tag of each operation. + // By default, this will be controller name but you can use the "GroupActionsBy" option to + // override with any value. + // + //c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString()); - // You can also specify a custom sort order for groups (as defined by "GroupActionsBy") to dictate - // the order in which operations are listed. For example, if the default grouping is in place - // (controller name) and you specify a descending alphabetic sort order, then actions from a - // ProductsController will be listed before those from a CustomersController. This is typically - // used to customize the order of groupings in the swagger-ui. - // - //c.OrderActionGroupsBy(new DescendingAlphabeticComparer()); + // You can also specify a custom sort order for groups (as defined by "GroupActionsBy") to dictate + // the order in which operations are listed. For example, if the default grouping is in place + // (controller name) and you specify a descending alphabetic sort order, then actions from a + // ProductsController will be listed before those from a CustomersController. This is typically + // used to customize the order of groupings in the swagger-ui. + // + //c.OrderActionGroupsBy(new DescendingAlphabeticComparer()); - // If you annotate Controllers and API Types with - // Xml comments (http://msdn.microsoft.com/en-us/library/b2s063f7(v=vs.110).aspx), you can incorporate - // those comments into the generated docs and UI. You can enable this by providing the path to one or - // more Xml comment files. - // - //c.IncludeXmlComments(GetXmlCommentsPath()); + // If you annotate Controllers and API Types with + // Xml comments (http://msdn.microsoft.com/en-us/library/b2s063f7(v=vs.110).aspx), you can incorporate + // those comments into the generated docs and UI. You can enable this by providing the path to one or + // more Xml comment files. + // + //c.IncludeXmlComments(GetXmlCommentsPath()); + //var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + //var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + //c.IncludeXmlComments(xmlPath); - // Swashbuckle makes a best attempt at generating Swagger compliant JSON schemas for the various types - // exposed in your API. However, there may be occasions when more control of the output is needed. - // This is supported through the "MapType" and "SchemaFilter" options: - // - // Use the "MapType" option to override the Schema generation for a specific type. - // It should be noted that the resulting Schema will be placed "inline" for any applicable Operations. - // While Swagger 2.0 supports inline definitions for "all" Schema types, the swagger-ui tool does not. - // It expects "complex" Schemas to be defined separately and referenced. For this reason, you should only - // use the "MapType" option when the resulting Schema is a primitive or array type. If you need to alter a - // complex Schema, use a Schema filter. - // - //c.MapType(() => new Schema { type = "integer", format = "int32" }); + // Swashbuckle makes a best attempt at generating Swagger compliant JSON schemas for the various types + // exposed in your API. However, there may be occasions when more control of the output is needed. + // This is supported through the "MapType" and "SchemaFilter" options: + // + // Use the "MapType" option to override the Schema generation for a specific type. + // It should be noted that the resulting Schema will be placed "inline" for any applicable Operations. + // While Swagger 2.0 supports inline definitions for "all" Schema types, the swagger-ui tool does not. + // It expects "complex" Schemas to be defined separately and referenced. For this reason, you should only + // use the "MapType" option when the resulting Schema is a primitive or array type. If you need to alter a + // complex Schema, use a Schema filter. + // + //c.MapType(() => new Schema { type = "integer", format = "int32" }); - // If you want to post-modify "complex" Schemas once they've been generated, across the board or for a - // specific type, you can wire up one or more Schema filters. - // - //c.SchemaFilter(); + // If you want to post-modify "complex" Schemas once they've been generated, across the board or for a + // specific type, you can wire up one or more Schema filters. + // + //c.SchemaFilter(); - // In a Swagger 2.0 document, complex types are typically declared globally and referenced by unique - // Schema Id. By default, Swashbuckle does NOT use the full type name in Schema Ids. In most cases, this - // works well because it prevents the "implementation detail" of type namespaces from leaking into your - // Swagger docs and UI. However, if you have multiple types in your API with the same class name, you'll - // need to opt out of this behavior to avoid Schema Id conflicts. - // - //c.UseFullTypeNameInSchemaIds(); + // In a Swagger 2.0 document, complex types are typically declared globally and referenced by unique + // Schema Id. By default, Swashbuckle does NOT use the full type name in Schema Ids. In most cases, this + // works well because it prevents the "implementation detail" of type namespaces from leaking into your + // Swagger docs and UI. However, if you have multiple types in your API with the same class name, you'll + // need to opt out of this behavior to avoid Schema Id conflicts. + // + //c.UseFullTypeNameInSchemaIds(); - // Alternatively, you can provide your own custom strategy for inferring SchemaId's for - // describing "complex" types in your API. - // - //c.SchemaId(t => t.FullName.Contains('`') ? t.FullName.Substring(0, t.FullName.IndexOf('`')) : t.FullName); + // Alternatively, you can provide your own custom strategy for inferring SchemaId's for + // describing "complex" types in your API. + // + //c.SchemaId(t => t.FullName.Contains('`') ? t.FullName.Substring(0, t.FullName.IndexOf('`')) : t.FullName); - // Set this flag to omit schema property descriptions for any type properties decorated with the - // Obsolete attribute - //c.IgnoreObsoleteProperties(); + // Set this flag to omit schema property descriptions for any type properties decorated with the + // Obsolete attribute + //c.IgnoreObsoleteProperties(); - // In accordance with the built in JsonSerializer, Swashbuckle will, by default, describe enums as integers. - // You can change the serializer behavior by configuring the StringToEnumConverter globally or for a given - // enum type. Swashbuckle will honor this change out-of-the-box. However, if you use a different - // approach to serialize enums as strings, you can also force Swashbuckle to describe them as strings. - // - //c.DescribeAllEnumsAsStrings(); + // In accordance with the built in JsonSerializer, Swashbuckle will, by default, describe enums as integers. + // You can change the serializer behavior by configuring the StringToEnumConverter globally or for a given + // enum type. Swashbuckle will honor this change out-of-the-box. However, if you use a different + // approach to serialize enums as strings, you can also force Swashbuckle to describe them as strings. + // + //c.DescribeAllEnumsAsStrings(); - // Similar to Schema filters, Swashbuckle also supports Operation and Document filters: - // - // Post-modify Operation descriptions once they've been generated by wiring up one or more - // Operation filters. - // - //c.OperationFilter(); - // - // If you've defined an OAuth2 flow as described above, you could use a custom filter - // to inspect some attribute on each action and infer which (if any) OAuth2 scopes are required - // to execute the operation - // - c.OperationFilter(() => new AddRequiredHeaderParameter()); + // Similar to Schema filters, Swashbuckle also supports Operation and Document filters: + // + // Post-modify Operation descriptions once they've been generated by wiring up one or more + // Operation filters. + // + //c.OperationFilter(); + // + // If you've defined an OAuth2 flow as described above, you could use a custom filter + // to inspect some attribute on each action and infer which (if any) OAuth2 scopes are required + // to execute the operation + // + c.OperationFilter(() => new AddRequiredHeaderParameter()); // Post-modify the entire Swagger document by wiring up one or more Document filters. // This gives full control to modify the final SwaggerDocument. You should have a good understanding of diff --git a/Thermo.Active/Controllers/WebApi/ConfigurationController.cs b/Thermo.Active/Controllers/WebApi/ConfigurationController.cs index 10ae29d5..35f08dbe 100644 --- a/Thermo.Active/Controllers/WebApi/ConfigurationController.cs +++ b/Thermo.Active/Controllers/WebApi/ConfigurationController.cs @@ -5,6 +5,7 @@ using Thermo.Active.Database.Controllers; using Thermo.Active.Model.ConfigModels; using Thermo.Active.Model.DTOModels; using Thermo.Active.Model.DTOModels.AlarmModels; +using Thermo.Active.Model.DTOModels.ThIO; using Thermo.Active.Model.DTOModels.ThProd; using Thermo.Active.Model.DTOModels.ThRecipe; using static Thermo.Active.Config.ServerConfig; @@ -124,6 +125,27 @@ namespace Thermo.Active.Controllers.WebApi return Ok(recipeConfig); } + [Route("configIO"), HttpGet] + public IHttpActionResult GetIOConfig() + { + // restituisce la configurazione come item dei banchi DI/DO/AI/AO + DTOChannelsSetup configIO = new DTOChannelsSetup(); + // leggo i 4 tipi di oggetti e popolo + List listDI = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.DI).GroupBy(x => x.Bank).Select(x => x.Key).ToList(); + List listDO = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.DO).GroupBy(x => x.Bank).Select(x => x.Key).ToList(); + List listAI = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.AI).GroupBy(x => x.Bank).Select(x => x.Key).ToList(); + List listAO = IOConfig.Where(x => x.Category == Model.Constants.TACT_IO_TYPE.AO).GroupBy(x => x.Bank).Select(x => x.Key).ToList(); + + // assegno! + configIO.DI = listDI; + configIO.DO = listDO; + configIO.AI = listAI; + configIO.AO = listAO; + + // restituisco + return Ok(configIO); + } + [Route("thermoProd"), HttpGet] public IHttpActionResult GetThermoProdConfig() { diff --git a/Thermo.Active/Controllers/WebApi/ThermocameraController.cs b/Thermo.Active/Controllers/WebApi/ThermocameraController.cs index 87fa7be3..2738f8c9 100644 --- a/Thermo.Active/Controllers/WebApi/ThermocameraController.cs +++ b/Thermo.Active/Controllers/WebApi/ThermocameraController.cs @@ -24,7 +24,7 @@ namespace Thermo.Active.Controllers.WebApi public class ThermocameraController : ApiController { [Route("show"), HttpPost] - public IHttpActionResult GetDataPaginated() + public IHttpActionResult showCamera() { String ThermoCameraXpos = AdditionalParametersConfig["ThermoCameraXpos"]; String ThermoCameraYpos = AdditionalParametersConfig["ThermoCameraYpos"]; diff --git a/Thermo.Active/Controllers/WebApi/UnderTheHoodController.cs b/Thermo.Active/Controllers/WebApi/UnderTheHoodController.cs new file mode 100644 index 00000000..04f40792 --- /dev/null +++ b/Thermo.Active/Controllers/WebApi/UnderTheHoodController.cs @@ -0,0 +1,278 @@ +using CMS_CORE_Library.Models; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Claims; +using System.Threading.Tasks; +using System.Web; +using System.Web.Http; +using System.Web.Http.Description; +using System.Windows.Media.Animation; +using TeamDev.SDK.MVVM; +using Thermo.Active.Config; +using Thermo.Active.Database.Controllers; +using Thermo.Active.Model.DTOModels; +using Thermo.Active.Model.DTOModels.ThIO; +using Thermo.Active.Model.DTOModels.ThRecipe; +using Thermo.Active.Model.DTOModels.ThWarmers; +using Thermo.Active.NC; +using Thermo.Active.Utils; +using static CMS_CORE_Library.Models.DataStructures; +using static Thermo.Active.Config.ServerConfig; +using static Thermo.Active.Model.Constants; + +namespace Thermo.Active.Controllers.WebApi +{ + [RoutePrefix("api/underthehood")] + public class UnderTheHoodController : ApiController + { + /// + /// Oggetto adapter condiviso da WebAPI + /// + protected static NcAdapter ncAdapter = new NcAdapter(); + + [ResponseType(typeof(DTOCycleLog))] + [Route("cyclelog"), HttpGet] + public IHttpActionResult GetCycleLog() + { + // // Try connection + CmsError libraryError = ncAdapter.Connect(); + DTOCycleLog currCycleLog = new DTOCycleLog(); + + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"ncAdapter Not connected! | GetCycleLog | {libraryError.exception}"); + } + + libraryError = ncAdapter.GetCycleLog(out Dictionary cycleLog); + foreach (var item in cycleLog) + { + currCycleLog.events.Add(new CycleEvent() { dtEvent=item.Key, code=item.Value }); + } + + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"GetCycleLog error | {libraryError.exception}"); + return BadRequest(libraryError.localizationKey); + } + + return Ok(currCycleLog); + } + + [ResponseType(typeof(DTOChannelsIO))] + [Route("channels_io"), HttpGet] + public IHttpActionResult GetChannelsIO() + { + // Try connection + CmsError libraryError = ncAdapter.Connect(); + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"NC Not connected! | GetChannelsIO | {libraryError.exception}"); + return BadRequest(libraryError.localizationKey); + } + + // leggo dati gauges + libraryError = ncAdapter.ReadFullIO(out DTOChannelsIO ChannelsIO); + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"GetChannelsIO error | {libraryError.exception}"); + return BadRequest(libraryError.localizationKey); + } + + // ritorno! + return Ok(ChannelsIO); + } + + [ResponseType(typeof(DTOChannelsIOVal))] + [Route("channels_io_val"), HttpGet] + public IHttpActionResult GetChannelsIoVal() + { + // Try connection + CmsError libraryError = ncAdapter.Connect(); + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"NC Not connected! | GetChannelsIoVal | {libraryError.exception}"); + return BadRequest(libraryError.localizationKey); + } + + // leggo dati gauges + libraryError = ncAdapter.ReadValIO(out DTOChannelsIOVal ChannelsIOVal); + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"GetChannelsIoVal error | {libraryError.exception}"); + return BadRequest(libraryError.localizationKey); + } + + // ritorno! + return Ok(ChannelsIOVal); + } + + [Route("io_force_ch_do"), HttpPut] + [WebApiAuthorize(FunctionAccess = FUNCTIONALITY_NAMES.MAINTENANCE, Action = ACTIONS.WRITE)] + public IHttpActionResult ForceChDO(List updVal) + { + if (updVal != null) + { + // Try connection + CmsError libraryError = ncAdapter.Connect(); + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"NC Not connected! | ForceChDO | {libraryError.exception}"); + return InternalServerError(); + } + + if (updVal.Count > 0) + { + // converto + Dictionary parametersList = new Dictionary(); + foreach (var item in updVal) + { + parametersList.Add(item.Id, item.value); + } + + // scrivo sul PLC con i parametri specificati + ncAdapter.Write_IO_DO_ToPLC(parametersList); + } + + // ritorno solo fatto! + return Ok(); + } + else + { + ThermoActiveLogger.LogError($"ForceChDO updatedVal null | Empty Parameters"); + return BadRequest(); + } + } + [Route("io_force_ch_ao"), HttpPut] + [WebApiAuthorize(FunctionAccess = FUNCTIONALITY_NAMES.MAINTENANCE, Action = ACTIONS.WRITE)] + public IHttpActionResult ForceChAO(List updVal) + { + if (updVal != null) + { + // Try connection + CmsError libraryError = ncAdapter.Connect(); + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"NC Not connected! | ForceChAO | {libraryError.exception}"); + return InternalServerError(); + } + + if (updVal.Count > 0) + { + Dictionary parametersList = new Dictionary(); + foreach (var item in updVal) + { + parametersList.Add(item.Id, item.value); + } + + // scrivo sul PLC con i parametri specificati + ncAdapter.Write_IO_AO_ToPLC(parametersList); + } + + // ritorno solo fatto! + return Ok(); + } + else + { + ThermoActiveLogger.LogError($"ForceChAO parametersList null | Empty Parameters"); + return BadRequest(); + } + } + [Route("io_reset_ch_do"), HttpPut] + [WebApiAuthorize(FunctionAccess = FUNCTIONALITY_NAMES.MAINTENANCE, Action = ACTIONS.WRITE)] + public IHttpActionResult ResetChDO(List parametersList) + { + if (parametersList != null) + { + // Try connection + CmsError libraryError = ncAdapter.Connect(); + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"NC Not connected! | ResetChDO | {libraryError.exception}"); + return InternalServerError(); + } + + if (parametersList.Count > 0) + { + // scrivo sul PLC con i parametri specificati x ritardo/raggruppamento + ncAdapter.WriteReset_IO_DO_ToPLC(parametersList); + } + + // ritorno solo fatto! + return Ok(); + } + else + { + ThermoActiveLogger.LogError($"ResetChDO parametersList null | Empty Parameters"); + return BadRequest(); + } + } + /// + /// Reset "force bit" on selected channels + /// + /// List of channels index, zero based + /// + [Route("io_reset_ch_ao"), HttpPut] + [WebApiAuthorize(FunctionAccess = FUNCTIONALITY_NAMES.MAINTENANCE, Action = ACTIONS.WRITE)] + public IHttpActionResult ResetChAO(List parametersList) + { + if (parametersList != null) + { + // Try connection + CmsError libraryError = ncAdapter.Connect(); + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"NC Not connected! | ResetChAO | {libraryError.exception}"); + return InternalServerError(); + } + + if (parametersList.Count > 0) + { + // scrivo sul PLC con i parametri specificati x ritardo/raggruppamento + ncAdapter.WriteReset_IO_AO_ToPLC(parametersList); + } + + // ritorno solo fatto! + return Ok(); + } + else + { + ThermoActiveLogger.LogError($"ResetChAO parametersList null | Empty Parameters"); + return BadRequest(); + } + } + [Route("io_reset_all_ch"), HttpPut] + [WebApiAuthorize(FunctionAccess = FUNCTIONALITY_NAMES.MAINTENANCE, Action = ACTIONS.WRITE)] + public IHttpActionResult ResetAllCh() + { + // Try connection + CmsError libraryError = ncAdapter.Connect(); + if (libraryError.IsError()) + { + ThermoActiveLogger.LogError($"NC Not connected! | ResetAllCh | {libraryError.exception}"); + return InternalServerError(); + } + + // scrivo sul PLC con i parametri specificati x ritardo/raggruppamento + ncAdapter.WriteReset_IO_ALL_ToPLC();//parametersList); + + // ritorno solo fatto! + return Ok(); + } + + [Route("io_sample_fast"), HttpPut] + [WebApiAuthorize(FunctionAccess = FUNCTIONALITY_NAMES.MAINTENANCE, Action = ACTIONS.WRITE)] + public IHttpActionResult SetFastSample(bool value) + { + // imposta (su redis) campionamento RAPIDO/lento x IO... + RedisController.FastIoSample = value; + + // ritorno solo fatto! + return Ok(); + } + } +} \ No newline at end of file diff --git a/Thermo.Active/Listeners/ListenersHandler.cs b/Thermo.Active/Listeners/ListenersHandler.cs index ec75eaa5..f9b7a02e 100644 --- a/Thermo.Active/Listeners/ListenersHandler.cs +++ b/Thermo.Active/Listeners/ListenersHandler.cs @@ -64,6 +64,10 @@ namespace Thermo.Active.Listeners { SignalRListener.SendThermoAxisInfoData(a); })); + infos.Add(MessageServices.Current.Subscribe(SEND_CHANNELS_IO_DATA, (a, b) => + { + SignalRListener.SendThermoChannelsIoData(a); + })); infos.Add(MessageServices.Current.Subscribe(SEND_ACTIVE_PROGRAM_DATA, (a, b) => { SignalRListener.SendActiveProgramData(a); diff --git a/Thermo.Active/Listeners/SignalR/SignalRListener.cs b/Thermo.Active/Listeners/SignalR/SignalRListener.cs index e0029ecb..bb86d9d1 100644 --- a/Thermo.Active/Listeners/SignalR/SignalRListener.cs +++ b/Thermo.Active/Listeners/SignalR/SignalRListener.cs @@ -13,6 +13,7 @@ using Thermo.Active.Model.DTOModels; using Thermo.Active.Model.DTOModels.AlarmModels; using Thermo.Active.Model.DTOModels.Scada; using Thermo.Active.Model.DTOModels.ThAxes; +using Thermo.Active.Model.DTOModels.ThIO; using Thermo.Active.Model.DTOModels.ThModules; using Thermo.Active.Model.DTOModels.ThProd; using Thermo.Active.Model.DTOModels.ThRecipe; @@ -59,7 +60,7 @@ namespace Thermo.Active.Listeners.SignalR { RedisController.WriteCurrentMachineEmergencyPath(LastPowerOnData.PrePowerOn.EmergencyButtons.Active); } - + } } @@ -456,8 +457,8 @@ namespace Thermo.Active.Listeners.SignalR LastProdPanelData = currProdPanel; var context = GlobalHost.ConnectionManager.GetHubContext(); - context.Clients.Group("ncData").prodPanelData(currProdPanel); - + context.Clients.Group("ncData").prodPanelData(currProdPanel); + if (Config.ServerConfig.ServerStartupConfig.CmsConnectReady) { if (newPiece) @@ -508,8 +509,8 @@ namespace Thermo.Active.Listeners.SignalR RedisController.WriteCurrentMachineStatus(CMSConnectConstants.ConvertThermoToConnectStatus(currProdCycle.Status)); RedisController.WriteCurrentMachinePowerPath(CMSConnectConstants.ConvertThermoToConnectPower(currProdCycle.Status)); - RedisController.WriteCurrentProcessStatus(1,CMSConnectConstants.ConvertThermoToConnectProcessStatus(currProdCycle.Status)); - RedisController.WriteCurrentProcessMode(1,CMSConnectConstants.ConvertThermoToConnectProcessMode(currProdCycle.Mode)); + RedisController.WriteCurrentProcessStatus(1, CMSConnectConstants.ConvertThermoToConnectProcessStatus(currProdCycle.Status)); + RedisController.WriteCurrentProcessMode(1, CMSConnectConstants.ConvertThermoToConnectProcessMode(currProdCycle.Mode)); } @@ -551,6 +552,65 @@ namespace Thermo.Active.Listeners.SignalR } } + public static void SendThermoChannelsIoData(object channelsIoData) + { + DTOChannelsIOVal currChannelsIoData = channelsIoData as DTOChannelsIOVal; + DTOChannelsIOVal diffChannelsData = new DTOChannelsIOVal(); + + // processing differenze DI + foreach (var item in currChannelsIoData.DI) + { + // recupero + var element = LastChannelsIoData.DI.Find(x => x.Id == item.Id); + if ((element == null) || (!item.Value.Equals(element.Value))) + { + diffChannelsData.DI.Add(item); + } + } + // processing differenze DO + foreach (var item in currChannelsIoData.DO) + { + // recupero + var element = LastChannelsIoData.DO.Find(x => x.Id == item.Id); + if ((element == null) || (!item.Value.Equals(element.Value))) + { + diffChannelsData.DO.Add(item); + } + } + // processing differenze AI + foreach (var item in currChannelsIoData.AI) + { + // recupero + var element = LastChannelsIoData.AI.Find(x => x.Id == item.Id); + if ((element == null) || (!item.Value.Equals(element.Value))) + { + diffChannelsData.AI.Add(item); + } + } + // processing differenze AO + foreach (var item in currChannelsIoData.AO) + { + // recupero + var element = LastChannelsIoData.AO.Find(x => x.Id == item.Id); + if ((element == null) || (!item.Value.Equals(element.Value))) + { + diffChannelsData.AO.Add(item); + } + } + + // se ho differenze invio! + if (diffChannelsData.DI.Count + diffChannelsData.DO.Count + diffChannelsData.AI.Count + diffChannelsData.AO.Count > 0) + { + // salvo update CLONANDO + LastChannelsIoData.DI = currChannelsIoData.DI; + LastChannelsIoData.DO = currChannelsIoData.DO; + LastChannelsIoData.AI = currChannelsIoData.AI; + LastChannelsIoData.AO = currChannelsIoData.AO; + var context = GlobalHost.ConnectionManager.GetHubContext(); + context.Clients.Group("ncData").channelsIoVal(diffChannelsData); + } + } + public static void SetGatewayRebootStatus(object status) { string msg = status.ToString(); diff --git a/Thermo.Active/Listeners/SignalRStaticObjects.cs b/Thermo.Active/Listeners/SignalRStaticObjects.cs index df07bf23..82d3e34f 100644 --- a/Thermo.Active/Listeners/SignalRStaticObjects.cs +++ b/Thermo.Active/Listeners/SignalRStaticObjects.cs @@ -1,9 +1,11 @@ using CMS_CORE_Library.Models; +using System; using System.Collections.Generic; using Thermo.Active.Model.DTOModels; using Thermo.Active.Model.DTOModels.AlarmModels; using Thermo.Active.Model.DTOModels.Scada; using Thermo.Active.Model.DTOModels.ThAxes; +using Thermo.Active.Model.DTOModels.ThIO; using Thermo.Active.Model.DTOModels.ThModules; using Thermo.Active.Model.DTOModels.ThProd; using Thermo.Active.Model.DTOModels.ThRecipe; @@ -46,6 +48,8 @@ namespace Thermo.Active.Listeners public static DTOThermoPanelProd LastProdPanelData = new DTOThermoPanelProd(); // Oggetti per assi THERMO public static Dictionary LastAxisInfoData = new Dictionary(); + // Oggetti x Valori Channels IO + public static DTOChannelsIOVal LastChannelsIoData = new DTOChannelsIOVal(); public static bool LastIsNcConnected = false; } diff --git a/Thermo.Active/Properties/AssemblyInfo.cs b/Thermo.Active/Properties/AssemblyInfo.cs index be6f7282..5f2eb65b 100644 --- a/Thermo.Active/Properties/AssemblyInfo.cs +++ b/Thermo.Active/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("CMS S.P.A.")] [assembly: AssemblyProduct("Thermo Active Server")] -[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyCopyright("Copyright � 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("0.110.146")] +[assembly: AssemblyVersion("1.1.157")] diff --git a/Thermo.Active/Thermo.Active.csproj b/Thermo.Active/Thermo.Active.csproj index 853d2397..0e2f0e06 100644 --- a/Thermo.Active/Thermo.Active.csproj +++ b/Thermo.Active/Thermo.Active.csproj @@ -223,6 +223,7 @@ + diff --git a/UpgradeLog.htm b/UpgradeLog.htm new file mode 100644 index 00000000..864f6498 Binary files /dev/null and b/UpgradeLog.htm differ