From 3aed59276c2b7275603f6da18377b77718948667 Mon Sep 17 00:00:00 2001 From: Evgeny Zinoviev Date: Mon, 7 Jun 2021 01:40:32 +0300 Subject: wip --- src/common.cc | 15 + src/common.h | 9 +- src/inverterctl.cc | 343 +++++++++------- src/p18/client.cc | 229 ----------- src/p18/client.h | 31 -- src/p18/commands.cc | 457 ---------------------- src/p18/commands.h | 35 -- src/p18/defines.cc | 262 ------------- src/p18/defines.h | 32 -- src/p18/exceptions.h | 22 -- src/p18/functions.cc | 12 - src/p18/functions.h | 12 - src/p18/response.cc | 815 --------------------------------------- src/p18/response.h | 511 ------------------------ src/p18/types.h | 174 --------- src/protocol/client.cc | 21 + src/protocol/client.h | 23 ++ src/protocol/exceptions.h | 22 ++ src/protocol/input.cc | 118 ++++++ src/protocol/input.h | 32 ++ src/protocol/response.cc | 19 + src/protocol/response.h | 44 +++ src/protocol_17/defines.cc | 138 +++++++ src/protocol_17/defines.h | 32 ++ src/protocol_17/input.cc | 117 ++++++ src/protocol_17/input.h | 21 + src/protocol_17/response.h | 462 ++++++++++++++++++++++ src/protocol_17/types.h | 103 +++++ src/protocol_18/client.cc | 216 +++++++++++ src/protocol_18/client.h | 32 ++ src/protocol_18/defines.cc | 262 +++++++++++++ src/protocol_18/defines.h | 32 ++ src/protocol_18/functions.cc | 12 + src/protocol_18/functions.h | 12 + src/protocol_18/input.cc | 341 ++++++++++++++++ src/protocol_18/input.h | 21 + src/protocol_18/response.cc | 808 ++++++++++++++++++++++++++++++++++++++ src/protocol_18/response.cc.orig | 812 ++++++++++++++++++++++++++++++++++++++ src/protocol_18/response.h | 496 ++++++++++++++++++++++++ src/protocol_18/response.h.orig | 500 ++++++++++++++++++++++++ src/protocol_18/types.h | 174 +++++++++ 41 files changed, 5104 insertions(+), 2725 deletions(-) delete mode 100644 src/p18/client.cc delete mode 100644 src/p18/client.h delete mode 100644 src/p18/commands.cc delete mode 100644 src/p18/commands.h delete mode 100644 src/p18/defines.cc delete mode 100644 src/p18/defines.h delete mode 100644 src/p18/exceptions.h delete mode 100644 src/p18/functions.cc delete mode 100644 src/p18/functions.h delete mode 100644 src/p18/response.cc delete mode 100644 src/p18/response.h delete mode 100644 src/p18/types.h create mode 100644 src/protocol/client.cc create mode 100644 src/protocol/client.h create mode 100644 src/protocol/exceptions.h create mode 100644 src/protocol/input.cc create mode 100644 src/protocol/input.h create mode 100644 src/protocol/response.cc create mode 100644 src/protocol/response.h create mode 100644 src/protocol_17/defines.cc create mode 100644 src/protocol_17/defines.h create mode 100644 src/protocol_17/input.cc create mode 100644 src/protocol_17/input.h create mode 100644 src/protocol_17/response.h create mode 100644 src/protocol_17/types.h create mode 100644 src/protocol_18/client.cc create mode 100644 src/protocol_18/client.h create mode 100644 src/protocol_18/defines.cc create mode 100644 src/protocol_18/defines.h create mode 100644 src/protocol_18/functions.cc create mode 100644 src/protocol_18/functions.h create mode 100644 src/protocol_18/input.cc create mode 100644 src/protocol_18/input.h create mode 100644 src/protocol_18/response.cc create mode 100644 src/protocol_18/response.cc.orig create mode 100644 src/protocol_18/response.h create mode 100644 src/protocol_18/response.h.orig create mode 100644 src/protocol_18/types.h (limited to 'src') 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 #include +// 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 @@ -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 : Execute arbitrary command and print response\n" + " -p, --protocol 17 or 18 (default)\n" " --device : 'usb' (default), 'serial' or 'pseudo'\n" " --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 \n" + " get-month-generated \n" + " get-day-generated
\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 \n" + " get-month-generated \n" + " get-day-generated
\n" + " get-series-number\n" + " get-cpu-version\n" + " get-rated\n" + " get-status\n" + " get-p-rated \n" + " id: Parallel machine ID\n" + "\n" + " get-p-status \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 0|1\n" + " set-rated-defaults\n" + " set-max-charging-current \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 \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 \n" + " cv: Constant voltage (48.0 ~ 58.4).\n" + " fv: Float voltage (48.0 ~ 58.4).\n" + "\n" + " set-ac-output-voltage \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 \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 \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 \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 \n" + " v: Cut-off voltage (40.0~48.0)\n" + "\n" + " set-solar-configuration \n" + " id: Serial number\n" + "\n" + " clear-generated-data\n" + " Clear all recorded stats about generated energy.\n" + "\n" + " set-date-time
\n" + " YYYY: Year\n" + " MM: Month\n" + " DD: Day\n" + " hh: Hours\n" + " mm: Minutes\n" + " ss: Seconds\n" + "\n" + " set-ac-charging-time \n" + " start: Starting time, hh:mm format\n" + " end: Ending time, hh:mm format\n" + "\n" + " set-ac-loads-supply-time \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 : Execute arbitrary command and print response\n" " (example: ^P005PI)\n" + " -p, --protocol Protocol ID (default: 18)\n" " --device : Device type to use. See below for list of supported\n" " devices\n" " --timeout : Device read/write timeout, in milliseconds\n" @@ -69,6 +220,10 @@ static void usage(const char* progname) { " device traffic)\n" " --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 \n" - " get-month-generated \n" - " get-day-generated
\n" - " get-series-number\n" - " get-cpu-version\n" - " get-rated\n" - " get-status\n" - " get-p-rated \n" - " id: Parallel machine ID\n" - "\n" - " get-p-status \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 0|1\n" - " set-rated-defaults\n" - " set-max-charging-current \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 \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 \n" - " cv: Constant voltage (48.0 ~ 58.4).\n" - " fv: Float voltage (48.0 ~ 58.4).\n" - "\n" - " set-ac-output-voltage \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 \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 \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 \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 \n" - " v: Cut-off voltage (40.0~48.0)\n" - "\n" - " set-solar-configuration \n" - " id: Serial number\n" - "\n" - " clear-generated-data\n" - " Clear all recorded stats about generated energy.\n" - "\n" - " set-date-time
\n" - " YYYY: Year\n" - " MM: Month\n" - " DD: Day\n" - " hh: Hours\n" - " mm: Minutes\n" - " ss: Seconds\n" - "\n" - " set-ac-charging-time \n" - " start: Starting time, hh:mm format\n" - " end: Ending time, hh:mm format\n" - "\n" - " set-ac-loads-supply-time \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 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(p17::validate_input(command, arguments, (void*)&input)); + break; + + case Protocol::P18: + commandType = static_cast(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/client.cc b/src/p18/client.cc deleted file mode 100644 index 1798be9..0000000 --- a/src/p18/client.cc +++ /dev/null @@ -1,229 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#include -#include -#include -#include -#include -#include - -#include "client.h" -#include "types.h" -#include "defines.h" -#include "exceptions.h" -#include "response.h" -#include "../voltronic/crc.h" - -#define MKRESPONSE(type) std::shared_ptr(new response_type::type(raw, rawSize)) - -#define RESPONSE_CASE(type) \ - case CommandType::Get ## type: \ - response = MKRESPONSE(type); \ - break; \ - - -namespace p18 { - -void Client::setDevice(std::shared_ptr device) { - device_ = std::move(device); -} - -std::shared_ptr Client::execute(p18::CommandType commandType, std::vector& arguments) { - std::ostringstream buf; - buf << std::setfill('0'); - - int iCommandType = static_cast(commandType); - bool isSetCommand = iCommandType >= 100; - - auto pos = raw_commands.find(commandType); - if (pos == raw_commands.end()) - throw std::runtime_error("packedCommand " + std::to_string(iCommandType) + " not found"); - - std::string packedCommand = pos->second; - std::string packedArguments = packArguments(commandType, arguments); - - size_t len = sizeof(voltronic::CRC) + 1 + packedCommand.size() + packedArguments.size(); - - buf << "^"; - buf << (isSetCommand ? "S" : "P"); - buf << std::setw(3) << len; - buf << packedCommand; - buf << packedArguments; - - std::string packed = buf.str(); - - auto result = runOnDevice(packed); - std::shared_ptr response; - - const auto raw = result.first; - const auto rawSize = result.second; - - switch (commandType) { - RESPONSE_CASE(ProtocolID) - RESPONSE_CASE(CurrentTime) - RESPONSE_CASE(TotalGenerated) - RESPONSE_CASE(YearGenerated) - RESPONSE_CASE(MonthGenerated) - RESPONSE_CASE(DayGenerated) - RESPONSE_CASE(SeriesNumber) - RESPONSE_CASE(CPUVersion) - RESPONSE_CASE(RatedInformation) - RESPONSE_CASE(GeneralStatus) - RESPONSE_CASE(WorkingMode) - RESPONSE_CASE(FaultsAndWarnings) - RESPONSE_CASE(FlagsAndStatuses) - RESPONSE_CASE(Defaults) - RESPONSE_CASE(AllowedChargingCurrents) - RESPONSE_CASE(AllowedACChargingCurrents) - RESPONSE_CASE(ParallelRatedInformation) - RESPONSE_CASE(ParallelGeneralStatus) - RESPONSE_CASE(ACChargingTimeBucket) - RESPONSE_CASE(ACLoadsSupplyTimeBucket) - - case CommandType::SetLoads: - case CommandType::SetFlag: - case CommandType::SetDefaults: - case CommandType::SetBatteryMaxChargingCurrent: - case CommandType::SetBatteryMaxACChargingCurrent: - case CommandType::SetACOutputFreq: - case CommandType::SetBatteryMaxChargingVoltage: - case CommandType::SetACOutputRatedVoltage: - case CommandType::SetOutputSourcePriority: - case CommandType::SetBatteryChargingThresholds: - case CommandType::SetChargingSourcePriority: - case CommandType::SetSolarPowerPriority: - case CommandType::SetACInputVoltageRange: - case CommandType::SetBatteryType: - case CommandType::SetOutputModel: - case CommandType::SetBatteryCutOffVoltage: - case CommandType::SetSolarConfig: - case CommandType::ClearGenerated: - case CommandType::SetDateTime: - case CommandType::SetACChargingTimeBucket: - case CommandType::SetACLoadsSupplyTimeBucket: - response = MKRESPONSE(SetResponse); - break; - } - - if (!response->validate()) - throw InvalidResponseError("validate() failed"); - - response->unpack(); - return std::move(response); -} - -std::pair, size_t> Client::runOnDevice(std::string& raw) { - size_t bufSize = 256; - std::shared_ptr buf(new char[bufSize]); - size_t responseSize = device_->run( - (const u8*)raw.c_str(), raw.size(), - (u8*)buf.get(), bufSize); - - return std::pair, size_t>(buf, responseSize); -} - -std::string Client::packArguments(p18::CommandType commandType, std::vector& arguments) { - std::ostringstream buf; - buf << std::setfill('0'); - - switch (commandType) { - case CommandType::GetYearGenerated: - case CommandType::SetOutputSourcePriority: - case CommandType::SetSolarPowerPriority: - case CommandType::SetACInputVoltageRange: - case CommandType::SetBatteryType: - case CommandType::SetLoads: - buf << arguments[0]; - break; - - case CommandType::GetMonthGenerated: - case CommandType::GetDayGenerated: - buf << arguments[0]; - for (int i = 1; i <= (commandType == CommandType::GetMonthGenerated ? 1 : 2); i++) - buf << std::setw(2) << std::stoi(arguments[i]); - break; - - case CommandType::GetParallelGeneralStatus: - case CommandType::GetParallelRatedInformation: - buf << std::stoi(arguments[0]); - break; - - case CommandType::SetFlag: - buf << (arguments[1] == "1" ? "E" : "D"); - buf << arguments[0]; - break; - - case CommandType::SetBatteryMaxChargingCurrent: - case CommandType::SetBatteryMaxACChargingCurrent: - buf << arguments[0] << ","; - buf << std::setw(3) << std::stoi(arguments[1]); - break; - - case CommandType::SetACOutputFreq: - buf << std::setw(2) << std::stoi(arguments[0]); - break; - - case CommandType::SetBatteryMaxChargingVoltage: - case CommandType::SetBatteryChargingThresholds: { - for (int i = 0; i < 2; i++) { - double val = std::stod(arguments[i]); - buf << std::setw(3) << (int)round(val*10); - if (i == 0) - buf << ","; - } - break; - } - - case CommandType::SetACOutputRatedVoltage: { - buf << std::setw(4) << (std::stoi(arguments[0])*10); - break; - } - - case CommandType::SetChargingSourcePriority: - case CommandType::SetOutputModel: - buf << arguments[0] << "," << arguments[1]; - break; - - case CommandType::SetBatteryCutOffVoltage: { - double v = std::stod(arguments[0]); - buf << std::setw(3) << ((int)round(v*10)); - break; - } - - case CommandType::SetSolarConfig: { - size_t len = arguments[0].size(); - buf << std::setw(2) << len << arguments[0]; - if (len < 20) { - for (int i = 0; i < 20-len; i++) - buf << "0"; - } - break; - } - - case CommandType::SetDateTime: { - for (int i = 0; i < 6; i++) { - int val = std::stoi(arguments[i]); - if (i == 0) - val -= 2000; - buf << std::setw(2) << val; - } - break; - } - - case CommandType::SetACChargingTimeBucket: - case CommandType::SetACLoadsSupplyTimeBucket: - for (int i = 0; i < 4; i++) { - buf << std::setw(2) << std::stoi(arguments[i]); - if (i == 1) - buf << ","; - } - break; - - default: - break; - } - - return buf.str(); -} - -} \ No newline at end of file diff --git a/src/p18/client.h b/src/p18/client.h deleted file mode 100644 index 8307bbb..0000000 --- a/src/p18/client.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#ifndef INVERTER_TOOLS_P18_CLIENT_H_ -#define INVERTER_TOOLS_P18_CLIENT_H_ - -#include "../voltronic/device.h" -#include "types.h" -#include "response.h" - -#include -#include -#include - - -namespace p18 { - -class Client { -private: - std::shared_ptr device_; - static std::string packArguments(p18::CommandType commandType, std::vector& arguments); - -public: - void setDevice(std::shared_ptr device); - std::shared_ptr execute(p18::CommandType commandType, std::vector& arguments); - std::pair, size_t> runOnDevice(std::string& raw); -}; - -} - - -#endif //INVERTER_TOOLS_P18_CLIENT_H_ 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 -#include -#include -#include - -#ifdef INVERTERCTL -#include -#endif - -#include "commands.h" -#include "defines.h" -#include "functions.h" -#include "../util.h" -#include "../logging.h" - -namespace p18 { - -const std::map 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(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(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(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& 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& 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& 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(std::stoul(arguments[0])); - auto amps = static_cast(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(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 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(std::stoul(arguments[0])); - if (!p18::is_valid_parallel_id(id)) - throw std::invalid_argument("invalid id"); - - std::array 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 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 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 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(std::stoul(arguments[0])); - if (!p18::is_valid_parallel_id(id)) - throw std::invalid_argument("invalid id"); - - std::array 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 start = split(arguments[0], ':'); - if (start.size() != 2) - throw std::invalid_argument("invalid start time"); - - std::vector end = split(arguments[1], ':'); - if (end.size() != 2) - throw std::invalid_argument("invalid end time"); - - auto startHour = static_cast(std::stoul(start[0])); - auto startMinute = static_cast(std::stoul(start[1])); - if (startHour > 23 || startMinute > 59) - throw std::invalid_argument("invalid start time"); - - auto endHour = static_cast(std::stoul(end[0])); - auto endMinute = static_cast(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 -#include -#include - -#include "types.h" - -namespace p18 { - -#ifdef INVERTERCTL -struct CommandInput { - int argc; - char** argv; -}; -#endif - -#ifdef INVERTERD -struct CommandInput { - std::vector* argv; -}; -#endif - -extern const std::map 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& arguments, void* input); - -} - -#endif //INVERTER_TOOLS_P18_COMMANDS_H diff --git a/src/p18/defines.cc b/src/p18/defines.cc deleted file mode 100644 index 999f81a..0000000 --- a/src/p18/defines.cc +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#include - -#include "defines.h" -#include "types.h" - -namespace p18 { - -const std::map 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::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"}, -}; - -const std::array ac_output_rated_voltages = {202, 208, 220, 230, 240}; - -const std::array bat_ac_recharging_voltages_12v = {11, 11.3, 11.5, 11.8, 12, 12.3, 12.5, 12.8}; -const std::array bat_ac_recharging_voltages_24v = {22, 22.5, 23, 23.5, 24, 24.5, 25, 25.5}; -const std::array bat_ac_recharging_voltages_48v = {44, 45, 46, 47, 48, 49, 50, 51}; - -const std::array bat_ac_redischarging_voltages_12v = {0, 12, 12.3, 12.5, 12.8, 13, 13.3, 13.5, 13.8, 14, 14.3, 14.5}; -const std::array bat_ac_redischarging_voltages_24v = {0, 24, 24.5, 25, 25.5, 26, 26.5, 27, 27.5, 28, 28.5, 29}; -const std::array bat_ac_redischarging_voltages_48v = {0, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58}; - -const std::map fault_codes = { - {1, "Fan is locked"}, - {2, "Over temperature"}, - {3, "Battery voltage is too high"}, - {4, "Battery voltage is too low"}, - {5, "Output short circuited or Over temperature"}, - {6, "Output voltage is too high"}, - {7, "Over load time out"}, - {8, "Bus voltage is too high"}, - {9, "Bus soft start failed"}, - {11, "Main relay failed"}, - {51, "Over current inverter"}, - {52, "Bus soft start failed"}, - {53, "Inverter soft start failed"}, - {54, "Self-test failed"}, - {55, "Over DC voltage on output of inverter"}, - {56, "Battery connection is open"}, - {57, "Current sensor failed"}, - {58, "Output voltage is too low"}, - {60, "Inverter negative power"}, - {71, "Parallel version different"}, - {72, "Output circuit failed"}, - {80, "CAN communication failed"}, - {81, "Parallel host line lost"}, - {82, "Parallel synchronized signal lost"}, - {83, "Parallel battery voltage detect different"}, - {84, "Parallel LINE voltage or frequency detect different"}, - {85, "Parallel LINE input current unbalanced"}, - {86, "Parallel output setting different"}, -}; - -const std::array flags = {{ - {"BUZZ", 'A', "Silence buzzer or open buzzer"}, - {"OLBP", 'B', "Overload bypass function"}, - {"LCDE", 'C', "LCD display escape to default page after 1min timeout"}, - {"OLRS", 'D', "Overload restart"}, - {"OTRS", 'E', "Overload temperature restart"}, - {"BLON", 'F', "Backlight on"}, - {"ALRM", 'G', "Alarm on primary source interrupt"}, - {"FTCR", 'H', "Fault code record"}, - {"MTYP", 'I', "Machine type (1=Grid-Tie, 0=Off-Grid-Tie)"}, -}}; - -ENUM_STR(BatteryType) { - switch (val) { - case BatteryType::AGM: return os << "AGM" ; - case BatteryType::Flooded: return os << "Flooded"; - case BatteryType::User: return os << "User"; - }; - ENUM_STR_DEFAULT; -} - -ENUM_STR(InputVoltageRange) { - switch (val) { - case InputVoltageRange::Appliance: return os << "Appliance"; - case InputVoltageRange::USP: return os << "USP"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(OutputSourcePriority) { - switch (val) { - case OutputSourcePriority::SolarUtilityBattery: - return os << "Solar-Utility-Battery"; - case OutputSourcePriority::SolarBatteryUtility: - return os << "Solar-Battery-Utility"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(ChargerSourcePriority) { - switch (val) { - case ChargerSourcePriority::SolarFirst: - return os << "Solar-First"; - case ChargerSourcePriority::SolarAndUtility: - return os << "Solar-and-Utility"; - case ChargerSourcePriority::SolarOnly: - return os << "Solar-only"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(MachineType) { - switch (val) { - case MachineType::OffGridTie: return os << "Off-Grid-Tie"; - case MachineType::GridTie: return os << "Grid-Tie"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(Topology) { - switch (val) { - case Topology::TransformerLess: return os << "Transformer-less"; - case Topology::Transformer: return os << "Transformer"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(OutputModelSetting) { - switch (val) { - case OutputModelSetting::SingleModule: - return os << "Single module"; - case OutputModelSetting::ParallelOutput: - return os << "Parallel output"; - case OutputModelSetting::Phase1OfThreePhaseOutput: - return os << "Phase 1 of three phase output"; - case OutputModelSetting::Phase2OfThreePhaseOutput: - return os << "Phase 2 of three phase output"; - case OutputModelSetting::Phase3OfThreePhaseOutput: - return os << "Phase 3 of three phase"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(SolarPowerPriority) { - switch (val) { - case SolarPowerPriority::BatteryLoadUtility: - return os << "Battery-Load-Utility"; - case SolarPowerPriority::LoadBatteryUtility: - return os << "Load-Battery-Utility"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(MPPTChargerStatus) { - switch (val) { - case MPPTChargerStatus::Abnormal: return os << "Abnormal"; - case MPPTChargerStatus::NotCharging: return os << "Not charging"; - case MPPTChargerStatus::Charging: return os << "Charging"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(BatteryPowerDirection) { - switch (val) { - case BatteryPowerDirection::DoNothing: return os << "Do nothing"; - case BatteryPowerDirection::Charge: return os << "Charge"; - case BatteryPowerDirection::Discharge: return os << "Discharge"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(DC_AC_PowerDirection) { - switch (val) { - case DC_AC_PowerDirection::DoNothing: return os << "Do nothing"; - case DC_AC_PowerDirection::AC_DC: return os << "AC/DC"; - case DC_AC_PowerDirection::DC_AC: return os << "DC/AC"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(LinePowerDirection) { - switch (val) { - case LinePowerDirection::DoNothing: return os << "Do nothing"; - case LinePowerDirection::Input: return os << "Input"; - case LinePowerDirection::Output: return os << "Output"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(WorkingMode) { - switch (val) { - case WorkingMode::PowerOnMode: return os << "Power on mode"; - case WorkingMode::StandbyMode: return os << "Standby mode"; - case WorkingMode::BypassMode: return os << "Bypass mode"; - case WorkingMode::BatteryMode: return os << "Battery mode"; - case WorkingMode::FaultMode: return os << "Fault mode"; - case WorkingMode::HybridMode: return os << "Hybrid mode"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(ParallelConnectionStatus) { - switch (val) { - case ParallelConnectionStatus::NotExistent: return os << "Non-existent"; - case ParallelConnectionStatus::Existent: return os << "Existent"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(LoadConnectionStatus) { - switch (val) { - case LoadConnectionStatus::Disconnected: return os << "Disconnected"; - case LoadConnectionStatus::Connected: return os << "Connected"; - } - ENUM_STR_DEFAULT; -} - -ENUM_STR(ConfigurationStatus) { - switch (val) { - case ConfigurationStatus::Default: return os << "Default"; - case ConfigurationStatus::Changed: return os << "Changed"; - } - ENUM_STR_DEFAULT; -} - -} \ No newline at end of file diff --git a/src/p18/defines.h b/src/p18/defines.h deleted file mode 100644 index 83728f8..0000000 --- a/src/p18/defines.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#ifndef INVERTER_TOOLS_P18_DEFINES_H -#define INVERTER_TOOLS_P18_DEFINES_H - -#include -#include -#include - -#include "types.h" - -namespace p18 { - -extern const std::map raw_commands; - -extern const std::array ac_output_rated_voltages; - -extern const std::array bat_ac_recharging_voltages_12v; -extern const std::array bat_ac_recharging_voltages_24v; -extern const std::array bat_ac_recharging_voltages_48v; - -extern const std::array bat_ac_redischarging_voltages_12v; -extern const std::array bat_ac_redischarging_voltages_24v; -extern const std::array bat_ac_redischarging_voltages_48v; - -extern const std::map fault_codes; - -extern const std::array flags; - -} - -#endif //INVERTER_TOOLS_P18_DEFINES_H diff --git a/src/p18/exceptions.h b/src/p18/exceptions.h deleted file mode 100644 index 9b79082..0000000 --- a/src/p18/exceptions.h +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#ifndef INFINISOLAR_TOOLS_P18_EXCEPTIONS_H -#define INFINISOLAR_TOOLS_P18_EXCEPTIONS_H - -#include - -namespace p18 { - -class InvalidResponseError : public std::runtime_error { -public: - using std::runtime_error::runtime_error; -}; - -class ParseError : public InvalidResponseError { -public: - using InvalidResponseError::InvalidResponseError; -}; - -} - -#endif //INFINISOLAR_TOOLS_P18_EXCEPTIONS_H diff --git a/src/p18/functions.cc b/src/p18/functions.cc deleted file mode 100644 index 9799fc0..0000000 --- a/src/p18/functions.cc +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#include "functions.h" - -namespace p18 { - -bool is_valid_parallel_id(unsigned id) -{ - return id >= 0 && id <= 6; -} - -} \ No newline at end of file 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/p18/response.cc b/src/p18/response.cc deleted file mode 100644 index e2cd9f4..0000000 --- a/src/p18/response.cc +++ /dev/null @@ -1,815 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#include -#include -#include -#include -#include - -#include "response.h" -#include "exceptions.h" - -#define RETURN_TABLE(...) \ - return std::shared_ptr>( \ - new formatter::Table(format, __VA_ARGS__) \ - ); - -#define RETURN_STATUS(...) \ - return std::shared_ptr( \ - new formatter::Status(format, __VA_ARGS__) \ - ); - - -namespace p18::response_type { - -typedef formatter::TableItem 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 - */ - -BaseResponse::BaseResponse(std::shared_ptr raw, size_t rawSize) - : raw_(std::move(raw)), rawSize_(rawSize) {} - -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(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 GetResponse::getList(std::vector 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 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 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(), ""); -} - -formattable_ptr ErrorResponse::format(formatter::Format format) { - return std::shared_ptr( - new formatter::Status(format, false, error_) - ); -} - - -/** - * 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 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(stou(list[13])); - max_ac_charging_current = stou(list[14]); - max_charging_current = stou(list[15]); - input_voltage_range = static_cast(stou(list[16])); - output_source_priority = static_cast(stou(list[17])); - charger_source_priority = static_cast(stou(list[18])); - parallel_max_num = stou(list[19]); - machine_type = static_cast(stou(list[20])); - topology = static_cast(stou(list[21])); - output_model_setting = static_cast(stou(list[22])); - solar_power_priority = static_cast(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(stou(list[20])); - mppt1_charger_status = static_cast(stou(list[21])); - mppt2_charger_status = static_cast(stou(list[22])); - load_connected = static_cast(stou(list[23])); - battery_power_direction = static_cast(stou(list[24])); - dc_ac_power_direction = static_cast(stou(list[25])); - line_power_direction = static_cast(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(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(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(stou(list[10])); - output_source_priority = static_cast(stou(list[11])); - charger_source_priority = static_cast(stou(list[12])); - solar_power_priority = static_cast(stou(list[13])); - machine_type = static_cast(stou(list[14])); - output_model_setting = static_cast(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> v; - for (const auto& n: amps) - v.emplace_back(n); - - return std::shared_ptr>( - new formatter::List(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(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(stou(list[3])); - max_charging_current = stou(list[4]); - max_ac_charging_current = stou(list[5]); - output_model_setting = static_cast(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(stou(list[0])); - work_mode = static_cast(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(stou(list[22])); - mppt2_charger_status = static_cast(stou(list[23])); - load_connected = static_cast(stou(list[24])); - battery_power_direction = static_cast(stou(list[25])); - dc_ac_power_direction = static_cast(stou(list[26])); - line_power_direction = static_cast(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(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>(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)), - }) -} - -} \ No newline at end of file diff --git a/src/p18/response.h b/src/p18/response.h deleted file mode 100644 index 3ffc6d4..0000000 --- a/src/p18/response.h +++ /dev/null @@ -1,511 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#ifndef INVERTER_TOOLS_P18_RESPONSE_H -#define INVERTER_TOOLS_P18_RESPONSE_H - -#include -#include -#include -#include -#include - -#include "types.h" -#include "src/formatter/formatter.h" - -namespace p18::response_type { - -using nlohmann::json; - -typedef std::shared_ptr 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: - // implicit conversion constructors - 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(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(v_) || - std::holds_alternative(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 raw_; - size_t rawSize_; - -public: - BaseResponse(std::shared_ptr 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 getList(std::vector itemLengths, int expectAtLeast = -1) const; - -public: - using BaseResponse::BaseResponse; - bool validate() override; -// virtual void output() = 0; -}; - -class SetResponse : public BaseResponse { -public: - using BaseResponse::BaseResponse; - void unpack() override; - bool validate() override; - formattable_ptr format(formatter::Format format) override; - 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 - */ - -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(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(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(0); - p18::OutputSourcePriority output_source_priority = static_cast(0); - p18::ChargerSourcePriority charger_source_priority = static_cast(0); - p18::SolarPowerPriority solar_power_priority = static_cast(0); - p18::MachineType machine_type = static_cast(0); - p18::OutputModelSetting output_model_setting = static_cast(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 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(0); - unsigned serial_number_valid_length = 0; - std::string serial_number; - p18::ChargerSourcePriority charger_source_priority = static_cast(0); - unsigned max_ac_charging_current = 0; // unit: A - unsigned max_charging_current = 0; // unit: A - p18::OutputModelSetting output_model_setting = static_cast(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 p18 - -#endif //INVERTER_TOOLS_P18_RESPONSE_H diff --git a/src/p18/types.h b/src/p18/types.h deleted file mode 100644 index bcabd7c..0000000 --- a/src/p18/types.h +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#ifndef INVERTER_TOOLS_P18_TYPES_H -#define INVERTER_TOOLS_P18_TYPES_H - -#include - -#define ENUM_STR(enum_type) std::ostream& operator<< (std::ostream& os, enum_type val) -#define ENUM_STR_DEFAULT return os << val - -namespace p18 { - -enum class CommandType { - GetProtocolID = 0, - GetCurrentTime, - GetTotalGenerated, - GetYearGenerated, - GetMonthGenerated, - GetDayGenerated, - GetSeriesNumber, - GetCPUVersion, - GetRatedInformation, - GetGeneralStatus, - GetWorkingMode, - GetFaultsAndWarnings, - GetFlagsAndStatuses, - GetDefaults, - GetAllowedChargingCurrents, - GetAllowedACChargingCurrents, - GetParallelRatedInformation, - GetParallelGeneralStatus, - GetACChargingTimeBucket, - GetACLoadsSupplyTimeBucket, - SetLoads = 100, - SetFlag, - SetDefaults, - SetBatteryMaxChargingCurrent, - SetBatteryMaxACChargingCurrent, - SetACOutputFreq, - SetBatteryMaxChargingVoltage, - SetACOutputRatedVoltage, - SetOutputSourcePriority, - SetBatteryChargingThresholds, /* Battery re-charging and re-discharing voltage when utility is available */ - SetChargingSourcePriority, - SetSolarPowerPriority, - SetACInputVoltageRange, - SetBatteryType, - SetOutputModel, - SetBatteryCutOffVoltage, - SetSolarConfig, - ClearGenerated, - SetDateTime, - SetACChargingTimeBucket, - SetACLoadsSupplyTimeBucket, -}; - -enum class BatteryType { - AGM = 0, - Flooded = 1, - User = 2, -}; -ENUM_STR(BatteryType); - -enum class InputVoltageRange { - Appliance = 0, - USP = 1, -}; -ENUM_STR(InputVoltageRange); - -enum class OutputSourcePriority { - SolarUtilityBattery = 0, - SolarBatteryUtility = 1, -}; -ENUM_STR(OutputSourcePriority); - -enum class ChargerSourcePriority { - SolarFirst = 0, - SolarAndUtility = 1, - SolarOnly = 2, -}; -ENUM_STR(ChargerSourcePriority); - -enum class MachineType { - OffGridTie = 0, - GridTie = 1, -}; -ENUM_STR(MachineType); - -enum class Topology { - TransformerLess = 0, - Transformer = 1, -}; -ENUM_STR(Topology); - -enum class OutputModelSetting { - SingleModule = 0, - ParallelOutput = 1, - Phase1OfThreePhaseOutput = 2, - Phase2OfThreePhaseOutput = 3, - Phase3OfThreePhaseOutput = 4, -}; -ENUM_STR(OutputModelSetting); - -enum class SolarPowerPriority { - BatteryLoadUtility = 0, - LoadBatteryUtility = 1, -}; -ENUM_STR(SolarPowerPriority); - -enum class MPPTChargerStatus { - Abnormal = 0, - NotCharging = 1, - Charging = 2, -}; -ENUM_STR(MPPTChargerStatus); - -enum class BatteryPowerDirection { - DoNothing = 0, - Charge = 1, - Discharge = 2, -}; -ENUM_STR(BatteryPowerDirection); - -enum class DC_AC_PowerDirection { - DoNothing = 0, - AC_DC = 1, - DC_AC = 2, -}; -ENUM_STR(DC_AC_PowerDirection); - -enum class LinePowerDirection { - DoNothing = 0, - Input = 1, - Output = 2, -}; -ENUM_STR(LinePowerDirection); - -enum class WorkingMode { - PowerOnMode = 0, - StandbyMode = 1, - BypassMode = 2, - BatteryMode = 3, - FaultMode = 4, - HybridMode = 5, -}; -ENUM_STR(WorkingMode); - -enum class ParallelConnectionStatus { - NotExistent = 0, - Existent = 1, -}; -ENUM_STR(ParallelConnectionStatus); - -enum class LoadConnectionStatus { - Disconnected = 0, - Connected = 1, -}; -ENUM_STR(LoadConnectionStatus); - -enum class ConfigurationStatus { - Default = 0, - Changed = 1, -}; -ENUM_STR(ConfigurationStatus); - -struct Flag { - std::string flag; - char letter; - std::string description; -}; - -} - -#endif //INVERTER_TOOLS_P18_TYPES_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 device) { + device_ = std::move(device); +} + +std::pair, size_t> BaseClient::runOnDevice(std::string& raw) { + size_t bufSize = 256; + std::shared_ptr buf(new char[bufSize]); + size_t responseSize = device_->run( + (const u8*)raw.c_str(), raw.size(), + (u8*)buf.get(), bufSize); + + return std::pair, 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 device_; + +public: + void setDevice(std::shared_ptr device); + virtual std::shared_ptr execute(int commandType, std::vector& arguments) = 0; + std::pair, size_t> runOnDevice(std::string& raw); +}; + +} + +#endif //INVERTER_TOOLS_PROTOCOL_CLIENT_H diff --git a/src/protocol/exceptions.h b/src/protocol/exceptions.h new file mode 100644 index 0000000..4febe28 --- /dev/null +++ b/src/protocol/exceptions.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef INVERTER_TOOLS_PROTOCOL_EXCEPTIONS_H +#define INVERTER_TOOLS_PROTOCOL_EXCEPTIONS_H + +#include + +namespace protocol { + +class InvalidResponseError : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +class ParseError : public InvalidResponseError { +public: + using InvalidResponseError::InvalidResponseError; +}; + +} + +#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 +#include +#include +#include + +#ifdef INVERTERCTL +#include +#endif + +#include "../util.h" +#include "input.h" + +namespace protocol { + +#ifdef INVERTERCTL +void get_args(CommandInput* input, std::vector& 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& 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(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(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(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 +#include + +#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* argv; +}; +#endif + +void get_args(CommandInput* input, std::vector& 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 raw, size_t rawSize) + : raw_(std::move(raw)), rawSize_(rawSize) +{} + + +formattable_ptr ErrorResponse::format(formatter::Format format) +{ + return std::shared_ptr( + 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 +#include "../formatter/formatter.h" + +namespace protocol { + +typedef std::shared_ptr formattable_ptr; + +class BaseResponse { +protected: + std::shared_ptr raw_; + size_t rawSize_; + +public: + BaseResponse(std::shared_ptr 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 + +#include "defines.h" +#include "types.h" + +namespace p17 { + +const std::map 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 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 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 +#include +#include + +#include "types.h" + +namespace p17 { + +extern const std::map raw_commands; + +//extern const std::array ac_output_rated_voltages; +// +//extern const std::array bat_ac_recharging_voltages_12v; +//extern const std::array bat_ac_recharging_voltages_24v; +//extern const std::array bat_ac_recharging_voltages_48v; +// +//extern const std::array bat_ac_redischarging_voltages_12v; +//extern const std::array bat_ac_redischarging_voltages_24v; +//extern const std::array bat_ac_redischarging_voltages_48v; + +extern const std::map fault_codes; + +extern const std::array 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 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& 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 +#include +#include + +#include "../protocol/input.h" +#include "types.h" + +namespace p17 { + +extern const std::map client_commands; + +CommandType validate_input(std::string& command, std::vector& 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 +#include +#include +#include +#include + +#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 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(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(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 getList(std::vector 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(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(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(0); + p18::OutputSourcePriority output_source_priority = static_cast(0); + p18::ChargerSourcePriority charger_source_priority = static_cast(0); + p18::SolarPowerPriority solar_power_priority = static_cast(0); + p18::MachineType machine_type = static_cast(0); + p18::OutputModelSetting output_model_setting = static_cast(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 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(0); + unsigned serial_number_valid_length = 0; + std::string serial_number; + p18::ChargerSourcePriority charger_source_priority = static_cast(0); + unsigned max_ac_charging_current = 0; // unit: A + unsigned max_charging_current = 0; // unit: A + p18::OutputModelSetting output_model_setting = static_cast(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/protocol_18/client.cc b/src/protocol_18/client.cc new file mode 100644 index 0000000..d42c24a --- /dev/null +++ b/src/protocol_18/client.cc @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include +#include +#include +#include +#include + +#include "client.h" +#include "types.h" +#include "defines.h" +#include "../protocol/exceptions.h" +#include "response.h" +#include "../voltronic/crc.h" + +#define MKRESPONSE(type) std::shared_ptr(new response_type::type(raw, rawSize)) + +#define RESPONSE_CASE(type) \ + case CommandType::Get ## type: \ + response = MKRESPONSE(type); \ + break; \ + + +namespace p18 { + +std::shared_ptr Client::execute(int commandType, std::vector& arguments) { + auto type = static_cast(commandType); + + std::ostringstream buf; + buf << std::setfill('0'); + + bool isSetCommand = commandType >= 100; + + auto pos = raw_commands.find(type); + if (pos == raw_commands.end()) + throw std::runtime_error("packedCommand " + std::to_string(commandType) + " not found"); + + std::string packedCommand = pos->second; + std::string packedArguments = packArguments(type, arguments); + + size_t len = sizeof(voltronic::CRC) + 1 + packedCommand.size() + packedArguments.size(); + + buf << "^"; + buf << (isSetCommand ? "S" : "P"); + buf << std::setw(3) << len; + buf << packedCommand; + buf << packedArguments; + + std::string packed = buf.str(); + + auto result = runOnDevice(packed); + std::shared_ptr response; + + const auto raw = result.first; + const auto rawSize = result.second; + + switch (type) { + RESPONSE_CASE(ProtocolID) + RESPONSE_CASE(CurrentTime) + RESPONSE_CASE(TotalGenerated) + RESPONSE_CASE(YearGenerated) + RESPONSE_CASE(MonthGenerated) + RESPONSE_CASE(DayGenerated) + RESPONSE_CASE(SeriesNumber) + RESPONSE_CASE(CPUVersion) + RESPONSE_CASE(RatedInformation) + RESPONSE_CASE(GeneralStatus) + RESPONSE_CASE(WorkingMode) + RESPONSE_CASE(FaultsAndWarnings) + RESPONSE_CASE(FlagsAndStatuses) + RESPONSE_CASE(Defaults) + RESPONSE_CASE(AllowedChargingCurrents) + RESPONSE_CASE(AllowedACChargingCurrents) + RESPONSE_CASE(ParallelRatedInformation) + RESPONSE_CASE(ParallelGeneralStatus) + RESPONSE_CASE(ACChargingTimeBucket) + RESPONSE_CASE(ACLoadsSupplyTimeBucket) + + case CommandType::SetLoads: + case CommandType::SetFlag: + case CommandType::SetDefaults: + case CommandType::SetBatteryMaxChargingCurrent: + case CommandType::SetBatteryMaxACChargingCurrent: + case CommandType::SetACOutputFreq: + case CommandType::SetBatteryMaxChargingVoltage: + case CommandType::SetACOutputRatedVoltage: + case CommandType::SetOutputSourcePriority: + case CommandType::SetBatteryChargingThresholds: + case CommandType::SetChargingSourcePriority: + case CommandType::SetSolarPowerPriority: + case CommandType::SetACInputVoltageRange: + case CommandType::SetBatteryType: + case CommandType::SetOutputModel: + case CommandType::SetBatteryCutOffVoltage: + case CommandType::SetSolarConfig: + case CommandType::ClearGenerated: + case CommandType::SetDateTime: + case CommandType::SetACChargingTimeBucket: + case CommandType::SetACLoadsSupplyTimeBucket: + response = MKRESPONSE(SetResponse); + break; + } + + if (!response->validate()) + throw protocol::InvalidResponseError("validate() failed"); + + response->unpack(); + return std::move(response); +} + +std::string Client::packArguments(p18::CommandType commandType, std::vector& arguments) { + std::ostringstream buf; + buf << std::setfill('0'); + + switch (commandType) { + case CommandType::GetYearGenerated: + case CommandType::SetOutputSourcePriority: + case CommandType::SetSolarPowerPriority: + case CommandType::SetACInputVoltageRange: + case CommandType::SetBatteryType: + case CommandType::SetLoads: + buf << arguments[0]; + break; + + case CommandType::GetMonthGenerated: + case CommandType::GetDayGenerated: + buf << arguments[0]; + for (int i = 1; i <= (commandType == CommandType::GetMonthGenerated ? 1 : 2); i++) + buf << std::setw(2) << std::stoi(arguments[i]); + break; + + case CommandType::GetParallelGeneralStatus: + case CommandType::GetParallelRatedInformation: + buf << std::stoi(arguments[0]); + break; + + case CommandType::SetFlag: + buf << (arguments[1] == "1" ? "E" : "D"); + buf << arguments[0]; + break; + + case CommandType::SetBatteryMaxChargingCurrent: + case CommandType::SetBatteryMaxACChargingCurrent: + buf << arguments[0] << ","; + buf << std::setw(3) << std::stoi(arguments[1]); + break; + + case CommandType::SetACOutputFreq: + buf << std::setw(2) << std::stoi(arguments[0]); + break; + + case CommandType::SetBatteryMaxChargingVoltage: + case CommandType::SetBatteryChargingThresholds: { + for (int i = 0; i < 2; i++) { + double val = std::stod(arguments[i]); + buf << std::setw(3) << (int)round(val*10); + if (i == 0) + buf << ","; + } + break; + } + + case CommandType::SetACOutputRatedVoltage: { + buf << std::setw(4) << (std::stoi(arguments[0])*10); + break; + } + + case CommandType::SetChargingSourcePriority: + case CommandType::SetOutputModel: + buf << arguments[0] << "," << arguments[1]; + break; + + case CommandType::SetBatteryCutOffVoltage: { + double v = std::stod(arguments[0]); + buf << std::setw(3) << ((int)round(v*10)); + break; + } + + case CommandType::SetSolarConfig: { + size_t len = arguments[0].size(); + buf << std::setw(2) << len << arguments[0]; + if (len < 20) { + for (int i = 0; i < 20-len; i++) + buf << "0"; + } + break; + } + + case CommandType::SetDateTime: { + for (int i = 0; i < 6; i++) { + int val = std::stoi(arguments[i]); + if (i == 0) + val -= 2000; + buf << std::setw(2) << val; + } + break; + } + + case CommandType::SetACChargingTimeBucket: + case CommandType::SetACLoadsSupplyTimeBucket: + for (int i = 0; i < 4; i++) { + buf << std::setw(2) << std::stoi(arguments[i]); + if (i == 1) + buf << ","; + } + break; + + default: + break; + } + + return buf.str(); +} + +} \ No newline at end of file diff --git a/src/protocol_18/client.h b/src/protocol_18/client.h new file mode 100644 index 0000000..3fb96fc --- /dev/null +++ b/src/protocol_18/client.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef INVERTER_TOOLS_P18_CLIENT_H_ +#define INVERTER_TOOLS_P18_CLIENT_H_ + +#include "../voltronic/device.h" +#include "types.h" +#include "response.h" +#include "../protocol/client.h" + +#include +#include +#include + + +namespace p18 { + +using protocol::BaseClient; +using protocol::BaseResponse; + +class Client : public BaseClient { +private: + static std::string packArguments(p18::CommandType commandType, std::vector& arguments); + +public: + std::shared_ptr execute(int commandType, std::vector& arguments) override; +}; + +} + + +#endif //INVERTER_TOOLS_P18_CLIENT_H_ diff --git a/src/protocol_18/defines.cc b/src/protocol_18/defines.cc new file mode 100644 index 0000000..207d909 --- /dev/null +++ b/src/protocol_18/defines.cc @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +#include "defines.h" +#include "types.h" + +namespace p18 { + +const std::map 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::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"}, +}; + +const std::array ac_output_rated_voltages = {202, 208, 220, 230, 240}; + +const std::array bat_ac_recharging_voltages_12v = {11, 11.3, 11.5, 11.8, 12, 12.3, 12.5, 12.8}; +const std::array bat_ac_recharging_voltages_24v = {22, 22.5, 23, 23.5, 24, 24.5, 25, 25.5}; +const std::array bat_ac_recharging_voltages_48v = {44, 45, 46, 47, 48, 49, 50, 51}; + +const std::array bat_ac_redischarging_voltages_12v = {0, 12, 12.3, 12.5, 12.8, 13, 13.3, 13.5, 13.8, 14, 14.3, 14.5}; +const std::array bat_ac_redischarging_voltages_24v = {0, 24, 24.5, 25, 25.5, 26, 26.5, 27, 27.5, 28, 28.5, 29}; +const std::array bat_ac_redischarging_voltages_48v = {0, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58}; + +const std::map fault_codes = { + {1, "Fan is locked"}, + {2, "Over temperature"}, + {3, "Battery voltage is too high"}, + {4, "Battery voltage is too low"}, + {5, "Output short circuited or Over temperature"}, + {6, "Output voltage is too high"}, + {7, "Over load time out"}, + {8, "Bus voltage is too high"}, + {9, "Bus soft start failed"}, + {11, "Main relay failed"}, + {51, "Over current inverter"}, + {52, "Bus soft start failed"}, + {53, "Inverter soft start failed"}, + {54, "Self-test failed"}, + {55, "Over DC voltage on output of inverter"}, + {56, "Battery connection is open"}, + {57, "Current sensor failed"}, + {58, "Output voltage is too low"}, + {60, "Inverter negative power"}, + {71, "Parallel version different"}, + {72, "Output circuit failed"}, + {80, "CAN communication failed"}, + {81, "Parallel host line lost"}, + {82, "Parallel synchronized signal lost"}, + {83, "Parallel battery voltage detect different"}, + {84, "Parallel LINE voltage or frequency detect different"}, + {85, "Parallel LINE input current unbalanced"}, + {86, "Parallel output setting different"}, +}; + +const std::array flags = {{ + {"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"}, + {"OTRS", 'E', "Overload temperature restart"}, + {"BLON", 'F', "Backlight on"}, + {"ALRM", 'G', "Alarm on primary source interrupt"}, + {"FTCR", 'H', "Fault code record"}, + {"MTYP", 'I', "Machine type (1=Grid-Tie, 0=Off-Grid-Tie)"}, +}}; + +ENUM_STR(BatteryType) { + switch (val) { + case BatteryType::AGM: return os << "AGM" ; + case BatteryType::Flooded: return os << "Flooded"; + case BatteryType::User: return os << "User"; + }; + ENUM_STR_DEFAULT; +} + +ENUM_STR(InputVoltageRange) { + switch (val) { + case InputVoltageRange::Appliance: return os << "Appliance"; + case InputVoltageRange::USP: return os << "USP"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(OutputSourcePriority) { + switch (val) { + case OutputSourcePriority::SolarUtilityBattery: + return os << "Solar-Utility-Battery"; + case OutputSourcePriority::SolarBatteryUtility: + return os << "Solar-Battery-Utility"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(ChargerSourcePriority) { + switch (val) { + case ChargerSourcePriority::SolarFirst: + return os << "Solar-First"; + case ChargerSourcePriority::SolarAndUtility: + return os << "Solar-and-Utility"; + case ChargerSourcePriority::SolarOnly: + return os << "Solar-only"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(MachineType) { + switch (val) { + case MachineType::OffGridTie: return os << "Off-Grid-Tie"; + case MachineType::GridTie: return os << "Grid-Tie"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(Topology) { + switch (val) { + case Topology::TransformerLess: return os << "Transformer-less"; + case Topology::Transformer: return os << "Transformer"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(OutputModelSetting) { + switch (val) { + case OutputModelSetting::SingleModule: + return os << "Single module"; + case OutputModelSetting::ParallelOutput: + return os << "Parallel output"; + case OutputModelSetting::Phase1OfThreePhaseOutput: + return os << "Phase 1 of three phase output"; + case OutputModelSetting::Phase2OfThreePhaseOutput: + return os << "Phase 2 of three phase output"; + case OutputModelSetting::Phase3OfThreePhaseOutput: + return os << "Phase 3 of three phase"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(SolarPowerPriority) { + switch (val) { + case SolarPowerPriority::BatteryLoadUtility: + return os << "Battery-Load-Utility"; + case SolarPowerPriority::LoadBatteryUtility: + return os << "Load-Battery-Utility"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(MPPTChargerStatus) { + switch (val) { + case MPPTChargerStatus::Abnormal: return os << "Abnormal"; + case MPPTChargerStatus::NotCharging: return os << "Not charging"; + case MPPTChargerStatus::Charging: return os << "Charging"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(BatteryPowerDirection) { + switch (val) { + case BatteryPowerDirection::DoNothing: return os << "Do nothing"; + case BatteryPowerDirection::Charge: return os << "Charge"; + case BatteryPowerDirection::Discharge: return os << "Discharge"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(DC_AC_PowerDirection) { + switch (val) { + case DC_AC_PowerDirection::DoNothing: return os << "Do nothing"; + case DC_AC_PowerDirection::AC_DC: return os << "AC/DC"; + case DC_AC_PowerDirection::DC_AC: return os << "DC/AC"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(LinePowerDirection) { + switch (val) { + case LinePowerDirection::DoNothing: return os << "Do nothing"; + case LinePowerDirection::Input: return os << "Input"; + case LinePowerDirection::Output: return os << "Output"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(WorkingMode) { + switch (val) { + case WorkingMode::PowerOnMode: return os << "Power on mode"; + case WorkingMode::StandbyMode: return os << "Standby mode"; + case WorkingMode::BypassMode: return os << "Bypass mode"; + case WorkingMode::BatteryMode: return os << "Battery mode"; + case WorkingMode::FaultMode: return os << "Fault mode"; + case WorkingMode::HybridMode: return os << "Hybrid mode"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(ParallelConnectionStatus) { + switch (val) { + case ParallelConnectionStatus::NotExistent: return os << "Non-existent"; + case ParallelConnectionStatus::Existent: return os << "Existent"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(LoadConnectionStatus) { + switch (val) { + case LoadConnectionStatus::Disconnected: return os << "Disconnected"; + case LoadConnectionStatus::Connected: return os << "Connected"; + } + ENUM_STR_DEFAULT; +} + +ENUM_STR(ConfigurationStatus) { + switch (val) { + case ConfigurationStatus::Default: return os << "Default"; + case ConfigurationStatus::Changed: return os << "Changed"; + } + ENUM_STR_DEFAULT; +} + +} \ No newline at end of file diff --git a/src/protocol_18/defines.h b/src/protocol_18/defines.h new file mode 100644 index 0000000..83728f8 --- /dev/null +++ b/src/protocol_18/defines.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef INVERTER_TOOLS_P18_DEFINES_H +#define INVERTER_TOOLS_P18_DEFINES_H + +#include +#include +#include + +#include "types.h" + +namespace p18 { + +extern const std::map raw_commands; + +extern const std::array ac_output_rated_voltages; + +extern const std::array bat_ac_recharging_voltages_12v; +extern const std::array bat_ac_recharging_voltages_24v; +extern const std::array bat_ac_recharging_voltages_48v; + +extern const std::array bat_ac_redischarging_voltages_12v; +extern const std::array bat_ac_redischarging_voltages_24v; +extern const std::array bat_ac_redischarging_voltages_48v; + +extern const std::map fault_codes; + +extern const std::array flags; + +} + +#endif //INVERTER_TOOLS_P18_DEFINES_H diff --git a/src/protocol_18/functions.cc b/src/protocol_18/functions.cc new file mode 100644 index 0000000..9799fc0 --- /dev/null +++ b/src/protocol_18/functions.cc @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include "functions.h" + +namespace p18 { + +bool is_valid_parallel_id(unsigned id) +{ + return id >= 0 && id <= 6; +} + +} \ No newline at end of file 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 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& 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(std::stoul(arguments[0])); + auto amps = static_cast(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(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 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(std::stoul(arguments[0])); + if (!is_valid_parallel_id(id)) + throw std::invalid_argument("invalid id"); + + std::array 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 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 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 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(std::stoul(arguments[0])); + if (!is_valid_parallel_id(id)) + throw std::invalid_argument("invalid id"); + + std::array 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 start = split(arguments[0], ':'); + if (start.size() != 2) + throw std::invalid_argument("invalid start time"); + + std::vector end = split(arguments[1], ':'); + if (end.size() != 2) + throw std::invalid_argument("invalid end time"); + + auto startHour = static_cast(std::stoul(start[0])); + auto startMinute = static_cast(std::stoul(start[1])); + if (startHour > 23 || startMinute > 59) + throw std::invalid_argument("invalid start time"); + + auto endHour = static_cast(std::stoul(end[0])); + auto endMinute = static_cast(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 +#include +#include + +#include "../protocol/input.h" +#include "types.h" + +namespace p18 { + +extern const std::map client_commands; + +CommandType validate_input(std::string& command, std::vector& 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 +#include +#include +#include +#include + +#include "response.h" +#include "../protocol/exceptions.h" +#include "../logging.h" + +#define RETURN_TABLE(...) \ + return std::shared_ptr>( \ + new formatter::Table(format, __VA_ARGS__) \ + ); + +#define RETURN_STATUS(...) \ + return std::shared_ptr( \ + new formatter::Status(format, __VA_ARGS__) \ + ); + + +namespace p18::response_type { + +typedef formatter::TableItem 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(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 GetResponse::getList(std::vector 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(stou(list[13])); + max_ac_charging_current = stou(list[14]); + max_charging_current = stou(list[15]); + input_voltage_range = static_cast(stou(list[16])); + output_source_priority = static_cast(stou(list[17])); + charger_source_priority = static_cast(stou(list[18])); + parallel_max_num = stou(list[19]); + machine_type = static_cast(stou(list[20])); + topology = static_cast(stou(list[21])); + output_model_setting = static_cast(stou(list[22])); + solar_power_priority = static_cast(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(stou(list[20])); + mppt1_charger_status = static_cast(stou(list[21])); + mppt2_charger_status = static_cast(stou(list[22])); + load_connected = static_cast(stou(list[23])); + battery_power_direction = static_cast(stou(list[24])); + dc_ac_power_direction = static_cast(stou(list[25])); + line_power_direction = static_cast(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(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(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(stou(list[10])); + output_source_priority = static_cast(stou(list[11])); + charger_source_priority = static_cast(stou(list[12])); + solar_power_priority = static_cast(stou(list[13])); + machine_type = static_cast(stou(list[14])); + output_model_setting = static_cast(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> v; + for (const auto& n: amps) + v.emplace_back(n); + + return std::shared_ptr>( + new formatter::List(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(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(stou(list[3])); + max_charging_current = stou(list[4]); + max_ac_charging_current = stou(list[5]); + output_model_setting = static_cast(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(stou(list[0])); + work_mode = static_cast(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(stou(list[22])); + mppt2_charger_status = static_cast(stou(list[23])); + load_connected = static_cast(stou(list[24])); + battery_power_direction = static_cast(stou(list[25])); + dc_ac_power_direction = static_cast(stou(list[26])); + line_power_direction = static_cast(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(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>(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/protocol_18/response.cc.orig b/src/protocol_18/response.cc.orig new file mode 100644 index 0000000..902fb95 --- /dev/null +++ b/src/protocol_18/response.cc.orig @@ -0,0 +1,812 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include +#include +#include +#include + +#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>( \ + new formatter::Table(format, __VA_ARGS__) \ + ); + +#define RETURN_STATUS(...) \ + return std::shared_ptr( \ + new formatter::Status(format, __VA_ARGS__) \ + ); + + +namespace p18::response_type { + +typedef formatter::TableItem 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(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 GetResponse::getList(std::vector 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(stou(list[13])); + max_ac_charging_current = stou(list[14]); + max_charging_current = stou(list[15]); + input_voltage_range = static_cast(stou(list[16])); + output_source_priority = static_cast(stou(list[17])); + charger_source_priority = static_cast(stou(list[18])); + parallel_max_num = stou(list[19]); + machine_type = static_cast(stou(list[20])); + topology = static_cast(stou(list[21])); + output_model_setting = static_cast(stou(list[22])); + solar_power_priority = static_cast(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(stou(list[20])); + mppt1_charger_status = static_cast(stou(list[21])); + mppt2_charger_status = static_cast(stou(list[22])); + load_connected = static_cast(stou(list[23])); + battery_power_direction = static_cast(stou(list[24])); + dc_ac_power_direction = static_cast(stou(list[25])); + line_power_direction = static_cast(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(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(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(stou(list[10])); + output_source_priority = static_cast(stou(list[11])); + charger_source_priority = static_cast(stou(list[12])); + solar_power_priority = static_cast(stou(list[13])); + machine_type = static_cast(stou(list[14])); + output_model_setting = static_cast(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> v; + for (const auto& n: amps) + v.emplace_back(n); + + return std::shared_ptr>( + new formatter::List(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(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(stou(list[3])); + max_charging_current = stou(list[4]); + max_ac_charging_current = stou(list[5]); + output_model_setting = static_cast(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(stou(list[0])); + work_mode = static_cast(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(stou(list[22])); + mppt2_charger_status = static_cast(stou(list[23])); + load_connected = static_cast(stou(list[24])); + battery_power_direction = static_cast(stou(list[25])); + dc_ac_power_direction = static_cast(stou(list[26])); + line_power_direction = static_cast(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(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>(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)), + }) +} + +} \ No newline at end of file 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 +#include +#include +#include +#include + +#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 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(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(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 raw_; + size_t rawSize_; + +public: + BaseResponse(std::shared_ptr 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 getList(std::vector 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(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(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(0); + p18::OutputSourcePriority output_source_priority = static_cast(0); + p18::ChargerSourcePriority charger_source_priority = static_cast(0); + p18::SolarPowerPriority solar_power_priority = static_cast(0); + p18::MachineType machine_type = static_cast(0); + p18::OutputModelSetting output_model_setting = static_cast(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 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(0); + unsigned serial_number_valid_length = 0; + std::string serial_number; + p18::ChargerSourcePriority charger_source_priority = static_cast(0); + unsigned max_ac_charging_current = 0; // unit: A + unsigned max_charging_current = 0; // unit: A + p18::OutputModelSetting output_model_setting = static_cast(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/protocol_18/response.h.orig b/src/protocol_18/response.h.orig new file mode 100644 index 0000000..9295943 --- /dev/null +++ b/src/protocol_18/response.h.orig @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef INVERTER_TOOLS_P18_RESPONSE_H +#define INVERTER_TOOLS_P18_RESPONSE_H + +#include +#include +#include +#include +#include + +#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 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: + // implicit conversion constructors + 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(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(v_) || + std::holds_alternative(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; + } +}; + + +<<<<<<< HEAD:src/p18/response.h +/** + * 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 raw_; + size_t rawSize_; + +public: + BaseResponse(std::shared_ptr raw, size_t rawSize); + virtual ~BaseResponse() = default; + virtual bool validate() = 0; + virtual void unpack() = 0; + virtual formattable_ptr format(formatter::Format format) = 0; +}; + +======= +>>>>>>> bf1b75a (wip):src/protocol_18/response.h +class GetResponse : public BaseResponse { +protected: + const char* getData() const; + size_t getDataSize() const; + std::vector getList(std::vector 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(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(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(0); + p18::OutputSourcePriority output_source_priority = static_cast(0); + p18::ChargerSourcePriority charger_source_priority = static_cast(0); + p18::SolarPowerPriority solar_power_priority = static_cast(0); + p18::MachineType machine_type = static_cast(0); + p18::OutputModelSetting output_model_setting = static_cast(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 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(0); + unsigned serial_number_valid_length = 0; + std::string serial_number; + p18::ChargerSourcePriority charger_source_priority = static_cast(0); + unsigned max_ac_charging_current = 0; // unit: A + unsigned max_charging_current = 0; // unit: A + p18::OutputModelSetting output_model_setting = static_cast(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/protocol_18/types.h b/src/protocol_18/types.h new file mode 100644 index 0000000..d3574e9 --- /dev/null +++ b/src/protocol_18/types.h @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef INVERTER_TOOLS_P18_TYPES_H +#define INVERTER_TOOLS_P18_TYPES_H + +#include + +#define ENUM_STR(enum_type) std::ostream& operator<< (std::ostream& os, enum_type val) +#define ENUM_STR_DEFAULT return os << val + +namespace p18 { + +enum class CommandType: int { + GetProtocolID = 0, + GetCurrentTime, + GetTotalGenerated, + GetYearGenerated, + GetMonthGenerated, + GetDayGenerated, + GetSeriesNumber, + GetCPUVersion, + GetRatedInformation, + GetGeneralStatus, + GetWorkingMode, + GetFaultsAndWarnings, + GetFlagsAndStatuses, + GetDefaults, + GetAllowedChargingCurrents, + GetAllowedACChargingCurrents, + GetParallelRatedInformation, + GetParallelGeneralStatus, + GetACChargingTimeBucket, + GetACLoadsSupplyTimeBucket, + SetLoads = 100, + SetFlag, + SetDefaults, + SetBatteryMaxChargingCurrent, + SetBatteryMaxACChargingCurrent, + SetACOutputFreq, + SetBatteryMaxChargingVoltage, + SetACOutputRatedVoltage, + SetOutputSourcePriority, + SetBatteryChargingThresholds, /* Battery re-charging and re-discharing voltage when utility is available */ + SetChargingSourcePriority, + SetSolarPowerPriority, + SetACInputVoltageRange, + SetBatteryType, + SetOutputModel, + SetBatteryCutOffVoltage, + SetSolarConfig, + ClearGenerated, + SetDateTime, + SetACChargingTimeBucket, + SetACLoadsSupplyTimeBucket, +}; + +enum class BatteryType { + AGM = 0, + Flooded = 1, + User = 2, +}; +ENUM_STR(BatteryType); + +enum class InputVoltageRange { + Appliance = 0, + USP = 1, +}; +ENUM_STR(InputVoltageRange); + +enum class OutputSourcePriority { + SolarUtilityBattery = 0, + SolarBatteryUtility = 1, +}; +ENUM_STR(OutputSourcePriority); + +enum class ChargerSourcePriority { + SolarFirst = 0, + SolarAndUtility = 1, + SolarOnly = 2, +}; +ENUM_STR(ChargerSourcePriority); + +enum class MachineType { + OffGridTie = 0, + GridTie = 1, +}; +ENUM_STR(MachineType); + +enum class Topology { + TransformerLess = 0, + Transformer = 1, +}; +ENUM_STR(Topology); + +enum class OutputModelSetting { + SingleModule = 0, + ParallelOutput = 1, + Phase1OfThreePhaseOutput = 2, + Phase2OfThreePhaseOutput = 3, + Phase3OfThreePhaseOutput = 4, +}; +ENUM_STR(OutputModelSetting); + +enum class SolarPowerPriority { + BatteryLoadUtility = 0, + LoadBatteryUtility = 1, +}; +ENUM_STR(SolarPowerPriority); + +enum class MPPTChargerStatus { + Abnormal = 0, + NotCharging = 1, + Charging = 2, +}; +ENUM_STR(MPPTChargerStatus); + +enum class BatteryPowerDirection { + DoNothing = 0, + Charge = 1, + Discharge = 2, +}; +ENUM_STR(BatteryPowerDirection); + +enum class DC_AC_PowerDirection { + DoNothing = 0, + AC_DC = 1, + DC_AC = 2, +}; +ENUM_STR(DC_AC_PowerDirection); + +enum class LinePowerDirection { + DoNothing = 0, + Input = 1, + Output = 2, +}; +ENUM_STR(LinePowerDirection); + +enum class WorkingMode { + PowerOnMode = 0, + StandbyMode = 1, + BypassMode = 2, + BatteryMode = 3, + FaultMode = 4, + HybridMode = 5, +}; +ENUM_STR(WorkingMode); + +enum class ParallelConnectionStatus { + NotExistent = 0, + Existent = 1, +}; +ENUM_STR(ParallelConnectionStatus); + +enum class LoadConnectionStatus { + Disconnected = 0, + Connected = 1, +}; +ENUM_STR(LoadConnectionStatus); + +enum class ConfigurationStatus { + Default = 0, + Changed = 1, +}; +ENUM_STR(ConfigurationStatus); + +struct Flag { + std::string flag; + char letter; + std::string description; +}; + +} + +#endif //INVERTER_TOOLS_P18_TYPES_H -- cgit v1.2.3