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