Skip to content

Commit d40722d

Browse files
authored
Merge branch 'master' into auto_polling_throttling
2 parents 864fb07 + 0fb590c commit d40722d

File tree

8 files changed

+273
-93
lines changed

8 files changed

+273
-93
lines changed

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ New features:
1313
- Added option `ConvertUnknown`, this defines if values of an unknown typed message (e.g., no entry in StiebelEltron.json) shall be converted with all possible formats and printed to console.
1414
- Added fallback value converter for unknown indexes in StiebelEltron.json for easier debugging.
1515
- Added automatic polling feature. It can be enabled using config option `AutoPolling`. The polling interval **in seconds** can be defined with `AutoPollingInterval`. To avoid connection loss to socketcand `AutoPollingThrottle` needs to be specified **in milliseconds**. This delay will be introduced between each sent read. If a value shall not be polled, add `"IgnorePolling": true` to `StiebelEltron.json`.
16+
- Added combined values feature that allows combining two elster indices together. See documentation below.
17+
1618

1719
Fixes:
1820
- Do not publish unknown values to MQTT broker
@@ -218,6 +220,60 @@ The following section describes the format of `can2mqtt_core/can2mqtt_core/trans
218220
## Ignore from auto polling
219221
If a value shall not be polled, add `"IgnorePolling": true` entry.
220222

223+
## Combined Values
224+
In case you want to combine the following two entries (the example was stripped and only relevant fields are kept):
225+
```json
226+
{
227+
"Index": "092E",
228+
"Sender": "180",
229+
"Unit": "Wh",
230+
"Converter": "Default",
231+
"Name": {
232+
"EN": "Heat Meter Heating Day",
233+
"DE": "Wärmemenge Heizen Tag"
234+
},
235+
"MqttTopic": "/meter/heating/day/Wh",
236+
},
237+
{
238+
"Index": "092F",
239+
"Sender": "180",
240+
"Unit": "KWh",
241+
"Converter": "Default",
242+
"Name": {
243+
"EN": "Heat Meter Heating Day",
244+
"DE": "Wärmemenge Heizen Tag"
245+
},
246+
"MqttTopic": "/meter/heating/day/KWh",
247+
},
248+
```
249+
250+
You can do it by removing one of the entries and adapt the other like this:
251+
252+
```json
253+
{
254+
"Index": "092F",
255+
"CombineIndex": "092E",
256+
"Sender": "180",
257+
"Unit": "KWh",
258+
"Converter": "Default",
259+
"Name": {
260+
"EN": "Heat Meter Heating Day",
261+
"DE": "Wärmemenge Heizen Tag"
262+
},
263+
"MqttTopic": "/meter/heating/day/KWh",
264+
},
265+
```
266+
267+
Possible converters:
268+
269+
Assume the following values for the given examples: `Index` = 19 and `CombineIndex`= 234
270+
271+
| Converter | Formula | Example |
272+
|-----------|-------------------------------------|-----------------------------|
273+
| `Default` | `Index`* 1000 + `CombineIndex` | 19 * 1000 + 234 = 19234 |
274+
| `Dec` | `Index` + (`CombineIndex` / 1000) | 19 + 234 / 1000 = 19,234 |
275+
276+
221277
# Troubleshooting
222278
## Issue: The connection is reconnecting over and over again multiple times within seconds until the application crashes
223279
Reason: Do you have a second MQTT client registered on the MQTT Broker with the same client ID?

can2mqtt_core/can2mqtt_core/CanFrame.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Globalization;
4-
using System.Text;
1+
using System.Globalization;
52

63
namespace can2mqtt
74
{
@@ -137,5 +134,11 @@ public string Value
137134
/// In case a translator was used, the value was extracted from the payload
138135
/// </summary>
139136
public string MqttValue { get; set; } = "";
137+
138+
/// <summary>
139+
/// Returns if the can frame is complete or if additional information is required
140+
/// (e.g., for combined values this can be false as long as not all parts are received)
141+
/// </summary>
142+
public bool IsComplete { get; set; } = false;
140143
}
141144
}
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
using can2mqtt;
2-
using System;
3-
using System.Collections.Generic;
4-
using System.Linq;
5-
using System.Text;
6-
using System.Threading.Tasks;
7-
8-
namespace can2mqtt
1+
namespace can2mqtt
92
{
103
public interface ITranslator
114
{
125
IEnumerable<string> MqttTopicsToPoll { get; }
136
CanFrame Translate(CanFrame rawData, bool noUnit, string language, bool convertUnknown);
14-
string TranslateBack(string mqttTopic, string value, string senderId, bool noUnit, string canOperation);
7+
IEnumerable<string> TranslateBack(string mqttTopic, string value, string senderId, bool noUnit, string canOperation);
158
}
169
}

can2mqtt_core/can2mqtt_core/can2mqtt.cs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class Can2Mqtt : BackgroundService
3232
private bool MqttAcceptSet = false;
3333
private NetworkStream TcpCanStream;
3434
private TcpClient ScdClient = null;
35-
private Translator.StiebelEltron.StiebelEltron Translator = null;
35+
private StiebelEltron Translator = null;
3636
private string Language = "EN";
3737
private bool ConvertUnknown = false;
3838

@@ -159,9 +159,8 @@ private async Task SendCan(string topic, byte[] payload, string canServer, int c
159159
var data = Encoding.UTF8.GetString(payload);
160160

161161
//Convert the data to the required format
162-
var canFrame = Translator.TranslateBack(topic, data, CanSenderId, NoUnit, "0");
163-
164-
await SendCanFrame(canServer, canPort, canFrame);
162+
var canFrames = Translator.TranslateBack(topic, data, CanSenderId, NoUnit, "0");
163+
await SendCanFrame(canServer, canPort, canFrames);
165164
}
166165
catch (Exception ex)
167166
{
@@ -183,9 +182,8 @@ private async Task ReadCan(string topic, string canServer, int canPort)
183182
try
184183
{
185184
//Convert the data to the required format
186-
var canFrame = Translator.TranslateBack(topic, null, CanSenderId, NoUnit, "1");
187-
188-
await SendCanFrame(canServer, canPort, canFrame);
185+
var canFrames = Translator.TranslateBack(topic, null, CanSenderId, NoUnit, "1");
186+
await SendCanFrame(canServer, canPort, canFrames);
189187
}
190188
catch (Exception ex)
191189
{
@@ -200,24 +198,27 @@ private async Task ReadCan(string topic, string canServer, int canPort)
200198
/// <param name="canPort">The CAN Server Port</param>
201199
/// <param name="canFrame">The actual frame to send</param>
202200
/// <returns></returns>
203-
private async Task SendCanFrame(string canServer, int canPort, string canFrame)
201+
private async Task SendCanFrame(string canServer, int canPort, IEnumerable<string> canFrames)
204202
{
205203
await ConnectTcpCanBus(canServer, canPort);
206204

207-
//Convert data part of the can Frame to socketcand required format
208-
var canFrameDataPart = canFrame.Split("#")[1];
209-
var canFrameSdData = "";
210-
211-
for (int i = 0; i < canFrameDataPart.Length; i += 2)
205+
foreach (var canFrame in canFrames)
212206
{
213-
canFrameSdData += Convert.ToInt32(canFrameDataPart.Substring(i, 2), 16).ToString("X1") + " ";
214-
}
215-
canFrameSdData = canFrameSdData.Trim();
207+
//Convert data part of the can Frame to socketcand required format
208+
var canFrameDataPart = canFrame.Split("#")[1];
209+
var canFrameSdData = "";
216210

217-
// < send can_id can_datalength [data]* >
218-
var canFrameSdCommand = string.Format("< send {0} {1} {2} >", CanSenderId, canFrameDataPart.Length / 2, canFrameSdData);
219-
Logger.LogInformation("Sending CAN Frame: {0}", canFrameSdCommand);
220-
TcpCanStream.Write(Encoding.Default.GetBytes(canFrameSdCommand));
211+
for (int i = 0; i < canFrameDataPart.Length; i += 2)
212+
{
213+
canFrameSdData += Convert.ToInt32(canFrameDataPart.Substring(i, 2), 16).ToString("X1") + " ";
214+
}
215+
canFrameSdData = canFrameSdData.Trim();
216+
217+
// < send can_id can_datalength [data]* >
218+
var canFrameSdCommand = string.Format("< send {0} {1} {2} >", CanSenderId, canFrameDataPart.Length / 2, canFrameSdData);
219+
Logger.LogInformation("Sending CAN Frame: {0}", canFrameSdCommand);
220+
TcpCanStream.Write(Encoding.Default.GetBytes(canFrameSdCommand));
221+
}
221222
}
222223

223224
/// <summary>
@@ -390,6 +391,10 @@ public async Task SendMQTT(CanFrame canMsg)
390391
if (Translator != null)
391392
{
392393
canMsg = Translator.Translate(canMsg, NoUnit, Language, ConvertUnknown);
394+
if (!canMsg.IsComplete) {
395+
Logger.LogDebug("Waiting for additional data for MQTT topic {0}", canMsg.MqttTopicExtention);
396+
return;
397+
}
393398
}
394399

395400
//Verify connection to MQTT Broker is established
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
namespace can2mqtt.Translator.StiebelEltron
2+
{
3+
public interface ICombinedValueConverter
4+
{
5+
string ConvertValue(int index, string hexData);
6+
string CombineValues(params string[] values);
7+
}
8+
9+
public abstract class CombinedValueConverterBase : ICombinedValueConverter
10+
{
11+
public abstract string ConvertValue(int index, string hexData);
12+
public abstract string CombineValues(params string[] values);
13+
14+
protected static int ConvertToInt(string hexData) => Convert.ToInt32(hexData, 16);
15+
protected static double ConvertToDouble(string hexData) => Convert.ToDouble(ConvertToInt(hexData));
16+
}
17+
18+
public class CombinedValueDefaultConverter(int integerIndex) : CombinedValueConverterBase
19+
{
20+
private int integerIndex = integerIndex;
21+
22+
public override string ConvertValue(int index, string hexData)
23+
{
24+
var value = ConvertToInt(hexData);
25+
if (index == integerIndex)
26+
{
27+
value *= 1000;
28+
}
29+
return value.ToString();
30+
}
31+
32+
public override string CombineValues(params string[] values)
33+
{
34+
return values.Sum(v => int.Parse(v)).ToString();
35+
}
36+
}
37+
38+
public class CombinedValueDecConverter(int fractionalIndex) : CombinedValueConverterBase
39+
{
40+
private int fractionalIndex = fractionalIndex;
41+
42+
public override string ConvertValue(int index, string hexData)
43+
{
44+
var value = ConvertToDouble(hexData);
45+
if (index == fractionalIndex)
46+
{
47+
value /= 1000;
48+
}
49+
return value.ToString();
50+
}
51+
52+
public override string CombineValues(params string[] values)
53+
{
54+
return values.Sum(v => double.Parse(v)).ToString();
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)