diff --git a/Barsik.csproj b/Barsik.csproj
index 3b9bef7..07a09e3 100644
--- a/Barsik.csproj
+++ b/Barsik.csproj
@@ -9,6 +9,7 @@
+
diff --git a/Logging.cs b/Logging.cs
new file mode 100644
index 0000000..810c1b0
--- /dev/null
+++ b/Logging.cs
@@ -0,0 +1,16 @@
+using NLog;
+
+namespace Barsik
+{
+ public static class Logging
+ {
+ public static void Setup()
+ {
+ var config = new NLog.Config.LoggingConfiguration();
+
+ var logConsole = new NLog.Targets.ConsoleTarget("logconsole");
+ config.AddRuleForAllLevels(logConsole);
+ LogManager.Configuration = config;
+ }
+ }
+}
diff --git a/MDStatMonitoringUnit.cs b/MDStatMonitoringUnit.cs
new file mode 100644
index 0000000..53ccbae
--- /dev/null
+++ b/MDStatMonitoringUnit.cs
@@ -0,0 +1,66 @@
+using IniParser.Model;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading;
+
+namespace Barsik
+{
+ ///
+ /// Monitors configured RAID array status via /proc/mdstat
+ /// Will fill registers info starting from baseAddress with
+ /// disk status (0 - fail, 1 - up) in following order:
+ /// raid #0 disk #0
+ /// raid #0 disk #1
+ /// ...
+ /// raid #0 disk #N
+ /// raid #1 disk #0
+ /// ...
+ /// etc
+ ///
+ class MDStatMonitoringUnit : BaseMonitoringUnit
+ {
+ public MDStatMonitoringUnit(SectionData config, RegistersContainer registers) : base(config, registers)
+ {
+ }
+
+ public override void Run()
+ {
+ while (true)
+ {
+ 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 []
+ currentAddress += FillDiskStatus(currentAddress, lines[i + 1]);
+ i += 2;
+ }
+ else i++;
+ }
+ Thread.Sleep(2000);
+ }
+ }
+
+ private int FillDiskStatus(int address, string v)
+ {
+ int last_pos = v.LastIndexOf(']');
+ int pos;
+ for (pos = last_pos; pos > 0 && v[pos] != '['; pos--) ;
+ if (v[pos] != '[') return 0;
+ for (int i = pos + 1; i < last_pos; i++)
+ {
+ if (v[i] == 'U') registers[address + i - (pos + 1)] = 1;
+ else registers[address + i - (pos + 1)] = 1;
+ }
+ return last_pos - pos - 1;
+ }
+ }
+}
diff --git a/ModbusServer.cs b/ModbusServer.cs
index 9ea8469..0d49f18 100644
--- a/ModbusServer.cs
+++ b/ModbusServer.cs
@@ -1,4 +1,5 @@
-using System;
+using NLog;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
@@ -20,11 +21,14 @@ namespace Barsik
internal void Run()
{
+ var logger = LogManager.GetCurrentClassLogger();
+ logger.Info("Accepting connections on port " + Port);
var listener = new TcpListener(IPAddress.Any, Port);
listener.Start();
while (true)
{
var client = listener.AcceptTcpClient();
+ logger.Info("Accepted connection from " + client.Client.RemoteEndPoint);
var worker = new ModbusServerWorker(registers, client);
Thread clientThread = new Thread(new ThreadStart(worker.Run));
clientThread.Start();
@@ -38,6 +42,7 @@ namespace Barsik
private RegistersContainer registers;
private TcpClient client;
+ private static Logger logger = LogManager.GetCurrentClassLogger();
public ModbusServerWorker(RegistersContainer registers, TcpClient client)
{
this.registers = registers;
@@ -52,6 +57,7 @@ namespace Barsik
public void Run()
{
+ logger.Debug("Working with client");
received = new List();
try
{
@@ -63,11 +69,13 @@ namespace Barsik
readCompleted.Reset();
stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(this.OnReadCompleted), this);
readCompleted.WaitOne();
- if (currentReceived < 0)
+ if (currentReceived <= 0)
{
+ logger.Debug("Received no data, closing ");
client.Close();
return;
}
+ logger.Debug("ProcessReceivedData of " + received.Count);
ProcessReceivedData();
}
}
@@ -75,6 +83,11 @@ namespace Barsik
{
client.Close();
}
+ finally
+ {
+ if (stream != null) stream.Dispose();
+ client.Dispose();
+ }
}
private void ProcessReceivedData()
@@ -123,12 +136,20 @@ namespace Barsik
private void OnReadCompleted(IAsyncResult ar)
{
- currentReceived = stream.EndRead(ar);
- lock (received)
+ try
{
- for (int i = 0; i < currentReceived; i++) received.Add(buffer[i]);
+ currentReceived = stream.EndRead(ar);
+ lock (received)
+ {
+ for (int i = 0; i < currentReceived; i++) received.Add(buffer[i]);
+ }
+ readCompleted.Set();
+ }
+ catch (IOException e)
+ {
+ logger.Info("Error receiving " + e);
+ currentReceived = -1;
}
- readCompleted.Set();
}
}
}
diff --git a/MonitoringUnit.cs b/MonitoringUnit.cs
index a38e1a1..88c5f4c 100644
--- a/MonitoringUnit.cs
+++ b/MonitoringUnit.cs
@@ -1,8 +1,5 @@
using IniParser.Model;
using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading;
namespace Barsik
{
@@ -31,66 +28,15 @@ namespace Barsik
{
return new SensorsMonitoringUnit(config, registers);
}
- else
+ else if (type == "MDStat")
+ {
+ return new MDStatMonitoringUnit(config, registers);
+ }
+ else
{
Console.WriteLine("Unknown unit type " + type);
return null;
}
}
}
-
- class SensorsMonitoringUnit : BaseMonitoringUnit
- {
- public SensorsMonitoringUnit(SectionData config, RegistersContainer registers) : base(config, registers)
- {
- }
-
- public override void Run()
- {
- var stringToFind = config.Keys["String"];
- var multiplier = float.Parse(config.Keys["Multiplier"]);
- while (true)
- {
- System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("sensors");
- procStartInfo.RedirectStandardOutput = true;
- procStartInfo.UseShellExecute = false;
- procStartInfo.CreateNoWindow = true;
- System.Diagnostics.Process proc = new System.Diagnostics.Process();
- proc.StartInfo = procStartInfo;
- proc.Start();
- while (!proc.HasExited)
- {
- Thread.Sleep(100);
- }
- string result = proc.StandardOutput.ReadToEnd();
- var lines = result.Split('\n');
- for (int i = 0; i < lines.Length; i++)
- {
- if (lines[i].Contains(stringToFind))
- {
- var value = ExtractValue(lines[i]);
- registers[baseAddress] = (int)(value * multiplier);
- }
- }
- }
- }
-
- bool Good(char c)
- {
- if (c == '-' || c == '+' || c == '.') return true;
- if (c >= '0' && c <= '9') return true;
- return false;
- }
-
- private float ExtractValue(string v)
- {
- int pos = 0;
- while (pos < v.Length && !Good(v[pos])) pos++;
- int pos2 = pos;
- while (pos2 < v.Length && Good(v[pos2])) pos2++;
- float res = 0;
- float.TryParse(v.Substring(pos, pos2 - pos), out res);
- return res;
- }
- }
}
diff --git a/Program.cs b/Program.cs
index d7da284..083a25e 100644
--- a/Program.cs
+++ b/Program.cs
@@ -1,6 +1,8 @@
using IniParser;
-using System;
+using NLog;
using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
using System.Threading;
namespace Barsik
@@ -10,14 +12,20 @@ namespace Barsik
private static RegistersContainer registers;
private static List units;
- static void Main(string[] args)
+ private static readonly Logger logger = LogManager.GetCurrentClassLogger();
+
+ static void Main(string[] args)
{
+ Logging.Setup();
+ logger.Info("Barsik monitoring service starting");
+
registers = new RegistersContainer();
var server = new ModbusServer(registers);
units = new List();
var configParser = new FileIniDataParser();
- var data = configParser.ReadFile("barsik.ini");
+ string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ var data = configParser.ReadFile(Path.Combine(assemblyFolder, "barsik.ini"));
foreach (var section in data.Sections)
{
if (section.SectionName.StartsWith("Unit"))
@@ -31,12 +39,10 @@ namespace Barsik
}
}
}
- server.Port = int.Parse(data["Server"]["Port"]);
- while (true)
- {
- server.Run();
- Thread.Sleep(1000);
- }
- }
+ logger.Info("Configuration loaded, we have " + units.Count + " units");
+ server.Port = int.Parse(data["Server"]["Port"]);
+ server.Run();
+ logger.Info("Exiting");
+ }
}
}
diff --git a/Properties/PublishProfiles/FolderProfile.pubxml b/Properties/PublishProfiles/FolderProfile.pubxml
index 3280d7f..bc9f40e 100644
--- a/Properties/PublishProfiles/FolderProfile.pubxml
+++ b/Properties/PublishProfiles/FolderProfile.pubxml
@@ -10,7 +10,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
netcoreapp2.1
bin\Debug\netcoreapp2.1\publish\
linux-x64
- true
- <_IsPortable>false
+ false
+ <_IsPortable>true
\ No newline at end of file
diff --git a/SensorsMonitoringUnit.cs b/SensorsMonitoringUnit.cs
new file mode 100644
index 0000000..b5cd164
--- /dev/null
+++ b/SensorsMonitoringUnit.cs
@@ -0,0 +1,88 @@
+using IniParser.Model;
+using System;
+using System.Threading;
+
+namespace Barsik
+{
+ ///
+ /// Parses output of "sensors" command and puts CPU core temperatures into
+ /// registers
+ ///
+ class SensorsMonitoringUnit : BaseMonitoringUnit
+ {
+ const string CORE_STR = "Core{0} Temp:";
+
+ public SensorsMonitoringUnit(SectionData config, RegistersContainer registers) : base(config, registers)
+ {
+ }
+
+ public override void Run()
+ {
+ int cores = int.Parse(config.Keys["Cores"]);
+ var multiplier = float.Parse(config.Keys["Multiplier"]);
+ while (true)
+ {
+ System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("sensors");
+ procStartInfo.RedirectStandardOutput = true;
+ procStartInfo.UseShellExecute = false;
+ procStartInfo.CreateNoWindow = true;
+ using (System.Diagnostics.Process proc = new System.Diagnostics.Process())
+ {
+ proc.StartInfo = procStartInfo;
+ proc.Start();
+ while (!proc.HasExited)
+ {
+ Thread.Sleep(100);
+ }
+ string result = proc.StandardOutput.ReadToEnd();
+ var lines = result.Split('\n');
+ // lines in output will be of format:
+ // Core0 Temp: +29.0°C
+ // and there could be several lines for one core
+ // so let's take all of them and use maximal value as core temp
+ float[] t = new float[cores];
+ for (int i = 0; i < cores; i++)
+ {
+ t[i] = 0;
+ }
+ for (int i = 0; i < lines.Length; i++)
+ {
+ for (int j = 0; j < cores; j++)
+ {
+ string toFind = string.Format(CORE_STR, j);
+ if (lines[i].Contains(toFind))
+ {
+ var value = ExtractValue(lines[i].Substring(toFind.Length));
+ t[j] = Math.Max(t[j], value);
+ }
+ }
+ }
+ // fill register data starting from baseAddress
+ for (int i = 0; i < cores; i++)
+ {
+ registers[baseAddress + i] = (int)(t[i] * multiplier);
+ }
+ }
+ Thread.Sleep(2000);
+ }
+ }
+
+ bool Good(char c)
+ {
+ if (c == '-' || c == '+' || c == '.') return true;
+ if (c >= '0' && c <= '9') return true;
+ return false;
+ }
+
+ private float ExtractValue(string v)
+ {
+ int pos = 0;
+ while (pos < v.Length && !Good(v[pos])) pos++;
+ int pos2 = pos;
+ while (pos2 < v.Length && Good(v[pos2])) pos2++;
+ float res;
+ float.TryParse(v.Substring(pos, pos2 - pos), out res);
+ return res;
+ }
+ }
+}
\ No newline at end of file
diff --git a/barsik.ini.sample b/barsik.ini.sample
index f9136f2..71280e9 100644
--- a/barsik.ini.sample
+++ b/barsik.ini.sample
@@ -4,5 +4,9 @@ Port = 4500
[Unit_CPU]
Type = Sensors
BaseAddress = 1
-String = CPU Temperature
-Multiplier = 100
\ No newline at end of file
+Multiplier = 100
+Cores = 2
+
+[Unit_RAID]
+Type = MDStat
+BaseAddress = 3
diff --git a/barsik.service b/barsik.service
new file mode 100644
index 0000000..67b0043
--- /dev/null
+++ b/barsik.service
@@ -0,0 +1,14 @@
+[Unit]
+Description=Barsik monitoring service
+After=network.target
+
+[Service]
+ExecStart=dotnet /root/barsik/Barsik.dll
+User=root
+Group=root
+RuntimeDirectory=/root/barsik
+RuntimeDirectoryMode=0755
+ProtectHome=false
+
+[Install]
+WantedBy=multi-user.target