diff --git a/MDStatMonitoringUnit.cs b/MDStatMonitoringUnit.cs index 88182ef..eedb397 100644 --- a/MDStatMonitoringUnit.cs +++ b/MDStatMonitoringUnit.cs @@ -21,8 +21,68 @@ namespace Barsik /// class MDStatMonitoringUnit : BaseMonitoringUnit { + List knownRaids; + public MDStatMonitoringUnit(JsonElement config, RegistersContainer registers) : base(config, registers) { + knownRaids = new List(); + int currentAddress = baseAddress; + // the file is not so big, so read it completely + var lines = File.ReadAllLines("/proc/mdstat"); + for (int i = 0; i < lines.Length;) + { + if (lines[i].StartsWith("md") && i < lines.Length - 1) + { + // we found device description + // second line of it is as follows: + // 2930264896 blocks super 1.0 [2/2] [UU] + // we need last characters in brackets [] + int last_pos = lines[i + 1].LastIndexOf(']'); + int pos; + for (pos = last_pos; pos > 0 && lines[i + 1][pos] != '['; pos--) ; + if (lines[i + 1][pos] != '[') knownRaids.Add(0); + else knownRaids.Add(last_pos - pos - 1); + i += 2; + } + else i++; + } + } + + public override void GenerateItemsConfig(string name, StringBuilder config) + { + for (int i = 0; i < knownRaids.Count; i++) + { + for (int j = 0; j < knownRaids[i]; j++) + { + config.Append(string.Format("Switch {0}_RAID{1}Disk{2} \"{0} RAID{1} Disk{2} status\" ", name, i, j)); + config.Append(" {channel=\""); + config.AppendFormat("modbus:data:{0}:{1}:", name, unitName); + config.AppendFormat("RAID{0}Disk{1}:switch", name, i, j); + config.Append("\"}\n"); + } + } + } + + public override void GenerateThingsConfig(StringBuilder config) + { + int diskAmount = 0; + foreach (var size in knownRaids) diskAmount += size; + config.Append("\tBridge poller "); + config.Append(unitName); + config.AppendFormat("[start = {0}, length = {1}, type = \"holding\", refresh = 500] {{\n", baseAddress, diskAmount); + int currentAddress = baseAddress; + for (int i = 0; i < knownRaids.Count; i++) + { + for (int j = 0; j < knownRaids[i]; j++) + { + string name = string.Format("RAID{0}Disk{1}", i, j); + config.Append("\t\tThing data "); + config.Append(name); + config.AppendFormat("[readStart = \"{0}\", readValueType = \"int16\"]\n", currentAddress); + currentAddress++; + } + } + config.Append("\t}\n"); } public override void Run() diff --git a/MonitoringUnit.cs b/MonitoringUnit.cs index 23caff9..737237c 100644 --- a/MonitoringUnit.cs +++ b/MonitoringUnit.cs @@ -1,25 +1,28 @@ using System; +using System.Text; using System.Text.Json; namespace Barsik { - class BaseMonitoringUnit + abstract class BaseMonitoringUnit { protected JsonElement config; protected RegistersContainer registers; protected int baseAddress; + protected string unitName; public BaseMonitoringUnit(JsonElement config, RegistersContainer registers) { this.config = config.Clone(); this.registers = registers; baseAddress = config.GetProperty("BaseAddress").GetInt32(); + unitName = config.GetProperty("Name").GetString(); } - public virtual void Run() - { - } + public abstract void Run(); + public abstract void GenerateThingsConfig(StringBuilder config); + public abstract void GenerateItemsConfig(string name, StringBuilder config); public static BaseMonitoringUnit Create(JsonElement config, RegistersContainer registers) { diff --git a/Program.cs b/Program.cs index aa7a99a..75b917f 100644 --- a/Program.cs +++ b/Program.cs @@ -1,10 +1,12 @@ using NLog; +using System; using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Invocation; using System.Globalization; using System.IO; using System.Reflection; +using System.Text; using System.Text.Json; using System.Threading; @@ -14,6 +16,7 @@ namespace Barsik { private static RegistersContainer registers; private static List units; + private static JsonDocument configJson; private static readonly Logger logger = LogManager.GetCurrentClassLogger(); @@ -27,42 +30,87 @@ namespace Barsik }; runCommand.Handler = CommandHandler.Create(RunService); rootCommand.AddCommand(runCommand); + var openhabConfigCommand = new Command("openhab-config") + { + new Option("--config", () => "", "Path to configuration file") + }; + openhabConfigCommand.Handler = CommandHandler.Create(GenerateOpenhabConfig); + rootCommand.AddCommand(openhabConfigCommand); + // Init common things + registers = new RegistersContainer(); + // rootCommand.InvokeAsync(args).Wait(); } - static void RunService(string config) - { - Logging.Setup(); - logger.Info("Barsik monitoring service starting"); - - registers = new RegistersContainer(); - var server = new ModbusServer(registers); - units = new List(); + private static JsonDocument LoadConfig(string config) + { if (string.IsNullOrEmpty(config)) { string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); config = Path.Combine(assemblyFolder, "barsik.json"); } string configStr = File.ReadAllText(config); - int port = 0; - using (JsonDocument doc = JsonDocument.Parse(configStr)) + return JsonDocument.Parse(configStr); + } + + private static List LoadUnits(JsonDocument config) + { + var units = new List(); + var root = config.RootElement; + foreach (var unitElement in root.GetProperty("Units").EnumerateArray()) { - var root = doc.RootElement; - port = root.GetProperty("Port").GetInt32(); - foreach (var unitElement in root.GetProperty("Units").EnumerateArray()) + var unit = BaseMonitoringUnit.Create(unitElement, registers); + if (unit != null) { - var unit = BaseMonitoringUnit.Create(unitElement, registers); - if (unit != null) - { - units.Add(unit); - Thread unitThread = new Thread(new ThreadStart(unit.Run)); - unitThread.Start(); - } + units.Add(unit); } } + return units; + } + + static void GenerateOpenhabConfig(string config) + { + configJson = LoadConfig(config); + units = LoadUnits(configJson); + var root = configJson.RootElement; + string name = root.GetProperty("Name").GetString(); + + StringBuilder thingsConfig = new StringBuilder(); + StringBuilder itemsConfig = new StringBuilder(); + thingsConfig.AppendFormat("Bridge modbus:tcp:{0} [ host=\"{1}\", port={2}, id=1 ] {{\n", + name, + root.GetProperty("IP").GetString(), + root.GetProperty("Port").GetInt32()); + // generate units configs + foreach (var unit in units) + { + unit.GenerateThingsConfig(thingsConfig); + unit.GenerateItemsConfig(name, itemsConfig); + } + // + thingsConfig.Append("}\n"); + + File.WriteAllText(name + ".things", thingsConfig.ToString()); + File.WriteAllText(name + ".items", itemsConfig.ToString()); + } + + static void RunService(string config) + { + Logging.Setup(); + logger.Info("Barsik monitoring service starting"); + + configJson = LoadConfig(config); + units = LoadUnits(configJson); + foreach (var unit in units) + { + Thread unitThread = new Thread(new ThreadStart(unit.Run)); + unitThread.Start(); + } + logger.Info("Configuration loaded, we have " + units.Count + " units"); - server.Port = port; + var server = new ModbusServer(registers); + server.Port = configJson.RootElement.GetProperty("Port").GetInt32(); server.Run(); logger.Info("Exiting"); } diff --git a/Properties/PublishProfiles/FolderProfile.pubxml b/Properties/PublishProfiles/FolderProfile.pubxml index bc9f40e..0abf88a 100644 --- a/Properties/PublishProfiles/FolderProfile.pubxml +++ b/Properties/PublishProfiles/FolderProfile.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release Any CPU - netcoreapp2.1 - bin\Debug\netcoreapp2.1\publish\ + netcoreapp3.1 + bin\Publish linux-x64 false <_IsPortable>true diff --git a/SensorsMonitoringUnit.cs b/SensorsMonitoringUnit.cs index 4c4fcca..86a6447 100644 --- a/SensorsMonitoringUnit.cs +++ b/SensorsMonitoringUnit.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using System.Text.Json; using System.Threading; @@ -11,15 +12,16 @@ namespace Barsik class SensorsMonitoringUnit : BaseMonitoringUnit { const string CORE_STR = "Core{0} Temp:"; + const float MULTIPLIER = 100; + int cores; public SensorsMonitoringUnit(JsonElement config, RegistersContainer registers) : base(config, registers) { + cores = config.GetProperty("Cores").GetInt32(); } public override void Run() { - int cores = config.GetProperty("Cores").GetInt32(); - var multiplier = config.GetProperty("Multiplier").GetDouble(); while (true) { System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("sensors"); @@ -60,7 +62,7 @@ namespace Barsik // fill register data starting from baseAddress for (int i = 0; i < cores; i++) { - registers[baseAddress + i] = (int)(t[i] * multiplier); + registers[baseAddress + i] = (int)(t[i] * MULTIPLIER); } } Thread.Sleep(2000); @@ -84,5 +86,33 @@ namespace Barsik float.TryParse(v.Substring(pos, pos2 - pos), out res); return res; } + + public override void GenerateThingsConfig(StringBuilder config) + { + config.Append("\tBridge poller "); + config.Append(unitName); + config.AppendFormat("[start = {0}, length = {1}, type = \"holding\", refresh = 500] {{\n", baseAddress, cores); + int currentAddress = baseAddress; + for (int i = 0; i < cores; i++) + { + string name = string.Format("Core{0}Temp", i); + config.Append("\t\tThing data "); + config.Append(name); + config.AppendFormat("[readStart = \"{0}\", readValueType = \"int16\", readTransform=\"JS(divide100.js)\"]\n", currentAddress); + currentAddress++; + } + config.Append("\t}\n"); + } + + public override void GenerateItemsConfig(string name, StringBuilder config) + { + for (int i = 0; i < cores; i++) + { + config.Append(string.Format("Number {0}_Core{1}Temp \"{0} CPU Core{1} [%.1f °C]\" ", name, i)); + config.Append(" {channel=\""); + config.AppendFormat("modbus:data:{0}:{1}:Core{2}Temp:number", name, unitName, i); + config.Append("\"}\n"); + } + } } } \ No newline at end of file diff --git a/barsik.json.sample b/barsik.json.sample index 694665f..b4d190f 100644 --- a/barsik.json.sample +++ b/barsik.json.sample @@ -1,11 +1,12 @@ { + "Name": "My_Server", + "IP": "192.168.0.5", "Port": 4500, "Units": [ { "Name": "CPU", "Type": "Sensors", "BaseAddress": 1, - "Multiplier": 100, "Cores": 2 }, { diff --git a/barsik.service b/barsik.service index 67b0043..271bb72 100644 --- a/barsik.service +++ b/barsik.service @@ -3,7 +3,7 @@ Description=Barsik monitoring service After=network.target [Service] -ExecStart=dotnet /root/barsik/Barsik.dll +ExecStart=/usr/bin/dotnet /root/barsik/Barsik.dll run User=root Group=root RuntimeDirectory=/root/barsik diff --git a/openhab/transform/divide100.js b/openhab/transform/divide100.js new file mode 100644 index 0000000..5dba846 --- /dev/null +++ b/openhab/transform/divide100.js @@ -0,0 +1,3 @@ +(function(i) { + return i / 100; +})(input) \ No newline at end of file diff --git a/openhab/transform/multiply100.js b/openhab/transform/multiply100.js new file mode 100644 index 0000000..3b08d27 --- /dev/null +++ b/openhab/transform/multiply100.js @@ -0,0 +1,3 @@ +(function(i) { + return i * 100; +})(input) \ No newline at end of file diff --git a/openhab/transform/readme.md b/openhab/transform/readme.md new file mode 100644 index 0000000..a16e853 --- /dev/null +++ b/openhab/transform/readme.md @@ -0,0 +1,3 @@ +Transformations should be put into `/opt/openhab2/conf/transform` to work. + +Some of monitoring units use them in configuration. \ No newline at end of file