From 4d43f8ef660a54e835fdd89b6357cd994d39f353 Mon Sep 17 00:00:00 2001 From: Bo Jeanes Date: Thu, 29 Sep 2022 18:19:06 +1000 Subject: [PATCH] Fix utf8 register parsing --- modbus-mqtt/README.md | 4 +- modbus-mqtt/examples/sungrow-sh5.0rs.json | 151 +++++++++++++++++++++- modbus-mqtt/src/modbus/register.rs | 28 +++- 3 files changed, 173 insertions(+), 10 deletions(-) diff --git a/modbus-mqtt/README.md b/modbus-mqtt/README.md index 85d7997..60a33d2 100644 --- a/modbus-mqtt/README.md +++ b/modbus-mqtt/README.md @@ -86,7 +86,9 @@ If the connection is successful, you will see the following message like the fol #### Full connection examples -All fields accepted (optional fields show defaults) +Check the `examples/` directory for some examples and please feel free to share your own examples, noting the appropriate vendor/device info. + +The following JSON is all supported connection configuration fields, where the optional fields show the default values in use. ```jsonc { diff --git a/modbus-mqtt/examples/sungrow-sh5.0rs.json b/modbus-mqtt/examples/sungrow-sh5.0rs.json index e32c350..9fdff1d 100644 --- a/modbus-mqtt/examples/sungrow-sh5.0rs.json +++ b/modbus-mqtt/examples/sungrow-sh5.0rs.json @@ -9,14 +9,20 @@ "type": "u32", "name": "dc_power", "swap_words": true, - "period": "1s" + "period": "500ms" }, { "address": 13034, "type": "u32", "name": "active_power", "swap_words": true, - "period": "1s" + "period": "500ms" + }, + { + "address": 4990, + "name": "serial_number", + "type": "string", + "length": 10 }, { "address": 5008, @@ -25,24 +31,45 @@ "period": "1m", "scale": -1 }, + { + "address": 5001, + "type": "u16", + "name": "nominal_output_power", + "period": "1m", + "scale": 2 + }, { "address": 13008, "type": "s32", "name": "load_power", "swap_words": true, - "period": "1s" + "period": "500ms" }, { "address": 13010, "type": "s32", "name": "export_power", "swap_words": true, - "period": "1s" + "period": "500ms" + }, + { + "address": 13020, + "name": "battery_voltage", + "period": "3s", + "type": "u16", + "scale": -1 }, { "address": 13022, "name": "battery_power", - "period": "1s" + "period": "500ms" + }, + { + "address": 13021, + "name": "battery_current", + "period": "500ms", + "type": "s16", + "scale": -1 }, { "address": 13023, @@ -85,12 +112,12 @@ "scale": -1 }, { - "address": 5012, + "address": 5013, "name": "mppt2_voltage", "scale": -1 }, { - "address": 5013, + "address": 5014, "name": "mppt2_current", "scale": -1 }, @@ -118,6 +145,116 @@ "address": 33148, "name": "forced_battery_power", "scale": 1 + }, + { + "address": 13002, + "type": "u16", + "name": "daily_pv_generation", + "scale": -1 + }, + { + "address": 13003, + "type": "u32", + "swap_words": true, + "name": "total_pv_generation", + "scale": -1 + }, + { + "address": 13005, + "type": "u16", + "name": "daily_export_energy", + "scale": -1 + }, + { + "address": 13006, + "type": "u32", + "swap_words": true, + "name": "total_export_energy", + "scale": -1 + }, + { + "address": 13012, + "type": "u16", + "name": "daily_battery_charge_energy", + "scale": -1 + }, + { + "address": 13013, + "type": "u32", + "swap_words": true, + "name": "total_battery_charge_energy", + "scale": -1 + }, + { + "address": 13026, + "type": "u16", + "name": "daily_battery_discharge_energy", + "scale": -1 + }, + { + "address": 13027, + "type": "u32", + "swap_words": true, + "name": "total_battery_discharge_energy", + "scale": -1 + }, + { + "address": 13017, + "type": "u16", + "name": "daily_direct_energy_consumption", + "scale": -1 + }, + { + "address": 13018, + "type": "u32", + "swap_words": true, + "name": "total_direct_energy_consumption", + "scale": -1 + }, + { + "address": 5003, + "type": "u16", + "name": "daily_output_energy", + "scale": -1 + }, + { + "address": 5004, + "type": "u32", + "swap_words": true, + "name": "total_output_energy", + "scale": -1 + }, + { + "address": 13029, + "type": "u16", + "name": "daily_self_consumption_rate", + "scale": -1 + }, + { + "address": 13036, + "type": "u16", + "name": "daily_import_energy", + "scale": -1 + }, + { + "address": 13037, + "type": "u32", + "swap_words": true, + "name": "total_import_energy", + "scale": -1 + }, + { + "address": 13040, + "type": "u16", + "name": "daily_charge_energy", + "scale": -1 + }, + { + "address": 13041, + "type": "u32", + "swap_words": true, + "name": "total_charge_energy", + "scale": -1 } ] } \ No newline at end of file diff --git a/modbus-mqtt/src/modbus/register.rs b/modbus-mqtt/src/modbus/register.rs index 1636a59..d755cf8 100644 --- a/modbus-mqtt/src/modbus/register.rs +++ b/modbus-mqtt/src/modbus/register.rs @@ -466,7 +466,7 @@ impl RegisterValueType { } } T::String(RegisterString { .. }) => { - json!(String::from_utf16_lossy(words)) + json!(String::from_utf8_lossy(&bytes).trim_end_matches(char::from(0))) } T::Array(RegisterArray { .. }) => todo!(), } @@ -511,7 +511,7 @@ impl Register { use pretty_assertions::assert_eq; #[test] -fn test_parse_1() { +fn test_parse_numeric() { use serde_json::json; let reg = Register { @@ -534,3 +534,27 @@ fn test_parse_1() { assert_eq!(reg.parse_words(&[843, 0]), json!(843)); } + +#[test] +fn test_parse_string() { + use serde_json::json; + + let reg = Register { + register_type: RegisterType::Input, + address: 42, + name: None, + interval: Default::default(), + parse: RegisterParse { + swap_bytes: Swap(false), + swap_words: Swap(false), + value_type: RegisterValueType::String(RegisterString { length: 10 }), + }, + }; + + assert_eq!( + reg.parse_words(&[ + 0x6865, 0x6c6c, 0x6f20, 0x776f, 0x726c, 0x6400, 0x0000, 0x0000, 0x0000, 0x0000, + ]), + json!("hello world") + ); +}