commit 092cba1ceebc346c509f3e4b7a94ae2f7636d4ca Author: Denis Denisov Date: Tue Aug 25 23:34:18 2020 +0300 Initial commit. Project, simple monitoring of CPU temp via "sensors" command diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8f710c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vs/ +bin/ +obj/ +Barsik.csproj.user diff --git a/Barsik.csproj b/Barsik.csproj new file mode 100644 index 0000000..3b9bef7 --- /dev/null +++ b/Barsik.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp2.1 + + Barsik.Program + + + + + + + diff --git a/Barsik.sln b/Barsik.sln new file mode 100644 index 0000000..7fdd03f --- /dev/null +++ b/Barsik.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29209.62 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Barsik", "Barsik.csproj", "{71549955-D26F-4B4F-BC3C-0D144DEAA2F7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {71549955-D26F-4B4F-BC3C-0D144DEAA2F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71549955-D26F-4B4F-BC3C-0D144DEAA2F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71549955-D26F-4B4F-BC3C-0D144DEAA2F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71549955-D26F-4B4F-BC3C-0D144DEAA2F7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F1F24D24-CD2E-47A6-87E0-3EBCD13C4D58} + EndGlobalSection +EndGlobal diff --git a/ModbusServer.cs b/ModbusServer.cs new file mode 100644 index 0000000..9ea8469 --- /dev/null +++ b/ModbusServer.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace Barsik +{ + class ModbusServer + { + private RegistersContainer registers; + + public int Port { get; set; } + + public ModbusServer(RegistersContainer registers) + { + this.registers = registers; + } + + internal void Run() + { + var listener = new TcpListener(IPAddress.Any, Port); + listener.Start(); + while (true) + { + var client = listener.AcceptTcpClient(); + var worker = new ModbusServerWorker(registers, client); + Thread clientThread = new Thread(new ThreadStart(worker.Run)); + clientThread.Start(); + } + } + } + + class ModbusServerWorker + { + const int RECEIVE_TIMEOUT = 10000; // 10 seconds + + private RegistersContainer registers; + private TcpClient client; + public ModbusServerWorker(RegistersContainer registers, TcpClient client) + { + this.registers = registers; + this.client = client; + } + + List received; + ManualResetEvent readCompleted; + int currentReceived; + NetworkStream stream; + byte[] buffer = new byte[256]; + + public void Run() + { + received = new List(); + try + { + client.ReceiveTimeout = RECEIVE_TIMEOUT; + stream = client.GetStream(); + readCompleted = new ManualResetEvent(false); + while (true) + { + readCompleted.Reset(); + stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(this.OnReadCompleted), this); + readCompleted.WaitOne(); + if (currentReceived < 0) + { + client.Close(); + return; + } + ProcessReceivedData(); + } + } + catch (IOException) + { + client.Close(); + } + } + + private void ProcessReceivedData() + { + lock (received) + { + if (received.Count < 6) return; + int len = (((int)received[4] << 8) | (int)received[5]); + if (received.Count < 6 + len) return; + if (len < 2) throw new Exception("Wrong length"); + int transactionId = (((int)received[0] << 8) | (int)received[1]); + byte unitId = received[6]; + byte function = received[7]; + List response = new List(); + if (function == 0x03) // read holding registers + { + int addr = (((int)received[8] << 8) | (int)received[9]); + int amount = (((int)received[10] << 8) | (int)received[11]); + for (int a = addr; a < addr + amount; a++) + { + int value = registers[a]; + response.Add((byte)((value >> 8) & 0xFF)); + response.Add((byte)(value & 0xFF)); + } + } + else + { + throw new Exception("Unsupported function: " + function); + } + List fullResponse = new List(); + fullResponse.Add(received[0]); + fullResponse.Add(received[1]); // transaction id + fullResponse.Add(received[2]); + fullResponse.Add(received[3]); // protocol id + int responseLength = 3 + response.Count; + fullResponse.Add((byte)((responseLength >> 8) & 0xFF)); + fullResponse.Add((byte)((responseLength) & 0xFF)); + fullResponse.Add(unitId); + fullResponse.Add(function); + fullResponse.Add((byte)response.Count); + fullResponse.AddRange(response); + received.RemoveRange(0, 6 + len); + stream.Write(fullResponse.ToArray(), 0, fullResponse.Count); + } + } + + private void OnReadCompleted(IAsyncResult ar) + { + currentReceived = stream.EndRead(ar); + lock (received) + { + for (int i = 0; i < currentReceived; i++) received.Add(buffer[i]); + } + readCompleted.Set(); + } + } +} diff --git a/MonitoringUnit.cs b/MonitoringUnit.cs new file mode 100644 index 0000000..a38e1a1 --- /dev/null +++ b/MonitoringUnit.cs @@ -0,0 +1,96 @@ +using IniParser.Model; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace Barsik +{ + class BaseMonitoringUnit + { + protected SectionData config; + protected RegistersContainer registers; + + protected int baseAddress; + + public BaseMonitoringUnit(SectionData config, RegistersContainer registers) + { + this.config = config; + this.registers = registers; + baseAddress = int.Parse(config.Keys["BaseAddress"]); + } + + public virtual void Run() + { + } + + public static BaseMonitoringUnit Create(SectionData config, RegistersContainer registers) + { + var type = config.Keys["Type"]; + if (type == "Sensors") + { + return new SensorsMonitoringUnit(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 new file mode 100644 index 0000000..d7da284 --- /dev/null +++ b/Program.cs @@ -0,0 +1,42 @@ +using IniParser; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Barsik +{ + class Program + { + private static RegistersContainer registers; + private static List units; + + static void Main(string[] args) + { + registers = new RegistersContainer(); + var server = new ModbusServer(registers); + units = new List(); + + var configParser = new FileIniDataParser(); + var data = configParser.ReadFile("barsik.ini"); + foreach (var section in data.Sections) + { + if (section.SectionName.StartsWith("Unit")) + { + var unit = BaseMonitoringUnit.Create(section, registers); + if (unit != null) + { + units.Add(unit); + Thread unitThread = new Thread(new ThreadStart(unit.Run)); + unitThread.Start(); + } + } + } + server.Port = int.Parse(data["Server"]["Port"]); + while (true) + { + server.Run(); + Thread.Sleep(1000); + } + } + } +} diff --git a/Properties/PublishProfiles/FolderProfile.pubxml b/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..3280d7f --- /dev/null +++ b/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,16 @@ + + + + + FileSystem + Release + Any CPU + netcoreapp2.1 + bin\Debug\netcoreapp2.1\publish\ + linux-x64 + true + <_IsPortable>false + + \ No newline at end of file diff --git a/Properties/PublishProfiles/FolderProfile.pubxml.user b/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..312c6e3 --- /dev/null +++ b/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/RegistersContainer.cs b/RegistersContainer.cs new file mode 100644 index 0000000..102dade --- /dev/null +++ b/RegistersContainer.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; + +namespace Barsik +{ + class RegistersContainer + { + private Dictionary registers; + + public RegistersContainer() + { + registers = new Dictionary(); + } + + public int this[int address] + { + get + { + lock(registers) + { + if (registers.ContainsKey(address)) return registers[address]; + return 0; + } + } + set + { + lock (registers) + { + registers[address] = value; + } + } + } + } +} diff --git a/barsik.ini.sample b/barsik.ini.sample new file mode 100644 index 0000000..f9136f2 --- /dev/null +++ b/barsik.ini.sample @@ -0,0 +1,8 @@ +[Server] +Port = 4500 + +[Unit_CPU] +Type = Sensors +BaseAddress = 1 +String = CPU Temperature +Multiplier = 100 \ No newline at end of file