Different types of units. Writing values, relay unit

This commit is contained in:
ftc
2021-09-12 16:01:26 +03:00
parent 80cad276e8
commit cfd8d4c018
10 changed files with 296 additions and 74 deletions
+26
View File
@@ -111,6 +111,32 @@ namespace ESPServer
response.Add((byte)(value & 0xFF)); response.Add((byte)(value & 0xFF));
} }
} }
else if (function == 0x06)
{
int addr = (((int)received[8] << 8) | (int)received[9]);
int value = (((int)received[10] << 8) | (int)received[11]);
registers.WriteRegister(addr, value);
response.Add((byte)((addr >> 8) & 0xFF));
response.Add((byte)(addr & 0xFF));
response.Add((byte)((value >> 8) & 0xFF));
response.Add((byte)(value & 0xFF));
}
else if (function == 0x10)
{
int addr = (((int)received[8] << 8) | (int)received[9]);
int amount = (((int)received[10] << 8) | (int)received[11]);
// received[12] is amount of data bytes (2 * amount)
List<int> values = new List<int>();
for (int a = 0; a < amount; a++)
{
values.Add(((int)received[13 + 2 * a] << 8) | (int)received[14 + 2 * a]);
}
registers.WriteRegisters(addr, values.ToArray());
response.Add((byte)((addr >> 8) & 0xFF));
response.Add((byte)(addr & 0xFF));
response.Add((byte)((amount >> 8) & 0xFF));
response.Add((byte)(amount & 0xFF));
}
else else
{ {
throw new Exception("Unsupported function: " + function); throw new Exception("Unsupported function: " + function);
+2
View File
@@ -62,6 +62,8 @@ namespace ESPServer
unitReceiver.Start(); unitReceiver.Start();
var unitPinger = new UnitPinger(db); var unitPinger = new UnitPinger(db);
unitPinger.Start(); unitPinger.Start();
// initialize data writer (do not need to save it as it will react on events by itself)
new UnitDataWriter(db, registers);
var modbusServer = new ModbusServer(registers); var modbusServer = new ModbusServer(registers);
modbusServer.Port = config.RootElement.GetProperty("modbusPort").GetInt32(); modbusServer.Port = config.RootElement.GetProperty("modbusPort").GetInt32();
modbusServer.Run(); modbusServer.Run();
@@ -0,0 +1,8 @@
{
"profiles": {
"esp_server": {
"commandName": "Project",
"commandLineArgs": "openhab-config"
}
}
}
+20
View File
@@ -7,6 +7,8 @@ namespace ESPServer
{ {
private Dictionary<int, int> registers; private Dictionary<int, int> registers;
public event Action<int, float> OnRegisterWritten;
public RegistersContainer() public RegistersContainer()
{ {
registers = new Dictionary<int, int>(); registers = new Dictionary<int, int>();
@@ -37,5 +39,23 @@ namespace ESPServer
registers[address + 1] = (int)bytes[0] + (((int)bytes[1]) << 8); registers[address + 1] = (int)bytes[0] + (((int)bytes[1]) << 8);
registers[address] = (int)bytes[2] + (((int)bytes[3]) << 8); registers[address] = (int)bytes[2] + (((int)bytes[3]) << 8);
} }
public void WriteRegister(int address, int value)
{
registers[address] = value;
OnRegisterWritten?.Invoke(address, value);
}
public void WriteRegisters(int address, int[] values)
{
for (int i = 0; i < values.Length; i++)
{
registers[address + i] = values[i];
}
for (int i = 0; i < values.Length; i++)
{
OnRegisterWritten?.Invoke(address + i, values[i]);
}
}
} }
} }
+35
View File
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ESPServer
{
class RelayUnit : Unit
{
public override byte[] GetWriteCommand(int address, float value)
{
foreach (var v in values)
{
if (v.Value.name == "r" && v.Value.addressOffset + baseAddress == address)
{
return GetWritePayload(value);
}
}
return null;
}
private byte[] GetWritePayload(float value)
{
string t = "r_write\x01";
if (MathF.Abs(value) < 1e-4) t += "0";
else t += "1";
t += "\x01";
return Encoding.ASCII.GetBytes(t);
}
public override bool IsValueWritable(UnitValue v)
{
return v.name == "r";
}
}
}
+85
View File
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ESPServer
{
/*
* Описание параметра, который может придти от юнита
*/
class UnitValue
{
// Название параметра
public string name;
// Текущее значение
public float value;
// Сдвиг адреса, по которому это значение будет храниться в modbus
public int addressOffset;
}
/*
* Описание конкретного модуля (релейный, датчик, whatever)
*/
class Unit
{
// Айдишник юнита (мак-адрес)
public string id;
// Название (для генерации конфигов например)
public string name;
// Базовый адрес в модбасе
public int baseAddress;
// Значения которые есть в юните
public Dictionary<string, UnitValue> values;
// текущий IP юнита
public string ip;
public UnitValue GetValue(string name)
{
if (!values.ContainsKey(name))
{
return null;
}
return values[name];
}
public int MinOffset
{
get
{
int min = int.MaxValue;
foreach (var u in values) if (u.Value.addressOffset < min) min = u.Value.addressOffset;
return min;
}
}
public int MaxOffset
{
get
{
int max = int.MinValue;
foreach (var u in values) if (u.Value.addressOffset > max) max = u.Value.addressOffset;
return max;
}
}
public bool HasAddress(int address)
{
foreach (var u in values)
{
if (u.Value.addressOffset + baseAddress == address) return true;
}
return false;
}
public virtual byte[] GetWriteCommand(int address, float value)
{
return null;
}
public virtual bool IsValueWritable(UnitValue v)
{
return false;
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
namespace ESPServer
{
static class UnitFactory
{
public static Unit CreateUnit(JsonElement config)
{
var type = "";
JsonElement val;
if (config.TryGetProperty("type", out val))
{
type = val.GetString();
}
Unit u;
if (type == "relay") u = new RelayUnit();
else u = new Unit();
u.id = config.GetProperty("id").GetString();
u.name = config.GetProperty("name").GetString();
u.baseAddress = config.GetProperty("address").GetInt32();
u.values = new Dictionary<string, UnitValue>();
foreach (var knownValue in config.GetProperty("values").EnumerateArray())
{
var uv = new UnitValue();
uv.name = knownValue.GetProperty("name").GetString();
uv.addressOffset = knownValue.GetProperty("offset").GetInt32();
u.values[uv.name] = uv;
}
return u;
}
}
}
+55
View File
@@ -0,0 +1,55 @@
using NLog;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading;
namespace ESPServer
{
class UnitDataWriter
{
const int UNIT_TARGET_PORT = 7000;
private readonly Logger logger = LogManager.GetCurrentClassLogger();
UnitsDatabase db;
public UnitDataWriter(UnitsDatabase db, RegistersContainer registers)
{
this.db = db;
registers.OnRegisterWritten += OnRegisterWritten;
}
private void OnRegisterWritten(int address, float value)
{
logger.Debug("Register written, address = " + address.ToString() + " value = " + value.ToString());
var unit = db.GetUnit(address);
if (unit == null)
{
return;
}
var payload = unit.GetWriteCommand(address, value);
if (payload == null)
{
logger.Info("Write of address " + address + " is not supported by unit " + unit.id);
return;
}
try
{
using (var udpClient = new UdpClient())
{
udpClient.Connect(unit.ip, UNIT_TARGET_PORT);
udpClient.Send(payload, payload.Length);
udpClient.Close();
}
}
catch (Exception e)
{
logger.Error(e.Message);
}
}
}
}
+24 -73
View File
@@ -5,65 +5,6 @@ using System.Text.Json;
namespace ESPServer namespace ESPServer
{ {
/*
* Описание параметра, который может придти от юнита
*/
class UnitValue
{
// Название параметра
public string name;
// Текущее значение
public float value;
// Сдвиг адреса, по которому это значение будет храниться в modbus
public int addressOffset;
}
/*
* Описание конкретного модуля (релейный, датчик, whatever)
*/
class Unit
{
// Айдишник юнита (мак-адрес)
public string id;
// Название (для генерации конфигов например)
public string name;
// Базовый адрес в модбасе
public int baseAddress;
// Значения которые есть в юните
public Dictionary<string, UnitValue> values;
// текущий IP юнита
public string ip;
public UnitValue GetValue(string name)
{
if (!values.ContainsKey(name))
{
return null;
}
return values[name];
}
public int MinOffset
{
get
{
int min = int.MaxValue;
foreach (var u in values) if (u.Value.addressOffset < min) min = u.Value.addressOffset;
return min;
}
}
public int MaxOffset
{
get
{
int max = int.MinValue;
foreach (var u in values) if (u.Value.addressOffset > max) max = u.Value.addressOffset;
return max;
}
}
}
class UnitsDatabase class UnitsDatabase
{ {
List<Unit> units; List<Unit> units;
@@ -73,19 +14,7 @@ namespace ESPServer
units = new List<Unit>(); units = new List<Unit>();
foreach (var unitConfig in config.EnumerateArray()) foreach (var unitConfig in config.EnumerateArray())
{ {
var u = new Unit(); units.Add(UnitFactory.CreateUnit(unitConfig));
u.id = unitConfig.GetProperty("id").GetString();
u.name = unitConfig.GetProperty("name").GetString();
u.baseAddress = unitConfig.GetProperty("address").GetInt32();
u.values = new Dictionary<string, UnitValue>();
foreach (var knownValue in unitConfig.GetProperty("values").EnumerateArray())
{
var uv = new UnitValue();
uv.name = knownValue.GetProperty("name").GetString();
uv.addressOffset = knownValue.GetProperty("offset").GetInt32();
u.values[uv.name] = uv;
}
units.Add(u);
} }
} }
@@ -131,6 +60,23 @@ namespace ESPServer
return null; return null;
} }
/// <summary>
/// Retrieves unit by modbus address of any value in it
/// </summary>
/// <param name="valueAddress"></param>
/// <returns></returns>
public Unit GetUnit(int valueAddress)
{
lock (units)
{
foreach (var u in units)
{
if (u.HasAddress(valueAddress)) return u;
}
}
return null;
}
#region OpenHAB config generation #region OpenHAB config generation
internal void GenerateItemsConfig(StringBuilder config) internal void GenerateItemsConfig(StringBuilder config)
{ {
@@ -175,7 +121,12 @@ namespace ESPServer
var v = uv.Value; var v = uv.Value;
config.Append("\t\tThing data "); config.Append("\t\tThing data ");
config.Append(u.name + "_" + v.name); config.Append(u.name + "_" + v.name);
config.AppendFormat("[readStart = \"{0}\", readValueType = \"float32\"]\n", u.baseAddress + v.addressOffset); config.AppendFormat("[readStart = \"{0}\", readValueType = \"float32\"", u.baseAddress + v.addressOffset);
if (u.IsValueWritable(v))
{
config.AppendFormat(", writeStart = \"{0}\", writeValueType = \"float32\", writeType=\"holding\"", u.baseAddress + v.addressOffset);
}
config.Append("]\n");
} }
} }
config.Append("\t}\n"); config.Append("\t}\n");
+5 -1
View File
@@ -22,4 +22,8 @@ optional arguments, everything separated with character 0x01.
Possible commands are: Possible commands are:
1. ping - no arguments. Informs unit that server is still alive 1. ping - no arguments. Informs unit that server is still alive
and able to communicate and able to communicate
2. r_write - one argument either '0' or '1'. Sets relay state
for unit (only for relay modules). Note that relay on relay modules
is normally opened, so '0' means turned on and '1' means turned off.