Added OpenHAB config files generation and command line command to use that. Also added transforms for OpenHAB integration.

This commit is contained in:
ftc
2021-01-08 16:55:19 +03:00
parent b9a1084d7a
commit 2e0b96ff7a
10 changed files with 183 additions and 32 deletions
+60
View File
@@ -21,8 +21,68 @@ namespace Barsik
/// </summary> /// </summary>
class MDStatMonitoringUnit : BaseMonitoringUnit class MDStatMonitoringUnit : BaseMonitoringUnit
{ {
List<int> knownRaids;
public MDStatMonitoringUnit(JsonElement config, RegistersContainer registers) : base(config, registers) public MDStatMonitoringUnit(JsonElement config, RegistersContainer registers) : base(config, registers)
{ {
knownRaids = new List<int>();
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\" <switch>", 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() public override void Run()
+7 -4
View File
@@ -1,25 +1,28 @@
using System; using System;
using System.Text;
using System.Text.Json; using System.Text.Json;
namespace Barsik namespace Barsik
{ {
class BaseMonitoringUnit abstract class BaseMonitoringUnit
{ {
protected JsonElement config; protected JsonElement config;
protected RegistersContainer registers; protected RegistersContainer registers;
protected int baseAddress; protected int baseAddress;
protected string unitName;
public BaseMonitoringUnit(JsonElement config, RegistersContainer registers) public BaseMonitoringUnit(JsonElement config, RegistersContainer registers)
{ {
this.config = config.Clone(); this.config = config.Clone();
this.registers = registers; this.registers = registers;
baseAddress = config.GetProperty("BaseAddress").GetInt32(); 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) public static BaseMonitoringUnit Create(JsonElement config, RegistersContainer registers)
{ {
+71 -23
View File
@@ -1,10 +1,12 @@
using NLog; using NLog;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.CommandLine;
using System.CommandLine.Invocation; using System.CommandLine.Invocation;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
@@ -14,6 +16,7 @@ namespace Barsik
{ {
private static RegistersContainer registers; private static RegistersContainer registers;
private static List<BaseMonitoringUnit> units; private static List<BaseMonitoringUnit> units;
private static JsonDocument configJson;
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
@@ -27,42 +30,87 @@ namespace Barsik
}; };
runCommand.Handler = CommandHandler.Create<string>(RunService); runCommand.Handler = CommandHandler.Create<string>(RunService);
rootCommand.AddCommand(runCommand); rootCommand.AddCommand(runCommand);
var openhabConfigCommand = new Command("openhab-config")
{
new Option<string>("--config", () => "", "Path to configuration file")
};
openhabConfigCommand.Handler = CommandHandler.Create<string>(GenerateOpenhabConfig);
rootCommand.AddCommand(openhabConfigCommand);
// Init common things
registers = new RegistersContainer();
//
rootCommand.InvokeAsync(args).Wait(); rootCommand.InvokeAsync(args).Wait();
} }
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);
return JsonDocument.Parse(configStr);
}
private static List<BaseMonitoringUnit> LoadUnits(JsonDocument config)
{
var units = new List<BaseMonitoringUnit>();
var root = config.RootElement;
foreach (var unitElement in root.GetProperty("Units").EnumerateArray())
{
var unit = BaseMonitoringUnit.Create(unitElement, registers);
if (unit != null)
{
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) static void RunService(string config)
{ {
Logging.Setup(); Logging.Setup();
logger.Info("Barsik monitoring service starting"); logger.Info("Barsik monitoring service starting");
registers = new RegistersContainer(); configJson = LoadConfig(config);
var server = new ModbusServer(registers); units = LoadUnits(configJson);
units = new List<BaseMonitoringUnit>(); foreach (var unit in units)
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))
{
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)
{
units.Add(unit);
Thread unitThread = new Thread(new ThreadStart(unit.Run)); Thread unitThread = new Thread(new ThreadStart(unit.Run));
unitThread.Start(); unitThread.Start();
} }
}
}
logger.Info("Configuration loaded, we have " + units.Count + " units"); 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(); server.Run();
logger.Info("Exiting"); logger.Info("Exiting");
} }
@@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Any CPU</Platform> <Platform>Any CPU</Platform>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<PublishDir>bin\Debug\netcoreapp2.1\publish\</PublishDir> <PublishDir>bin\Publish</PublishDir>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier> <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<SelfContained>false</SelfContained> <SelfContained>false</SelfContained>
<_IsPortable>true</_IsPortable> <_IsPortable>true</_IsPortable>
+33 -3
View File
@@ -1,4 +1,5 @@
using System; using System;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
@@ -11,15 +12,16 @@ namespace Barsik
class SensorsMonitoringUnit : BaseMonitoringUnit class SensorsMonitoringUnit : BaseMonitoringUnit
{ {
const string CORE_STR = "Core{0} Temp:"; const string CORE_STR = "Core{0} Temp:";
const float MULTIPLIER = 100;
int cores;
public SensorsMonitoringUnit(JsonElement config, RegistersContainer registers) : base(config, registers) public SensorsMonitoringUnit(JsonElement config, RegistersContainer registers) : base(config, registers)
{ {
cores = config.GetProperty("Cores").GetInt32();
} }
public override void Run() public override void Run()
{ {
int cores = config.GetProperty("Cores").GetInt32();
var multiplier = config.GetProperty("Multiplier").GetDouble();
while (true) while (true)
{ {
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("sensors"); System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("sensors");
@@ -60,7 +62,7 @@ namespace Barsik
// fill register data starting from baseAddress // fill register data starting from baseAddress
for (int i = 0; i < cores; i++) for (int i = 0; i < cores; i++)
{ {
registers[baseAddress + i] = (int)(t[i] * multiplier); registers[baseAddress + i] = (int)(t[i] * MULTIPLIER);
} }
} }
Thread.Sleep(2000); Thread.Sleep(2000);
@@ -84,5 +86,33 @@ namespace Barsik
float.TryParse(v.Substring(pos, pos2 - pos), out res); float.TryParse(v.Substring(pos, pos2 - pos), out res);
return 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]\" <temperature>", name, i));
config.Append(" {channel=\"");
config.AppendFormat("modbus:data:{0}:{1}:Core{2}Temp:number", name, unitName, i);
config.Append("\"}\n");
}
}
} }
} }
+2 -1
View File
@@ -1,11 +1,12 @@
{ {
"Name": "My_Server",
"IP": "192.168.0.5",
"Port": 4500, "Port": 4500,
"Units": [ "Units": [
{ {
"Name": "CPU", "Name": "CPU",
"Type": "Sensors", "Type": "Sensors",
"BaseAddress": 1, "BaseAddress": 1,
"Multiplier": 100,
"Cores": 2 "Cores": 2
}, },
{ {
+1 -1
View File
@@ -3,7 +3,7 @@ Description=Barsik monitoring service
After=network.target After=network.target
[Service] [Service]
ExecStart=dotnet /root/barsik/Barsik.dll ExecStart=/usr/bin/dotnet /root/barsik/Barsik.dll run
User=root User=root
Group=root Group=root
RuntimeDirectory=/root/barsik RuntimeDirectory=/root/barsik
+3
View File
@@ -0,0 +1,3 @@
(function(i) {
return i / 100;
})(input)
+3
View File
@@ -0,0 +1,3 @@
(function(i) {
return i * 100;
})(input)
+3
View File
@@ -0,0 +1,3 @@
Transformations should be put into `/opt/openhab2/conf/transform` to work.
Some of monitoring units use them in configuration.