summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common.cc15
-rw-r--r--src/common.h9
-rw-r--r--src/inverterctl.cc343
-rw-r--r--src/p18/commands.cc457
-rw-r--r--src/p18/commands.h35
-rw-r--r--src/p18/functions.h12
-rw-r--r--src/protocol/client.cc21
-rw-r--r--src/protocol/client.h23
-rw-r--r--src/protocol/exceptions.h (renamed from src/p18/exceptions.h)8
-rw-r--r--src/protocol/input.cc118
-rw-r--r--src/protocol/input.h32
-rw-r--r--src/protocol/response.cc19
-rw-r--r--src/protocol/response.h44
-rw-r--r--src/protocol_17/defines.cc138
-rw-r--r--src/protocol_17/defines.h32
-rw-r--r--src/protocol_17/input.cc117
-rw-r--r--src/protocol_17/input.h21
-rw-r--r--src/protocol_17/response.h462
-rw-r--r--src/protocol_17/types.h103
-rw-r--r--src/protocol_18/client.cc (renamed from src/p18/client.cc)31
-rw-r--r--src/protocol_18/client.h (renamed from src/p18/client.h)11
-rw-r--r--src/protocol_18/defines.cc (renamed from src/p18/defines.cc)80
-rw-r--r--src/protocol_18/defines.h (renamed from src/p18/defines.h)0
-rw-r--r--src/protocol_18/functions.cc (renamed from src/p18/functions.cc)0
-rw-r--r--src/protocol_18/functions.h12
-rw-r--r--src/protocol_18/input.cc341
-rw-r--r--src/protocol_18/input.h21
-rw-r--r--src/protocol_18/response.cc808
-rw-r--r--src/protocol_18/response.cc.orig (renamed from src/p18/response.cc)19
-rw-r--r--src/protocol_18/response.h496
-rw-r--r--src/protocol_18/response.h.orig (renamed from src/p18/response.h)25
-rw-r--r--src/protocol_18/types.h (renamed from src/p18/types.h)2
32 files changed, 3117 insertions, 738 deletions
diff --git a/src/common.cc b/src/common.cc
index fb2acea..711c7e0 100644
--- a/src/common.cc
+++ b/src/common.cc
@@ -6,12 +6,27 @@
formatter::Format format_from_string(std::string& s) {
if (s == "json")
return formatter::Format::JSON;
+
else if (s == "simple-json")
return formatter::Format::SimpleJSON;
+
else if (s == "table")
return formatter::Format::Table;
+
else if (s == "simple-table")
return formatter::Format::SimpleTable;
+
else
throw std::invalid_argument("invalid format");
+}
+
+Protocol protocol_from_string(std::string& s) {
+ if (s == "17")
+ return Protocol::P17;
+
+ else if (s == "18")
+ return Protocol::P18;
+
+ else
+ throw std::invalid_argument("invalid protocol");
} \ No newline at end of file
diff --git a/src/common.h b/src/common.h
index c0b5a36..9723a27 100644
--- a/src/common.h
+++ b/src/common.h
@@ -11,11 +11,17 @@ enum class DeviceType {
Pseudo
};
+enum class Protocol {
+ P17,
+ P18,
+};
+
// long opts
enum {
- LO_HELP = 1,
+ LO_HELP = 1000,
LO_VERBOSE,
LO_RAW,
+ LO_PROTOCOL,
LO_TIMEOUT,
LO_CACHE_TIMEOUT,
LO_DELAY,
@@ -34,5 +40,6 @@ enum {
};
formatter::Format format_from_string(std::string& s);
+Protocol protocol_from_string(std::string& s);
#endif //INVERTER_TOOLS_COMMON_H
diff --git a/src/inverterctl.cc b/src/inverterctl.cc
index f071f2e..d22f53a 100644
--- a/src/inverterctl.cc
+++ b/src/inverterctl.cc
@@ -10,19 +10,33 @@
#include <stdexcept>
#include <getopt.h>
+// common stuff
#include "logging.h"
#include "util.h"
#include "common.h"
-#include "p18/client.h"
-#include "p18/types.h"
-#include "p18/defines.h"
-#include "p18/exceptions.h"
-#include "p18/commands.h"
#include "formatter/formatter.h"
#include "voltronic/device.h"
#include "voltronic/exceptions.h"
#include "hexdump/hexdump.h"
+#include "protocol/input.h"
+#include "protocol/exceptions.h"
+#include "protocol/response.h"
+
+// p18
+#include "protocol_18/client.h"
+#include "protocol_18/types.h"
+#include "protocol_18/defines.h"
+#include "protocol_18/input.h"
+
+// p17
+//#include "protocol_17/client.h"
+#include "protocol_17/types.h"
+#include "protocol_17/defines.h"
+#include "protocol_17/input.h"
+
+
+
const size_t MAX_RAW_COMMAND_LENGTH = 128;
template <typename T, std::size_t N>
@@ -42,6 +56,7 @@ static void short_usage(const char* progname) {
" -h: Show this help\n"
" --help: Show full help (with all commands)\n"
" --raw <DATA>: Execute arbitrary command and print response\n"
+ " -p, --protocol <ID> 17 or 18 (default)\n"
" --device <DEVICE>: 'usb' (default), 'serial' or 'pseudo'\n"
" --timeout <TIMEOUT>: Timeout in ms (default: " << voltronic::Device::TIMEOUT << ")\n"
" --verbose: Be verbose\n"
@@ -52,7 +67,142 @@ static void short_usage(const char* progname) {
exit(1);
}
-static void usage(const char* progname) {
+static void usage_p17() {
+ std::cout <<
+ "P17 commands:\n"
+ " get-protocol-id\n"
+ " get-date-time\n"
+ " get-total-generated\n"
+ " get-year-generated <yyyy>\n"
+ " get-month-generated <yyyy> <mm>\n"
+ " get-day-generated <yyyy> <mm> <dd> <HH>\n"
+ " get-series-number\n"
+ " get-cpu-version\n"
+ " get-secondary-cpu-version\n"
+
+ ;
+}
+
+static void usage_p18() {
+ std::cout <<
+ "P18 commands:\n"
+ " get-protocol-id\n"
+ " get-date-time\n"
+ " get-total-generated\n"
+ " get-year-generated <yyyy>\n"
+ " get-month-generated <yyyy> <mm>\n"
+ " get-day-generated <yyyy> <mm> <dd>\n"
+ " get-series-number\n"
+ " get-cpu-version\n"
+ " get-rated\n"
+ " get-status\n"
+ " get-p-rated <id>\n"
+ " id: Parallel machine ID\n"
+ "\n"
+ " get-p-status <id>\n"
+ " id: Parallel machine ID\n"
+ "\n"
+ " get-mode\n"
+ " get-errors\n"
+ " get-flags\n"
+ " get-rated-defaults\n"
+ " get-allowed-charging-currents\n"
+ " get-allowed-ac-charging-currents\n"
+ " get-ac-charging-time\n"
+ " get-ac-loads-supply-time\n"
+ " set-loads-supply 0|1\n"
+ " set-flag <flag> 0|1\n"
+ " set-rated-defaults\n"
+ " set-max-charging-current <id> <amps>\n"
+ " id: Parallel machine ID (use 0 for single model)\n"
+ " amps: Use get-allowed-charging-currents\n"
+ " to see a list of allowed values.\n"
+ "\n"
+ " set-max-ac-charging-current <id> <amps>\n"
+ " id: Parallel machine ID (use 0 for single model)\n"
+ " amps: Use get-allowed-ac-charging-currents\n"
+ " to see a list of allowed values.\n"
+ "\n"
+ " set-ac-output-freq 50|60\n"
+ " set-max-charging-voltage <cv> <fv>\n"
+ " cv: Constant voltage (48.0 ~ 58.4).\n"
+ " fv: Float voltage (48.0 ~ 58.4).\n"
+ "\n"
+ " set-ac-output-voltage <v>\n"
+ " v: " << p18::ac_output_rated_voltages << "\n"
+ "\n"
+ " set-output-source-priority SUB|SBU\n"
+ " 'SUB' means " << p18::OutputSourcePriority::SolarUtilityBattery << "\n"
+ " 'SBU' means " << p18::OutputSourcePriority::SolarBatteryUtility << "\n"
+ "\n"
+ " set-charging-thresholds <cv> <dv>\n"
+ " Set battery re-charging and re-discharging voltages when\n"
+ " utility is available.\n"
+ "\n"
+ " cv: re-charging voltage\n"
+ " For 12 V unit: " << p18::bat_ac_recharging_voltages_12v << "\n"
+ " For 24 V unit: " << p18::bat_ac_recharging_voltages_24v << "\n"
+ " For 48 V unit: " << p18::bat_ac_recharging_voltages_48v << "\n"
+ "\n"
+ " dv: re-discharging voltage\n"
+ " For 12 V unit: " << p18::bat_ac_redischarging_voltages_12v << "\n"
+ " For 24 V unit: " << p18::bat_ac_redischarging_voltages_24v << "\n"
+ " For 48 V unit: " << p18::bat_ac_redischarging_voltages_48v << "\n"
+ "\n"
+ " set-charging-source-priority <id> <priority>\n"
+ " id: Parallel machine ID (use 0 for a single model)\n"
+ " priority: SF|SU|S\n"
+ " 'SF' means " << p18::ChargerSourcePriority::SolarFirst << ",\n"
+ " 'SU' means " << p18::ChargerSourcePriority::SolarAndUtility << "\n"
+ " 'S' means " << p18::ChargerSourcePriority::SolarOnly << "\n"
+ "\n"
+ " set-solar-power-priority BLU|LBU\n"
+ " 'BLU' means " << p18::SolarPowerPriority::BatteryLoadUtility << "\n"
+ " 'LBU' means " << p18::SolarPowerPriority::LoadBatteryUtility << "\n"
+ "\n"
+ " set-ac-input-voltage-range APPLIANCE|UPS\n"
+ " set-battery-type AGM|FLOODED|USER\n"
+ " set-output-model <id> <model>\n"
+ " id: Parallel machine ID (use 0 for a single model)\n"
+ " model: SM|P|P1|P2|P3\n"
+ " SM: " << p18::OutputModelSetting::SingleModule << "\n"
+ " P: " << p18::OutputModelSetting::ParallelOutput << "\n"
+ " P1: " << p18::OutputModelSetting::Phase1OfThreePhaseOutput << "\n"
+ " P2: " << p18::OutputModelSetting::Phase2OfThreePhaseOutput << "\n"
+ " P3: " << p18::OutputModelSetting::Phase3OfThreePhaseOutput << "\n"
+ "\n"
+ " set-battery-cut-off-voltage <v>\n"
+ " v: Cut-off voltage (40.0~48.0)\n"
+ "\n"
+ " set-solar-configuration <id>\n"
+ " id: Serial number\n"
+ "\n"
+ " clear-generated-data\n"
+ " Clear all recorded stats about generated energy.\n"
+ "\n"
+ " set-date-time <YYYY> <MM> <DD> <hh> <mm> <ss>\n"
+ " YYYY: Year\n"
+ " MM: Month\n"
+ " DD: Day\n"
+ " hh: Hours\n"
+ " mm: Minutes\n"
+ " ss: Seconds\n"
+ "\n"
+ " set-ac-charging-time <start> <end>\n"
+ " start: Starting time, hh:mm format\n"
+ " end: Ending time, hh:mm format\n"
+ "\n"
+ " set-ac-loads-supply-time <start> <end>\n"
+ " start: Starting time, hh:mm format\n"
+ " end: Ending time, hh:mm format\n"
+ "\n"
+ "Flags:\n";
+
+ for (const p18::Flag& flag: p18::flags)
+ std::cout << " " << flag.flag << ": " << flag.description << "\n";
+}
+
+static void usage(const char* progname, Protocol p) {
std::ios_base::fmtflags f(std::cout.flags());
std::cout << "Usage: " << progname << " OPTIONS [COMMAND]\n" <<
"\n"
@@ -61,6 +211,7 @@ static void usage(const char* progname) {
" --help: Show this help\n"
" --raw <DATA>: Execute arbitrary command and print response\n"
" (example: ^P005PI)\n"
+ " -p, --protocol <ID> Protocol ID (default: 18)\n"
" --device <DEVICE>: Device type to use. See below for list of supported\n"
" devices\n"
" --timeout <TIMEOUT>: Device read/write timeout, in milliseconds\n"
@@ -69,6 +220,10 @@ static void usage(const char* progname) {
" device traffic)\n"
" --format <FORMAT>: Output format for command responses\n"
"\n"
+ "Protocols:\n"
+ " 17\n"
+ " 18\n"
+ "\n"
"Device types:\n"
" usb USB device\n"
" serial Serial device\n"
@@ -87,121 +242,13 @@ static void usage(const char* progname) {
" --serial-data-bits 5|6|7|8\n"
" --serial-stop-bits 1|1.5|2\n"
" --serial-parity none|odd|even|mark|space\n"
- "\n"
- "Commands:\n"
- " get-protocol-id\n"
- " get-date-time\n"
- " get-total-generated\n"
- " get-year-generated <yyyy>\n"
- " get-month-generated <yyyy> <mm>\n"
- " get-day-generated <yyyy> <mm> <dd>\n"
- " get-series-number\n"
- " get-cpu-version\n"
- " get-rated\n"
- " get-status\n"
- " get-p-rated <id>\n"
- " id: Parallel machine ID\n"
- "\n"
- " get-p-status <id>\n"
- " id: Parallel machine ID\n"
- "\n"
- " get-mode\n"
- " get-errors\n"
- " get-flags\n"
- " get-rated-defaults\n"
- " get-allowed-charging-currents\n"
- " get-allowed-ac-charging-currents\n"
- " get-ac-charging-time\n"
- " get-ac-loads-supply-time\n"
- " set-loads-supply 0|1\n"
- " set-flag <flag> 0|1\n"
- " set-rated-defaults\n"
- " set-max-charging-current <id> <amps>\n"
- " id: Parallel machine ID (use 0 for single model)\n"
- " amps: Use get-allowed-charging-currents\n"
- " to see a list of allowed values.\n"
- "\n"
- " set-max-ac-charging-current <id> <amps>\n"
- " id: Parallel machine ID (use 0 for single model)\n"
- " amps: Use get-allowed-ac-charging-currents\n"
- " to see a list of allowed values.\n"
- "\n"
- " set-ac-output-freq 50|60\n"
- " set-max-charging-voltage <cv> <fv>\n"
- " cv: Constant voltage (48.0 ~ 58.4).\n"
- " fv: Float voltage (48.0 ~ 58.4).\n"
- "\n"
- " set-ac-output-voltage <v>\n"
- " v: " << p18::ac_output_rated_voltages << "\n"
- "\n"
- " set-output-source-priority SUB|SBU\n"
- " 'SUB' means " << p18::OutputSourcePriority::SolarUtilityBattery << "\n"
- " 'SBU' means " << p18::OutputSourcePriority::SolarBatteryUtility << "\n"
- "\n"
- " set-charging-thresholds <cv> <dv>\n"
- " Set battery re-charging and re-discharging voltages when\n"
- " utility is available.\n"
- "\n"
- " cv: re-charging voltage\n"
- " For 12 V unit: " << p18::bat_ac_recharging_voltages_12v << "\n"
- " For 24 V unit: " << p18::bat_ac_recharging_voltages_24v << "\n"
- " For 48 V unit: " << p18::bat_ac_recharging_voltages_48v << "\n"
- "\n"
- " dv: re-discharging voltage\n"
- " For 12 V unit: " << p18::bat_ac_redischarging_voltages_12v << "\n"
- " For 24 V unit: " << p18::bat_ac_redischarging_voltages_24v << "\n"
- " For 48 V unit: " << p18::bat_ac_redischarging_voltages_48v << "\n"
- "\n"
- " set-charging-source-priority <id> <priority>\n"
- " id: Parallel machine ID (use 0 for a single model)\n"
- " priority: SF|SU|S\n"
- " 'SF' means " << p18::ChargerSourcePriority::SolarFirst << ",\n"
- " 'SU' means " << p18::ChargerSourcePriority::SolarAndUtility << "\n"
- " 'S' means " << p18::ChargerSourcePriority::SolarOnly << "\n"
- "\n"
- " set-solar-power-priority BLU|LBU\n"
- " 'BLU' means " << p18::SolarPowerPriority::BatteryLoadUtility << "\n"
- " 'LBU' means " << p18::SolarPowerPriority::LoadBatteryUtility << "\n"
- "\n"
- " set-ac-input-voltage-range APPLIANCE|UPS\n"
- " set-battery-type AGM|FLOODED|USER\n"
- " set-output-model <id> <model>\n"
- " id: Parallel machine ID (use 0 for a single model)\n"
- " model: SM|P|P1|P2|P3\n"
- " SM: " << p18::OutputModelSetting::SingleModule << "\n"
- " P: " << p18::OutputModelSetting::ParallelOutput << "\n"
- " P1: " << p18::OutputModelSetting::Phase1OfThreePhaseOutput << "\n"
- " P2: " << p18::OutputModelSetting::Phase2OfThreePhaseOutput << "\n"
- " P3: " << p18::OutputModelSetting::Phase3OfThreePhaseOutput << "\n"
- "\n"
- " set-battery-cut-off-voltage <v>\n"
- " v: Cut-off voltage (40.0~48.0)\n"
- "\n"
- " set-solar-configuration <id>\n"
- " id: Serial number\n"
- "\n"
- " clear-generated-data\n"
- " Clear all recorded stats about generated energy.\n"
- "\n"
- " set-date-time <YYYY> <MM> <DD> <hh> <mm> <ss>\n"
- " YYYY: Year\n"
- " MM: Month\n"
- " DD: Day\n"
- " hh: Hours\n"
- " mm: Minutes\n"
- " ss: Seconds\n"
- "\n"
- " set-ac-charging-time <start> <end>\n"
- " start: Starting time, hh:mm format\n"
- " end: Ending time, hh:mm format\n"
- "\n"
- " set-ac-loads-supply-time <start> <end>\n"
- " start: Starting time, hh:mm format\n"
- " end: Ending time, hh:mm format\n"
- "\n"
- "Flags:\n";
- for (const p18::Flag& flag: p18::flags)
- std::cout << " " << flag.flag << ": " << flag.description << "\n";
+ "\n";
+
+ switch (p) {
+ case Protocol::P17: usage_p17(); break;
+ case Protocol::P18: usage_p18(); break;
+ }
+
std::cout <<
"\n"
"Formats:\n"
@@ -219,7 +266,7 @@ static void output_formatted_error(formatter::Format format, std::exception& e,
buf << s << ": ";
buf << e.what();
- auto err = p18::response_type::ErrorResponse(buf.str());
+ auto err = protocol::ErrorResponse(buf.str());
auto output = err.format(format);
if (format == formatter::Format::JSON) {
@@ -245,8 +292,9 @@ int main(int argc, char *argv[]) {
Action action = Action::Command;
u64 timeout = voltronic::Device::TIMEOUT;
bool verbose = false;
- p18::CommandType commandType;
+ int commandType;
std::vector<std::string> arguments;
+ Protocol protocol = Protocol::P18;
// format params
bool formatChanged = false;
@@ -273,6 +321,7 @@ int main(int argc, char *argv[]) {
{"help", no_argument, nullptr, LO_HELP},
{"verbose", no_argument, nullptr, LO_VERBOSE},
{"raw", required_argument, nullptr, LO_RAW},
+ {"protocol", required_argument, nullptr, LO_PROTOCOL},
{"timeout", required_argument, nullptr, LO_TIMEOUT},
{"format", required_argument, nullptr, LO_FORMAT},
{"device", required_argument, nullptr, LO_DEVICE},
@@ -287,7 +336,7 @@ int main(int argc, char *argv[]) {
};
bool getoptError = false; // FIXME
- while ((opt = getopt_long(argc, argv, "h", long_options, nullptr)) != EOF) {
+ while ((opt = getopt_long(argc, argv, "hp:", long_options, nullptr)) != EOF) {
if (opt == '?') {
getoptError = true;
break;
@@ -295,10 +344,26 @@ int main(int argc, char *argv[]) {
// simple options (flags), no arguments
switch (opt) {
- case 'h': action = Action::ShortHelp; continue;
- case LO_HELP: action = Action::FullHelp; continue;
- case LO_VERBOSE: verbose = true; continue;
- default: break;
+ case 'h':
+ action = Action::ShortHelp;
+ continue;
+
+ case 'p': {
+ std::string s(optarg);
+ protocol = protocol_from_string(s);
+ break;
+ }
+
+ case LO_HELP:
+ action = Action::FullHelp;
+ continue;
+
+ case LO_VERBOSE:
+ verbose = true;
+ continue;
+
+ default:
+ break;
}
// options with arguments
@@ -331,6 +396,10 @@ int main(int argc, char *argv[]) {
action = Action::Raw;
break;
+ case LO_PROTOCOL:
+ protocol = protocol_from_string(arg);
+ break;
+
case LO_TIMEOUT:
timeout = std::stoull(arg);
break;
@@ -408,7 +477,7 @@ int main(int argc, char *argv[]) {
break;
case Action::FullHelp:
- usage(argv[0]);
+ usage(argv[0], protocol);
break;
case Action::Command: {
@@ -417,8 +486,16 @@ int main(int argc, char *argv[]) {
std::string command = argv[optind++];
- p18::CommandInput input{argc, argv};
- commandType = p18::validate_input(command, arguments, (void*)&input);
+ protocol::CommandInput input{argc, argv};
+ switch (protocol) {
+ case Protocol::P17:
+ commandType = static_cast<int>(p17::validate_input(command, arguments, (void*)&input));
+ break;
+
+ case Protocol::P18:
+ commandType = static_cast<int>(p18::validate_input(command, arguments, (void*)&input));
+ break;
+ }
break;
}
@@ -457,6 +534,8 @@ int main(int argc, char *argv[]) {
break;
}
+ dev->setFlags(voltronic::FLAG_READ_CRC | voltronic::FLAG_VERIFY_CRC);
+
dev->setVerbose(verbose);
dev->setTimeout(timeout);
@@ -484,7 +563,7 @@ int main(int argc, char *argv[]) {
catch (voltronic::InvalidDataError& e) {
output_formatted_error(format, e, "data is invalid");
}
- catch (p18::InvalidResponseError& e) {
+ catch (protocol::InvalidResponseError& e) {
output_formatted_error(format, e, "response is invalid");
}
diff --git a/src/p18/commands.cc b/src/p18/commands.cc
deleted file mode 100644
index 5060d49..0000000
--- a/src/p18/commands.cc
+++ /dev/null
@@ -1,457 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-
-#include <stdexcept>
-#include <sstream>
-#include <vector>
-#include <string>
-
-#ifdef INVERTERCTL
-#include <getopt.h>
-#endif
-
-#include "commands.h"
-#include "defines.h"
-#include "functions.h"
-#include "../util.h"
-#include "../logging.h"
-
-namespace p18 {
-
-const std::map<std::string, p18::CommandType> client_commands = {
- {"get-protocol-id", p18::CommandType::GetProtocolID},
- {"get-date-time", p18::CommandType::GetCurrentTime},
- {"get-total-generated", p18::CommandType::GetTotalGenerated},
- {"get-year-generated", p18::CommandType::GetYearGenerated},
- {"get-month-generated", p18::CommandType::GetMonthGenerated},
- {"get-day-generated", p18::CommandType::GetDayGenerated},
- {"get-series-number", p18::CommandType::GetSeriesNumber},
- {"get-cpu-version", p18::CommandType::GetCPUVersion},
- {"get-rated", p18::CommandType::GetRatedInformation},
- {"get-status", p18::CommandType::GetGeneralStatus},
- {"get-mode", p18::CommandType::GetWorkingMode},
- {"get-errors", p18::CommandType::GetFaultsAndWarnings},
- {"get-flags", p18::CommandType::GetFlagsAndStatuses},
- {"get-rated-defaults", p18::CommandType::GetDefaults},
- {"get-allowed-charging-currents", p18::CommandType::GetAllowedChargingCurrents},
- {"get-allowed-ac-charging-currents", p18::CommandType::GetAllowedACChargingCurrents},
- {"get-p-rated", p18::CommandType::GetParallelRatedInformation},
- {"get-p-status", p18::CommandType::GetParallelGeneralStatus},
- {"get-ac-charging-time", p18::CommandType::GetACChargingTimeBucket},
- {"get-ac-loads-supply-time", p18::CommandType::GetACLoadsSupplyTimeBucket},
- {"set-loads-supply", p18::CommandType::SetLoads},
- {"set-flag", p18::CommandType::SetFlag},
- {"set-rated-defaults", p18::CommandType::SetDefaults},
- {"set-max-charging-current", p18::CommandType::SetBatteryMaxChargingCurrent},
- {"set-max-ac-charging-current", p18::CommandType::SetBatteryMaxACChargingCurrent},
- {"set-ac-output-freq", p18::CommandType::SetACOutputFreq},
- {"set-max-charging-voltage", p18::CommandType::SetBatteryMaxChargingVoltage},
- {"set-ac-output-voltage", p18::CommandType::SetACOutputRatedVoltage},
- {"set-output-source-priority", p18::CommandType::SetOutputSourcePriority},
- {"set-charging-thresholds", p18::CommandType::SetBatteryChargingThresholds}, /* Battery re-charging and re-discharging voltage when utility is available */
- {"set-charging-source-priority", p18::CommandType::SetChargingSourcePriority},
- {"set-solar-power-priority", p18::CommandType::SetSolarPowerPriority},
- {"set-ac-input-voltage-range", p18::CommandType::SetACInputVoltageRange},
- {"set-battery-type", p18::CommandType::SetBatteryType},
- {"set-output-model", p18::CommandType::SetOutputModel},
- {"set-battery-cut-off-voltage", p18::CommandType::SetBatteryCutOffVoltage},
- {"set-solar-configuration", p18::CommandType::SetSolarConfig},
- {"clear-generated-data", p18::CommandType::ClearGenerated},
- {"set-date-time", p18::CommandType::SetDateTime},
- {"set-ac-charging-time", p18::CommandType::SetACChargingTimeBucket},
- {"set-ac-loads-supply-time", p18::CommandType::SetACLoadsSupplyTimeBucket},
-};
-
-static void validate_date_args(const std::string* ys, const std::string* ms, const std::string* ds) {
- static const std::string err_year = "invalid year";
- static const std::string err_month = "invalid month";
- static const std::string err_day = "invalid day";
-
- int y, m = 0, d = 0;
-
- // validate year
- if (!is_numeric(*ys) || ys->size() != 4)
- throw std::invalid_argument(err_year);
-
- y = std::stoi(*ys);
- if (y < 2000 || y > 2099)
- throw std::invalid_argument(err_year);
-
- // validate month
- if (ms != nullptr) {
- if (!is_numeric(*ms) || ms->size() > 2)
- throw std::invalid_argument(err_month);
-
- m = std::stoi(*ms);
- if (m < 1 || m > 12)
- throw std::invalid_argument(err_month);
- }
-
- // validate day
- if (ds != nullptr) {
- if (!is_numeric(*ds) || ds->size() > 2)
- throw std::invalid_argument(err_day);
-
- d = std::stoi(*ds);
- if (d < 1 || d > 31)
- throw std::invalid_argument(err_day);
- }
-
- if (y != 0 && m != 0 && d != 0) {
- if (!is_date_valid(y, m, d))
- throw std::invalid_argument("invalid date");
- }
-}
-
-static void validate_time_args(const std::string* hs, const std::string* ms, const std::string* ss) {
- static const std::string err_hour = "invalid hour";
- static const std::string err_minute = "invalid minute";
- static const std::string err_second = "invalid second";
-
- unsigned h, m, s;
-
- if (!is_numeric(*hs) || hs->size() > 2)
- throw std::invalid_argument(err_hour);
-
- h = static_cast<unsigned>(std::stoul(*hs));
- if (h > 23)
- throw std::invalid_argument(err_hour);
-
- if (!is_numeric(*ms) || ms->size() > 2)
- throw std::invalid_argument(err_minute);
-
- m = static_cast<unsigned>(std::stoul(*ms));
- if (m > 59)
- throw std::invalid_argument(err_minute);
-
- if (!is_numeric(*ss) || ss->size() > 2)
- throw std::invalid_argument(err_second);
-
- s = static_cast<unsigned>(std::stoul(*ss));
- if (s > 59)
- throw std::invalid_argument(err_second);
-}
-
-
-#define GET_ARGS(__len__) get_args((CommandInput*)input, arguments, (__len__))
-
-#ifdef INVERTERCTL
-static void get_args(CommandInput* input,
- std::vector<std::string>& arguments,
- size_t count) {
- for (size_t i = 0; i < count; i++) {
- if (optind < input->argc && *input->argv[optind] != '-')
- arguments.emplace_back(input->argv[optind++]);
- else {
- std::ostringstream error;
- error << "this command requires " << count << " argument";
- if (count > 1)
- error << "s";
- throw std::invalid_argument(error.str());
- }
- }
-}
-#endif
-
-#ifdef INVERTERD
-static void get_args(CommandInput* input,
- std::vector<std::string>& arguments,
- size_t count) {
- if (input->argv->size() < count) {
- std::ostringstream error;
- error << "this command requires " << count << " argument";
- if (count > 1)
- error << "s";
- throw std::invalid_argument(error.str());
- }
-
- for (size_t i = 0; i < count; i++)
- arguments.emplace_back((*input->argv)[i]);
-}
-#endif
-
-p18::CommandType validate_input(std::string& command,
- std::vector<std::string>& arguments,
- void* input) {
- auto it = p18::client_commands.find(command);
- if (it == p18::client_commands.end())
- throw std::invalid_argument("invalid command");
-
- auto commandType = it->second;
- switch (commandType) {
- case p18::CommandType::GetYearGenerated:
- GET_ARGS(1);
- validate_date_args(&arguments[0], nullptr, nullptr);
- break;
-
- case p18::CommandType::GetMonthGenerated:
- GET_ARGS(2);
- validate_date_args(&arguments[0], &arguments[1], nullptr);
- break;
-
- case p18::CommandType::GetDayGenerated:
- GET_ARGS(3);
- validate_date_args(&arguments[0], &arguments[1], &arguments[2]);
- break;
-
- case p18::CommandType::GetParallelRatedInformation:
- case p18::CommandType::GetParallelGeneralStatus:
- GET_ARGS(1);
- if (!is_numeric(arguments[0]) || arguments[0].size() > 1)
- throw std::invalid_argument("invalid argument");
- break;
-
- case p18::CommandType::SetLoads: {
- GET_ARGS(1);
- std::string &arg = arguments[0];
- if (arg != "0" && arg != "1")
- throw std::invalid_argument("invalid argument, only 0 or 1 allowed");
- break;
- }
-
- case p18::CommandType::SetFlag: {
- GET_ARGS(2);
-
- bool match_found = false;
- for (auto const& item: p18::flags) {
- if (arguments[0] == item.flag) {
- arguments[0] = item.letter;
- match_found = true;
- break;
- }
- }
-
- if (!match_found)
- throw std::invalid_argument("invalid flag");
-
- if (arguments[1] != "0" && arguments[1] != "1")
- throw std::invalid_argument("invalid flag state, only 0 or 1 allowed");
-
- break;
- }
-
- case p18::CommandType::SetBatteryMaxChargingCurrent:
- case p18::CommandType::SetBatteryMaxACChargingCurrent: {
- GET_ARGS(2);
-
- auto id = static_cast<unsigned>(std::stoul(arguments[0]));
- auto amps = static_cast<unsigned>(std::stoul(arguments[1]));
-
- if (!p18::is_valid_parallel_id(id))
- throw std::invalid_argument("invalid id");
-
- // 3 characters max
- if (amps > 999)
- throw std::invalid_argument("invalid amps");
-
- break;
- }
-
- case p18::CommandType::SetACOutputFreq: {
- GET_ARGS(1);
- std::string &freq = arguments[0];
- if (freq != "50" && freq != "60")
- throw std::invalid_argument("invalid frequency, only 50 or 60 allowed");
- break;
- }
-
- case p18::CommandType::SetBatteryMaxChargingVoltage: {
- GET_ARGS(2);
-
- float cv = std::stof(arguments[0]);
- float fv = std::stof(arguments[1]);
-
- if (cv < 48.0 || cv > 58.4)
- throw std::invalid_argument("invalid CV");
-
- if (fv < 48.0 || fv > 58.4)
- throw std::invalid_argument("invalid FV");
-
- break;
- }
-
- case p18::CommandType::SetACOutputRatedVoltage: {
- GET_ARGS(1);
-
- auto v = static_cast<unsigned>(std::stoul(arguments[0]));
-
- bool matchFound = false;
- for (const auto &item: p18::ac_output_rated_voltages) {
- if (v == item) {
- matchFound = true;
- break;
- }
- }
-
- if (!matchFound)
- throw std::invalid_argument("invalid voltage");
-
- break;
- }
-
- case p18::CommandType::SetOutputSourcePriority: {
- GET_ARGS(1);
-
- std::array<std::string, 2> priorities({"SUB", "SBU"});
-
- long index = index_of(priorities, arguments[0]);
- if (index == -1)
- throw std::invalid_argument("invalid argument");
-
- arguments[0] = std::to_string(index);
- break;
- }
-
- case p18::CommandType::SetBatteryChargingThresholds: {
- GET_ARGS(2);
-
- float cv = std::stof(arguments[0]);
- float dv = std::stof(arguments[1]);
-
- if (index_of(p18::bat_ac_recharging_voltages_12v, cv) == -1 &&
- index_of(p18::bat_ac_recharging_voltages_24v, cv) == -1 &&
- index_of(p18::bat_ac_recharging_voltages_48v, cv) == -1)
- throw std::invalid_argument("invalid CV");
-
- if (index_of(p18::bat_ac_redischarging_voltages_12v, dv) == -1 &&
- index_of(p18::bat_ac_redischarging_voltages_24v, dv) == -1 &&
- index_of(p18::bat_ac_redischarging_voltages_48v, dv) == -1)
- throw std::invalid_argument("invalid DV");
-
- break;
- }
-
- case p18::CommandType::SetChargingSourcePriority: {
- GET_ARGS(2);
-
- auto id = static_cast<unsigned>(std::stoul(arguments[0]));
- if (!p18::is_valid_parallel_id(id))
- throw std::invalid_argument("invalid id");
-
- std::array<std::string, 3> priorities({"SF", "SU", "S"});
- long index = index_of(priorities, arguments[1]);
- if (index == -1)
- throw std::invalid_argument("invalid argument");
-
- arguments[1] = std::to_string(index);
- break;
- }
-
- case p18::CommandType::SetSolarPowerPriority: {
- GET_ARGS(1);
-
- std::array<std::string, 2> allowed({"BLU", "LBU"});
- long index = index_of(allowed, arguments[0]);
- if (index == -1)
- throw std::invalid_argument("invalid priority");
-
- arguments[0] = std::to_string(index);
- break;
- }
-
- case p18::CommandType::SetACInputVoltageRange: {
- GET_ARGS(1);
- std::array<std::string, 2> allowed({"APPLIANCE", "UPS"});
- long index = index_of(allowed, arguments[0]);
- if (index == -1)
- throw std::invalid_argument("invalid argument");
- arguments[0] = std::to_string(index);
- break;
- }
-
- case p18::CommandType::SetBatteryType: {
- GET_ARGS(1);
-
- std::array<std::string, 3> allowed({"AGM", "FLOODED", "USER"});
- long index = index_of(allowed, arguments[0]);
- if (index == -1)
- throw std::invalid_argument("invalid type");
- arguments[0] = std::to_string(index);
-
- break;
- }
-
- case p18::CommandType::SetOutputModel: {
- GET_ARGS(2);
-
- auto id = static_cast<unsigned>(std::stoul(arguments[0]));
- if (!p18::is_valid_parallel_id(id))
- throw std::invalid_argument("invalid id");
-
- std::array<std::string, 5> allowed({"SM", "P", "P1", "P2", "P3"});
- long index = index_of(allowed, arguments[1]);
- if (index == -1)
- throw std::invalid_argument("invalid model");
- arguments[1] = std::to_string(index);
-
- break;
- }
-
- case p18::CommandType::SetBatteryCutOffVoltage: {
- GET_ARGS(1);
-
- float v = std::stof(arguments[0]);
- if (v < 40.0 || v > 48.0)
- throw std::invalid_argument("invalid voltage");
-
- break;
- }
-
- case p18::CommandType::SetSolarConfig: {
- GET_ARGS(1);
-
- if (!is_numeric(arguments[0]) || arguments[0].size() > 20)
- throw std::invalid_argument("invalid argument");
-
- break;
- }
-
- case p18::CommandType::SetDateTime: {
- GET_ARGS(6);
-
- validate_date_args(&arguments[0], &arguments[1], &arguments[2]);
- validate_time_args(&arguments[3], &arguments[4], &arguments[5]);
-
- break;
- }
-
- case p18::CommandType::SetACChargingTimeBucket:
- case p18::CommandType::SetACLoadsSupplyTimeBucket: {
- GET_ARGS(2);
-
- std::vector<std::string> start = split(arguments[0], ':');
- if (start.size() != 2)
- throw std::invalid_argument("invalid start time");
-
- std::vector<std::string> end = split(arguments[1], ':');
- if (end.size() != 2)
- throw std::invalid_argument("invalid end time");
-
- auto startHour = static_cast<unsigned short>(std::stoul(start[0]));
- auto startMinute = static_cast<unsigned short>(std::stoul(start[1]));
- if (startHour > 23 || startMinute > 59)
- throw std::invalid_argument("invalid start time");
-
- auto endHour = static_cast<unsigned short>(std::stoul(end[0]));
- auto endMinute = static_cast<unsigned short>(std::stoul(end[1]));
- if (endHour > 23 || endMinute > 59)
- throw std::invalid_argument("invalid end time");
-
- arguments.clear();
-
- arguments.emplace_back(std::to_string(startHour));
- arguments.emplace_back(std::to_string(startMinute));
-
- arguments.emplace_back(std::to_string(endHour));
- arguments.emplace_back(std::to_string(endMinute));
-
- break;
- }
-
- default:
- break;
- }
-
- return commandType;
-}
-
-} \ No newline at end of file
diff --git a/src/p18/commands.h b/src/p18/commands.h
deleted file mode 100644
index 0b597e9..0000000
--- a/src/p18/commands.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-
-#ifndef INVERTER_TOOLS_P18_COMMANDS_H
-#define INVERTER_TOOLS_P18_COMMANDS_H
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "types.h"
-
-namespace p18 {
-
-#ifdef INVERTERCTL
-struct CommandInput {
- int argc;
- char** argv;
-};
-#endif
-
-#ifdef INVERTERD
-struct CommandInput {
- std::vector<std::string>* argv;
-};
-#endif
-
-extern const std::map<std::string, p18::CommandType> client_commands;
-
-static void validate_date_args(const std::string* ys, const std::string* ms, const std::string* ds);
-static void validate_time_args(const std::string* hs, const std::string* ms, const std::string* ss);
-CommandType validate_input(std::string& command, std::vector<std::string>& arguments, void* input);
-
-}
-
-#endif //INVERTER_TOOLS_P18_COMMANDS_H
diff --git a/src/p18/functions.h b/src/p18/functions.h
deleted file mode 100644
index c372242..0000000
--- a/src/p18/functions.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-
-#ifndef INFINISOLAR_TOOLS_P18_FUNCTIONS_H
-#define INFINISOLAR_TOOLS_P18_FUNCTIONS_H
-
-namespace p18 {
-
-bool is_valid_parallel_id(unsigned id);
-
-}
-
-#endif //INFINISOLAR_TOOLS_P18_FUNCTIONS_H
diff --git a/src/protocol/client.cc b/src/protocol/client.cc
new file mode 100644
index 0000000..40ffa36
--- /dev/null
+++ b/src/protocol/client.cc
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include "client.h"
+
+namespace protocol {
+
+void BaseClient::setDevice(std::shared_ptr<voltronic::Device> device) {
+ device_ = std::move(device);
+}
+
+std::pair<std::shared_ptr<char>, size_t> BaseClient::runOnDevice(std::string& raw) {
+ size_t bufSize = 256;
+ std::shared_ptr<char> buf(new char[bufSize]);
+ size_t responseSize = device_->run(
+ (const u8*)raw.c_str(), raw.size(),
+ (u8*)buf.get(), bufSize);
+
+ return std::pair<std::shared_ptr<char>, size_t>(buf, responseSize);
+}
+
+} \ No newline at end of file
diff --git a/src/protocol/client.h b/src/protocol/client.h
new file mode 100644
index 0000000..8ed49aa
--- /dev/null
+++ b/src/protocol/client.h
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_PROTOCOL_CLIENT_H
+#define INVERTER_TOOLS_PROTOCOL_CLIENT_H
+
+#include "../voltronic/device.h"
+#include "response.h"
+
+namespace protocol {
+
+class BaseClient {
+private:
+ std::shared_ptr<voltronic::Device> device_;
+
+public:
+ void setDevice(std::shared_ptr<voltronic::Device> device);
+ virtual std::shared_ptr<BaseResponse> execute(int commandType, std::vector<std::string>& arguments) = 0;
+ std::pair<std::shared_ptr<char>, size_t> runOnDevice(std::string& raw);
+};
+
+}
+
+#endif //INVERTER_TOOLS_PROTOCOL_CLIENT_H
diff --git a/src/p18/exceptions.h b/src/protocol/exceptions.h
index 9b79082..4febe28 100644
--- a/src/p18/exceptions.h
+++ b/src/protocol/exceptions.h
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: BSD-3-Clause
-#ifndef INFINISOLAR_TOOLS_P18_EXCEPTIONS_H
-#define INFINISOLAR_TOOLS_P18_EXCEPTIONS_H
+#ifndef INVERTER_TOOLS_PROTOCOL_EXCEPTIONS_H
+#define INVERTER_TOOLS_PROTOCOL_EXCEPTIONS_H
#include <stdexcept>
-namespace p18 {
+namespace protocol {
class InvalidResponseError : public std::runtime_error {
public:
@@ -19,4 +19,4 @@ public:
}
-#endif //INFINISOLAR_TOOLS_P18_EXCEPTIONS_H
+#endif //INVERTER_TOOLS_PROTOCOL_EXCEPTIONS_H
diff --git a/src/protocol/input.cc b/src/protocol/input.cc
new file mode 100644
index 0000000..a44ee3e
--- /dev/null
+++ b/src/protocol/input.cc
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <stdexcept>
+#include <sstream>
+#include <vector>
+#include <string>
+
+#ifdef INVERTERCTL
+#include <getopt.h>
+#endif
+
+#include "../util.h"
+#include "input.h"
+
+namespace protocol {
+
+#ifdef INVERTERCTL
+void get_args(CommandInput* input, std::vector<std::string>& arguments, size_t count) {
+ for (size_t i = 0; i < count; i++) {
+ if (optind < input->argc && *input->argv[optind] != '-')
+ arguments.emplace_back(input->argv[optind++]);
+ else {
+ std::ostringstream error;
+ error << "this command requires " << count << " argument";
+ if (count > 1)
+ error << "s";
+ throw std::invalid_argument(error.str());
+ }
+ }
+}
+#endif
+
+#ifdef INVERTERD
+void get_args(CommandInput* input, std::vector<std::string>& arguments, size_t count) {
+ if (input->argv->size() < count) {
+ std::ostringstream error;
+ error << "this command requires " << count << " argument";
+ if (count > 1)
+ error << "s";
+ throw std::invalid_argument(error.str());
+ }
+
+ for (size_t i = 0; i < count; i++)
+ arguments.emplace_back((*input->argv)[i]);
+}
+#endif
+
+void validate_date_args(const std::string* ys, const std::string* ms, const std::string* ds) {
+ static const std::string err_year = "invalid year";
+ static const std::string err_month = "invalid month";
+ static const std::string err_day = "invalid day";
+
+ int y, m = 0, d = 0;
+
+ // validate year
+ if (!is_numeric(*ys) || ys->size() != 4)
+ throw std::invalid_argument(err_year);
+
+ y = std::stoi(*ys);
+ if (y < 2000 || y > 2099)
+ throw std::invalid_argument(err_year);
+
+ // validate month
+ if (ms != nullptr) {
+ if (!is_numeric(*ms) || ms->size() > 2)
+ throw std::invalid_argument(err_month);
+
+ m = std::stoi(*ms);
+ if (m < 1 || m > 12)
+ throw std::invalid_argument(err_month);
+ }
+
+ // validate day
+ if (ds != nullptr) {
+ if (!is_numeric(*ds) || ds->size() > 2)
+ throw std::invalid_argument(err_day);
+
+ d = std::stoi(*ds);
+ if (d < 1 || d > 31)
+ throw std::invalid_argument(err_day);
+ }
+
+ if (y != 0 && m != 0 && d != 0) {
+ if (!is_date_valid(y, m, d))
+ throw std::invalid_argument("invalid date");
+ }
+}
+
+void validate_time_args(const std::string* hs, const std::string* ms, const std::string* ss) {
+ static const std::string err_hour = "invalid hour";
+ static const std::string err_minute = "invalid minute";
+ static const std::string err_second = "invalid second";
+
+ unsigned h, m, s;
+
+ if (!is_numeric(*hs) || hs->size() > 2)
+ throw std::invalid_argument(err_hour);
+
+ h = static_cast<unsigned>(std::stoul(*hs));
+ if (h > 23)
+ throw std::invalid_argument(err_hour);
+
+ if (!is_numeric(*ms) || ms->size() > 2)
+ throw std::invalid_argument(err_minute);
+
+ m = static_cast<unsigned>(std::stoul(*ms));
+ if (m > 59)
+ throw std::invalid_argument(err_minute);
+
+ if (!is_numeric(*ss) || ss->size() > 2)
+ throw std::invalid_argument(err_second);
+
+ s = static_cast<unsigned>(std::stoul(*ss));
+ if (s > 59)
+ throw std::invalid_argument(err_second);
+}
+
+} \ No newline at end of file
diff --git a/src/protocol/input.h b/src/protocol/input.h
new file mode 100644
index 0000000..1791f9a
--- /dev/null
+++ b/src/protocol/input.h
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_PROTOCOL_COMMON_COMMANDS_H
+#define INVERTER_TOOLS_PROTOCOL_COMMON_COMMANDS_H
+
+#include <string>
+#include <vector>
+
+#define GET_ARGS(__len__) protocol::get_args((protocol::CommandInput*)input, arguments, (__len__))
+
+namespace protocol {
+
+#ifdef INVERTERCTL
+struct CommandInput {
+ int argc;
+ char **argv;
+};
+#endif
+
+#ifdef INVERTERD
+struct CommandInput {
+ std::vector<std::string>* argv;
+};
+#endif
+
+void get_args(CommandInput* input, std::vector<std::string>& arguments, size_t count);
+void validate_date_args(const std::string* ys, const std::string* ms, const std::string* ds);
+void validate_time_args(const std::string* hs, const std::string* ms, const std::string* ss);
+
+}
+
+#endif //INVERTER_TOOLS_PROTOCOL_COMMON_COMMANDS_H
diff --git a/src/protocol/response.cc b/src/protocol/response.cc
new file mode 100644
index 0000000..0d6eeab
--- /dev/null
+++ b/src/protocol/response.cc
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include "response.h"
+
+namespace protocol {
+
+BaseResponse::BaseResponse(std::shared_ptr<char> raw, size_t rawSize)
+ : raw_(std::move(raw)), rawSize_(rawSize)
+{}
+
+
+formattable_ptr ErrorResponse::format(formatter::Format format)
+{
+ return std::shared_ptr<formatter::Status>(
+ new formatter::Status(format, false, error_)
+ );
+}
+
+}
diff --git a/src/protocol/response.h b/src/protocol/response.h
new file mode 100644
index 0000000..e6e258d
--- /dev/null
+++ b/src/protocol/response.h
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_PROTOCOL_RESPONSE_H
+#define INVERTER_TOOLS_PROTOCOL_RESPONSE_H
+
+#include <memory>
+#include "../formatter/formatter.h"
+
+namespace protocol {
+
+typedef std::shared_ptr<formatter::Formattable> formattable_ptr;
+
+class BaseResponse {
+protected:
+ std::shared_ptr<char> raw_;
+ size_t rawSize_;
+
+public:
+ BaseResponse(std::shared_ptr<char> raw, size_t rawSize);
+ virtual ~BaseResponse() = default;
+ virtual bool validate() = 0;
+ virtual void unpack() = 0;
+ virtual formattable_ptr format(formatter::Format format) = 0;
+};
+
+
+class ErrorResponse : public BaseResponse {
+private:
+ std::string error_;
+
+public:
+ explicit ErrorResponse(std::string error)
+ : BaseResponse(nullptr, 0), error_(std::move(error)) {}
+
+ bool validate() override {
+ return true;
+ }
+ void unpack() override {}
+ formattable_ptr format(formatter::Format format) override;
+};
+
+}
+
+#endif //INVERTER_TOOLS_PROTOCOL_RESPONSE_H
diff --git a/src/protocol_17/defines.cc b/src/protocol_17/defines.cc
new file mode 100644
index 0000000..2589861
--- /dev/null
+++ b/src/protocol_17/defines.cc
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <iostream>
+
+#include "defines.h"
+#include "types.h"
+
+namespace p17 {
+
+const std::map<CommandType, std::string> raw_commands = {
+ {CommandType::GetProtocolID, "PI"},
+ {CommandType::GetSeriesNumber, "ID"},
+ {CommandType::GetCPUVersion, "VFW"},
+ {CommandType::GetSecondaryCPUVersion, "VFW2"},
+ {CommandType::GetDeviceModel, "MD"},
+ {CommandType::GetRatedInformation, "PIRI"},
+ {CommandType::GetGeneralStatus, "GS"},
+ {CommandType::GetPowerStatus, "PS"},
+ {CommandType::GetWorkingMode, "MOD"},
+ {CommandType::GetWarningStatus, "WS"},
+ {CommandType::GetFlags, "FLAG"},
+ {CommandType::GetCurrentTime, "T"},
+ {CommandType::GetTotalGenerated, "ET"},
+ {CommandType::GetYearGenerated, "EY"},
+ {CommandType::GetMonthGenerated, "EM"},
+ {CommandType::GetDayGenerated, "ED"},
+ {CommandType::GetHourGenerated, "EH"},
+ {CommandType::GetACInputVoltageRange, "GOV"},
+ {CommandType::GetACInputFrequencyRange, "GOF"},
+ {CommandType::GetMaximumOutputPower, "OPMP"},
+ {CommandType::GetMaximumGridOutputPower, "GPMP"},
+ {CommandType::GetSolarInputMPPTRange, "MPPTV"},
+ {CommandType::GetSolarInputVoltageRange, "SV"},
+ {CommandType::GetLCDSleepTimeout, "LST"},
+ {CommandType::GetDefaults, "DI"},
+ {CommandType::GetBatterySettings, "BATS"},
+ {CommandType::GetMachineModel, "DM"},
+ {CommandType::GetMachineAdjustableRanges, "MAR"},
+ {CommandType::GetFaults, "CFS"},
+ {CommandType::GetFaultsHistory, "HFS"},
+ {CommandType::GetEnergyControlStatus, "HECS"},
+ {CommandType::GetACInputLongTimeHighestAvgVoltage, "GLTHV"},
+ {CommandType::GetFirstGeneratedEnergySavedTime, "FET"},
+ {CommandType::GetFeedWaitTime, "FT"},
+ {CommandType::GetACChargingTimeBucket, "ACCT"},
+ {CommandType::GetACLoadsSupplyTimeBucket, "ACLT"},
+ {CommandType::GetFeedingGridPowerCalibration, "FPADJ"},
+ {CommandType::GetFeedInPowerFactor, "FPPF"},
+ {CommandType::GetAutoAdjustPFWithPowerInformation, "AAPF"},
+ {CommandType::GetAllowOneOfSTPhaseLossStatus, "PLE"},
+ {CommandType::SetLoads, "LON"},
+ {CommandType::SetFlag, "P"},
+ {CommandType::SetDateTime, "DAT"},
+ {CommandType::SetACInputHighestVoltageForFeedingPower, "GOHV"},
+ {CommandType::SetACInputHighestFrequencyForFeedingPower, "GOHF"},
+ {CommandType::SetACInputLowestVoltageForFeedingPower, "GOLV"},
+ {CommandType::SetACInputLowestFrequencyForFeedingPower, "GOLF"},
+ {CommandType::SetMaxOutputPower, "OPMP"},
+ {CommandType::SetMaxFeedingGridPower, "GPMP"},
+ {CommandType::SetSolarInputHighestVoltage, "SIHV"},
+ {CommandType::SetSolarInputLowestVoltage, "SILV"},
+ {CommandType::SetSolarInputHighestMPPTVoltage, "MPPTHV"},
+ {CommandType::SetSolarInputLowestMPPTVoltage, "MPPTLV"},
+ {CommandType::SetLCDSleepTimeout, "LST"},
+ {CommandType::SetBatteryMaxChargingCurrent, "MCHGC"},
+ {CommandType::SetBatteryMaxChargingVoltage, "MCHGV"},
+ {CommandType::SetACInputLongTimeHighestAverageVoltage, "GLTHV"},
+ {CommandType::SetBatteryDischargingVoltage, "BATDV"},
+ {CommandType::SetSolarEnergyDistributionOfPriority, "SEP"},
+ {CommandType::SetEnergyDistribution, "ED"},
+ {CommandType::SetBatteryChargerApplicationInFloatingCharging, "BCA"},
+ {CommandType::SetMachineModel, "DM"},
+ {CommandType::SetDefaults, "PF"},
+ {CommandType::SetACOutputFreq, "F"},
+ {CommandType::SetACOutputRatedVoltage, "V"},
+ {CommandType::SetFeedWaitTime, "FT"},
+ {CommandType::SetACChargingTimeBucket, "ACCT"},
+ {CommandType::SetACLoadsSupplyTimeBucket, "ACLT"},
+ {CommandType::SetBatteryType, "BT"},
+ {CommandType::SetBatteryInstallTime, "BIT"},
+ {CommandType::SetLiFeBatterySelfTest, "BST"},
+ {CommandType::SetACChargerKeepBatteryVoltageSetting, "ACCB"},
+ {CommandType::SetBatteryTempSensorCompensation, "BTS"},
+ {CommandType::SetBatteryMaxACChargingCurrent, "MUCHGC"},
+ {CommandType::SetFeedingGridPowerCalibration, "FPADJ"},
+ {CommandType::SetBatteryMaxDischargingCurrentInHybridMode, "BDCM"},
+ {CommandType::SetFeedInPowerFactor, "FPPF"},
+ {CommandType::SetParallelOutput, "PALE"},
+ {CommandType::SetRPhaseFeedingGridPowerCalibration, "FPRADJ"},
+ {CommandType::SetSPhaseFeedingGridPowerCalibration, "FPSADJ"},
+ {CommandType::SetTPhaseFeedingGridPowerCalibration, "FPTADJ"},
+ {CommandType::SetAutoAdjustPFWithPowerInformation, "AAPF"},
+ {CommandType::SetAllowOneOfSTPhaseLoss, "PLE"},
+ {CommandType::SetEmergencyPowerSupplyControl, "EPS"}
+};
+
+const std::map<int, std::string> fault_codes = {
+ {1, "BUS exceed the upper limit"},
+ {2, "BUS dropp to the lower limit"},
+ {3, "BUS soft start circuit timeout"},
+ {4, "Inverter voltage soft start timeout"},
+ {5, "Inverter current exceed the upper limit"},
+ {6, "Temperature over"},
+ {7, "Inverter relay work abnormal"},
+ {8, "Current sample abnormal when inverter doesn't work"},
+ {9, "Solar input voltage exceed upper limit"},
+ {10, "SPS power voltage abnormal"},
+ {11, "Solar input current exceed upper limit"},
+ {12, "Leakage current exceed permit range"},
+ {13, "Solar insulation resistance too low"},
+ {14, "Inverter DC current exceed permit range when feed power"},
+ {15, "The AC input voltage or frequency has been detected different between master CPU and slave CPU"},
+ {16, "Leakage current detect circuit abnormal when inverter doesn't work"},
+ {17, "Communication loss between master CPU and slave CPU"},
+ {18, "Communicate data discordant between master CPU and slave CPU"},
+ {19, "AC input ground wire loss"},
+ {22, "Battery voltage exceed upper limit"},
+ {23, "Over load"},
+ {24, "Battery disconnected"},
+ {26, "AC output short"},
+ {27, "Fan lock"},
+ {32, "Battery DC-DC current over"},
+ {33, "AC output voltage too low"},
+ {34, "AC output voltage too high"},
+ {35, "Control board wiring error"},
+ {36, "AC circuit voltage sample error"},
+};
+
+const std::array<Flag, 6> flags = {{
+ {"BUZZ", 'A', "Mute buzzer beep"},
+ {"BUZS", 'B', "Mute buzzer beep in standby mode"},
+ {"BUZB", 'C', "Mute buzzer beep only on battery discharged status"},
+ {"GENI", 'D', "Generator as AC input"},
+ {"WAIR", 'E', "Wide AC input range"},
+ {"NGRF", 'F', "N/G relay function"},
+}};
+
+} \ No newline at end of file
diff --git a/src/protocol_17/defines.h b/src/protocol_17/defines.h
new file mode 100644
index 0000000..2a50110
--- /dev/null
+++ b/src/protocol_17/defines.h
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_P17_DEFINES_H
+#define INVERTER_TOOLS_P17_DEFINES_H
+
+#include <map>
+#include <string>
+#include <array>
+
+#include "types.h"
+
+namespace p17 {
+
+extern const std::map<CommandType, std::string> raw_commands;
+
+//extern const std::array<int, 5> ac_output_rated_voltages;
+//
+//extern const std::array<float, 8> bat_ac_recharging_voltages_12v;
+//extern const std::array<float, 8> bat_ac_recharging_voltages_24v;
+//extern const std::array<float, 8> bat_ac_recharging_voltages_48v;
+//
+//extern const std::array<float, 12> bat_ac_redischarging_voltages_12v;
+//extern const std::array<float, 12> bat_ac_redischarging_voltages_24v;
+//extern const std::array<float, 12> bat_ac_redischarging_voltages_48v;
+
+extern const std::map<int, std::string> fault_codes;
+
+extern const std::array<Flag, 6> flags;
+
+}
+
+#endif //INVERTER_TOOLS_P17_DEFINES_H
diff --git a/src/protocol_17/input.cc b/src/protocol_17/input.cc
new file mode 100644
index 0000000..b35bf20
--- /dev/null
+++ b/src/protocol_17/input.cc
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include "input.h"
+#include "defines.h"
+//#include "functions.h"
+#include "../util.h"
+
+namespace p17 {
+
+using namespace protocol;
+
+const std::map<std::string, CommandType> client_commands = {
+ {"get-protocol-id", CommandType::GetProtocolID},
+ {"get-date-time", CommandType::GetCurrentTime},
+ {"get-total-generated", CommandType::GetTotalGenerated},
+ {"get-year-generated", CommandType::GetYearGenerated},
+ {"get-month-generated", CommandType::GetMonthGenerated},
+ {"get-day-generated", CommandType::GetDayGenerated},
+ {"get-hour-generated", CommandType::GetHourGenerated},
+ {"get-series-number", CommandType::GetSeriesNumber},
+ {"get-cpu-version", CommandType::GetCPUVersion},
+ {"get-secondary-cpu-version", CommandType::GetSecondaryCPUVersion},
+ {"get-device-model", CommandType::GetDeviceModel},
+ {"get-rated", CommandType::GetRatedInformation},
+ {"get-general-status", CommandType::GetGeneralStatus},
+ {"get-power-status", CommandType::GetPowerStatus},
+ {"get-working-mode", CommandType::GetWorkingMode},
+ {"get-warnings", CommandType::GetWarningStatus},
+ {"get-flags", CommandType::GetFlags},
+ {"get-ac-input-voltage-range", CommandType::GetACInputVoltageRange},
+ {"get-ac-input-frequency-range", CommandType::GetACInputFrequencyRange},
+ {"get-max-grid-output-power", CommandType::GetMaximumGridOutputPower},
+ {"get-max-output-power", CommandType::GetMaximumOutputPower},
+ {"get-solar-input-mppt-range", CommandType::GetSolarInputMPPTRange},
+ {"get-solar-input-voltage-range", CommandType::GetSolarInputVoltageRange},
+ {"get-lcd-sleep-timeout", CommandType::GetLCDSleepTimeout},
+ {"get-defaults", CommandType::GetDefaults},
+ {"get-battery-settings", CommandType::GetBatterySettings},
+ {"get-machine-model", CommandType::GetMachineModel},
+ {"get-machine-adjustable-ranges", CommandType::GetMachineAdjustableRanges},
+ {"get-faults", CommandType::GetFaults},
+ {"get-faults-history", CommandType::GetFaultsHistory},
+ {"get-energy-control-status", CommandType::GetEnergyControlStatus},
+ {"get-ac-input-long-time-highest-average-voltage", CommandType::GetACInputLongTimeHighestAvgVoltage},
+ {"get-first-generated-energy-saved-time", CommandType::GetFirstGeneratedEnergySavedTime},
+ {"get-feed-wait-time", CommandType::GetFeedWaitTime},
+ {"get-ac-charging-time", CommandType::GetACChargingTimeBucket},
+ {"get-ac-loads-supply-time", CommandType::GetACLoadsSupplyTimeBucket},
+ {"get-feeding-grid-power-calibration", CommandType::GetFeedingGridPowerCalibration},
+ {"get-feed-in-power-factor", CommandType::GetFeedInPowerFactor},
+ {"get-auto-adjust-pf-with-power-info", CommandType::GetAutoAdjustPFWithPowerInformation},
+ {"get-allow-one-of-s-t-phase-loss-state", CommandType::GetAllowOneOfSTPhaseLossStatus},
+ {"set-loads", CommandType::SetLoads},
+ {"set-flag", CommandType::SetFlag},
+ {"set-date-time", CommandType::SetDateTime},
+ {"set-ac-input-highest-voltage-for-feeding-power", CommandType::SetACInputHighestVoltageForFeedingPower},
+ {"set-ac-input-lowest-voltage-for-feeding-power", CommandType::SetACInputLowestVoltageForFeedingPower},
+ {"set-ac-input-highest-frequency-for-feeding-power", CommandType::SetACInputHighestFrequencyForFeedingPower},
+ {"set-ac-input-lowest-frequency-for-feeding-power", CommandType::SetACInputLowestFrequencyForFeedingPower},
+ {"set-max-output-power", CommandType::SetMaxOutputPower},
+ {"set-max-feeding-grid-power", CommandType::SetMaxFeedingGridPower},
+ {"set-solar-input-highest-voltage", CommandType::SetSolarInputHighestVoltage},
+ {"set-solar-input-lowest-voltage", CommandType::SetSolarInputLowestVoltage},
+ {"set-solar-input-highest-mppt-voltage", CommandType::SetSolarInputHighestMPPTVoltage},
+ {"set-solar-input-lowest-mppt-voltage", CommandType::SetSolarInputLowestMPPTVoltage},
+ {"set-lcd-sleep-timeout", CommandType::SetLCDSleepTimeout},
+ {"set-battery-max-charging-current", CommandType::SetBatteryMaxChargingCurrent},
+ {"set-battery-max-ac-charging-current", CommandType::SetBatteryMaxACChargingCurrent},
+ {"set-battery-max-charging-voltage", CommandType::SetBatteryMaxChargingVoltage},
+ {"set-ac-input-long-time-highest-average-voltage", CommandType::SetACInputLongTimeHighestAverageVoltage},
+ {"set-battery-discharging-voltage", CommandType::SetBatteryDischargingVoltage},
+ {"set-solar-energy-distribution-of-priority", CommandType::SetSolarEnergyDistributionOfPriority},
+ {"set-energy-distribution", CommandType::SetEnergyDistribution},
+ {"set-battery-charger-application-in-floating-charging", CommandType::SetBatteryChargerApplicationInFloatingCharging},
+ {"set-machine-model", CommandType::SetMachineModel},
+ {"set-defaults", CommandType::SetDefaults},
+ {"set-ac-output-freq", CommandType::SetACOutputFreq},
+ {"set-ac-output-rated-voltage", CommandType::SetACOutputRatedVoltage},
+ {"set-feed-wait-time", CommandType::SetFeedWaitTime},
+ {"set-ac-charging-time", CommandType::SetACChargingTimeBucket},
+ {"set-ac-loads-supply-time", CommandType::SetACLoadsSupplyTimeBucket},
+ {"set-battery-type", CommandType::SetBatteryType},
+ {"set-battery-install-time", CommandType::SetBatteryInstallTime},
+ {"set-li-fe-battery-self-test", CommandType::SetLiFeBatterySelfTest},
+ {"set-ac-charger-keep-battery-voltage-setting", CommandType::SetACChargerKeepBatteryVoltageSetting},
+ {"set-battery-temp-sensor-compensation", CommandType::SetBatteryTempSensorCompensation},
+ {"set-feeding-grid-power-calibration", CommandType::SetFeedingGridPowerCalibration},
+ {"set-battery-max-discharging-current-in-hybrid-mode", CommandType::SetBatteryMaxDischargingCurrentInHybridMode},
+ {"set-feed-in-power-factor", CommandType::SetFeedInPowerFactor},
+ {"set-parallel-output", CommandType::SetParallelOutput},
+ {"set-r-phase-feeding-grid-power-calibration", CommandType::SetRPhaseFeedingGridPowerCalibration},
+ {"set-s-phase-feeding-grid-power-calibration", CommandType::SetSPhaseFeedingGridPowerCalibration},
+ {"set-t-phase-feeding-grid-power-calibration", CommandType::SetTPhaseFeedingGridPowerCalibration},
+ {"set-auto-adjust-pf-with-power-info", CommandType::SetAutoAdjustPFWithPowerInformation},
+ {"set-allow-one-of-s-t-phase-loss", CommandType::SetAllowOneOfSTPhaseLoss},
+ {"set-emergency-power-supply-control", CommandType::SetEmergencyPowerSupplyControl},
+};
+
+CommandType validate_input(std::string& command,
+ std::vector<std::string>& arguments,
+ void* input) {
+ auto it = client_commands.find(command);
+ if (it == client_commands.end())
+ throw std::invalid_argument("invalid command");
+
+ auto commandType = it->second;
+ switch (commandType) {
+
+
+ default:
+ break;
+ }
+
+ return commandType;
+}
+
+} \ No newline at end of file
diff --git a/src/protocol_17/input.h b/src/protocol_17/input.h
new file mode 100644
index 0000000..5ac64fe
--- /dev/null
+++ b/src/protocol_17/input.h
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_P17_INPUT_H
+#define INVERTER_TOOLS_P17_INPUT_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "../protocol/input.h"
+#include "types.h"
+
+namespace p17 {
+
+extern const std::map<std::string, p17::CommandType> client_commands;
+
+CommandType validate_input(std::string& command, std::vector<std::string>& arguments, void* input);
+
+}
+
+#endif //INVERTER_TOOLS_P17_INPUT_H
diff --git a/src/protocol_17/response.h b/src/protocol_17/response.h
new file mode 100644
index 0000000..93a4ffe
--- /dev/null
+++ b/src/protocol_17/response.h
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_P17_RESPONSE_H
+#define INVERTER_TOOLS_P17_RESPONSE_H
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <variant>
+#include <nlohmann/json.hpp>
+
+#include "types.h"
+#include "../formatter/formatter.h"
+#include "../protocol/response.h"
+
+namespace p17::response_type {
+
+using protocol::BaseResponse;
+using nlohmann::json;
+
+typedef std::shared_ptr<formatter::Formattable> formattable_ptr;
+
+
+/**
+ * Value holder for the formatter module
+ */
+
+/*typedef std::variant<
+ unsigned,
+ unsigned short,
+ unsigned long,
+ bool,
+ double,
+ std::string,
+ p18::BatteryType,
+ p18::BatteryPowerDirection,
+ p18::ChargerSourcePriority,
+ p18::DC_AC_PowerDirection,
+ p18::InputVoltageRange,
+ p18::LinePowerDirection,
+ p18::MachineType,
+ p18::MPPTChargerStatus,
+ p18::Topology,
+ p18::OutputSourcePriority,
+ p18::OutputModelSetting,
+ p18::ParallelConnectionStatus,
+ p18::SolarPowerPriority,
+ p18::WorkingMode,
+ p18::LoadConnectionStatus,
+ p18::ConfigurationStatus
+> Variant;
+
+class VariantHolder {
+private:
+ Variant v_;
+
+public:
+ VariantHolder(unsigned v) : v_(v) {}
+ VariantHolder(unsigned short v) : v_(v) {}
+ VariantHolder(unsigned long v) : v_(v) {}
+ VariantHolder(bool v) : v_(v) {}
+ VariantHolder(double v) : v_(v) {}
+ VariantHolder(std::string v) : v_(v) {}
+ VariantHolder(p18::BatteryType v) : v_(v) {}
+ VariantHolder(p18::BatteryPowerDirection v) : v_(v) {}
+ VariantHolder(p18::ChargerSourcePriority v) : v_(v) {}
+ VariantHolder(p18::DC_AC_PowerDirection v) : v_(v) {}
+ VariantHolder(p18::InputVoltageRange v) : v_(v) {}
+ VariantHolder(p18::LinePowerDirection v) : v_(v) {}
+ VariantHolder(p18::MachineType v) : v_(v) {}
+ VariantHolder(p18::MPPTChargerStatus v) : v_(v) {}
+ VariantHolder(p18::Topology v) : v_(v) {}
+ VariantHolder(p18::OutputSourcePriority v) : v_(v) {}
+ VariantHolder(p18::OutputModelSetting v) : v_(v) {}
+ VariantHolder(p18::ParallelConnectionStatus v) : v_(v) {}
+ VariantHolder(p18::SolarPowerPriority v) : v_(v) {}
+ VariantHolder(p18::WorkingMode v) : v_(v) {}
+ VariantHolder(p18::LoadConnectionStatus v) : v_(v) {}
+ VariantHolder(p18::ConfigurationStatus v) : v_(v) {}
+
+ friend std::ostream &operator<<(std::ostream &os, VariantHolder const& ref) {
+ std::visit([&os](const auto& elem) {
+ os << elem;
+ }, ref.v_);
+ return os;
+ }
+
+ inline json toJSON() const {
+ json j;
+ bool isEnum =
+ std::holds_alternative<p18::BatteryType>(v_) ||
+ std::holds_alternative<p18::BatteryPowerDirection>(v_) ||
+ std::holds_alternative<p18::ChargerSourcePriority>(v_) ||
+ std::holds_alternative<p18::DC_AC_PowerDirection>(v_) ||
+ std::holds_alternative<p18::InputVoltageRange>(v_) ||
+ std::holds_alternative<p18::LinePowerDirection>(v_) ||
+ std::holds_alternative<p18::MachineType>(v_) ||
+ std::holds_alternative<p18::MPPTChargerStatus>(v_) ||
+ std::holds_alternative<p18::Topology>(v_) ||
+ std::holds_alternative<p18::OutputSourcePriority>(v_) ||
+ std::holds_alternative<p18::OutputModelSetting>(v_) ||
+ std::holds_alternative<p18::ParallelConnectionStatus>(v_) ||
+ std::holds_alternative<p18::SolarPowerPriority>(v_) ||
+ std::holds_alternative<p18::WorkingMode>(v_) ||
+ std::holds_alternative<p18::LoadConnectionStatus>(v_) ||
+ std::holds_alternative<p18::ConfigurationStatus>(v_);
+
+ std::visit([&j, &isEnum](const auto& elem) {
+ if (isEnum)
+ j = formatter::to_str(elem);
+ else
+ j = elem;
+ }, v_);
+
+ return j;
+ }
+
+ inline json toSimpleJSON() const {
+ json j;
+ std::visit([&j](const auto& elem) {
+ j = elem;
+ }, v_);
+ return j;
+ }
+};
+
+
+class GetResponse : public BaseResponse {
+protected:
+ const char* getData() const;
+ size_t getDataSize() const;
+ std::vector<std::string> getList(std::vector<size_t> itemLengths) const;
+
+public:
+ using BaseResponse::BaseResponse;
+ bool validate() override;
+};
+
+class SetResponse : public BaseResponse {
+public:
+ using BaseResponse::BaseResponse;
+ void unpack() override;
+ bool validate() override;
+ formattable_ptr format(formatter::Format format) override;
+ bool get();
+};*/
+
+
+/**
+ * Actual typed responses
+ */
+
+class ProtocolID : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned id = 0;
+};
+
+class CurrentTime : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned year = 0;
+ unsigned short month = 0;
+ unsigned short day = 0;
+ unsigned short hour = 0;
+ unsigned short minute = 0;
+ unsigned short second = 0;
+};
+
+class TotalGenerated : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned long wh = 0;
+};
+
+class YearGenerated : public TotalGenerated {
+public:
+ using TotalGenerated::TotalGenerated;
+};
+
+class MonthGenerated : public TotalGenerated {
+public:
+ using TotalGenerated::TotalGenerated;
+};
+
+class DayGenerated : public TotalGenerated {
+public:
+ using TotalGenerated::TotalGenerated;
+};
+
+class HourGenerated : public TotalGenerated {
+public:
+ using TotalGenerated::TotalGenerated;
+};
+
+class SeriesNumber : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ std::string id;
+};
+
+class CPUVersion : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ std::string main_cpu_version;
+ std::string slave1_cpu_version;
+ std::string slave2_cpu_version;
+};
+
+class RatedInformation : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned ac_input_rating_voltage; /* unit: 0.1V */
+ unsigned ac_input_rating_current; /* unit: 0.1A */
+ unsigned ac_output_rating_voltage; /* unit: 0.1A */
+ unsigned ac_output_rating_freq; /* unit: 0.1Hz */
+ unsigned ac_output_rating_current; /* unit: 0.1A */
+ unsigned ac_output_rating_apparent_power; /* unit: VA */
+ unsigned ac_output_rating_active_power; /* unit: W */
+ unsigned battery_rating_voltage; /* unit: 0.1V */
+ unsigned battery_recharge_voltage; /* unit: 0.1V */
+ unsigned battery_redischarge_voltage; /* unit: 0.1V */
+ unsigned battery_under_voltage; /* unit: 0.1V */
+ unsigned battery_bulk_voltage; /* unit: 0.1V */
+ unsigned battery_float_voltage; /* unit: 0.1V */
+ p18::BatteryType battery_type;
+ unsigned max_ac_charging_current; /* unit: A */
+ unsigned max_charging_current; /* unit: A */
+ p18::InputVoltageRange input_voltage_range;
+ p18::OutputModelSetting output_source_priority;
+ p18::ChargerSourcePriority charger_source_priority;
+ unsigned parallel_max_num;
+ p18::MachineType machine_type;
+ p18::Topology topology;
+ p18::OutputModelSetting output_model_setting;
+ p18::SolarPowerPriority solar_power_priority;
+ std::string mppt;
+};
+
+class GeneralStatus : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned grid_voltage; /* unit: 0.1V */
+ unsigned grid_freq; /* unit: 0.1Hz */
+ unsigned ac_output_voltage; /* unit: 0.1V */
+ unsigned ac_output_freq; /* unit: 0.1Hz */
+ unsigned ac_output_apparent_power; /* unit: VA */
+ unsigned ac_output_active_power; /* unit: W */
+ unsigned output_load_percent; /* unit: % */
+ unsigned battery_voltage; /* unit: 0.1V */
+ unsigned battery_voltage_scc; /* unit: 0.1V */
+ unsigned battery_voltage_scc2; /* unit: 0.1V */
+ unsigned battery_discharge_current; /* unit: A */
+ unsigned battery_charging_current; /* unit: A */
+ unsigned battery_capacity; /* unit: % */
+ unsigned inverter_heat_sink_temp; /* unit: C */
+ unsigned mppt1_charger_temp; /* unit: C */
+ unsigned mppt2_charger_temp; /* unit: C */
+ unsigned pv1_input_power; /* unit: W */
+ unsigned pv2_input_power; /* unit: W */
+ unsigned pv1_input_voltage; /* unit: 0.1V */
+ unsigned pv2_input_voltage; /* unit: 0.1V */
+ p18::ConfigurationStatus configuration_status;
+ p18::MPPTChargerStatus mppt1_charger_status;
+ p18::MPPTChargerStatus mppt2_charger_status;
+ p18::LoadConnectionStatus load_connected;
+ p18::BatteryPowerDirection battery_power_direction;
+ p18::DC_AC_PowerDirection dc_ac_power_direction;
+ p18::LinePowerDirection line_power_direction;
+ unsigned local_parallel_id; /* 0 .. (parallel number - 1) */
+};
+
+class WorkingMode : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ p18::WorkingMode mode = static_cast<p18::WorkingMode>(0);
+};
+
+class FaultsAndWarnings : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned fault_code = 0;
+ bool line_fail = false;
+ bool output_circuit_short = false;
+ bool inverter_over_temperature = false;
+ bool fan_lock = false;
+ bool battery_voltage_high = false;
+ bool battery_low = false;
+ bool battery_under = false;
+ bool over_load = false;
+ bool eeprom_fail = false;
+ bool power_limit = false;
+ bool pv1_voltage_high = false;
+ bool pv2_voltage_high = false;
+ bool mppt1_overload_warning = false;
+ bool mppt2_overload_warning = false;
+ bool battery_too_low_to_charge_for_scc1 = false;
+ bool battery_too_low_to_charge_for_scc2 = false;
+};
+
+class FlagsAndStatuses : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ bool buzzer = false;
+ bool overload_bypass = false;
+ bool lcd_escape_to_default_page_after_1min_timeout = false;
+ bool overload_restart = false;
+ bool over_temp_restart = false;
+ bool backlight_on = false;
+ bool alarm_on_primary_source_interrupt = false;
+ bool fault_code_record = false;
+ char reserved = '0';
+};
+
+class Defaults : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned ac_output_voltage = 0; /* unit: 0.1V */
+ unsigned ac_output_freq = 0;
+ p18::InputVoltageRange ac_input_voltage_range = static_cast<InputVoltageRange>(0);
+ unsigned battery_under_voltage = 0;
+ unsigned charging_float_voltage = 0;
+ unsigned charging_bulk_voltage = 0;
+ unsigned battery_recharge_voltage = 0;
+ unsigned battery_redischarge_voltage = 0;
+ unsigned max_charging_current = 0;
+ unsigned max_ac_charging_current = 0;
+ p18::BatteryType battery_type = static_cast<BatteryType>(0);
+ p18::OutputSourcePriority output_source_priority = static_cast<OutputSourcePriority>(0);
+ p18::ChargerSourcePriority charger_source_priority = static_cast<ChargerSourcePriority>(0);
+ p18::SolarPowerPriority solar_power_priority = static_cast<SolarPowerPriority>(0);
+ p18::MachineType machine_type = static_cast<MachineType>(0);
+ p18::OutputModelSetting output_model_setting = static_cast<OutputModelSetting>(0);
+ bool flag_buzzer = false;
+ bool flag_overload_restart = false;
+ bool flag_over_temp_restart = false;
+ bool flag_backlight_on = false;
+ bool flag_alarm_on_primary_source_interrupt = false;
+ bool flag_fault_code_record = false;
+ bool flag_overload_bypass = false;
+ bool flag_lcd_escape_to_default_page_after_1min_timeout = false;
+};
+
+class AllowedChargingCurrents : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ std::vector<unsigned> amps;
+};
+
+class AllowedACChargingCurrents : public AllowedChargingCurrents {
+public:
+ using AllowedChargingCurrents::AllowedChargingCurrents;
+};
+
+class ParallelRatedInformation : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ p18::ParallelConnectionStatus parallel_id_connection_status = static_cast<ParallelConnectionStatus>(0);
+ unsigned serial_number_valid_length = 0;
+ std::string serial_number;
+ p18::ChargerSourcePriority charger_source_priority = static_cast<ChargerSourcePriority>(0);
+ unsigned max_ac_charging_current = 0; // unit: A
+ unsigned max_charging_current = 0; // unit: A
+ p18::OutputModelSetting output_model_setting = static_cast<OutputModelSetting>(0);
+};
+
+class ParallelGeneralStatus : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ p18::ParallelConnectionStatus parallel_id_connection_status;
+ p18::WorkingMode work_mode;
+ unsigned fault_code;
+ unsigned grid_voltage; /* unit: 0.1V */
+ unsigned grid_freq; /* unit: 0.1Hz */
+ unsigned ac_output_voltage; /* unit: 0.1V */
+ unsigned ac_output_freq; /* unit: 0.1Hz */
+ unsigned ac_output_apparent_power; /* unit: VA */
+ unsigned ac_output_active_power; /* unit: W */
+ unsigned total_ac_output_apparent_power; /* unit: VA */
+ unsigned total_ac_output_active_power; /* unit: W */
+ unsigned output_load_percent; /* unit: % */
+ unsigned total_output_load_percent; /* unit: % */
+ unsigned battery_voltage; /* unit: 0.1V */
+ unsigned battery_discharge_current; /* unit: A */
+ unsigned battery_charging_current; /* unit: A */
+ unsigned total_battery_charging_current; /* unit: A */
+ unsigned battery_capacity; /* unit: % */
+ unsigned pv1_input_power; /* unit: W */
+ unsigned pv2_input_power; /* unit: W */
+ unsigned pv1_input_voltage; /* unit: 0.1V */
+ unsigned pv2_input_voltage; /* unit: 0.1V */
+ p18::MPPTChargerStatus mppt1_charger_status;
+ p18::MPPTChargerStatus mppt2_charger_status;
+ p18::LoadConnectionStatus load_connected;
+ p18::BatteryPowerDirection battery_power_direction;
+ p18::DC_AC_PowerDirection dc_ac_power_direction;
+ p18::LinePowerDirection line_power_direction;
+ unsigned max_temp; /* unit: C */
+};
+
+class ACChargingTimeBucket : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned short start_h = 0;
+ unsigned short start_m = 0;
+ unsigned short end_h = 0;
+ unsigned short end_m = 0;
+};
+
+class ACLoadsSupplyTimeBucket : public ACChargingTimeBucket {
+public:
+ using ACChargingTimeBucket::ACChargingTimeBucket;
+};
+
+} // namespace protocol_18
+
+#endif //INVERTER_TOOLS_P17_RESPONSE_H
diff --git a/src/protocol_17/types.h b/src/protocol_17/types.h
new file mode 100644
index 0000000..daec2e1
--- /dev/null
+++ b/src/protocol_17/types.h
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_P17_TYPES_H
+#define INVERTER_TOOLS_P17_TYPES_H
+
+namespace p17 {
+
+enum class CommandType: int {
+ GetProtocolID = 0,
+ GetCurrentTime,
+ GetTotalGenerated,
+ GetYearGenerated,
+ GetMonthGenerated,
+ GetDayGenerated,
+ GetHourGenerated,
+ GetSeriesNumber,
+ GetCPUVersion,
+ GetSecondaryCPUVersion,
+ GetDeviceModel,
+ GetRatedInformation,
+ GetGeneralStatus,
+ GetPowerStatus,
+ GetWorkingMode,
+ GetWarningStatus,
+ GetFlags,
+ GetACInputVoltageRange,
+ GetACInputFrequencyRange,
+ GetMaximumGridOutputPower,
+ GetMaximumOutputPower,
+ GetSolarInputMPPTRange,
+ GetSolarInputVoltageRange,
+ GetLCDSleepTimeout,
+ GetDefaults,
+ GetBatterySettings,
+ GetMachineModel,
+ GetMachineAdjustableRanges,
+ GetFaults,
+ GetFaultsHistory,
+ GetEnergyControlStatus,
+ GetACInputLongTimeHighestAvgVoltage,
+ GetFirstGeneratedEnergySavedTime,
+ GetFeedWaitTime,
+ GetACChargingTimeBucket,
+ GetACLoadsSupplyTimeBucket,
+ GetFeedingGridPowerCalibration,
+ GetFeedInPowerFactor,
+ GetAutoAdjustPFWithPowerInformation,
+ GetAllowOneOfSTPhaseLossStatus,
+ SetLoads = 100,
+ SetFlag,
+ SetDateTime,
+ SetACInputHighestVoltageForFeedingPower,
+ SetACInputLowestVoltageForFeedingPower,
+ SetACInputHighestFrequencyForFeedingPower,
+ SetACInputLowestFrequencyForFeedingPower,
+ SetMaxOutputPower,
+ SetMaxFeedingGridPower,
+ SetSolarInputHighestVoltage,
+ SetSolarInputLowestVoltage,
+ SetSolarInputHighestMPPTVoltage,
+ SetSolarInputLowestMPPTVoltage,
+ SetLCDSleepTimeout,
+ SetBatteryMaxChargingCurrent,
+ SetBatteryMaxACChargingCurrent,
+ SetBatteryMaxChargingVoltage,
+ SetACInputLongTimeHighestAverageVoltage,
+ SetBatteryDischargingVoltage,
+ SetSolarEnergyDistributionOfPriority,
+ SetEnergyDistribution,
+ SetBatteryChargerApplicationInFloatingCharging,
+ SetMachineModel,
+ SetDefaults,
+ SetACOutputFreq,
+ SetACOutputRatedVoltage,
+ SetFeedWaitTime,
+ SetACChargingTimeBucket,
+ SetACLoadsSupplyTimeBucket,
+ SetBatteryType,
+ SetBatteryInstallTime,
+ SetLiFeBatterySelfTest,
+ SetACChargerKeepBatteryVoltageSetting,
+ SetBatteryTempSensorCompensation,
+ SetFeedingGridPowerCalibration,
+ SetBatteryMaxDischargingCurrentInHybridMode,
+ SetFeedInPowerFactor,
+ SetParallelOutput,
+ SetRPhaseFeedingGridPowerCalibration, // ???
+ SetSPhaseFeedingGridPowerCalibration, // ???
+ SetTPhaseFeedingGridPowerCalibration, // ???
+ SetAutoAdjustPFWithPowerInformation,
+ SetAllowOneOfSTPhaseLoss,
+ SetEmergencyPowerSupplyControl,
+};
+
+struct Flag {
+ std::string flag;
+ char letter;
+ std::string description;
+};
+
+}
+
+#endif //INVERTER_TOOLS_P17_TYPES_H \ No newline at end of file
diff --git a/src/p18/client.cc b/src/protocol_18/client.cc
index 1798be9..d42c24a 100644
--- a/src/p18/client.cc
+++ b/src/protocol_18/client.cc
@@ -10,7 +10,7 @@
#include "client.h"
#include "types.h"
#include "defines.h"
-#include "exceptions.h"
+#include "../protocol/exceptions.h"
#include "response.h"
#include "../voltronic/crc.h"
@@ -24,23 +24,20 @@
namespace p18 {
-void Client::setDevice(std::shared_ptr<voltronic::Device> device) {
- device_ = std::move(device);
-}
+std::shared_ptr<response_type::BaseResponse> Client::execute(int commandType, std::vector<std::string>& arguments) {
+ auto type = static_cast<CommandType>(commandType);
-std::shared_ptr<response_type::BaseResponse> Client::execute(p18::CommandType commandType, std::vector<std::string>& arguments) {
std::ostringstream buf;
buf << std::setfill('0');
- int iCommandType = static_cast<int>(commandType);
- bool isSetCommand = iCommandType >= 100;
+ bool isSetCommand = commandType >= 100;
- auto pos = raw_commands.find(commandType);
+ auto pos = raw_commands.find(type);
if (pos == raw_commands.end())
- throw std::runtime_error("packedCommand " + std::to_string(iCommandType) + " not found");
+ throw std::runtime_error("packedCommand " + std::to_string(commandType) + " not found");
std::string packedCommand = pos->second;
- std::string packedArguments = packArguments(commandType, arguments);
+ std::string packedArguments = packArguments(type, arguments);
size_t len = sizeof(voltronic::CRC) + 1 + packedCommand.size() + packedArguments.size();
@@ -58,7 +55,7 @@ std::shared_ptr<response_type::BaseResponse> Client::execute(p18::CommandType co
const auto raw = result.first;
const auto rawSize = result.second;
- switch (commandType) {
+ switch (type) {
RESPONSE_CASE(ProtocolID)
RESPONSE_CASE(CurrentTime)
RESPONSE_CASE(TotalGenerated)
@@ -106,22 +103,12 @@ std::shared_ptr<response_type::BaseResponse> Client::execute(p18::CommandType co
}
if (!response->validate())
- throw InvalidResponseError("validate() failed");
+ throw protocol::InvalidResponseError("validate() failed");
response->unpack();
return std::move(response);
}
-std::pair<std::shared_ptr<char>, size_t> Client::runOnDevice(std::string& raw) {
- size_t bufSize = 256;
- std::shared_ptr<char> buf(new char[bufSize]);
- size_t responseSize = device_->run(
- (const u8*)raw.c_str(), raw.size(),
- (u8*)buf.get(), bufSize);
-
- return std::pair<std::shared_ptr<char>, size_t>(buf, responseSize);
-}
-
std::string Client::packArguments(p18::CommandType commandType, std::vector<std::string>& arguments) {
std::ostringstream buf;
buf << std::setfill('0');
diff --git a/src/p18/client.h b/src/protocol_18/client.h
index 8307bbb..3fb96fc 100644
--- a/src/p18/client.h
+++ b/src/protocol_18/client.h
@@ -6,6 +6,7 @@
#include "../voltronic/device.h"
#include "types.h"
#include "response.h"
+#include "../protocol/client.h"
#include <memory>
#include <vector>
@@ -14,15 +15,15 @@
namespace p18 {
-class Client {
+using protocol::BaseClient;
+using protocol::BaseResponse;
+
+class Client : public BaseClient {
private:
- std::shared_ptr<voltronic::Device> device_;
static std::string packArguments(p18::CommandType commandType, std::vector<std::string>& arguments);
public:
- void setDevice(std::shared_ptr<voltronic::Device> device);
- std::shared_ptr<response_type::BaseResponse> execute(p18::CommandType commandType, std::vector<std::string>& arguments);
- std::pair<std::shared_ptr<char>, size_t> runOnDevice(std::string& raw);
+ std::shared_ptr<BaseResponse> execute(int commandType, std::vector<std::string>& arguments) override;
};
}
diff --git a/src/p18/defines.cc b/src/protocol_18/defines.cc
index 999f81a..207d909 100644
--- a/src/p18/defines.cc
+++ b/src/protocol_18/defines.cc
@@ -8,49 +8,49 @@
namespace p18 {
const std::map<CommandType, std::string> raw_commands = {
- {CommandType::GetProtocolID, "PI"},
- {CommandType::GetCurrentTime, "T"},
- {CommandType::GetTotalGenerated, "ET"},
- {CommandType::GetYearGenerated, "EY"},
- {CommandType::GetMonthGenerated, "EM"},
- {CommandType::GetDayGenerated, "ED"},
- {CommandType::GetSeriesNumber, "ID"},
- {CommandType::GetCPUVersion, "VFW"},
- {CommandType::GetRatedInformation, "PIRI"},
- {CommandType::GetGeneralStatus, "GS"},
- {CommandType::GetWorkingMode, "MOD"},
- {CommandType::GetFaultsAndWarnings, "FWS"},
- {CommandType::GetFlagsAndStatuses, "FLAG"},
- {CommandType::GetDefaults, "DI"},
- {CommandType::GetAllowedChargingCurrents, "MCHGCR"},
- {CommandType::GetAllowedACChargingCurrents, "MUCHGCR"},
- {CommandType::GetParallelRatedInformation, "PRI"},
- {CommandType::GetParallelGeneralStatus, "PGS"},
- {CommandType::GetACChargingTimeBucket, "ACCT"},
- {CommandType::GetACLoadsSupplyTimeBucket, "ACLT"},
- {CommandType::SetLoads, "LON"},
- {CommandType::SetFlag, "P"},
- {CommandType::SetDefaults, "PF"},
+ {CommandType::GetProtocolID, "PI"},
+ {CommandType::GetCurrentTime, "T"},
+ {CommandType::GetTotalGenerated, "ET"},
+ {CommandType::GetYearGenerated, "EY"},
+ {CommandType::GetMonthGenerated, "EM"},
+ {CommandType::GetDayGenerated, "ED"},
+ {CommandType::GetSeriesNumber, "ID"},
+ {CommandType::GetCPUVersion, "VFW"},
+ {CommandType::GetRatedInformation, "PIRI"},
+ {CommandType::GetGeneralStatus, "GS"},
+ {CommandType::GetWorkingMode, "MOD"},
+ {CommandType::GetFaultsAndWarnings, "FWS"},
+ {CommandType::GetFlagsAndStatuses, "FLAG"},
+ {CommandType::GetDefaults, "DI"},
+ {CommandType::GetAllowedChargingCurrents, "MCHGCR"},
+ {CommandType::GetAllowedACChargingCurrents, "MUCHGCR"},
+ {CommandType::GetParallelRatedInformation, "PRI"},
+ {CommandType::GetParallelGeneralStatus, "PGS"},
+ {CommandType::GetACChargingTimeBucket, "ACCT"},
+ {CommandType::GetACLoadsSupplyTimeBucket, "ACLT"},
+ {CommandType::SetLoads, "LON"},
+ {CommandType::SetFlag, "P"},
+ {CommandType::SetDefaults, "PF"},
{CommandType::SetBatteryMaxChargingCurrent, "MCHGC"},
{CommandType::SetBatteryMaxACChargingCurrent, "MUCHGC"},
/* The protocol documentation defines two commands, "F50" and "F60",
but it's identical as if there were just one "F" command with an argument. */
- {CommandType::SetACOutputFreq, "F"},
- {CommandType::SetBatteryMaxChargingVoltage, "MCHGV"},
- {CommandType::SetACOutputRatedVoltage, "V"},
- {CommandType::SetOutputSourcePriority, "POP"},
- {CommandType::SetBatteryChargingThresholds, "BUCD"},
- {CommandType::SetChargingSourcePriority, "PCP"},
- {CommandType::SetSolarPowerPriority, "PSP"},
- {CommandType::SetACInputVoltageRange, "PGR"},
- {CommandType::SetBatteryType, "PBT"},
- {CommandType::SetOutputModel, "POPM"},
- {CommandType::SetBatteryCutOffVoltage, "PSDV"},
- {CommandType::SetSolarConfig, "ID"},
- {CommandType::ClearGenerated, "CLE"},
- {CommandType::SetDateTime, "DAT"},
- {CommandType::SetACChargingTimeBucket, "ACCT"},
- {CommandType::SetACLoadsSupplyTimeBucket, "ACLT"},
+ {CommandType::SetACOutputFreq, "F"},
+ {CommandType::SetBatteryMaxChargingVoltage, "MCHGV"},
+ {CommandType::SetACOutputRatedVoltage, "V"},
+ {CommandType::SetOutputSourcePriority, "POP"},
+ {CommandType::SetBatteryChargingThresholds, "BUCD"},
+ {CommandType::SetChargingSourcePriority, "PCP"},
+ {CommandType::SetSolarPowerPriority, "PSP"},
+ {CommandType::SetACInputVoltageRange, "PGR"},
+ {CommandType::SetBatteryType, "PBT"},
+ {CommandType::SetOutputModel, "POPM"},
+ {CommandType::SetBatteryCutOffVoltage, "PSDV"},
+ {CommandType::SetSolarConfig, "ID"},
+ {CommandType::ClearGenerated, "CLE"},
+ {CommandType::SetDateTime, "DAT"},
+ {CommandType::SetACChargingTimeBucket, "ACCT"},
+ {CommandType::SetACLoadsSupplyTimeBucket, "ACLT"},
};
const std::array<int, 5> ac_output_rated_voltages = {202, 208, 220, 230, 240};
@@ -95,7 +95,7 @@ const std::map<int, std::string> fault_codes = {
};
const std::array<Flag, 9> flags = {{
- {"BUZZ", 'A', "Silence buzzer or open buzzer"},
+ {"BUZZ", 'A', "Mute buzzer beep"},
{"OLBP", 'B', "Overload bypass function"},
{"LCDE", 'C', "LCD display escape to default page after 1min timeout"},
{"OLRS", 'D', "Overload restart"},
diff --git a/src/p18/defines.h b/src/protocol_18/defines.h
index 83728f8..83728f8 100644
--- a/src/p18/defines.h
+++ b/src/protocol_18/defines.h
diff --git a/src/p18/functions.cc b/src/protocol_18/functions.cc
index 9799fc0..9799fc0 100644
--- a/src/p18/functions.cc
+++ b/src/protocol_18/functions.cc
diff --git a/src/protocol_18/functions.h b/src/protocol_18/functions.h
new file mode 100644
index 0000000..1f15dcc
--- /dev/null
+++ b/src/protocol_18/functions.h
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_P18_FUNCTIONS_H
+#define INVERTER_TOOLS_P18_FUNCTIONS_H
+
+namespace p18 {
+
+bool is_valid_parallel_id(unsigned id);
+
+}
+
+#endif //INVERTER_TOOLS_P18_FUNCTIONS_H
diff --git a/src/protocol_18/input.cc b/src/protocol_18/input.cc
new file mode 100644
index 0000000..5647ee6
--- /dev/null
+++ b/src/protocol_18/input.cc
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include "input.h"
+#include "defines.h"
+#include "functions.h"
+#include "../util.h"
+
+namespace p18 {
+
+using namespace protocol;
+
+const std::map<std::string, CommandType> client_commands = {
+ {"get-protocol-id", CommandType::GetProtocolID},
+ {"get-date-time", CommandType::GetCurrentTime},
+ {"get-total-generated", CommandType::GetTotalGenerated},
+ {"get-year-generated", CommandType::GetYearGenerated},
+ {"get-month-generated", CommandType::GetMonthGenerated},
+ {"get-day-generated", CommandType::GetDayGenerated},
+ {"get-series-number", CommandType::GetSeriesNumber},
+ {"get-cpu-version", CommandType::GetCPUVersion},
+ {"get-rated", CommandType::GetRatedInformation},
+ {"get-status", CommandType::GetGeneralStatus},
+ {"get-mode", CommandType::GetWorkingMode},
+ {"get-errors", CommandType::GetFaultsAndWarnings},
+ {"get-flags", CommandType::GetFlagsAndStatuses},
+ {"get-rated-defaults", CommandType::GetDefaults},
+ {"get-allowed-charging-currents", CommandType::GetAllowedChargingCurrents},
+ {"get-allowed-ac-charging-currents", CommandType::GetAllowedACChargingCurrents},
+ {"get-p-rated", CommandType::GetParallelRatedInformation},
+ {"get-p-status", CommandType::GetParallelGeneralStatus},
+ {"get-ac-charging-time", CommandType::GetACChargingTimeBucket},
+ {"get-ac-loads-supply-time", CommandType::GetACLoadsSupplyTimeBucket},
+ {"set-loads-supply", CommandType::SetLoads},
+ {"set-flag", CommandType::SetFlag},
+ {"set-rated-defaults", CommandType::SetDefaults},
+ {"set-max-charging-current", CommandType::SetBatteryMaxChargingCurrent},
+ {"set-max-ac-charging-current", CommandType::SetBatteryMaxACChargingCurrent},
+ {"set-ac-output-freq", CommandType::SetACOutputFreq},
+ {"set-max-charging-voltage", CommandType::SetBatteryMaxChargingVoltage},
+ {"set-ac-output-voltage", CommandType::SetACOutputRatedVoltage},
+ {"set-output-source-priority", CommandType::SetOutputSourcePriority},
+ {"set-charging-thresholds", CommandType::SetBatteryChargingThresholds}, /* Battery re-charging and re-discharging voltage when utility is available */
+ {"set-charging-source-priority", CommandType::SetChargingSourcePriority},
+ {"set-solar-power-priority", CommandType::SetSolarPowerPriority},
+ {"set-ac-input-voltage-range", CommandType::SetACInputVoltageRange},
+ {"set-battery-type", CommandType::SetBatteryType},
+ {"set-output-model", CommandType::SetOutputModel},
+ {"set-battery-cut-off-voltage", CommandType::SetBatteryCutOffVoltage},
+ {"set-solar-configuration", CommandType::SetSolarConfig},
+ {"clear-generated-data", CommandType::ClearGenerated},
+ {"set-date-time", CommandType::SetDateTime},
+ {"set-ac-charging-time", CommandType::SetACChargingTimeBucket},
+ {"set-ac-loads-supply-time", CommandType::SetACLoadsSupplyTimeBucket},
+};
+
+CommandType validate_input(std::string& command,
+ std::vector<std::string>& arguments,
+ void* input) {
+ auto it = client_commands.find(command);
+ if (it == client_commands.end())
+ throw std::invalid_argument("invalid command");
+
+ auto commandType = it->second;
+ switch (commandType) {
+ case CommandType::GetYearGenerated:
+ GET_ARGS(1);
+ validate_date_args(&arguments[0], nullptr, nullptr);
+ break;
+
+ case CommandType::GetMonthGenerated:
+ GET_ARGS(2);
+ validate_date_args(&arguments[0], &arguments[1], nullptr);
+ break;
+
+ case CommandType::GetDayGenerated:
+ GET_ARGS(3);
+ validate_date_args(&arguments[0], &arguments[1], &arguments[2]);
+ break;
+
+ case CommandType::GetParallelRatedInformation:
+ case CommandType::GetParallelGeneralStatus:
+ GET_ARGS(1);
+ if (!is_numeric(arguments[0]) || arguments[0].size() > 1)
+ throw std::invalid_argument("invalid argument");
+ break;
+
+ case CommandType::SetLoads: {
+ GET_ARGS(1);
+ std::string &arg = arguments[0];
+ if (arg != "0" && arg != "1")
+ throw std::invalid_argument("invalid argument, only 0 or 1 allowed");
+ break;
+ }
+
+ case CommandType::SetFlag: {
+ GET_ARGS(2);
+
+ bool match_found = false;
+ for (auto const& item: flags) {
+ if (arguments[0] == item.flag) {
+ arguments[0] = item.letter;
+ match_found = true;
+ break;
+ }
+ }
+
+ if (!match_found)
+ throw std::invalid_argument("invalid flag");
+
+ if (arguments[1] != "0" && arguments[1] != "1")
+ throw std::invalid_argument("invalid flag state, only 0 or 1 allowed");
+
+ break;
+ }
+
+ case CommandType::SetBatteryMaxChargingCurrent:
+ case CommandType::SetBatteryMaxACChargingCurrent: {
+ GET_ARGS(2);
+
+ auto id = static_cast<unsigned>(std::stoul(arguments[0]));
+ auto amps = static_cast<unsigned>(std::stoul(arguments[1]));
+
+ if (!is_valid_parallel_id(id))
+ throw std::invalid_argument("invalid id");
+
+ // 3 characters max
+ if (amps > 999)
+ throw std::invalid_argument("invalid amps");
+
+ break;
+ }
+
+ case CommandType::SetACOutputFreq: {
+ GET_ARGS(1);
+ std::string &freq = arguments[0];
+ if (freq != "50" && freq != "60")
+ throw std::invalid_argument("invalid frequency, only 50 or 60 allowed");
+ break;
+ }
+
+ case CommandType::SetBatteryMaxChargingVoltage: {
+ GET_ARGS(2);
+
+ float cv = std::stof(arguments[0]);
+ float fv = std::stof(arguments[1]);
+
+ if (cv < 48.0 || cv > 58.4)
+ throw std::invalid_argument("invalid CV");
+
+ if (fv < 48.0 || fv > 58.4)
+ throw std::invalid_argument("invalid FV");
+
+ break;
+ }
+
+ case CommandType::SetACOutputRatedVoltage: {
+ GET_ARGS(1);
+
+ auto v = static_cast<unsigned>(std::stoul(arguments[0]));
+
+ bool matchFound = false;
+ for (const auto &item: ac_output_rated_voltages) {
+ if (v == item) {
+ matchFound = true;
+ break;
+ }
+ }
+
+ if (!matchFound)
+ throw std::invalid_argument("invalid voltage");
+
+ break;
+ }
+
+ case CommandType::SetOutputSourcePriority: {
+ GET_ARGS(1);
+
+ std::array<std::string, 2> priorities({"SUB", "SBU"});
+
+ long index = index_of(priorities, arguments[0]);
+ if (index == -1)
+ throw std::invalid_argument("invalid argument");
+
+ arguments[0] = std::to_string(index);
+ break;
+ }
+
+ case CommandType::SetBatteryChargingThresholds: {
+ GET_ARGS(2);
+
+ float cv = std::stof(arguments[0]);
+ float dv = std::stof(arguments[1]);
+
+ if (index_of(bat_ac_recharging_voltages_12v, cv) == -1 &&
+ index_of(bat_ac_recharging_voltages_24v, cv) == -1 &&
+ index_of(bat_ac_recharging_voltages_48v, cv) == -1)
+ throw std::invalid_argument("invalid CV");
+
+ if (index_of(bat_ac_redischarging_voltages_12v, dv) == -1 &&
+ index_of(bat_ac_redischarging_voltages_24v, dv) == -1 &&
+ index_of(bat_ac_redischarging_voltages_48v, dv) == -1)
+ throw std::invalid_argument("invalid DV");
+
+ break;
+ }
+
+ case CommandType::SetChargingSourcePriority: {
+ GET_ARGS(2);
+
+ auto id = static_cast<unsigned>(std::stoul(arguments[0]));
+ if (!is_valid_parallel_id(id))
+ throw std::invalid_argument("invalid id");
+
+ std::array<std::string, 3> priorities({"SF", "SU", "S"});
+ long index = index_of(priorities, arguments[1]);
+ if (index == -1)
+ throw std::invalid_argument("invalid argument");
+
+ arguments[1] = std::to_string(index);
+ break;
+ }
+
+ case CommandType::SetSolarPowerPriority: {
+ GET_ARGS(1);
+
+ std::array<std::string, 2> allowed({"BLU", "LBU"});
+ long index = index_of(allowed, arguments[0]);
+ if (index == -1)
+ throw std::invalid_argument("invalid priority");
+
+ arguments[0] = std::to_string(index);
+ break;
+ }
+
+ case CommandType::SetACInputVoltageRange: {
+ GET_ARGS(1);
+ std::array<std::string, 2> allowed({"APPLIANCE", "UPS"});
+ long index = index_of(allowed, arguments[0]);
+ if (index == -1)
+ throw std::invalid_argument("invalid argument");
+ arguments[0] = std::to_string(index);
+ break;
+ }
+
+ case CommandType::SetBatteryType: {
+ GET_ARGS(1);
+
+ std::array<std::string, 3> allowed({"AGM", "FLOODED", "USER"});
+ long index = index_of(allowed, arguments[0]);
+ if (index == -1)
+ throw std::invalid_argument("invalid type");
+ arguments[0] = std::to_string(index);
+
+ break;
+ }
+
+ case CommandType::SetOutputModel: {
+ GET_ARGS(2);
+
+ auto id = static_cast<unsigned>(std::stoul(arguments[0]));
+ if (!is_valid_parallel_id(id))
+ throw std::invalid_argument("invalid id");
+
+ std::array<std::string, 5> allowed({"SM", "P", "P1", "P2", "P3"});
+ long index = index_of(allowed, arguments[1]);
+ if (index == -1)
+ throw std::invalid_argument("invalid model");
+ arguments[1] = std::to_string(index);
+
+ break;
+ }
+
+ case CommandType::SetBatteryCutOffVoltage: {
+ GET_ARGS(1);
+
+ float v = std::stof(arguments[0]);
+ if (v < 40.0 || v > 48.0)
+ throw std::invalid_argument("invalid voltage");
+
+ break;
+ }
+
+ case CommandType::SetSolarConfig: {
+ GET_ARGS(1);
+
+ if (!is_numeric(arguments[0]) || arguments[0].size() > 20)
+ throw std::invalid_argument("invalid argument");
+
+ break;
+ }
+
+ case CommandType::SetDateTime: {
+ GET_ARGS(6);
+
+ validate_date_args(&arguments[0], &arguments[1], &arguments[2]);
+ validate_time_args(&arguments[3], &arguments[4], &arguments[5]);
+
+ break;
+ }
+
+ case CommandType::SetACChargingTimeBucket:
+ case CommandType::SetACLoadsSupplyTimeBucket: {
+ GET_ARGS(2);
+
+ std::vector<std::string> start = split(arguments[0], ':');
+ if (start.size() != 2)
+ throw std::invalid_argument("invalid start time");
+
+ std::vector<std::string> end = split(arguments[1], ':');
+ if (end.size() != 2)
+ throw std::invalid_argument("invalid end time");
+
+ auto startHour = static_cast<unsigned short>(std::stoul(start[0]));
+ auto startMinute = static_cast<unsigned short>(std::stoul(start[1]));
+ if (startHour > 23 || startMinute > 59)
+ throw std::invalid_argument("invalid start time");
+
+ auto endHour = static_cast<unsigned short>(std::stoul(end[0]));
+ auto endMinute = static_cast<unsigned short>(std::stoul(end[1]));
+ if (endHour > 23 || endMinute > 59)
+ throw std::invalid_argument("invalid end time");
+
+ arguments.clear();
+
+ arguments.emplace_back(std::to_string(startHour));
+ arguments.emplace_back(std::to_string(startMinute));
+
+ arguments.emplace_back(std::to_string(endHour));
+ arguments.emplace_back(std::to_string(endMinute));
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return commandType;
+}
+
+} \ No newline at end of file
diff --git a/src/protocol_18/input.h b/src/protocol_18/input.h
new file mode 100644
index 0000000..939497e
--- /dev/null
+++ b/src/protocol_18/input.h
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_P18_INPUT_H
+#define INVERTER_TOOLS_P18_INPUT_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "../protocol/input.h"
+#include "types.h"
+
+namespace p18 {
+
+extern const std::map<std::string, CommandType> client_commands;
+
+CommandType validate_input(std::string& command, std::vector<std::string>& arguments, void* input);
+
+}
+
+#endif //INVERTER_TOOLS_P18_INPUT_H
diff --git a/src/protocol_18/response.cc b/src/protocol_18/response.cc
new file mode 100644
index 0000000..4062b37
--- /dev/null
+++ b/src/protocol_18/response.cc
@@ -0,0 +1,808 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <utility>
+#include <cstring>
+#include <sstream>
+#include <iomanip>
+#include <typeinfo>
+
+#include "response.h"
+#include "../protocol/exceptions.h"
+#include "../logging.h"
+
+#define RETURN_TABLE(...) \
+ return std::shared_ptr<formatter::Table<VariantHolder>>( \
+ new formatter::Table<VariantHolder>(format, __VA_ARGS__) \
+ );
+
+#define RETURN_STATUS(...) \
+ return std::shared_ptr<formatter::Status>( \
+ new formatter::Status(format, __VA_ARGS__) \
+ );
+
+
+namespace p18::response_type {
+
+typedef formatter::TableItem<VariantHolder> LINE;
+
+using formatter::Unit;
+
+
+/**
+ * Helpers
+ */
+std::ostream& operator<<(std::ostream& os, FieldLength fl) {
+ if (fl.min_ == fl.max_)
+ os << fl.min_;
+ else
+ os << "[" << fl.min_ << ", " << fl.max_ << "]";
+ return os;
+}
+
+
+/**
+ * Base responses
+ */
+
+
+bool GetResponse::validate() {
+ if (rawSize_ < 5)
+ return false;
+
+ const char* raw = raw_.get();
+ if (raw[0] != '^' || raw[1] != 'D')
+ return false;
+
+ char lenbuf[4];
+ memcpy(lenbuf, &raw[2], 3);
+ lenbuf[3] = '\0';
+
+ auto len = static_cast<size_t>(std::stoul(lenbuf));
+ return rawSize_ >= len-5 /* exclude ^Dxxx*/;
+}
+
+const char* GetResponse::getData() const {
+ return raw_.get() + 5;
+}
+
+size_t GetResponse::getDataSize() const {
+ return rawSize_ - 5;
+}
+
+std::vector<std::string> GetResponse::getList(std::vector<FieldLength> itemLengths, int expectAtLeast) const {
+ std::string buf(getData(), getDataSize());
+ auto list = ::split(buf, ',');
+
+ if (expectAtLeast == -1)
+ expectAtLeast = (int)itemLengths.size();
+
+ if (!itemLengths.empty()) {
+ // check list length
+ if (list.size() < expectAtLeast) {
+ std::ostringstream error;
+ error << "while parsing " << demangle_type_name(typeid(*this).name());
+ error << ": list is expected to be " << expectAtLeast << " items long, ";
+ error << "got only " << list.size() << " items";
+ throw protocol::ParseError(error.str());
+ }
+
+ // check each item's length
+ for (int i = 0; i < list.size(); i++) {
+ if (!itemLengths[i].validate(list[i].size())) {
+ std::ostringstream error;
+ error << "while parsing " << demangle_type_name(typeid(*this).name());
+ error << ": item " << i << " is expected to be " << itemLengths[i] << " characters long, ";
+ error << "got " << list[i].size() << " characters";
+ throw protocol::ParseError(error.str());
+ }
+ }
+ }
+
+ return list;
+}
+
+bool SetResponse::validate() {
+ if (rawSize_ < 2)
+ return false;
+
+ const char* raw = raw_.get();
+ return raw[0] == '^' && (raw[1] == '0' || raw[1] == '1');
+}
+
+bool SetResponse::get() {
+ return raw_.get()[1] == '1';
+}
+
+void SetResponse::unpack() {}
+
+formattable_ptr SetResponse::format(formatter::Format format) {
+ RETURN_STATUS(get(), "");
+}
+
+
+/**
+ * Actual typed responses
+ */
+
+void ProtocolID::unpack() {
+ auto data = getData();
+
+ char s[4];
+ strncpy(s, data, 2);
+ s[2] = '\0';
+
+ id = stou(s);
+}
+
+formattable_ptr ProtocolID::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("id", "Protocol ID", id),
+ });
+}
+
+
+void CurrentTime::unpack() {
+ auto data = getData();
+
+ std::string buf;
+ buf = std::string(data, 4);
+
+ year = stou(buf);
+
+ for (int i = 0; i < 5; i++) {
+ buf = std::string(data + 4 + (i * 2), 2);
+ auto n = stou(buf);
+
+ switch (i) {
+ case 0:
+ month = n;
+ break;
+
+ case 1:
+ day = n;
+ break;
+
+ case 2:
+ hour = n;
+ break;
+
+ case 3:
+ minute = n;
+ break;
+
+ case 4:
+ second = n;
+ break;
+
+ default:
+ std::ostringstream error;
+ error << "unexpected value while parsing CurrentTime (i = " << i << ")";
+ throw protocol::ParseError(error.str());
+ }
+ }
+}
+
+formattable_ptr CurrentTime::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("year", "Year", year),
+ LINE("month", "Month", month),
+ LINE("day", "Day", day),
+ LINE("hour", "Hour", hour),
+ LINE("minute", "Minute", minute),
+ LINE("second", "Second", second),
+ });
+}
+
+
+void TotalGenerated::unpack() {
+ auto data = getData();
+
+ std::string buf(data, 8);
+ wh = stou(buf);
+}
+
+formattable_ptr TotalGenerated::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("wh", "Wh", wh)
+ });
+}
+
+
+void SeriesNumber::unpack() {
+ auto data = getData();
+
+ std::string buf(data, 2);
+ size_t len = std::stoul(buf);
+
+ id = std::string(data+2, len);
+}
+
+formattable_ptr SeriesNumber::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("sn", "Series number", id)
+ });
+}
+
+
+void CPUVersion::unpack() {
+ auto list = getList({5, 5, 5});
+
+ main_cpu_version = list[0];
+ slave1_cpu_version = list[1];
+ slave2_cpu_version = list[2];
+}
+
+formattable_ptr CPUVersion::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("main_v", "Main CPU version", main_cpu_version),
+ LINE("slave1_v", "Slave 1 CPU version", slave1_cpu_version),
+ LINE("slave2_v", "Slave 2 CPU version", slave2_cpu_version)
+ });
+}
+
+
+void RatedInformation::unpack() {
+ auto list = getList({
+ 4, // AAAA
+ 3, // BBB
+ 4, // CCCC
+ 3, // DDD
+ 3, // EEE
+ 4, // FFFF
+ 4, // GGGG
+ 3, // HHH
+ 3, // III
+ 3, // JJJ
+ 3, // KKK
+ 3, // LLL
+ 3, // MMM
+ 1, // N
+ 2, // OO
+ 3, // PPP
+ 1, // O
+ 1, // R
+ 1, // S
+ 1, // T
+ 1, // U
+ 1, // V
+ 1, // W
+ 1, // Z
+ 1, // a
+ });
+
+ ac_input_rating_voltage = stou(list[0]);
+ ac_input_rating_current = stou(list[1]);
+ ac_output_rating_voltage = stou(list[2]);
+ ac_output_rating_freq = stou(list[3]);
+ ac_output_rating_current = stou(list[4]);
+ ac_output_rating_apparent_power = stou(list[5]);
+ ac_output_rating_active_power = stou(list[6]);
+ battery_rating_voltage = stou(list[7]);
+ battery_recharge_voltage = stou(list[8]);
+ battery_redischarge_voltage = stou(list[9]);
+ battery_under_voltage = stou(list[10]);
+ battery_bulk_voltage = stou(list[11]);
+ battery_float_voltage = stou(list[12]);
+ battery_type = static_cast<BatteryType>(stou(list[13]));
+ max_ac_charging_current = stou(list[14]);
+ max_charging_current = stou(list[15]);
+ input_voltage_range = static_cast<InputVoltageRange>(stou(list[16]));
+ output_source_priority = static_cast<OutputModelSetting>(stou(list[17]));
+ charger_source_priority = static_cast<ChargerSourcePriority>(stou(list[18]));
+ parallel_max_num = stou(list[19]);
+ machine_type = static_cast<MachineType>(stou(list[20]));
+ topology = static_cast<Topology>(stou(list[21]));
+ output_model_setting = static_cast<OutputModelSetting>(stou(list[22]));
+ solar_power_priority = static_cast<SolarPowerPriority>(stou(list[23]));
+ mppt = list[24];
+}
+
+formattable_ptr RatedInformation::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("ac_input_rating_voltage", "AC input rating voltage", ac_input_rating_voltage / 10.0, Unit::V),
+ LINE("ac_input_rating_current", "AC input rating current", ac_input_rating_current / 10.0, Unit::A),
+ LINE("ac_output_rating_voltage", "AC output rating voltage", ac_output_rating_voltage / 10.0, Unit::V),
+ LINE("ac_output_rating_freq", "AC output rating frequency", ac_output_rating_freq / 10.0, Unit::Hz),
+ LINE("ac_output_rating_current", "AC output rating current", ac_output_rating_current / 10.0, Unit::A),
+ LINE("ac_output_rating_apparent_power", "AC output rating apparent power", ac_output_rating_apparent_power, Unit::VA),
+ LINE("ac_output_rating_active_power", "AC output rating active power", ac_output_rating_active_power, Unit::Wh),
+ LINE("battery_rating_voltage", "Battery rating voltage", battery_rating_voltage / 10.0, Unit::V),
+ LINE("battery_recharge_voltage", "Battery re-charge voltage", battery_recharge_voltage / 10.0, Unit::V),
+ LINE("battery_redischarge_voltage", "Battery re-discharge voltage", battery_redischarge_voltage / 10.0, Unit::V),
+ LINE("battery_under_voltage", "Battery under voltage", battery_under_voltage / 10.0, Unit::V),
+ LINE("battery_bulk_voltage", "Battery bulk voltage", battery_bulk_voltage / 10.0, Unit::V),
+ LINE("battery_float_voltage", "Battery float voltage", battery_float_voltage / 10.0, Unit::V),
+ LINE("battery_type", "Battery type", battery_type),
+ LINE("max_charging_current", "Max charging current", max_charging_current, Unit::A),
+ LINE("max_ac_charging_current", "Max AC charging current", max_ac_charging_current, Unit::A),
+ LINE("input_voltage_range", "Input voltage range", input_voltage_range),
+ LINE("output_source_priority", "Output source priority", output_source_priority),
+ LINE("charge_source_priority", "Charge source priority", charger_source_priority),
+ LINE("parallel_max_num", "Parallel max num", parallel_max_num),
+ LINE("machine_type", "Machine type", machine_type),
+ LINE("topology", "Topology", topology),
+ LINE("output_model_setting", "Output model setting", output_model_setting),
+ LINE("solar_power_priority", "Solar power priority", solar_power_priority),
+ LINE("mppt", "MPPT string", mppt)
+ });
+}
+
+
+void GeneralStatus::unpack() {
+ auto list = getList({
+ 4, // AAAA
+ 3, // BBB
+ 4, // CCCC
+ 3, // DDD
+ 4, // EEEE
+ 4, // FFFF
+ 3, // GGG
+ 3, // HHH
+ 3, // III
+ 3, // JJJ
+ 3, // KKK
+ 3, // LLL
+ 3, // MMM
+ 3, // NNN
+ 3, // OOO
+ 3, // PPP
+ 4, // QQQQ
+ 4, // RRRR
+ 4, // SSSS
+ 4, // TTTT
+ 1, // U
+ 1, // V
+ 1, // W
+ 1, // X
+ 1, // Y
+ 1, // Z
+ 1, // a
+ 1, // b
+ });
+
+ grid_voltage = stou(list[0]);
+ grid_freq = stou(list[1]);
+ ac_output_voltage = stou(list[2]);
+ ac_output_freq = stou(list[3]);
+ ac_output_apparent_power = stou(list[4]);
+ ac_output_active_power = stou(list[5]);
+ output_load_percent = stou(list[6]);
+ battery_voltage = stou(list[7]);
+ battery_voltage_scc = stou(list[8]);
+ battery_voltage_scc2 = stou(list[9]);
+ battery_discharge_current = stou(list[10]);
+ battery_charging_current = stou(list[11]);
+ battery_capacity = stou(list[12]);
+ inverter_heat_sink_temp = stou(list[13]);
+ mppt1_charger_temp = stou(list[14]);
+ mppt2_charger_temp = stou(list[15]);
+ pv1_input_power = stou(list[16]);
+ pv2_input_power = stou(list[17]);
+ pv1_input_voltage = stou(list[18]);
+ pv2_input_voltage = stou(list[19]);
+ configuration_status = static_cast<ConfigurationStatus>(stou(list[20]));
+ mppt1_charger_status = static_cast<MPPTChargerStatus>(stou(list[21]));
+ mppt2_charger_status = static_cast<MPPTChargerStatus>(stou(list[22]));
+ load_connected = static_cast<LoadConnectionStatus>(stou(list[23]));
+ battery_power_direction = static_cast<BatteryPowerDirection>(stou(list[24]));
+ dc_ac_power_direction = static_cast<DC_AC_PowerDirection>(stou(list[25]));
+ line_power_direction = static_cast<LinePowerDirection>(stou(list[26]));
+ local_parallel_id = stou(list[27]);
+}
+
+formattable_ptr GeneralStatus::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("grid_voltage", "Grid voltage", grid_voltage / 10.0, Unit::V),
+ LINE("grid_freq", "Grid frequency", grid_freq / 10.0, Unit::Hz),
+ LINE("ac_output_voltage", "AC output voltage", ac_output_voltage / 10.0, Unit::V),
+ LINE("ac_output_freq", "AC output frequency", ac_output_freq / 10.0, Unit::Hz),
+ LINE("ac_output_apparent_power", "AC output apparent power", ac_output_apparent_power, Unit::VA),
+ LINE("ac_output_active_power", "AC output active power", ac_output_active_power, Unit::Wh),
+ LINE("output_load_percent", "Output load percent", output_load_percent, Unit::Percentage),
+ LINE("battery_voltage", "Battery voltage", battery_voltage / 10.0, Unit::V),
+ LINE("battery_voltage_scc", "Battery voltage from SCC", battery_voltage_scc / 10.0, Unit::V),
+ LINE("battery_voltage_scc2", "Battery voltage from SCC2", battery_voltage_scc2 / 10.0, Unit::V),
+ LINE("battery_discharging_current", "Battery discharging current", battery_discharge_current, Unit::A),
+ LINE("battery_charging_current", "Battery charging current", battery_charging_current, Unit::A),
+ LINE("battery_capacity", "Battery capacity", battery_capacity, Unit::Percentage),
+ LINE("inverter_heat_sink_temp", "Inverter heat sink temperature", inverter_heat_sink_temp, Unit::Celsius),
+ LINE("mppt1_charger_temp", "MPPT1 charger temperature", mppt1_charger_temp, Unit::Celsius),
+ LINE("mppt2_charger_temp", "MPPT2 charger temperature", mppt2_charger_temp, Unit::Celsius),
+ LINE("pv1_input_power", "PV1 input power", pv1_input_power, Unit::Wh),
+ LINE("pv2_input_power", "PV2 input power", pv2_input_power, Unit::Wh),
+ LINE("pv1_input_voltage", "PV1 input voltage", pv1_input_voltage / 10.0, Unit::V),
+ LINE("pv2_input_voltage", "PV2 input voltage", pv2_input_voltage / 10.0, Unit::V),
+ LINE("configuration_status", "Configuration state", configuration_status),
+ LINE("mppt1_charger_status", "MPPT1 charger status", mppt1_charger_status),
+ LINE("mppt2_charger_status", "MPPT2 charger status", mppt2_charger_status),
+ LINE("load_connected", "Load connection", load_connected),
+ LINE("battery_power_direction", "Battery power direction", battery_power_direction),
+ LINE("dc_ac_power_direction", "DC/AC power direction", dc_ac_power_direction),
+ LINE("line_power_direction", "Line power direction", line_power_direction),
+ LINE("local_parallel_id", "Local parallel ID", local_parallel_id),
+ });
+}
+
+
+void WorkingMode::unpack() {
+ auto data = getData();
+ mode = static_cast<p18::WorkingMode>(stou(std::string(data, 2)));
+}
+
+formattable_ptr WorkingMode::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("mode", "Working mode", mode)
+ })
+}
+
+
+void FaultsAndWarnings::unpack() {
+ auto list = getList({2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1});
+
+ fault_code = stou(list[0]);
+ line_fail = stou(list[1]) > 0;
+ output_circuit_short = stou(list[2]) > 0;
+ inverter_over_temperature = stou(list[3]) > 0;
+ fan_lock = stou(list[4]) > 0;
+ battery_voltage_high = stou(list[5]) > 0;
+ battery_low = stou(list[6]) > 0;
+ battery_under = stou(list[7]) > 0;
+ over_load = stou(list[8]) > 0;
+ eeprom_fail = stou(list[9]) > 0;
+ power_limit = stou(list[10]) > 0;
+ pv1_voltage_high = stou(list[11]) > 0;
+ pv2_voltage_high = stou(list[12]) > 0;
+ mppt1_overload_warning = stou(list[13]) > 0;
+ mppt2_overload_warning = stou(list[14]) > 0;
+ battery_too_low_to_charge_for_scc1 = stou(list[15]) > 0;
+ battery_too_low_to_charge_for_scc2 = stou(list[16]) > 0;
+}
+
+formattable_ptr FaultsAndWarnings::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("fault_code", "Fault code", fault_code),
+ LINE("line_fail", "Line fail", line_fail),
+ LINE("output_circuit_short", "Output circuit short", output_circuit_short),
+ LINE("inverter_over_temperature", "Inverter over temperature", inverter_over_temperature),
+ LINE("fan_lock", "Fan lock", fan_lock),
+ LINE("battery_voltage_high", "Battery voltage high", battery_voltage_high),
+ LINE("battery_low", "Battery low", battery_low),
+ LINE("battery_under", "Battery under", battery_under),
+ LINE("over_load", "Over load", over_load),
+ LINE("eeprom_fail", "EEPROM fail", eeprom_fail),
+ LINE("power_limit", "Power limit", power_limit),
+ LINE("pv1_voltage_high", "PV1 voltage high", pv1_voltage_high),
+ LINE("pv2_voltage_high", "PV2 voltage high", pv2_voltage_high),
+ LINE("mppt1_overload_warning", "MPPT1 overload warning", mppt1_overload_warning),
+ LINE("mppt2_overload_warning", "MPPT2 overload warning", mppt2_overload_warning),
+ LINE("battery_too_low_to_charge_for_scc1", "Battery too low to charge for SCC1", battery_too_low_to_charge_for_scc1),
+ LINE("battery_too_low_to_charge_for_scc2", "Battery too low to charge for SCC2", battery_too_low_to_charge_for_scc2),
+ })
+}
+
+
+void FlagsAndStatuses::unpack() {
+ auto list = getList({1, 1, 1, 1, 1, 1, 1, 1, 1});
+
+ buzzer = stou(list[0]) > 0;
+ overload_bypass = stou(list[1]) > 0;
+ lcd_escape_to_default_page_after_1min_timeout = stou(list[2]) > 0;
+ overload_restart = stou(list[3]) > 0;
+ over_temp_restart = stou(list[4]) > 0;
+ backlight_on = stou(list[5]) > 0;
+ alarm_on_primary_source_interrupt = stou(list[6]) > 0;
+ fault_code_record = stou(list[7]) > 0;
+ reserved = *list[8].c_str();
+}
+
+formattable_ptr FlagsAndStatuses::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("buzzer",
+ "Buzzer",
+ buzzer),
+
+ LINE("overload_bypass",
+ "Overload bypass function",
+ overload_bypass),
+
+ LINE("escape_to_default_screen_after_1min_timeout",
+ "Escape to default screen after 1min timeout",
+ lcd_escape_to_default_page_after_1min_timeout),
+
+ LINE("overload_restart",
+ "Overload restart",
+ overload_restart),
+
+ LINE("over_temp_restart",
+ "Over temperature restart",
+ over_temp_restart),
+
+ LINE("backlight_on",
+ "Backlight on",
+ backlight_on),
+
+ LINE("alarm_on_on_primary_source_interrupt",
+ "Alarm on on primary source interrupt",
+ alarm_on_primary_source_interrupt),
+
+ LINE("fault_code_record",
+ "Fault code record",
+ fault_code_record)
+ })
+}
+
+
+void Defaults::unpack() {
+ auto list = getList({
+ 4, // AAAA
+ 3, // BBB
+ 1, // C
+ 3, // DDD
+ 3, // EEE
+ 3, // FFF
+ 3, // GGG
+ 3, // HHH
+ 3, // III
+ 2, // JJ
+ 1, // K
+ 1, // L
+ 1, // M
+ 1, // N
+ 1, // O
+ 1, // P
+ 1, // S
+ 1, // T
+ 1, // U
+ 1, // V
+ 1, // W
+ 1, // X
+ 1, // Y
+ 1, // Z
+ });
+
+ ac_output_voltage = stou(list[0]);
+ ac_output_freq = stou(list[1]);
+ ac_input_voltage_range = static_cast<InputVoltageRange>(stou(list[2]));
+ battery_under_voltage = stou(list[3]);
+ charging_float_voltage = stou(list[4]);
+ charging_bulk_voltage = stou(list[5]);
+ battery_recharge_voltage = stou(list[6]);
+ battery_redischarge_voltage = stou(list[7]);
+ max_charging_current = stou(list[8]);
+ max_ac_charging_current = stou(list[9]);
+ battery_type = static_cast<BatteryType>(stou(list[10]));
+ output_source_priority = static_cast<OutputSourcePriority>(stou(list[11]));
+ charger_source_priority = static_cast<ChargerSourcePriority>(stou(list[12]));
+ solar_power_priority = static_cast<SolarPowerPriority>(stou(list[13]));
+ machine_type = static_cast<MachineType>(stou(list[14]));
+ output_model_setting = static_cast<OutputModelSetting>(stou(list[15]));
+ flag_buzzer = stou(list[16]) > 0;
+ flag_overload_restart = stou(list[17]) > 0;
+ flag_over_temp_restart = stou(list[18]) > 0;
+ flag_backlight_on = stou(list[19]) > 0;
+ flag_alarm_on_primary_source_interrupt = stou(list[20]) > 0;
+ flag_fault_code_record = stou(list[21]) > 0;
+ flag_overload_bypass = stou(list[22]) > 0;
+ flag_lcd_escape_to_default_page_after_1min_timeout = stou(list[23]) > 0;
+}
+
+formattable_ptr Defaults::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("ac_output_voltage", "AC output voltage", ac_output_voltage / 10.0, Unit::V),
+ LINE("ac_output_freq", "AC output frequency", ac_output_freq / 10.0, Unit::Hz),
+ LINE("ac_input_voltage_range", "AC input voltage range", ac_input_voltage_range),
+ LINE("battery_under_voltage", "Battery under voltage", battery_under_voltage / 10.0, Unit::V),
+ LINE("battery_bulk_voltage", "Charging bulk voltage", charging_bulk_voltage / 10.0, Unit::V),
+ LINE("battery_float_voltage", "Charging float voltage", charging_float_voltage / 10.0, Unit::V),
+ LINE("battery_recharging_voltage", "Battery re-charging voltage", battery_recharge_voltage / 10.0, Unit::V),
+ LINE("battery_redischarging_voltage", "Battery re-discharging voltage", battery_redischarge_voltage / 10.0, Unit::V),
+ LINE("max_charging_current", "Max charging current", max_charging_current, Unit::A),
+ LINE("max_ac_charging_current", "Max AC charging current", max_ac_charging_current, Unit::A),
+ LINE("battery_type", "Battery type", battery_type),
+ LINE("output_source_priority", "Output source priority", output_source_priority),
+ LINE("charger_source_priority", "Charger source priority", charger_source_priority),
+ LINE("solar_power_priority", "Solar power priority", solar_power_priority),
+ LINE("machine_type", "Machine type", machine_type),
+ LINE("output_model_setting", "Output model setting", output_model_setting),
+ LINE("buzzer_flag", "Buzzer flag", flag_buzzer),
+ LINE("overload_bypass_flag", "Overload bypass function flag", flag_overload_bypass),
+ LINE("escape_to_default_screen_after_1min_timeout_flag", "Escape to default screen after 1min timeout flag", flag_lcd_escape_to_default_page_after_1min_timeout),
+ LINE("overload_restart_flag", "Overload restart flag", flag_overload_restart),
+ LINE("over_temp_restart_flag", "Over temperature restart flag", flag_over_temp_restart),
+ LINE("backlight_on_flag", "Backlight on flag", flag_backlight_on),
+ LINE("alarm_on_on_primary_source_interrupt_flag", "Alarm on on primary source interrupt flag", flag_alarm_on_primary_source_interrupt),
+ LINE("fault_code_record_flag", "Fault code record flag", flag_fault_code_record),
+ })
+}
+
+void AllowedChargingCurrents::unpack() {
+ auto list = getList({});
+ for (const std::string& i: list) {
+ amps.emplace_back(stou(i));
+ }
+}
+
+formattable_ptr AllowedChargingCurrents::format(formatter::Format format) {
+ std::vector<formatter::ListItem<VariantHolder>> v;
+ for (const auto& n: amps)
+ v.emplace_back(n);
+
+ return std::shared_ptr<formatter::List<VariantHolder>>(
+ new formatter::List<VariantHolder>(format, v)
+ );
+}
+
+
+void ParallelRatedInformation::unpack() {
+ auto list = getList({
+ 1, // A
+ 2, // BB
+ 20, // CCCCCCCCCCCCCCCCCCCC
+ 1, // D
+ 3, // EEE
+
+ // FF
+ // note: protocol documentation says that the following field is 2 bytes long,
+ // but actual tests of the 6kw unit shows it can be 3 bytes long
+ FieldLength(2, 3),
+
+ 1 // G
+ });
+
+ parallel_id_connection_status = static_cast<ParallelConnectionStatus>(stou(list[0]));
+ serial_number_valid_length = stou(list[1]);
+ serial_number = std::string(list[2], 0, serial_number_valid_length);
+ charger_source_priority = static_cast<ChargerSourcePriority>(stou(list[3]));
+ max_charging_current = stou(list[4]);
+ max_ac_charging_current = stou(list[5]);
+ output_model_setting = static_cast<OutputModelSetting>(stou(list[6]));
+}
+
+formattable_ptr ParallelRatedInformation::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("parallel_id_connection_status", "Parallel ID connection status", parallel_id_connection_status),
+ LINE("serial_number", "Serial number", serial_number),
+ LINE("charger_source_priority", "Charger source priority", charger_source_priority),
+ LINE("max_charging_current", "Max charging current", max_charging_current, Unit::A),
+ LINE("max_ac_charging_current", "Max AC charging current", max_ac_charging_current, Unit::A),
+ LINE("output_model_setting", "Output model setting", output_model_setting),
+ })
+}
+
+
+void ParallelGeneralStatus::unpack() {
+ auto list = getList({
+ 1, // A
+ 1, // B
+ 2, // CC
+ 4, // DDDD
+ 3, // EEE
+ 4, // FFFF
+ 3, // GGG
+ 4, // HHHH
+ 4, // IIII
+ 5, // JJJJJ
+ 5, // KKKKK
+ 3, // LLL
+ 3, // MMM
+ 3, // NNN
+ 3, // OOO
+ 3, // PPP
+ 3, // QQQ
+ 3, // MMM. It's not my mistake, it's per the doc.
+ 4, // RRRR
+ 4, // SSSS
+ 4, // TTTT
+ 4, // UUUU
+ 1, // V
+ // FIXME: marked red in the docs
+ 1, // W
+ // FIXME: marked red in the docs
+ 1, // X
+ 1, // Y
+ 1, // Z
+ 1, // a
+ 3, // bbb. Note: this one is marked in red in the doc. Apparently it means
+ // that it may be missing on some models, see
+ // https://github.com/gch1p/inverter-tools/issues/1#issuecomment-981158688
+ }, 28);
+
+ parallel_id_connection_status = static_cast<ParallelConnectionStatus>(stou(list[0]));
+ work_mode = static_cast<p18::WorkingMode>(stou(list[1]));
+ fault_code = stou(list[2]);
+ grid_voltage = stou(list[3]);
+ grid_freq = stou(list[4]);
+ ac_output_voltage = stou(list[5]);
+ ac_output_freq = stou(list[6]);
+ ac_output_apparent_power = stou(list[7]);
+ ac_output_active_power = stou(list[8]);
+ total_ac_output_apparent_power = stou(list[9]);
+ total_ac_output_active_power = stou(list[10]);
+ output_load_percent = stou(list[11]);
+ total_output_load_percent = stou(list[12]);
+ battery_voltage = stou(list[13]);
+ battery_discharge_current = stou(list[14]);
+ battery_charging_current = stou(list[15]);
+ total_battery_charging_current = stou(list[16]);
+ battery_capacity = stou(list[17]);
+ pv1_input_power = stou(list[18]);
+ pv2_input_power = stou(list[19]);
+ pv1_input_voltage = stou(list[20]);
+ pv2_input_voltage = stou(list[21]);
+ mppt1_charger_status = static_cast<MPPTChargerStatus>(stou(list[22]));
+ mppt2_charger_status = static_cast<MPPTChargerStatus>(stou(list[23]));
+ load_connected = static_cast<LoadConnectionStatus>(stou(list[24]));
+ battery_power_direction = static_cast<BatteryPowerDirection>(stou(list[25]));
+ dc_ac_power_direction = static_cast<DC_AC_PowerDirection>(stou(list[26]));
+ line_power_direction = static_cast<LinePowerDirection>(stou(list[27]));
+ if (list.size() >= 29) {
+ max_temp_present = true;
+ max_temp = stou(list[28]);
+ }
+}
+
+formattable_ptr ParallelGeneralStatus::format(formatter::Format format) {
+ auto table = new formatter::Table<VariantHolder>(format, {
+ LINE("parallel_id_connection_status", "Parallel ID connection status", parallel_id_connection_status),
+ LINE("mode", "Working mode", work_mode),
+ LINE("fault_code", "Fault code", fault_code),
+ LINE("grid_voltage", "Grid voltage", grid_voltage / 10.0, Unit::V),
+ LINE("grid_freq", "Grid frequency", grid_freq / 10.0, Unit::Hz),
+ LINE("ac_output_voltage", "AC output voltage", ac_output_voltage / 10.0, Unit::V),
+ LINE("ac_output_freq", "AC output frequency", ac_output_freq / 10.0, Unit::Hz),
+ LINE("ac_output_apparent_power", "AC output apparent power", ac_output_apparent_power, Unit::VA),
+ LINE("ac_output_active_power", "AC output active power", ac_output_active_power, Unit::Wh),
+ LINE("total_ac_output_apparent_power", "Total AC output apparent power", total_ac_output_apparent_power, Unit::VA),
+ LINE("total_ac_output_active_power", "Total AC output active power", total_ac_output_active_power, Unit::Wh),
+ LINE("output_load_percent", "Output load percent", output_load_percent, Unit::Percentage),
+ LINE("total_output_load_percent", "Total output load percent", total_output_load_percent, Unit::Percentage),
+ LINE("battery_voltage", "Battery voltage", battery_voltage / 10.0, Unit::V),
+ LINE("battery_discharge_current", "Battery discharge current", battery_discharge_current, Unit::A),
+ LINE("battery_charging_current", "Battery charging current", battery_charging_current, Unit::A),
+ LINE("pv1_input_power", "PV1 Input power", pv1_input_power, Unit::Wh),
+ LINE("pv2_input_power", "PV2 Input power", pv2_input_power, Unit::Wh),
+ LINE("pv1_input_voltage", "PV1 Input voltage", pv1_input_voltage / 10.0, Unit::V),
+ LINE("pv2_input_voltage", "PV2 Input voltage", pv2_input_voltage / 10.0, Unit::V),
+ LINE("mppt1_charger_status", "MPPT1 charger status", mppt1_charger_status),
+ LINE("mppt2_charger_status", "MPPT2 charger status", mppt2_charger_status),
+ LINE("load_connected", "Load connection", load_connected),
+ LINE("battery_power_direction", "Battery power direction", battery_power_direction),
+ LINE("dc_ac_power_direction", "DC/AC power direction", dc_ac_power_direction),
+ LINE("line_power_direction", "Line power direction", line_power_direction),
+ });
+
+ if (max_temp_present) {
+ table->push(
+ LINE("max_temp", "Max. temperature", max_temp)
+ );
+ }
+
+ return std::shared_ptr<formatter::Table<VariantHolder>>(table);
+}
+
+
+void ACChargingTimeBucket::unpack() {
+ auto list = getList({4 /* AAAA */, 4 /* BBBB */});
+
+ start_h = stouh(list[0].substr(0, 2));
+ start_m = stouh(list[0].substr(2, 2));
+
+ end_h = stouh(list[1].substr(0, 2));
+ end_m = stouh(list[1].substr(2, 2));
+}
+
+static inline std::string get_time(unsigned short h, unsigned short m) {
+ std::ostringstream buf;
+ buf << std::setfill('0');
+ buf << std::setw(2) << h << ":" << std::setw(2) << m;
+ return buf.str();
+}
+
+formattable_ptr ACChargingTimeBucket::format(formatter::Format format) {
+ RETURN_TABLE({
+ LINE("start_time", "Start time", get_time(start_h, start_m)),
+ LINE("end_time", "End time", get_time(end_h, end_m)),
+ })
+}
+
+}
diff --git a/src/p18/response.cc b/src/protocol_18/response.cc.orig
index e2cd9f4..902fb95 100644
--- a/src/p18/response.cc
+++ b/src/protocol_18/response.cc.orig
@@ -7,7 +7,12 @@
#include <typeinfo>
#include "response.h"
+<<<<<<< HEAD:src/p18/response.cc
#include "exceptions.h"
+=======
+#include "../protocol/exceptions.h"
+#include "../logging.h"
+>>>>>>> bf1b75a (wip):src/protocol_18/response.cc
#define RETURN_TABLE(...) \
return std::shared_ptr<formatter::Table<VariantHolder>>( \
@@ -43,8 +48,6 @@ std::ostream& operator<<(std::ostream& os, FieldLength fl) {
* Base responses
*/
-BaseResponse::BaseResponse(std::shared_ptr<char> raw, size_t rawSize)
- : raw_(std::move(raw)), rawSize_(rawSize) {}
bool GetResponse::validate() {
if (rawSize_ < 5)
@@ -84,7 +87,7 @@ std::vector<std::string> GetResponse::getList(std::vector<FieldLength> itemLengt
error << "while parsing " << demangle_type_name(typeid(*this).name());
error << ": list is expected to be " << expectAtLeast << " items long, ";
error << "got only " << list.size() << " items";
- throw ParseError(error.str());
+ throw protocol::ParseError(error.str());
}
// check each item's length
@@ -94,7 +97,7 @@ std::vector<std::string> GetResponse::getList(std::vector<FieldLength> itemLengt
error << "while parsing " << demangle_type_name(typeid(*this).name());
error << ": item " << i << " is expected to be " << itemLengths[i] << " characters long, ";
error << "got " << list[i].size() << " characters";
- throw ParseError(error.str());
+ throw protocol::ParseError(error.str());
}
}
}
@@ -120,12 +123,6 @@ formattable_ptr SetResponse::format(formatter::Format format) {
RETURN_STATUS(get(), "");
}
-formattable_ptr ErrorResponse::format(formatter::Format format) {
- return std::shared_ptr<formatter::Status>(
- new formatter::Status(format, false, error_)
- );
-}
-
/**
* Actual typed responses
@@ -184,7 +181,7 @@ void CurrentTime::unpack() {
default:
std::ostringstream error;
error << "unexpected value while parsing CurrentTime (i = " << i << ")";
- throw ParseError(error.str());
+ throw protocol::ParseError(error.str());
}
}
}
diff --git a/src/protocol_18/response.h b/src/protocol_18/response.h
new file mode 100644
index 0000000..239c474
--- /dev/null
+++ b/src/protocol_18/response.h
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INVERTER_TOOLS_P18_RESPONSE_H
+#define INVERTER_TOOLS_P18_RESPONSE_H
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <variant>
+#include <nlohmann/json.hpp>
+
+#include "types.h"
+#include "../formatter/formatter.h"
+#include "../protocol/response.h"
+
+namespace p18::response_type {
+
+using protocol::BaseResponse;
+using nlohmann::json;
+
+typedef std::shared_ptr<formatter::Formattable> formattable_ptr;
+
+
+/**
+ * Value holder for the formatter module
+ */
+
+typedef std::variant<
+ unsigned,
+ unsigned short,
+ unsigned long,
+ bool,
+ double,
+ std::string,
+ p18::BatteryType,
+ p18::BatteryPowerDirection,
+ p18::ChargerSourcePriority,
+ p18::DC_AC_PowerDirection,
+ p18::InputVoltageRange,
+ p18::LinePowerDirection,
+ p18::MachineType,
+ p18::MPPTChargerStatus,
+ p18::Topology,
+ p18::OutputSourcePriority,
+ p18::OutputModelSetting,
+ p18::ParallelConnectionStatus,
+ p18::SolarPowerPriority,
+ p18::WorkingMode,
+ p18::LoadConnectionStatus,
+ p18::ConfigurationStatus
+> Variant;
+
+class VariantHolder {
+private:
+ Variant v_;
+
+public:
+ VariantHolder(unsigned v) : v_(v) {}
+ VariantHolder(unsigned short v) : v_(v) {}
+ VariantHolder(unsigned long v) : v_(v) {}
+ VariantHolder(bool v) : v_(v) {}
+ VariantHolder(double v) : v_(v) {}
+ VariantHolder(std::string v) : v_(v) {}
+ VariantHolder(p18::BatteryType v) : v_(v) {}
+ VariantHolder(p18::BatteryPowerDirection v) : v_(v) {}
+ VariantHolder(p18::ChargerSourcePriority v) : v_(v) {}
+ VariantHolder(p18::DC_AC_PowerDirection v) : v_(v) {}
+ VariantHolder(p18::InputVoltageRange v) : v_(v) {}
+ VariantHolder(p18::LinePowerDirection v) : v_(v) {}
+ VariantHolder(p18::MachineType v) : v_(v) {}
+ VariantHolder(p18::MPPTChargerStatus v) : v_(v) {}
+ VariantHolder(p18::Topology v) : v_(v) {}
+ VariantHolder(p18::OutputSourcePriority v) : v_(v) {}
+ VariantHolder(p18::OutputModelSetting v) : v_(v) {}
+ VariantHolder(p18::ParallelConnectionStatus v) : v_(v) {}
+ VariantHolder(p18::SolarPowerPriority v) : v_(v) {}
+ VariantHolder(p18::WorkingMode v) : v_(v) {}
+ VariantHolder(p18::LoadConnectionStatus v) : v_(v) {}
+ VariantHolder(p18::ConfigurationStatus v) : v_(v) {}
+
+ friend std::ostream &operator<<(std::ostream &os, VariantHolder const& ref) {
+ std::visit([&os](const auto& elem) {
+ os << elem;
+ }, ref.v_);
+ return os;
+ }
+
+ inline json toJSON() const {
+ json j;
+ bool isEnum =
+ std::holds_alternative<p18::BatteryType>(v_) ||
+ std::holds_alternative<p18::BatteryPowerDirection>(v_) ||
+ std::holds_alternative<p18::ChargerSourcePriority>(v_) ||
+ std::holds_alternative<p18::DC_AC_PowerDirection>(v_) ||
+ std::holds_alternative<p18::InputVoltageRange>(v_) ||
+ std::holds_alternative<p18::LinePowerDirection>(v_) ||
+ std::holds_alternative<p18::MachineType>(v_) ||
+ std::holds_alternative<p18::MPPTChargerStatus>(v_) ||
+ std::holds_alternative<p18::Topology>(v_) ||
+ std::holds_alternative<p18::OutputSourcePriority>(v_) ||
+ std::holds_alternative<p18::OutputModelSetting>(v_) ||
+ std::holds_alternative<p18::ParallelConnectionStatus>(v_) ||
+ std::holds_alternative<p18::SolarPowerPriority>(v_) ||
+ std::holds_alternative<p18::WorkingMode>(v_) ||
+ std::holds_alternative<p18::LoadConnectionStatus>(v_) ||
+ std::holds_alternative<p18::ConfigurationStatus>(v_);
+
+ std::visit([&j, &isEnum](const auto& elem) {
+ if (isEnum)
+ j = formatter::to_str(elem);
+ else
+ j = elem;
+ }, v_);
+
+ return j;
+ }
+
+ inline json toSimpleJSON() const {
+ json j;
+ std::visit([&j](const auto& elem) {
+ j = elem;
+ }, v_);
+ return j;
+ }
+};
+
+
+/**
+ * Some helpers
+ */
+class FieldLength {
+protected:
+ size_t min_;
+ size_t max_;
+
+public:
+ FieldLength(size_t n) : min_(n), max_(n) {}
+ FieldLength(size_t min, size_t max) : min_(min), max_(max) {}
+
+ [[nodiscard]] bool validate(size_t len) const {
+ return len >= min_ && len <= max_;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, FieldLength fl);
+};
+
+
+/**
+ * Base responses
+ */
+
+class BaseResponse {
+protected:
+ std::shared_ptr<char> raw_;
+ size_t rawSize_;
+
+public:
+ BaseResponse(std::shared_ptr<char> raw, size_t rawSize);
+ virtual ~BaseResponse() = default;
+ virtual bool validate() = 0;
+ virtual void unpack() = 0;
+ virtual formattable_ptr format(formatter::Format format) = 0;
+};
+
+class GetResponse : public BaseResponse {
+protected:
+ const char* getData() const;
+ size_t getDataSize() const;
+ std::vector<std::string> getList(std::vector<FieldLength> itemLengths, int expectAtLeast = -1) const;
+
+public:
+ using BaseResponse::BaseResponse;
+ bool validate() override;
+};
+
+class SetResponse : public BaseResponse {
+public:
+ using BaseResponse::BaseResponse;
+ void unpack() override;
+ bool validate() override;
+ formattable_ptr format(formatter::Format format) override;
+ bool get();
+};
+
+
+/**
+ * Actual typed responses
+ */
+
+class ProtocolID : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned id = 0;
+};
+
+class CurrentTime : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned year = 0;
+ unsigned short month = 0;
+ unsigned short day = 0;
+ unsigned short hour = 0;
+ unsigned short minute = 0;
+ unsigned short second = 0;
+};
+
+class TotalGenerated : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned long wh = 0;
+};
+
+class YearGenerated : public TotalGenerated {
+public:
+ using TotalGenerated::TotalGenerated;
+};
+
+class MonthGenerated : public TotalGenerated {
+public:
+ using TotalGenerated::TotalGenerated;
+};
+
+class DayGenerated : public TotalGenerated {
+public:
+ using TotalGenerated::TotalGenerated;
+};
+
+class SeriesNumber : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ std::string id;
+};
+
+class CPUVersion : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ std::string main_cpu_version;
+ std::string slave1_cpu_version;
+ std::string slave2_cpu_version;
+};
+
+class RatedInformation : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned ac_input_rating_voltage; /* unit: 0.1V */
+ unsigned ac_input_rating_current; /* unit: 0.1A */
+ unsigned ac_output_rating_voltage; /* unit: 0.1A */
+ unsigned ac_output_rating_freq; /* unit: 0.1Hz */
+ unsigned ac_output_rating_current; /* unit: 0.1A */
+ unsigned ac_output_rating_apparent_power; /* unit: VA */
+ unsigned ac_output_rating_active_power; /* unit: W */
+ unsigned battery_rating_voltage; /* unit: 0.1V */
+ unsigned battery_recharge_voltage; /* unit: 0.1V */
+ unsigned battery_redischarge_voltage; /* unit: 0.1V */
+ unsigned battery_under_voltage; /* unit: 0.1V */
+ unsigned battery_bulk_voltage; /* unit: 0.1V */
+ unsigned battery_float_voltage; /* unit: 0.1V */
+ p18::BatteryType battery_type;
+ unsigned max_ac_charging_current; /* unit: A */
+ unsigned max_charging_current; /* unit: A */
+ p18::InputVoltageRange input_voltage_range;
+ p18::OutputModelSetting output_source_priority;
+ p18::ChargerSourcePriority charger_source_priority;
+ unsigned parallel_max_num;
+ p18::MachineType machine_type;
+ p18::Topology topology;
+ p18::OutputModelSetting output_model_setting;
+ p18::SolarPowerPriority solar_power_priority;
+ std::string mppt;
+};
+
+class GeneralStatus : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned grid_voltage; /* unit: 0.1V */
+ unsigned grid_freq; /* unit: 0.1Hz */
+ unsigned ac_output_voltage; /* unit: 0.1V */
+ unsigned ac_output_freq; /* unit: 0.1Hz */
+ unsigned ac_output_apparent_power; /* unit: VA */
+ unsigned ac_output_active_power; /* unit: W */
+ unsigned output_load_percent; /* unit: % */
+ unsigned battery_voltage; /* unit: 0.1V */
+ unsigned battery_voltage_scc; /* unit: 0.1V */
+ unsigned battery_voltage_scc2; /* unit: 0.1V */
+ unsigned battery_discharge_current; /* unit: A */
+ unsigned battery_charging_current; /* unit: A */
+ unsigned battery_capacity; /* unit: % */
+ unsigned inverter_heat_sink_temp; /* unit: C */
+ unsigned mppt1_charger_temp; /* unit: C */
+ unsigned mppt2_charger_temp; /* unit: C */
+ unsigned pv1_input_power; /* unit: W */
+ unsigned pv2_input_power; /* unit: W */
+ unsigned pv1_input_voltage; /* unit: 0.1V */
+ unsigned pv2_input_voltage; /* unit: 0.1V */
+ p18::ConfigurationStatus configuration_status;
+ p18::MPPTChargerStatus mppt1_charger_status;
+ p18::MPPTChargerStatus mppt2_charger_status;
+ p18::LoadConnectionStatus load_connected;
+ p18::BatteryPowerDirection battery_power_direction;
+ p18::DC_AC_PowerDirection dc_ac_power_direction;
+ p18::LinePowerDirection line_power_direction;
+ unsigned local_parallel_id; /* 0 .. (parallel number - 1) */
+};
+
+class WorkingMode : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ p18::WorkingMode mode = static_cast<p18::WorkingMode>(0);
+};
+
+class FaultsAndWarnings : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned fault_code = 0;
+ bool line_fail = false;
+ bool output_circuit_short = false;
+ bool inverter_over_temperature = false;
+ bool fan_lock = false;
+ bool battery_voltage_high = false;
+ bool battery_low = false;
+ bool battery_under = false;
+ bool over_load = false;
+ bool eeprom_fail = false;
+ bool power_limit = false;
+ bool pv1_voltage_high = false;
+ bool pv2_voltage_high = false;
+ bool mppt1_overload_warning = false;
+ bool mppt2_overload_warning = false;
+ bool battery_too_low_to_charge_for_scc1 = false;
+ bool battery_too_low_to_charge_for_scc2 = false;
+};
+
+class FlagsAndStatuses : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ bool buzzer = false;
+ bool overload_bypass = false;
+ bool lcd_escape_to_default_page_after_1min_timeout = false;
+ bool overload_restart = false;
+ bool over_temp_restart = false;
+ bool backlight_on = false;
+ bool alarm_on_primary_source_interrupt = false;
+ bool fault_code_record = false;
+ char reserved = '0';
+};
+
+class Defaults : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned ac_output_voltage = 0; /* unit: 0.1V */
+ unsigned ac_output_freq = 0;
+ p18::InputVoltageRange ac_input_voltage_range = static_cast<InputVoltageRange>(0);
+ unsigned battery_under_voltage = 0;
+ unsigned charging_float_voltage = 0;
+ unsigned charging_bulk_voltage = 0;
+ unsigned battery_recharge_voltage = 0;
+ unsigned battery_redischarge_voltage = 0;
+ unsigned max_charging_current = 0;
+ unsigned max_ac_charging_current = 0;
+ p18::BatteryType battery_type = static_cast<BatteryType>(0);
+ p18::OutputSourcePriority output_source_priority = static_cast<OutputSourcePriority>(0);
+ p18::ChargerSourcePriority charger_source_priority = static_cast<ChargerSourcePriority>(0);
+ p18::SolarPowerPriority solar_power_priority = static_cast<SolarPowerPriority>(0);
+ p18::MachineType machine_type = static_cast<MachineType>(0);
+ p18::OutputModelSetting output_model_setting = static_cast<OutputModelSetting>(0);
+ bool flag_buzzer = false;
+ bool flag_overload_restart = false;
+ bool flag_over_temp_restart = false;
+ bool flag_backlight_on = false;
+ bool flag_alarm_on_primary_source_interrupt = false;
+ bool flag_fault_code_record = false;
+ bool flag_overload_bypass = false;
+ bool flag_lcd_escape_to_default_page_after_1min_timeout = false;
+};
+
+class AllowedChargingCurrents : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ std::vector<unsigned> amps;
+};
+
+class AllowedACChargingCurrents : public AllowedChargingCurrents {
+public:
+ using AllowedChargingCurrents::AllowedChargingCurrents;
+};
+
+class ParallelRatedInformation : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ p18::ParallelConnectionStatus parallel_id_connection_status = static_cast<ParallelConnectionStatus>(0);
+ unsigned serial_number_valid_length = 0;
+ std::string serial_number;
+ p18::ChargerSourcePriority charger_source_priority = static_cast<ChargerSourcePriority>(0);
+ unsigned max_ac_charging_current = 0; // unit: A
+ unsigned max_charging_current = 0; // unit: A
+ p18::OutputModelSetting output_model_setting = static_cast<OutputModelSetting>(0);
+};
+
+class ParallelGeneralStatus : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ p18::ParallelConnectionStatus parallel_id_connection_status;
+ p18::WorkingMode work_mode;
+ unsigned fault_code;
+ unsigned grid_voltage; /* unit: 0.1V */
+ unsigned grid_freq; /* unit: 0.1Hz */
+ unsigned ac_output_voltage; /* unit: 0.1V */
+ unsigned ac_output_freq; /* unit: 0.1Hz */
+ unsigned ac_output_apparent_power; /* unit: VA */
+ unsigned ac_output_active_power; /* unit: W */
+ unsigned total_ac_output_apparent_power; /* unit: VA */
+ unsigned total_ac_output_active_power; /* unit: W */
+ unsigned output_load_percent; /* unit: % */
+ unsigned total_output_load_percent; /* unit: % */
+ unsigned battery_voltage; /* unit: 0.1V */
+ unsigned battery_discharge_current; /* unit: A */
+ unsigned battery_charging_current; /* unit: A */
+ unsigned total_battery_charging_current; /* unit: A */
+ unsigned battery_capacity; /* unit: % */
+ unsigned pv1_input_power; /* unit: W */
+ unsigned pv2_input_power; /* unit: W */
+ unsigned pv1_input_voltage; /* unit: 0.1V */
+ unsigned pv2_input_voltage; /* unit: 0.1V */
+ p18::MPPTChargerStatus mppt1_charger_status;
+ p18::MPPTChargerStatus mppt2_charger_status;
+ p18::LoadConnectionStatus load_connected;
+ p18::BatteryPowerDirection battery_power_direction;
+ p18::DC_AC_PowerDirection dc_ac_power_direction;
+ p18::LinePowerDirection line_power_direction;
+
+ bool max_temp_present = false;
+ unsigned max_temp; /* unit: C */
+};
+
+class ACChargingTimeBucket : public GetResponse {
+public:
+ using GetResponse::GetResponse;
+ void unpack() override;
+ formattable_ptr format(formatter::Format format) override;
+
+ unsigned short start_h = 0;
+ unsigned short start_m = 0;
+ unsigned short end_h = 0;
+ unsigned short end_m = 0;
+};
+
+class ACLoadsSupplyTimeBucket : public ACChargingTimeBucket {
+public:
+ using ACChargingTimeBucket::ACChargingTimeBucket;
+};
+
+} // namespace protocol_18
+
+#endif //INVERTER_TOOLS_P18_RESPONSE_H
diff --git a/src/p18/response.h b/src/protocol_18/response.h.orig
index 3ffc6d4..9295943 100644
--- a/src/p18/response.h
+++ b/src/protocol_18/response.h.orig
@@ -10,10 +10,12 @@
#include <nlohmann/json.hpp>
#include "types.h"
-#include "src/formatter/formatter.h"
+#include "../formatter/formatter.h"
+#include "../protocol/response.h"
namespace p18::response_type {
+using protocol::BaseResponse;
using nlohmann::json;
typedef std::shared_ptr<formatter::Formattable> formattable_ptr;
@@ -124,6 +126,7 @@ public:
};
+<<<<<<< HEAD:src/p18/response.h
/**
* Some helpers
*/
@@ -161,6 +164,8 @@ public:
virtual formattable_ptr format(formatter::Format format) = 0;
};
+=======
+>>>>>>> bf1b75a (wip):src/protocol_18/response.h
class GetResponse : public BaseResponse {
protected:
const char* getData() const;
@@ -170,7 +175,6 @@ protected:
public:
using BaseResponse::BaseResponse;
bool validate() override;
-// virtual void output() = 0;
};
class SetResponse : public BaseResponse {
@@ -182,21 +186,6 @@ public:
bool get();
};
-class ErrorResponse : public BaseResponse {
-private:
- std::string error_;
-
-public:
- explicit ErrorResponse(std::string error)
- : BaseResponse(nullptr, 0), error_(std::move(error)) {}
-
- bool validate() override {
- return true;
- }
- void unpack() override {}
- formattable_ptr format(formatter::Format format) override;
-};
-
/**
* Actual typed responses
@@ -506,6 +495,6 @@ public:
using ACChargingTimeBucket::ACChargingTimeBucket;
};
-} // namespace p18
+} // namespace protocol_18
#endif //INVERTER_TOOLS_P18_RESPONSE_H
diff --git a/src/p18/types.h b/src/protocol_18/types.h
index bcabd7c..d3574e9 100644
--- a/src/p18/types.h
+++ b/src/protocol_18/types.h
@@ -10,7 +10,7 @@
namespace p18 {
-enum class CommandType {
+enum class CommandType: int {
GetProtocolID = 0,
GetCurrentTime,
GetTotalGenerated,