Different types of units. Writing values, relay unit
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
@@ -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");
|
||||||
|
|||||||
@@ -23,3 +23,7 @@ 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.
|
||||||
Reference in New Issue
Block a user