Compare commits

..

1 Commits

Author SHA1 Message Date
Evgeny Zinoviev
5758e0315f device: separate high-priority thread for interacting with hw 2021-05-23 22:59:46 +03:00
28 changed files with 735 additions and 586 deletions

View File

@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 17)
add_compile_options(-Wno-psabi)
project(inverter-tools VERSION 1.4.99)
project(inverter-tools VERSION 1.5.0)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX /usr/local/bin)
@ -29,24 +29,30 @@ find_library(LIBSERIALPORT_LIBRARY serialport)
find_path(LIBSERIALPORT_INCLUDE_DIR libserialport.h)
add_executable(inverterctl
src/inverterctl.cc
# shared list of sources
set(sources
src/common.cc
src/logging.cc
src/util.cc
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/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)
src/voltronic/pseudo_device.cc
src/voltronic/shared_buf.cc)
add_executable(inverterctl
src/inverterctl.cc
${sources})
target_include_directories(inverterctl PRIVATE .)
target_link_libraries(inverterctl m ${HIDAPI_LIBRARY} ${LIBSERIALPORT_LIBRARY})
target_link_libraries(inverterctl m pthread ${HIDAPI_LIBRARY} ${LIBSERIALPORT_LIBRARY})
target_compile_definitions(inverterctl PUBLIC INVERTERCTL)
target_include_directories(inverterctl PRIVATE
${HIDAPI_INCLUDE_DIR}
@ -59,23 +65,10 @@ install(TARGETS inverterctl
add_executable(inverterd
src/inverterd.cc
src/common.cc
src/util.cc
${sources}
src/server/server.cc
src/server/connection.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)
src/server/signal.cc)
target_include_directories(inverterd PRIVATE .)
target_compile_definitions(inverterd PUBLIC INVERTERD)
target_link_libraries(inverterd
@ -97,3 +90,10 @@ target_link_libraries(testserial ${LIBSERIALPORT_LIBRARY})
target_include_directories(testserial PRIVATE
${LIBSERIALPORT_INCLUDE_DIR}
third_party/hexdump)
# inverterd
#add_executable(inverterd
# src/inverterd.cc)
#target_link_libraries(inverterd ${HIDAPI_LIBRARY} ${LIBSERIALPORT_LIBRARY} m)
# TODO install

View File

@ -20,10 +20,9 @@ is planned.
- HIDAPI
- libserialport
## Tested devices
## Supported devices
- [InfiniSolar V 5KW](https://ekoproekt-energo.ru/infinisolar-v-5k-48)
- [Crown Micro Elego 6KW](https://coollineenergy.com/product/crown-micro-elego-6kw-solar-hybrid-inverter/)
As of time of writing, only InfiniSolar V 5KW was tested.
## Supported interfaces
@ -41,8 +40,10 @@ for all possible options and commands.
for querying inverterd server.
- [inverter-bot](https://github.com/gch1p/inverter-bot) - Telegram bot that uses inverterd
for querying data.
- [inverter-http-proxy](https://github.com/gch1p/inverter-http-proxy) - HTTP
"frontend" for inverterd
## TODO
- Implement proper logging with levels.
## License

View File

@ -24,7 +24,6 @@ enum {
LO_DEVICE_ERROR_LIMIT,
LO_USB_VENDOR_ID,
LO_USB_DEVICE_ID,
LO_USB_PATH,
LO_SERIAL_NAME,
LO_SERIAL_BAUD_RATE,
LO_SERIAL_DATA_BITS,

View File

@ -127,10 +127,6 @@ public:
explicit Table(Format format, std::vector<TableItem<T>> v)
: Formattable(format), v_(v) {}
void push(TableItem<T> item) {
v_.push_back(item);
}
std::ostream& writeSimpleTable(std::ostream& os) const override {
for (const auto& item: v_) {
os << item.key << " ";

View File

@ -78,9 +78,6 @@ static void usage(const char* progname) {
"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"
" Alternatively, you can specify device path (e.g., /dev/hidraw0):\n"
" --usb-path <PATH>: Device path\n"
"\n";
std::cout.flags(f);
std::cout <<
@ -98,7 +95,7 @@ static void usage(const char* progname) {
" get-year-generated <yyyy>\n"
" get-month-generated <yyyy> <mm>\n"
" get-day-generated <yyyy> <mm> <dd>\n"
" get-serial-number\n"
" get-series-number\n"
" get-cpu-version\n"
" get-rated\n"
" get-status\n"
@ -112,55 +109,55 @@ static void usage(const char* progname) {
" get-errors\n"
" get-flags\n"
" get-rated-defaults\n"
" get-allowed-charge-currents\n"
" get-allowed-ac-charge-currents\n"
" get-ac-charge-time\n"
" get-ac-supply-time\n"
" set-ac-supply 0|1\n"
" get-allowed-charging-currents\n"
" get-allowed-ac-charging-currents\n"
" get-ac-charging-time\n"
" get-ac-loads-supply-time\n"
" set-loads-supply 0|1\n"
" set-flag <flag> 0|1\n"
" set-rated-defaults\n"
" set-max-charge-current <id> <amps>\n"
" id: Parallel machine ID\n"
" amps: Use get-allowed-charge-currents\n"
" set-max-charging-current <id> <amps>\n"
" id: Parallel machine ID (use 0 for single model)\n"
" amps: Use get-allowed-charging-currents\n"
" to see a list of allowed values.\n"
"\n"
" set-max-ac-charge-current <id> <amps>\n"
" id: Parallel machine ID\n"
" amps: Use get-allowed-ac-charge-currents\n"
" set-max-ac-charging-current <id> <amps>\n"
" id: Parallel machine ID (use 0 for single model)\n"
" amps: Use get-allowed-ac-charging-currents\n"
" to see a list of allowed values.\n"
"\n"
" set-max-charge-voltage <cv> <fv>\n"
" cv: Constant voltage (48.0 ~ 58.4)\n"
" fv: Float voltage (48.0 ~ 58.4)\n"
"\n"
" set-ac-output-freq 50|60\n"
" set-max-charging-voltage <cv> <fv>\n"
" cv: Constant voltage (48.0 ~ 58.4).\n"
" fv: Float voltage (48.0 ~ 58.4).\n"
"\n"
" set-ac-output-voltage <v>\n"
" v: " << p18::ac_output_voltages << "\n"
" v: " << p18::ac_output_rated_voltages << "\n"
"\n"
" set-output-source-priority SUB|SBU\n"
" 'SUB' means " << p18::OutputSourcePriority::SolarUtilityBattery << "\n"
" 'SBU' means " << p18::OutputSourcePriority::SolarBatteryUtility << "\n"
"\n"
" set-charge-thresholds <cv> <dv>\n"
" Set battery re-charge and re-discharge voltages when\n"
" grid is connected.\n"
" set-charging-thresholds <cv> <dv>\n"
" Set battery re-charging and re-discharging voltages when\n"
" utility is available.\n"
"\n"
" cv: re-charge voltage\n"
" For 12 V unit: " << p18::bat_ac_recharge_voltages_12v << "\n"
" For 24 V unit: " << p18::bat_ac_recharge_voltages_24v << "\n"
" For 48 V unit: " << p18::bat_ac_recharge_voltages_48v << "\n"
" cv: re-charging voltage\n"
" For 12 V unit: " << p18::bat_ac_recharging_voltages_12v << "\n"
" For 24 V unit: " << p18::bat_ac_recharging_voltages_24v << "\n"
" For 48 V unit: " << p18::bat_ac_recharging_voltages_48v << "\n"
"\n"
" dv: re-discharge voltage\n"
" For 12 V unit: " << p18::bat_ac_redischarge_voltages_12v << "\n"
" For 24 V unit: " << p18::bat_ac_redischarge_voltages_24v << "\n"
" For 48 V unit: " << p18::bat_ac_redischarge_voltages_48v << "\n"
" dv: re-discharging voltage\n"
" For 12 V unit: " << p18::bat_ac_redischarging_voltages_12v << "\n"
" For 24 V unit: " << p18::bat_ac_redischarging_voltages_24v << "\n"
" For 48 V unit: " << p18::bat_ac_redischarging_voltages_48v << "\n"
"\n"
" set-charge-source-priority <id> <priority>\n"
" id: Parallel machine ID\n"
" set-charging-source-priority <id> <priority>\n"
" id: Parallel machine ID (use 0 for a single model)\n"
" priority: SF|SU|S\n"
" 'SF' means " << p18::ChargeSourcePriority::SolarFirst << "\n"
" 'SU' means " << p18::ChargeSourcePriority::SolarAndUtility << "\n"
" 'S' means " << p18::ChargeSourcePriority::SolarOnly << "\n"
" 'SF' means " << p18::ChargerSourcePriority::SolarFirst << ",\n"
" 'SU' means " << p18::ChargerSourcePriority::SolarAndUtility << "\n"
" 'S' means " << p18::ChargerSourcePriority::SolarOnly << "\n"
"\n"
" set-solar-power-priority BLU|LBU\n"
" 'BLU' means " << p18::SolarPowerPriority::BatteryLoadUtility << "\n"
@ -168,16 +165,16 @@ static void usage(const char* progname) {
"\n"
" set-ac-input-voltage-range APPLIANCE|UPS\n"
" set-battery-type AGM|FLOODED|USER\n"
" set-output-mode <id> <mode>\n"
" id: Machine ID\n"
" mode: S|P|1|2|3\n"
" S: " << p18::OutputMode::SingleOutput << "\n"
" P: " << p18::OutputMode::ParallelOutput << "\n"
" 1: " << p18::OutputMode::Phase_1_of_3 << "\n"
" 2: " << p18::OutputMode::Phase_2_of_3 << "\n"
" 3: " << p18::OutputMode::Phase_3_of_3 << "\n"
" set-output-model <id> <model>\n"
" id: Parallel machine ID (use 0 for a single model)\n"
" model: SM|P|P1|P2|P3\n"
" SM: " << p18::OutputModelSetting::SingleModule << "\n"
" P: " << p18::OutputModelSetting::ParallelOutput << "\n"
" P1: " << p18::OutputModelSetting::Phase1OfThreePhaseOutput << "\n"
" P2: " << p18::OutputModelSetting::Phase2OfThreePhaseOutput << "\n"
" P3: " << p18::OutputModelSetting::Phase3OfThreePhaseOutput << "\n"
"\n"
" set-battery-cutoff-voltage <v>\n"
" set-battery-cut-off-voltage <v>\n"
" v: Cut-off voltage (40.0~48.0)\n"
"\n"
" set-solar-configuration <id>\n"
@ -194,16 +191,14 @@ static void usage(const char* progname) {
" mm: Minutes\n"
" ss: Seconds\n"
"\n"
" set-ac-charge-time <start> <end>\n"
" set-ac-charging-time <start> <end>\n"
" start: Starting time, hh:mm format\n"
" end: Ending time, hh:mm format\n"
"\n"
" set-ac-supply-time <start> <end>\n"
" set-ac-loads-supply-time <start> <end>\n"
" start: Starting time, hh:mm format\n"
" end: Ending time, hh:mm format\n"
"\n"
"Note: use 0 as parallel machine ID for single machine.\n"
"\n"
"Flags:\n";
for (const p18::Flag& flag: p18::flags)
std::cout << " " << flag.flag << ": " << flag.description << "\n";
@ -265,7 +260,6 @@ int main(int argc, char *argv[]) {
u16 usbVendorId = voltronic::USBDevice::VENDOR_ID;
u16 usbDeviceId = voltronic::USBDevice::PRODUCT_ID;
std::string usbDevicePath {};
std::string serialDeviceName(voltronic::SerialDevice::DEVICE_NAME);
voltronic::SerialBaudRate serialBaudRate = voltronic::SerialDevice::BAUD_RATE;
@ -284,7 +278,6 @@ int main(int argc, char *argv[]) {
{"device", required_argument, nullptr, LO_DEVICE},
{"usb-vendor-id", required_argument, nullptr, LO_USB_VENDOR_ID},
{"usb-device-id", required_argument, nullptr, LO_USB_DEVICE_ID},
{"usb-path", required_argument, nullptr, LO_USB_PATH},
{"serial-name", required_argument, nullptr, LO_SERIAL_NAME},
{"serial-baud-rate", required_argument, nullptr, LO_SERIAL_BAUD_RATE},
{"serial-data-bits", required_argument, nullptr, LO_SERIAL_DATA_BITS},
@ -362,10 +355,6 @@ int main(int argc, char *argv[]) {
}
break;
case LO_USB_PATH:
usbDevicePath = arg;
break;
case LO_SERIAL_NAME:
serialDeviceName = arg;
break;
@ -451,12 +440,8 @@ int main(int argc, char *argv[]) {
std::shared_ptr<voltronic::Device> dev;
switch (deviceType) {
case DeviceType::USB:
if (usbDevicePath.empty()) {
dev = std::shared_ptr<voltronic::Device>(new voltronic::USBDevice(usbVendorId,
usbDeviceId));
} else {
dev = std::shared_ptr<voltronic::Device>(new voltronic::USBDevice(usbDevicePath));
}
break;
case DeviceType::Pseudo:
@ -472,6 +457,7 @@ int main(int argc, char *argv[]) {
break;
}
dev->setWorkerType(voltronic::WorkerType::OneShot);
dev->setVerbose(verbose);
dev->setTimeout(timeout);

View File

@ -6,8 +6,9 @@
#include <iomanip>
#include <ios>
#include <getopt.h>
#include <thread>
#include "numeric_types.h"
#include "types.h"
#include "common.h"
#include "voltronic/device.h"
#include "voltronic/exceptions.h"
@ -42,10 +43,7 @@ static void usage(const char* progname) {
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"
" Alternatively, you can specify device path (e.g., /dev/hidraw0):\n"
" --usb-path <PATH>: Device path\n";
" --usb-device-id <ID>: Device ID (default: " << std::setw(4) << voltronic::USBDevice::PRODUCT_ID << ")\n";
std::cout.flags(f);
std::cout << "\n"
@ -76,7 +74,6 @@ int main(int argc, char *argv[]) {
unsigned short usbVendorId = voltronic::USBDevice::VENDOR_ID;
unsigned short usbDeviceId = voltronic::USBDevice::PRODUCT_ID;
std::string usbDevicePath {};
std::string serialDeviceName(voltronic::SerialDevice::DEVICE_NAME);
voltronic::SerialBaudRate serialBaudRate = voltronic::SerialDevice::BAUD_RATE;
@ -96,7 +93,6 @@ int main(int argc, char *argv[]) {
{"device-error-limit", required_argument, nullptr, LO_DEVICE_ERROR_LIMIT},
{"usb-vendor-id", required_argument, nullptr, LO_USB_VENDOR_ID},
{"usb-device-id", required_argument, nullptr, LO_USB_DEVICE_ID},
{"usb-path", required_argument, nullptr, LO_USB_PATH},
{"serial-name", required_argument, nullptr, LO_SERIAL_NAME},
{"serial-baud-rate", required_argument, nullptr, LO_SERIAL_BAUD_RATE},
{"serial-data-bits", required_argument, nullptr, LO_SERIAL_DATA_BITS},
@ -181,10 +177,6 @@ int main(int argc, char *argv[]) {
}
break;
case LO_USB_PATH:
usbDevicePath = arg;
break;
case LO_SERIAL_NAME:
serialDeviceName = arg;
break;
@ -252,12 +244,7 @@ int main(int argc, char *argv[]) {
try {
switch (deviceType) {
case DeviceType::USB:
if (usbDevicePath.empty()) {
dev = std::shared_ptr<voltronic::Device>(new voltronic::USBDevice(usbVendorId,
usbDeviceId));
} else {
dev = std::shared_ptr<voltronic::Device>(new voltronic::USBDevice(usbDevicePath));
}
dev = std::shared_ptr<voltronic::Device>(new voltronic::USBDevice(usbVendorId, usbDeviceId));
break;
case DeviceType::Pseudo:

17
src/logging.cc Normal file
View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: BSD-3-Clause
#include "logging.h"
std::mutex custom_log::mutex_;
custom_log::custom_log(std::ostream& os, const std::string& func)
: os_(os)
{
mutex_.lock();
os_ << func << ": ";
}
custom_log::~custom_log() {
os_ << std::endl;
mutex_.unlock();
}

View File

@ -6,26 +6,24 @@
#include <iostream>
#include <string>
#include <string_view>
#include <mutex>
#include "./types.h"
class custom_log
{
private:
std::ostream& os_;
static std::mutex mutex_;
public:
custom_log(std::ostream& os, const std::string& func) : os_(os) {
os_ << func << ": ";
}
custom_log(std::ostream& os, const std::string& func);
~custom_log();
template <class T>
custom_log &operator<<(const T &v) {
custom_log& operator<<(const T& v) {
os_ << v;
return *this;
}
~custom_log() {
os_ << std::endl;
}
};
inline std::string method_name(const std::string& function, const std::string& pretty) {
@ -33,7 +31,7 @@ inline std::string method_name(const std::string& function, const std::string& p
size_t begin = pretty.rfind(" ", locFunName) + 1;
size_t end = pretty.find("(", locFunName + function.length());
return pretty.substr(begin, end - begin) + "()";
}
}
#define __METHOD_NAME__ method_name(__FUNCTION__, __PRETTY_FUNCTION__)

View File

@ -1,13 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
#ifndef INVERTER_TOOLS_NUMERIC_TYPES_H
#define INVERTER_TOOLS_NUMERIC_TYPES_H
#include <cstdint>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#endif //INVERTER_TOOLS_NUMERIC_TYPES_H

View File

@ -65,42 +65,42 @@ std::shared_ptr<response_type::BaseResponse> Client::execute(p18::CommandType co
RESPONSE_CASE(YearGenerated)
RESPONSE_CASE(MonthGenerated)
RESPONSE_CASE(DayGenerated)
RESPONSE_CASE(SerialNumber)
RESPONSE_CASE(SeriesNumber)
RESPONSE_CASE(CPUVersion)
RESPONSE_CASE(RatedInformation)
RESPONSE_CASE(GeneralStatus)
RESPONSE_CASE(WorkingMode)
RESPONSE_CASE(FaultsAndWarnings)
RESPONSE_CASE(FlagsAndStatuses)
RESPONSE_CASE(RatedDefaults)
RESPONSE_CASE(AllowedChargeCurrents)
RESPONSE_CASE(AllowedACChargeCurrents)
RESPONSE_CASE(Defaults)
RESPONSE_CASE(AllowedChargingCurrents)
RESPONSE_CASE(AllowedACChargingCurrents)
RESPONSE_CASE(ParallelRatedInformation)
RESPONSE_CASE(ParallelGeneralStatus)
RESPONSE_CASE(ACChargeTimeBucket)
RESPONSE_CASE(ACSupplyTimeBucket)
RESPONSE_CASE(ACChargingTimeBucket)
RESPONSE_CASE(ACLoadsSupplyTimeBucket)
case CommandType::SetACSupply:
case CommandType::SetLoads:
case CommandType::SetFlag:
case CommandType::SetDefaults:
case CommandType::SetBatteryMaxChargeCurrent:
case CommandType::SetBatteryMaxACChargeCurrent:
case CommandType::SetBatteryMaxChargingCurrent:
case CommandType::SetBatteryMaxACChargingCurrent:
case CommandType::SetACOutputFreq:
case CommandType::SetBatteryMaxChargeVoltage:
case CommandType::SetACOutputVoltage:
case CommandType::SetBatteryMaxChargingVoltage:
case CommandType::SetACOutputRatedVoltage:
case CommandType::SetOutputSourcePriority:
case CommandType::SetBatteryChargeThresholds:
case CommandType::SetChargeSourcePriority:
case CommandType::SetBatteryChargingThresholds:
case CommandType::SetChargingSourcePriority:
case CommandType::SetSolarPowerPriority:
case CommandType::SetACInputVoltageRange:
case CommandType::SetBatteryType:
case CommandType::SetOutputMode:
case CommandType::SetOutputModel:
case CommandType::SetBatteryCutOffVoltage:
case CommandType::SetSolarConfig:
case CommandType::ClearGenerated:
case CommandType::SetDateTime:
case CommandType::SetACChargeTimeBucket:
case CommandType::SetACSupplyTimeBucket:
case CommandType::SetACChargingTimeBucket:
case CommandType::SetACLoadsSupplyTimeBucket:
response = MKRESPONSE(SetResponse);
break;
}
@ -115,9 +115,9 @@ std::shared_ptr<response_type::BaseResponse> Client::execute(p18::CommandType co
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);
size_t responseSize = device_->enqueue(
(const u8*) raw.c_str(), raw.size(),
(u8*) buf.get(), bufSize);
return std::pair<std::shared_ptr<char>, size_t>(buf, responseSize);
}
@ -132,7 +132,7 @@ std::string Client::packArguments(p18::CommandType commandType, std::vector<std:
case CommandType::SetSolarPowerPriority:
case CommandType::SetACInputVoltageRange:
case CommandType::SetBatteryType:
case CommandType::SetACSupply:
case CommandType::SetLoads:
buf << arguments[0];
break;
@ -153,8 +153,8 @@ std::string Client::packArguments(p18::CommandType commandType, std::vector<std:
buf << arguments[0];
break;
case CommandType::SetBatteryMaxChargeCurrent:
case CommandType::SetBatteryMaxACChargeCurrent:
case CommandType::SetBatteryMaxChargingCurrent:
case CommandType::SetBatteryMaxACChargingCurrent:
buf << arguments[0] << ",";
buf << std::setw(3) << std::stoi(arguments[1]);
break;
@ -163,8 +163,8 @@ std::string Client::packArguments(p18::CommandType commandType, std::vector<std:
buf << std::setw(2) << std::stoi(arguments[0]);
break;
case CommandType::SetBatteryMaxChargeVoltage:
case CommandType::SetBatteryChargeThresholds: {
case CommandType::SetBatteryMaxChargingVoltage:
case CommandType::SetBatteryChargingThresholds: {
for (int i = 0; i < 2; i++) {
double val = std::stod(arguments[i]);
buf << std::setw(3) << (int)round(val*10);
@ -174,13 +174,13 @@ std::string Client::packArguments(p18::CommandType commandType, std::vector<std:
break;
}
case CommandType::SetACOutputVoltage: {
case CommandType::SetACOutputRatedVoltage: {
buf << std::setw(4) << (std::stoi(arguments[0])*10);
break;
}
case CommandType::SetChargeSourcePriority:
case CommandType::SetOutputMode:
case CommandType::SetChargingSourcePriority:
case CommandType::SetOutputModel:
buf << arguments[0] << "," << arguments[1];
break;
@ -210,8 +210,8 @@ std::string Client::packArguments(p18::CommandType commandType, std::vector<std:
break;
}
case CommandType::SetACChargeTimeBucket:
case CommandType::SetACSupplyTimeBucket:
case CommandType::SetACChargingTimeBucket:
case CommandType::SetACLoadsSupplyTimeBucket:
for (int i = 0; i < 4; i++) {
buf << std::setw(2) << std::stoi(arguments[i]);
if (i == 1)

View File

@ -24,41 +24,41 @@ const std::map<std::string, p18::CommandType> client_commands = {
{"get-year-generated", p18::CommandType::GetYearGenerated},
{"get-month-generated", p18::CommandType::GetMonthGenerated},
{"get-day-generated", p18::CommandType::GetDayGenerated},
{"get-serial-number", p18::CommandType::GetSerialNumber},
{"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::GetRatedDefaults},
{"get-allowed-charge-currents", p18::CommandType::GetAllowedChargeCurrents},
{"get-allowed-ac-charge-currents", p18::CommandType::GetAllowedACChargeCurrents},
{"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-charge-time", p18::CommandType::GetACChargeTimeBucket},
{"get-ac-supply-time", p18::CommandType::GetACSupplyTimeBucket},
{"set-ac-supply", p18::CommandType::SetACSupply},
{"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-charge-current", p18::CommandType::SetBatteryMaxChargeCurrent},
{"set-max-ac-charge-current", p18::CommandType::SetBatteryMaxACChargeCurrent},
{"set-max-charging-current", p18::CommandType::SetBatteryMaxChargingCurrent},
{"set-max-ac-charging-current", p18::CommandType::SetBatteryMaxACChargingCurrent},
{"set-ac-output-freq", p18::CommandType::SetACOutputFreq},
{"set-max-charge-voltage", p18::CommandType::SetBatteryMaxChargeVoltage},
{"set-ac-output-voltage", p18::CommandType::SetACOutputVoltage},
{"set-max-charging-voltage", p18::CommandType::SetBatteryMaxChargingVoltage},
{"set-ac-output-voltage", p18::CommandType::SetACOutputRatedVoltage},
{"set-output-source-priority", p18::CommandType::SetOutputSourcePriority},
{"set-charge-thresholds", p18::CommandType::SetBatteryChargeThresholds}, /* Battery re-charge and re-discharge voltage when utility is available */
{"set-charge-source-priority", p18::CommandType::SetChargeSourcePriority},
{"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-mode", p18::CommandType::SetOutputMode},
{"set-battery-cutoff-voltage", p18::CommandType::SetBatteryCutOffVoltage},
{"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-charge-time", p18::CommandType::SetACChargeTimeBucket},
{"set-ac-supply-time", p18::CommandType::SetACSupplyTimeBucket},
{"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) {
@ -200,7 +200,7 @@ p18::CommandType validate_input(std::string& command,
throw std::invalid_argument("invalid argument");
break;
case p18::CommandType::SetACSupply: {
case p18::CommandType::SetLoads: {
GET_ARGS(1);
std::string &arg = arguments[0];
if (arg != "0" && arg != "1")
@ -229,8 +229,8 @@ p18::CommandType validate_input(std::string& command,
break;
}
case p18::CommandType::SetBatteryMaxChargeCurrent:
case p18::CommandType::SetBatteryMaxACChargeCurrent: {
case p18::CommandType::SetBatteryMaxChargingCurrent:
case p18::CommandType::SetBatteryMaxACChargingCurrent: {
GET_ARGS(2);
auto id = static_cast<unsigned>(std::stoul(arguments[0]));
@ -254,7 +254,7 @@ p18::CommandType validate_input(std::string& command,
break;
}
case p18::CommandType::SetBatteryMaxChargeVoltage: {
case p18::CommandType::SetBatteryMaxChargingVoltage: {
GET_ARGS(2);
float cv = std::stof(arguments[0]);
@ -269,13 +269,13 @@ p18::CommandType validate_input(std::string& command,
break;
}
case p18::CommandType::SetACOutputVoltage: {
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_voltages) {
for (const auto &item: p18::ac_output_rated_voltages) {
if (v == item) {
matchFound = true;
break;
@ -301,26 +301,26 @@ p18::CommandType validate_input(std::string& command,
break;
}
case p18::CommandType::SetBatteryChargeThresholds: {
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_recharge_voltages_12v, cv) == -1 &&
index_of(p18::bat_ac_recharge_voltages_24v, cv) == -1 &&
index_of(p18::bat_ac_recharge_voltages_48v, cv) == -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_redischarge_voltages_12v, dv) == -1 &&
index_of(p18::bat_ac_redischarge_voltages_24v, dv) == -1 &&
index_of(p18::bat_ac_redischarge_voltages_48v, dv) == -1)
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::SetChargeSourcePriority: {
case p18::CommandType::SetChargingSourcePriority: {
GET_ARGS(2);
auto id = static_cast<unsigned>(std::stoul(arguments[0]));
@ -370,14 +370,14 @@ p18::CommandType validate_input(std::string& command,
break;
}
case p18::CommandType::SetOutputMode: {
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({"S", "P", "1", "2", "3"});
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");
@ -414,8 +414,8 @@ p18::CommandType validate_input(std::string& command,
break;
}
case p18::CommandType::SetACChargeTimeBucket:
case p18::CommandType::SetACSupplyTimeBucket: {
case p18::CommandType::SetACChargingTimeBucket:
case p18::CommandType::SetACLoadsSupplyTimeBucket: {
GET_ARGS(2);
std::vector<std::string> start = split(arguments[0], ':');

View File

@ -14,54 +14,54 @@ const std::map<CommandType, std::string> raw_commands = {
{CommandType::GetYearGenerated, "EY"},
{CommandType::GetMonthGenerated, "EM"},
{CommandType::GetDayGenerated, "ED"},
{CommandType::GetSerialNumber, "ID"},
{CommandType::GetSeriesNumber, "ID"},
{CommandType::GetCPUVersion, "VFW"},
{CommandType::GetRatedInformation, "PIRI"},
{CommandType::GetGeneralStatus, "GS"},
{CommandType::GetWorkingMode, "MOD"},
{CommandType::GetFaultsAndWarnings, "FWS"},
{CommandType::GetFlagsAndStatuses, "FLAG"},
{CommandType::GetRatedDefaults, "DI"},
{CommandType::GetAllowedChargeCurrents, "MCHGCR"},
{CommandType::GetAllowedACChargeCurrents, "MUCHGCR"},
{CommandType::GetDefaults, "DI"},
{CommandType::GetAllowedChargingCurrents, "MCHGCR"},
{CommandType::GetAllowedACChargingCurrents, "MUCHGCR"},
{CommandType::GetParallelRatedInformation, "PRI"},
{CommandType::GetParallelGeneralStatus, "PGS"},
{CommandType::GetACChargeTimeBucket, "ACCT"},
{CommandType::GetACSupplyTimeBucket, "ACLT"},
{CommandType::SetACSupply, "LON"},
{CommandType::GetACChargingTimeBucket, "ACCT"},
{CommandType::GetACLoadsSupplyTimeBucket, "ACLT"},
{CommandType::SetLoads, "LON"},
{CommandType::SetFlag, "P"},
{CommandType::SetDefaults, "PF"},
{CommandType::SetBatteryMaxChargeCurrent, "MCHGC"},
{CommandType::SetBatteryMaxACChargeCurrent, "MUCHGC"},
{CommandType::SetBatteryMaxChargingCurrent, "MCHGC"},
{CommandType::SetBatteryMaxACChargingCurrent, "MUCHGC"},
/* The protocol documentation defines two commands, "F50" and "F60",
but it's identical as if there were just one "F" command with an argument. */
{CommandType::SetACOutputFreq, "F"},
{CommandType::SetBatteryMaxChargeVoltage, "MCHGV"},
{CommandType::SetACOutputVoltage, "V"},
{CommandType::SetBatteryMaxChargingVoltage, "MCHGV"},
{CommandType::SetACOutputRatedVoltage, "V"},
{CommandType::SetOutputSourcePriority, "POP"},
{CommandType::SetBatteryChargeThresholds, "BUCD"},
{CommandType::SetChargeSourcePriority, "PCP"},
{CommandType::SetBatteryChargingThresholds, "BUCD"},
{CommandType::SetChargingSourcePriority, "PCP"},
{CommandType::SetSolarPowerPriority, "PSP"},
{CommandType::SetACInputVoltageRange, "PGR"},
{CommandType::SetBatteryType, "PBT"},
{CommandType::SetOutputMode, "POPM"},
{CommandType::SetOutputModel, "POPM"},
{CommandType::SetBatteryCutOffVoltage, "PSDV"},
{CommandType::SetSolarConfig, "ID"},
{CommandType::ClearGenerated, "CLE"},
{CommandType::SetDateTime, "DAT"},
{CommandType::SetACChargeTimeBucket, "ACCT"},
{CommandType::SetACSupplyTimeBucket, "ACLT"},
{CommandType::SetACChargingTimeBucket, "ACCT"},
{CommandType::SetACLoadsSupplyTimeBucket, "ACLT"},
};
const std::array<int, 5> ac_output_voltages = {202, 208, 220, 230, 240};
const std::array<int, 5> ac_output_rated_voltages = {202, 208, 220, 230, 240};
const std::array<float, 8> bat_ac_recharge_voltages_12v = {11, 11.3, 11.5, 11.8, 12, 12.3, 12.5, 12.8};
const std::array<float, 8> bat_ac_recharge_voltages_24v = {22, 22.5, 23, 23.5, 24, 24.5, 25, 25.5};
const std::array<float, 8> bat_ac_recharge_voltages_48v = {44, 45, 46, 47, 48, 49, 50, 51};
const std::array<float, 8> bat_ac_recharging_voltages_12v = {11, 11.3, 11.5, 11.8, 12, 12.3, 12.5, 12.8};
const std::array<float, 8> bat_ac_recharging_voltages_24v = {22, 22.5, 23, 23.5, 24, 24.5, 25, 25.5};
const std::array<float, 8> bat_ac_recharging_voltages_48v = {44, 45, 46, 47, 48, 49, 50, 51};
const std::array<float, 12> bat_ac_redischarge_voltages_12v = {0, 12, 12.3, 12.5, 12.8, 13, 13.3, 13.5, 13.8, 14, 14.3, 14.5};
const std::array<float, 12> bat_ac_redischarge_voltages_24v = {0, 24, 24.5, 25, 25.5, 26, 26.5, 27, 27.5, 28, 28.5, 29};
const std::array<float, 12> bat_ac_redischarge_voltages_48v = {0, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58};
const std::array<float, 12> bat_ac_redischarging_voltages_12v = {0, 12, 12.3, 12.5, 12.8, 13, 13.3, 13.5, 13.8, 14, 14.3, 14.5};
const std::array<float, 12> bat_ac_redischarging_voltages_24v = {0, 24, 24.5, 25, 25.5, 26, 26.5, 27, 27.5, 28, 28.5, 29};
const std::array<float, 12> bat_ac_redischarging_voltages_48v = {0, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58};
const std::map<int, std::string> fault_codes = {
{1, "Fan is locked"},
@ -133,13 +133,13 @@ ENUM_STR(OutputSourcePriority) {
ENUM_STR_DEFAULT;
}
ENUM_STR(ChargeSourcePriority) {
ENUM_STR(ChargerSourcePriority) {
switch (val) {
case ChargeSourcePriority::SolarFirst:
case ChargerSourcePriority::SolarFirst:
return os << "Solar-First";
case ChargeSourcePriority::SolarAndUtility:
case ChargerSourcePriority::SolarAndUtility:
return os << "Solar-and-Utility";
case ChargeSourcePriority::SolarOnly:
case ChargerSourcePriority::SolarOnly:
return os << "Solar-only";
}
ENUM_STR_DEFAULT;
@ -161,18 +161,18 @@ ENUM_STR(Topology) {
ENUM_STR_DEFAULT;
}
ENUM_STR(OutputMode) {
ENUM_STR(OutputModelSetting) {
switch (val) {
case OutputMode::SingleOutput:
return os << "Single output";
case OutputMode::ParallelOutput:
case OutputModelSetting::SingleModule:
return os << "Single module";
case OutputModelSetting::ParallelOutput:
return os << "Parallel output";
case OutputMode::Phase_1_of_3:
return os << "Phase 1 of 3-phase output";
case OutputMode::Phase_2_of_3:
return os << "Phase 2 of 3-phase output";
case OutputMode::Phase_3_of_3:
return os << "Phase 3 of 3-phase";
case OutputModelSetting::Phase1OfThreePhaseOutput:
return os << "Phase 1 of three phase output";
case OutputModelSetting::Phase2OfThreePhaseOutput:
return os << "Phase 2 of three phase output";
case OutputModelSetting::Phase3OfThreePhaseOutput:
return os << "Phase 3 of three phase";
}
ENUM_STR_DEFAULT;
}

View File

@ -13,15 +13,15 @@ namespace p18 {
extern const std::map<CommandType, std::string> raw_commands;
extern const std::array<int, 5> ac_output_voltages;
extern const std::array<int, 5> ac_output_rated_voltages;
extern const std::array<float, 8> bat_ac_recharge_voltages_12v;
extern const std::array<float, 8> bat_ac_recharge_voltages_24v;
extern const std::array<float, 8> bat_ac_recharge_voltages_48v;
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_redischarge_voltages_12v;
extern const std::array<float, 12> bat_ac_redischarge_voltages_24v;
extern const std::array<float, 12> bat_ac_redischarge_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;

View File

@ -27,19 +27,6 @@ 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
*/
@ -71,32 +58,23 @@ size_t GetResponse::getDataSize() const {
return rawSize_ - 5;
}
std::vector<std::string> GetResponse::getList(std::vector<FieldLength> itemLengths, int expectAtLeast) const {
std::vector<std::string> GetResponse::getList(std::vector<size_t> itemLengths) 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) {
if (list.size() < itemLengths.size()) {
std::ostringstream error;
error << "while parsing " << demangle_type_name(typeid(*this).name());
error << ": list is expected to be " << expectAtLeast << " items long, ";
error << ": list is expected to be " << itemLengths.size() << " items long, ";
error << "got only " << list.size() << " items";
throw ParseError(error.str());
}
// check each item's length
for (int i = 0; i < list.size(); i++) {
if (i >= itemLengths.size()) {
myerr << "while parsing " << demangle_type_name(typeid(*this).name())
<< ": item " << i << " is not expected";
break;
}
if (!itemLengths[i].validate(list[i].size())) {
for (int i = 0; i < itemLengths.size(); i++) {
if (list[i].size() != itemLengths[i]) {
std::ostringstream error;
error << "while parsing " << demangle_type_name(typeid(*this).name());
error << ": item " << i << " is expected to be " << itemLengths[i] << " characters long, ";
@ -222,7 +200,7 @@ formattable_ptr TotalGenerated::format(formatter::Format format) {
}
void SerialNumber::unpack() {
void SeriesNumber::unpack() {
auto data = getData();
std::string buf(data, 2);
@ -231,9 +209,9 @@ void SerialNumber::unpack() {
id = std::string(data+2, len);
}
formattable_ptr SerialNumber::format(formatter::Format format) {
formattable_ptr SeriesNumber::format(formatter::Format format) {
RETURN_TABLE({
LINE("sn", "Serial number", id)
LINE("sn", "Series number", id)
});
}
@ -271,7 +249,7 @@ void RatedInformation::unpack() {
3, // LLL
3, // MMM
1, // N
FieldLength(2, 3), // OO
2, // OO
3, // PPP
1, // O
1, // R
@ -298,15 +276,15 @@ void RatedInformation::unpack() {
battery_bulk_voltage = stou(list[11]);
battery_float_voltage = stou(list[12]);
battery_type = static_cast<BatteryType>(stou(list[13]));
max_ac_charge_current = stou(list[14]);
max_charge_current = stou(list[15]);
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<OutputSourcePriority>(stou(list[17]));
charge_source_priority = static_cast<ChargeSourcePriority>(stou(list[18]));
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_mode = static_cast<OutputMode>(stou(list[22]));
output_model_setting = static_cast<OutputModelSetting>(stou(list[22]));
solar_power_priority = static_cast<SolarPowerPriority>(stou(list[23]));
mppt = list[24];
}
@ -327,15 +305,15 @@ formattable_ptr RatedInformation::format(formatter::Format format) {
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_charge_current", "Max charge current", max_charge_current, Unit::A),
LINE("max_ac_charge_current", "Max AC charge current", max_ac_charge_current, Unit::A),
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", charge_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_mode", "Output mode", output_mode),
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)
});
@ -385,7 +363,7 @@ void GeneralStatus::unpack() {
battery_voltage_scc = stou(list[8]);
battery_voltage_scc2 = stou(list[9]);
battery_discharge_current = stou(list[10]);
battery_charge_current = stou(list[11]);
battery_charging_current = stou(list[11]);
battery_capacity = stou(list[12]);
inverter_heat_sink_temp = stou(list[13]);
mppt1_charger_temp = stou(list[14]);
@ -416,8 +394,8 @@ formattable_ptr GeneralStatus::format(formatter::Format format) {
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_discharge_current", "Battery discharge current", battery_discharge_current, Unit::A),
LINE("battery_charge_current", "Battery charge current", battery_charge_current, Unit::A),
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),
@ -546,7 +524,7 @@ formattable_ptr FlagsAndStatuses::format(formatter::Format format) {
}
void RatedDefaults::unpack() {
void Defaults::unpack() {
auto list = getList({
4, // AAAA
3, // BBB
@ -582,14 +560,14 @@ void RatedDefaults::unpack() {
charging_bulk_voltage = stou(list[5]);
battery_recharge_voltage = stou(list[6]);
battery_redischarge_voltage = stou(list[7]);
max_charge_current = stou(list[8]);
max_ac_charge_current = stou(list[9]);
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]));
charge_source_priority = static_cast<ChargeSourcePriority>(stou(list[12]));
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_mode = static_cast<OutputMode>(stou(list[15]));
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;
@ -600,7 +578,7 @@ void RatedDefaults::unpack() {
flag_lcd_escape_to_default_page_after_1min_timeout = stou(list[23]) > 0;
}
formattable_ptr RatedDefaults::format(formatter::Format format) {
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),
@ -608,16 +586,16 @@ formattable_ptr RatedDefaults::format(formatter::Format format) {
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_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("max_charge_current", "Max charge current", max_charge_current, Unit::A),
LINE("max_ac_charge_current", "Max AC charge current", max_ac_charge_current, Unit::A),
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("charge_source_priority", "Charge source priority", charge_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_mode", "Output mode", output_mode),
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),
@ -629,14 +607,14 @@ formattable_ptr RatedDefaults::format(formatter::Format format) {
})
}
void AllowedChargeCurrents::unpack() {
void AllowedChargingCurrents::unpack() {
auto list = getList({});
for (const std::string& i: list) {
amps.emplace_back(stou(i));
}
}
formattable_ptr AllowedChargeCurrents::format(formatter::Format format) {
formattable_ptr AllowedChargingCurrents::format(formatter::Format format) {
std::vector<formatter::ListItem<VariantHolder>> v;
for (const auto& n: amps)
v.emplace_back(n);
@ -654,32 +632,27 @@ void ParallelRatedInformation::unpack() {
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),
2, // FF
1 // G
});
parallel_connection_status = static_cast<ParallelConnectionStatus>(stou(list[0]));
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);
charge_source_priority = static_cast<ChargeSourcePriority>(stou(list[3]));
max_charge_current = stou(list[4]);
max_ac_charge_current = stou(list[5]);
output_mode = static_cast<OutputMode>(stou(list[6]));
serial_number = std::string(list[2], 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_connection_status", "Parallel connection status", parallel_connection_status),
LINE("parallel_id_connection_status", "Parallel ID connection status", parallel_id_connection_status),
LINE("serial_number", "Serial number", serial_number),
LINE("charge_source_priority", "Charge source priority", charge_source_priority),
LINE("max_charge_current", "Max charge current", max_charge_current, Unit::A),
LINE("max_ac_charge_current", "Max AC charge current", max_ac_charge_current, Unit::A),
LINE("output_mode", "Output mode", output_mode),
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),
})
}
@ -709,19 +682,15 @@ void ParallelGeneralStatus::unpack() {
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);
3, // bbb. Note: this one is marked in red in the doc. I don't know what that means.
});
parallel_connection_status = static_cast<ParallelConnectionStatus>(stou(list[0]));
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]);
@ -736,8 +705,8 @@ void ParallelGeneralStatus::unpack() {
total_output_load_percent = stou(list[12]);
battery_voltage = stou(list[13]);
battery_discharge_current = stou(list[14]);
battery_charge_current = stou(list[15]);
total_battery_charge_current = stou(list[16]);
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]);
@ -749,15 +718,12 @@ void ParallelGeneralStatus::unpack() {
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_connection_status", "Parallel connection status", parallel_connection_status),
RETURN_TABLE({
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),
@ -772,32 +738,23 @@ formattable_ptr ParallelGeneralStatus::format(formatter::Format format) {
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_charge_current", "Battery charge current", battery_charge_current, Unit::A),
LINE("total_battery_charge_current", "Total battery charge current", total_battery_charge_current, Unit::A),
LINE("battery_capacity", "Battery capacity", battery_capacity, Unit::Percentage),
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("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);
LINE("max_temp", "Max. temperature", max_temp),
})
}
void ACChargeTimeBucket::unpack() {
void ACChargingTimeBucket::unpack() {
auto list = getList({4 /* AAAA */, 4 /* BBBB */});
start_h = stouh(list[0].substr(0, 2));
@ -814,7 +771,7 @@ static inline std::string get_time(unsigned short h, unsigned short m) {
return buf.str();
}
formattable_ptr ACChargeTimeBucket::format(formatter::Format format) {
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)),

View File

@ -32,7 +32,7 @@ typedef std::variant<
std::string,
p18::BatteryType,
p18::BatteryPowerDirection,
p18::ChargeSourcePriority,
p18::ChargerSourcePriority,
p18::DC_AC_PowerDirection,
p18::InputVoltageRange,
p18::LinePowerDirection,
@ -40,7 +40,7 @@ typedef std::variant<
p18::MPPTChargerStatus,
p18::Topology,
p18::OutputSourcePriority,
p18::OutputMode,
p18::OutputModelSetting,
p18::ParallelConnectionStatus,
p18::SolarPowerPriority,
p18::WorkingMode,
@ -62,7 +62,7 @@ public:
VariantHolder(std::string v) : v_(v) {}
VariantHolder(p18::BatteryType v) : v_(v) {}
VariantHolder(p18::BatteryPowerDirection v) : v_(v) {}
VariantHolder(p18::ChargeSourcePriority 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) {}
@ -70,7 +70,7 @@ public:
VariantHolder(p18::MPPTChargerStatus v) : v_(v) {}
VariantHolder(p18::Topology v) : v_(v) {}
VariantHolder(p18::OutputSourcePriority v) : v_(v) {}
VariantHolder(p18::OutputMode 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) {}
@ -89,7 +89,7 @@ public:
bool isEnum =
std::holds_alternative<p18::BatteryType>(v_) ||
std::holds_alternative<p18::BatteryPowerDirection>(v_) ||
std::holds_alternative<p18::ChargeSourcePriority>(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_) ||
@ -97,7 +97,7 @@ public:
std::holds_alternative<p18::MPPTChargerStatus>(v_) ||
std::holds_alternative<p18::Topology>(v_) ||
std::holds_alternative<p18::OutputSourcePriority>(v_) ||
std::holds_alternative<p18::OutputMode>(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_) ||
@ -124,26 +124,6 @@ public:
};
/**
* 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
*/
@ -165,7 +145,7 @@ 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;
std::vector<std::string> getList(std::vector<size_t> itemLengths) const;
public:
using BaseResponse::BaseResponse;
@ -249,7 +229,7 @@ public:
using TotalGenerated::TotalGenerated;
};
class SerialNumber : public GetResponse {
class SeriesNumber : public GetResponse {
public:
using GetResponse::GetResponse;
void unpack() override;
@ -289,15 +269,15 @@ public:
unsigned battery_bulk_voltage; /* unit: 0.1V */
unsigned battery_float_voltage; /* unit: 0.1V */
p18::BatteryType battery_type;
unsigned max_ac_charge_current; /* unit: A */
unsigned max_charge_current; /* unit: A */
unsigned max_ac_charging_current; /* unit: A */
unsigned max_charging_current; /* unit: A */
p18::InputVoltageRange input_voltage_range;
p18::OutputSourcePriority output_source_priority;
p18::ChargeSourcePriority charge_source_priority;
p18::OutputModelSetting output_source_priority;
p18::ChargerSourcePriority charger_source_priority;
unsigned parallel_max_num;
p18::MachineType machine_type;
p18::Topology topology;
p18::OutputMode output_mode;
p18::OutputModelSetting output_model_setting;
p18::SolarPowerPriority solar_power_priority;
std::string mppt;
};
@ -319,7 +299,7 @@ public:
unsigned battery_voltage_scc; /* unit: 0.1V */
unsigned battery_voltage_scc2; /* unit: 0.1V */
unsigned battery_discharge_current; /* unit: A */
unsigned battery_charge_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 */
@ -389,7 +369,7 @@ public:
char reserved = '0';
};
class RatedDefaults : public GetResponse {
class Defaults : public GetResponse {
public:
using GetResponse::GetResponse;
void unpack() override;
@ -403,14 +383,14 @@ public:
unsigned charging_bulk_voltage = 0;
unsigned battery_recharge_voltage = 0;
unsigned battery_redischarge_voltage = 0;
unsigned max_charge_current = 0;
unsigned max_ac_charge_current = 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::ChargeSourcePriority charge_source_priority = static_cast<ChargeSourcePriority>(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::OutputMode output_mode = static_cast<OutputMode>(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;
@ -421,7 +401,7 @@ public:
bool flag_lcd_escape_to_default_page_after_1min_timeout = false;
};
class AllowedChargeCurrents : public GetResponse {
class AllowedChargingCurrents : public GetResponse {
public:
using GetResponse::GetResponse;
void unpack() override;
@ -430,9 +410,9 @@ public:
std::vector<unsigned> amps;
};
class AllowedACChargeCurrents : public AllowedChargeCurrents {
class AllowedACChargingCurrents : public AllowedChargingCurrents {
public:
using AllowedChargeCurrents::AllowedChargeCurrents;
using AllowedChargingCurrents::AllowedChargingCurrents;
};
class ParallelRatedInformation : public GetResponse {
@ -441,13 +421,13 @@ public:
void unpack() override;
formattable_ptr format(formatter::Format format) override;
p18::ParallelConnectionStatus parallel_connection_status = static_cast<ParallelConnectionStatus>(0);
p18::ParallelConnectionStatus parallel_id_connection_status = static_cast<ParallelConnectionStatus>(0);
unsigned serial_number_valid_length = 0;
std::string serial_number;
p18::ChargeSourcePriority charge_source_priority = static_cast<ChargeSourcePriority>(0);
unsigned max_ac_charge_current = 0; // unit: A
unsigned max_charge_current = 0; // unit: A
p18::OutputMode output_mode = static_cast<OutputMode>(0);
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 {
@ -456,7 +436,7 @@ public:
void unpack() override;
formattable_ptr format(formatter::Format format) override;
p18::ParallelConnectionStatus parallel_connection_status;
p18::ParallelConnectionStatus parallel_id_connection_status;
p18::WorkingMode work_mode;
unsigned fault_code;
unsigned grid_voltage; /* unit: 0.1V */
@ -471,8 +451,8 @@ public:
unsigned total_output_load_percent; /* unit: % */
unsigned battery_voltage; /* unit: 0.1V */
unsigned battery_discharge_current; /* unit: A */
unsigned battery_charge_current; /* unit: A */
unsigned total_battery_charge_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 */
@ -484,12 +464,10 @@ public:
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 ACChargeTimeBucket : public GetResponse {
class ACChargingTimeBucket : public GetResponse {
public:
using GetResponse::GetResponse;
void unpack() override;
@ -501,9 +479,9 @@ public:
unsigned short end_m = 0;
};
class ACSupplyTimeBucket : public ACChargeTimeBucket {
class ACLoadsSupplyTimeBucket : public ACChargingTimeBucket {
public:
using ACChargeTimeBucket::ACChargeTimeBucket;
using ACChargingTimeBucket::ACChargingTimeBucket;
};
} // namespace p18

View File

@ -17,41 +17,41 @@ enum class CommandType {
GetYearGenerated,
GetMonthGenerated,
GetDayGenerated,
GetSerialNumber,
GetSeriesNumber,
GetCPUVersion,
GetRatedInformation,
GetGeneralStatus,
GetWorkingMode,
GetFaultsAndWarnings,
GetFlagsAndStatuses,
GetRatedDefaults,
GetAllowedChargeCurrents,
GetAllowedACChargeCurrents,
GetDefaults,
GetAllowedChargingCurrents,
GetAllowedACChargingCurrents,
GetParallelRatedInformation,
GetParallelGeneralStatus,
GetACChargeTimeBucket,
GetACSupplyTimeBucket,
SetACSupply = 100,
GetACChargingTimeBucket,
GetACLoadsSupplyTimeBucket,
SetLoads = 100,
SetFlag,
SetDefaults,
SetBatteryMaxChargeCurrent,
SetBatteryMaxACChargeCurrent,
SetBatteryMaxChargingCurrent,
SetBatteryMaxACChargingCurrent,
SetACOutputFreq,
SetBatteryMaxChargeVoltage,
SetACOutputVoltage,
SetBatteryMaxChargingVoltage,
SetACOutputRatedVoltage,
SetOutputSourcePriority,
SetBatteryChargeThresholds, /* Battery re-charge and re-discharge voltage when utility is available */
SetChargeSourcePriority,
SetBatteryChargingThresholds, /* Battery re-charging and re-discharing voltage when utility is available */
SetChargingSourcePriority,
SetSolarPowerPriority,
SetACInputVoltageRange,
SetBatteryType,
SetOutputMode,
SetOutputModel,
SetBatteryCutOffVoltage,
SetSolarConfig,
ClearGenerated,
SetDateTime,
SetACChargeTimeBucket,
SetACSupplyTimeBucket,
SetACChargingTimeBucket,
SetACLoadsSupplyTimeBucket,
};
enum class BatteryType {
@ -73,12 +73,12 @@ enum class OutputSourcePriority {
};
ENUM_STR(OutputSourcePriority);
enum class ChargeSourcePriority {
enum class ChargerSourcePriority {
SolarFirst = 0,
SolarAndUtility = 1,
SolarOnly = 2,
};
ENUM_STR(ChargeSourcePriority);
ENUM_STR(ChargerSourcePriority);
enum class MachineType {
OffGridTie = 0,
@ -92,14 +92,14 @@ enum class Topology {
};
ENUM_STR(Topology);
enum class OutputMode {
SingleOutput = 0,
enum class OutputModelSetting {
SingleModule = 0,
ParallelOutput = 1,
Phase_1_of_3 = 2,
Phase_2_of_3 = 3,
Phase_3_of_3 = 4,
Phase1OfThreePhaseOutput = 2,
Phase2OfThreePhaseOutput = 3,
Phase3OfThreePhaseOutput = 4,
};
ENUM_STR(OutputMode);
ENUM_STR(OutputModelSetting);
enum class SolarPowerPriority {
BatteryLoadUtility = 0,

View File

@ -5,14 +5,12 @@
#include <ios>
#include <arpa/inet.h>
#include <cerrno>
#include <nlohmann/json.hpp>
#include "connection.h"
#include "../p18/commands.h"
#include "../p18/response.h"
#include "../logging.h"
#include "../common.h"
#include "hexdump/hexdump.h"
#include "signal.h"
#define CHECK_ARGUMENTS_LENGTH(__size__) \
if (arguments.size() != (__size__)) { \
@ -239,8 +237,13 @@ Response Connection::processRequest(char* buf) {
resp.type = ResponseType::Error;
try {
auto err = p18::response_type::ErrorResponse(e.what());
resp.buf << *(err.format(options_.format));
} catch (nlohmann::detail::exception& e) {
myerr << e.what();
resp.buf << "error while formatting json: " << e.what();
}
}
return resp;

View File

@ -8,13 +8,13 @@
#include <utility>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdexcept>
#include <unistd.h>
#include "../voltronic/exceptions.h"
#include "../p18/exceptions.h"
#include "../voltronic/time.h"
#include "../logging.h"
//#include "hexdump/hexdump.h"
#include "server.h"
#include "connection.h"
#include "signal.h"
@ -31,9 +31,20 @@ Server::Server(std::shared_ptr<voltronic::Device> device)
, deviceErrorCounter_(0)
, verbose_(false)
, device_(std::move(device)) {
client_.setDevice(device_);
}
Server::~Server() {
if (sock_ > 0)
close(sock_);
}
/**
* Common stuff, getters, setters
*/
void Server::setVerbose(bool verbose) {
verbose_ = verbose;
device_->setVerbose(verbose);
@ -51,10 +62,10 @@ void Server::setDeviceErrorLimit(u32 deviceErrorLimit) {
deviceErrorLimit_ = deviceErrorLimit;
}
Server::~Server() {
if (sock_ > 0)
close(sock_);
}
/**
* TCP Server
*/
void Server::start(std::string& host, int port) {
host_ = host;

View File

@ -13,7 +13,7 @@
#include <netinet/in.h>
#include "connection.h"
#include "../numeric_types.h"
#include "../types.h"
#include "../formatter/formatter.h"
#include "../p18/client.h"
#include "../p18/types.h"
@ -22,8 +22,6 @@
namespace server {
typedef std::lock_guard<std::mutex> LockGuard;
class Connection;
struct CachedResponse {
@ -69,6 +67,7 @@ public:
void setDeviceErrorLimit(u32 deviceErrorLimit);
void start(std::string& host, int port);
void createDeviceThread();
bool verbose() const { return verbose_; }
void addConnection(Connection* conn);

16
src/types.h Normal file
View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: BSD-3-Clause
#ifndef INVERTER_TOOLS_TYPES_H
#define INVERTER_TOOLS_TYPES_H
#include <mutex>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef std::lock_guard<std::mutex> LockGuard;
typedef std::unique_lock<std::mutex> UniqueLock;
#endif //INVERTER_TOOLS_TYPES_H

View File

@ -5,7 +5,7 @@
#include <cstdint>
#include <cstdlib>
#include "../numeric_types.h"
#include "../types.h"
namespace voltronic {

View File

@ -4,7 +4,7 @@
#include <iostream>
#include <limits>
#include <cstring>
#include <sstream>
#include <chrono>
#include "crc.h"
#include "device.h"
@ -13,11 +13,38 @@
#include "hexdump/hexdump.h"
#include "../logging.h"
using namespace std::chrono_literals;
namespace voltronic {
Device::Device() :
flags_(FLAG_WRITE_CRC | FLAG_READ_CRC | FLAG_VERIFY_CRC),
timeout_(TIMEOUT) {}
Device::Device()
: flags_(FLAG_WRITE_CRC | FLAG_READ_CRC | FLAG_VERIFY_CRC)
, timeout_(TIMEOUT)
, timeStarted_(0)
, verbose_(false)
, workerType_(WorkerType::Normal)
{
thread_ = std::thread(&Device::threadLoop, this);
#ifdef __linux__
// try to set the highest priority for this thread (requires root privs)
struct sched_param sp;
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (sp.sched_priority == -1) {
myerr << "sched_get_priority_max: " << std::string(strerror(errno));
} else {
int res = pthread_setschedparam(thread_.native_handle(), SCHED_FIFO, &sp);
if (res)
myerr << "pthread_setschedparam: error " << std::to_string(res);
}
#endif
thread_.detach();
}
Device::~Device() {
workerType_ = WorkerType::Dead;
}
void Device::setFlags(int flags) {
flags_ = flags;
@ -27,6 +54,10 @@ int Device::getFlags() const {
return flags_;
}
void Device::setWorkerType(WorkerType wt) {
workerType_ = wt;
}
void Device::setVerbose(bool verbose) {
verbose_ = verbose;
}
@ -50,10 +81,130 @@ u64 Device::getTimeLeft() const {
return timeout_ - elapsed;
}
size_t Device::enqueue(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize) {
if (verbose_)
mylog << "waiting to accept new task...";
mutex_.lock();
shio_.resetWith(inbuf, inbufSize, outbuf, outbufSize);
mutex_.unlock();
cv_.notify_all();
if (verbose_)
mylog << "notify the worker thread";
UniqueLock lock(mutex_);
cv_.wait(lock, [this]{
return shio_.state == SharedIOBufferState::Done;
});
if (verbose_)
mylog << "worker thread done it's job";
switch (shio_.errorType) {
case ErrorType::DeviceError: throw DeviceError(shio_.errorMessage);
case ErrorType::TimeoutError: throw TimeoutError(shio_.errorMessage);
case ErrorType::InvalidDataError: throw InvalidDataError(shio_.errorMessage);
case ErrorType::OverflowError: throw OverflowError(shio_.errorMessage);
default: break;
}
return shio_.dataSize;
}
// ----------------------------------------
// all code below runs in a separate thread
// ----------------------------------------
void Device::threadLoop() {
while (workerType_ != Dead) {
if (verbose_)
mylog << "waiting for new task...";
// wait for new task
UniqueLock lock(mutex_);
auto pred = [this]{
return shio_.state == SharedIOBufferState::Ready;
};
if (workerType_ == OneShot) {
cv_.wait(lock, pred);
} else {
cv_.wait_for(lock, 1s, pred);
if (!pred())
continue;
};
if (verbose_)
mylog << "got something";
shio_.state = SharedIOBufferState::InProgress;
try {
shio_.setResult(run(shio_.inputBuffer,
shio_.inputBufferSize,
shio_.outputBuffer,
shio_.outputBufferSize));
}
catch (DeviceError& e) {
shio_.setResult(ErrorType::DeviceError, e.what());
}
catch (TimeoutError& e) {
shio_.setResult(ErrorType::TimeoutError, e.what());
}
catch (InvalidDataError& e) {
shio_.setResult(ErrorType::InvalidDataError, e.what());
}
catch (OverflowError& e) {
shio_.setResult(ErrorType::OverflowError, e.what());
}
if (verbose_)
mylog << "unlocking";
lock.unlock();
cv_.notify_all();
if (workerType_ == OneShot)
break;
}
}
size_t Device::run(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize) {
timeStarted_ = timestamp();
send(inbuf, inbufSize);
// ------------------------------
// add CRC and send to the device
// ------------------------------
size_t dataLen;
std::shared_ptr<u8> data;
if ((flags_ & FLAG_WRITE_CRC) == FLAG_WRITE_CRC) {
const CRC crc = crc_calculate(inbuf, inbufSize);
dataLen = inbufSize + sizeof(u16) + 1;
data = std::unique_ptr<u8>(new u8[dataLen]);
crc_write(crc, &data.get()[inbufSize]);
} else {
dataLen = inbufSize + 1;
data = std::unique_ptr<u8>(new u8[dataLen]);
}
u8* dataPtr = data.get();
memcpy((void*)dataPtr, inbuf, inbufSize);
dataPtr[dataLen - 1] = '\r';
if (verbose_) {
myerr << "writing " << dataLen << (dataLen > 1 ? " bytes" : " byte");
std::cerr << hexdump(dataPtr, dataLen);
}
writeAll(dataPtr, dataLen);
// -----------------
// check for timeout
// -----------------
if (!getTimeLeft()) {
// FIXME
@ -63,37 +214,42 @@ size_t Device::run(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufS
throw TimeoutError("sending already took " + std::to_string(getElapsedTime()) + " ms");
}
return recv(outbuf, outbufSize);
}
// ------------------------------
// read from device and check CRC
// ------------------------------
void Device::send(const u8* buf, size_t bufSize) {
size_t dataLen;
std::shared_ptr<u8> data;
if ((flags_ & FLAG_WRITE_CRC) == FLAG_WRITE_CRC) {
const CRC crc = crc_calculate(buf, bufSize);
dataLen = bufSize + sizeof(u16) + 1;
data = std::unique_ptr<u8>(new u8[dataLen]);
crc_write(crc, &data.get()[bufSize]);
} else {
dataLen = bufSize + 1;
data = std::unique_ptr<u8>(new u8[dataLen]);
}
u8* dataPtr = data.get();
memcpy((void*)dataPtr, buf, bufSize);
dataPtr[dataLen - 1] = '\r';
size_t bytesRead = readAll(outbuf, outbufSize);
if (verbose_) {
myerr << "writing " << dataLen << (dataLen > 1 ? " bytes" : " byte");
std::cerr << hexdump(dataPtr, dataLen);
myerr << "got " << bytesRead << (bytesRead > 1 ? " bytes" : " byte");
std::cerr << hexdump(outbuf, bytesRead);
}
writeLoop(dataPtr, dataLen);
bool crcNeeded = (flags_ & FLAG_READ_CRC) == FLAG_READ_CRC;
size_t minSize = crcNeeded ? sizeof(u16) + 1 : 1;
if (bytesRead < minSize)
throw InvalidDataError("response is too small");
const size_t dataSize = bytesRead - minSize;
if (crcNeeded) {
const CRC crcActual = crc_read(&outbuf[dataSize]);
const CRC crcExpected = crc_calculate(outbuf, dataSize);
if ((flags_ & FLAG_VERIFY_CRC) == FLAG_VERIFY_CRC && crcActual == crcExpected)
return dataSize;
std::ostringstream error;
error << std::hex;
error << "crc is invalid: expected 0x" << crcExpected << ", got 0x" << crcActual;
throw InvalidDataError(error.str());
}
return dataSize;
}
void Device::writeLoop(const u8* data, size_t dataSize) {
void Device::writeAll(const u8* data, size_t dataSize) {
int bytesLeft = static_cast<int>(dataSize);
while (true) {
@ -112,42 +268,7 @@ void Device::writeLoop(const u8* data, size_t dataSize) {
}
}
size_t Device::recv(u8* buf, size_t bufSize) {
size_t bytesRead = readLoop(buf, bufSize);
if (verbose_) {
myerr << "got " << bytesRead << (bytesRead > 1 ? " bytes" : " byte");
std::cerr << hexdump(buf, bytesRead);
}
bool crcNeeded = (flags_ & FLAG_READ_CRC) == FLAG_READ_CRC;
size_t minSize = crcNeeded ? sizeof(u16) + 1 : 1;
if (bytesRead < minSize)
throw InvalidDataError("response is too small");
const size_t dataSize = bytesRead - minSize;
if (crcNeeded) {
const CRC crcActual = crc_read(&buf[dataSize]);
const CRC crcExpected = crc_calculate(buf, dataSize);
// buf[dataSize] = 0;
if ((flags_ & FLAG_VERIFY_CRC) == FLAG_VERIFY_CRC && crcActual == crcExpected)
return dataSize;
std::ostringstream error;
error << std::hex;
error << "crc is invalid: expected 0x" << crcExpected << ", got 0x" << crcActual;
throw InvalidDataError(error.str());
}
// buf[dataSize] = 0;
return dataSize;
}
size_t Device::readLoop(u8 *buf, size_t bufSize) {
size_t Device::readAll(u8 *buf, size_t bufSize) {
size_t size = 0;
while(true) {
@ -170,7 +291,7 @@ size_t Device::readLoop(u8 *buf, size_t bufSize) {
throw TimeoutError("data reading already took " + std::to_string(getElapsedTime()) + " ms");
if (bufSize <= 0)
throw std::overflow_error("input buffer is not large enough");
throw OverflowError("input buffer is not large enough");
}
}

View File

@ -5,10 +5,15 @@
#include <string>
#include <memory>
#include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <hidapi/hidapi.h>
#include <libserialport.h>
#include "../numeric_types.h"
#include "exceptions.h"
#include "../types.h"
namespace voltronic {
@ -18,10 +23,40 @@ enum {
FLAG_VERIFY_CRC = 4,
};
enum class SharedIOBufferState {
Ready,
InProgress,
Done,
};
/**
* Common device
*/
struct SharedIOBuffer {
// helper methods
void resetWith(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize);
void setResult(size_t dataSize);
void setResult(ErrorType type, std::string message);
// input
const u8* inputBuffer;
u8* outputBuffer;
size_t inputBufferSize;
size_t outputBufferSize;
// output
SharedIOBufferState state = SharedIOBufferState::Done;
size_t dataSize;
ErrorType errorType = ErrorType::None;
std::string errorMessage;
};
enum WorkerType {
OneShot,
Normal,
Dead,
};
// -------------
// Common device
// -------------
class Device {
protected:
@ -30,11 +65,15 @@ protected:
u64 timeStarted_;
bool verbose_;
void send(const u8* buf, size_t bufSize);
size_t recv(u8* buf, size_t bufSize);
SharedIOBuffer shio_;
std::thread thread_;
// std::mutex enqueueMutex_;
std::mutex mutex_;
std::condition_variable cv_;
WorkerType workerType_;
void writeLoop(const u8* data, size_t dataSize);
size_t readLoop(u8* buf, size_t bufSize);
void writeAll(const u8* data, size_t dataSize);
size_t readAll(u8* buf, size_t bufSize);
u64 getElapsedTime() const;
u64 getTimeLeft() const;
@ -43,23 +82,26 @@ public:
static const u64 TIMEOUT = 1000;
Device();
virtual size_t read(u8* buf, size_t bufSize) = 0;
virtual size_t write(const u8* data, size_t dataSize) = 0;
void setTimeout(u64 timeout);
size_t run(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize);
~Device();
void setFlags(int flags);
int getFlags() const;
void setWorkerType(WorkerType wt);
void setVerbose(bool verbose);
void setTimeout(u64 timeout);
size_t enqueue(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize);
size_t run(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize);
void threadLoop();
virtual size_t read(u8* buf, size_t bufSize) = 0;
virtual size_t write(const u8* data, size_t dataSize) = 0;
};
/**
* USB device
*/
// ----------
// USB device
// ----------
class USBDevice : public Device {
private:
@ -72,19 +114,16 @@ public:
static u16 GET_HID_REPORT_SIZE(size_t size);
USBDevice(u16 vendorId, u16 productId);
explicit USBDevice(const std::string& path);
~USBDevice();
static inline void init();
size_t read(u8* buf, size_t bufSize) override;
size_t write(const u8* data, size_t dataSize) override;
};
/**
* Serial device
*/
// -------------
// Serial device
// -------------
typedef unsigned SerialBaudRate;
@ -152,7 +191,10 @@ public:
explicit SerialPortConfiguration(SerialDevice& device);
~SerialPortConfiguration();
void setConfiguration(SerialBaudRate baudRate, SerialDataBits dataBits, SerialStopBits stopBits, SerialParity parity);
void setConfiguration(SerialBaudRate baudRate,
SerialDataBits dataBits,
SerialStopBits stopBits,
SerialParity parity);
};
bool is_serial_baud_rate_valid(SerialBaudRate baudRate);
@ -161,9 +203,9 @@ bool is_serial_stop_bits_valid(SerialStopBits stopBits);
bool is_serial_parity_valid(SerialParity parity);
/**
* Pseudo device
*/
// -------------
// Pseudo device
// -------------
class PseudoDevice : public Device {
public:

View File

@ -7,6 +7,10 @@
namespace voltronic {
// ----------
// exceptions
// ----------
class DeviceError : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
@ -22,6 +26,24 @@ public:
using std::runtime_error::runtime_error;
};
class OverflowError : public std::overflow_error {
public:
using std::overflow_error::overflow_error;
};
// ---------------
// exception types
// ---------------
enum class ErrorType {
None = 0,
DeviceError,
TimeoutError,
InvalidDataError,
OverflowError,
};
}
#endif //INVERTER_TOOLS_VOLTRONIC_EXCEPTIONS_H

View File

@ -8,6 +8,7 @@
#include "crc.h"
#include "hexdump/hexdump.h"
#include "../logging.h"
#include "exceptions.h"
namespace voltronic {
@ -26,7 +27,7 @@ static const char* response = "^D1060000,000,2300,500,0115,0018,002,500,000,000,
// set response
//static const char* response = "^1";
// TODO: maybe move size and crc stuff to readLoop()?
// TODO: maybe move size and crc stuff to readAll()?
size_t PseudoDevice::read(u8* buf, size_t bufSize) {
size_t pseudoResponseSize = strlen(response);
@ -37,7 +38,7 @@ size_t PseudoDevice::read(u8* buf, size_t bufSize) {
if (responseSize + 1 > bufSize) {
std::ostringstream error;
error << "buffer is not large enough (" << (responseSize + 1) << " > " << bufSize << ")";
throw std::overflow_error(error.str());
throw OverflowError(error.str());
}
memcpy(buf, response, responseSize);

View File

@ -0,0 +1,38 @@
// SPDX-License-Identifier: BSD-3-Clause
#include "device.h"
namespace voltronic {
void SharedIOBuffer::resetWith(const u8* inbuf,
size_t inbufSize,
u8* outbuf,
size_t outbufSize) {
// set input
inputBuffer = inbuf;
inputBufferSize = inbufSize;
outputBuffer = outbuf;
outputBufferSize = outbufSize;
// clean output
errorType = ErrorType::None;
errorMessage.erase();
dataSize = 0;
// mark as ready
state = SharedIOBufferState::Ready;
}
void SharedIOBuffer::setResult(ErrorType type, std::string message) {
errorType = type;
errorMessage = std::move(message);
state = SharedIOBufferState::Done;
}
void SharedIOBuffer::setResult(size_t _dataSize) {
dataSize = _dataSize;
state = SharedIOBufferState::Done;
}
}

View File

@ -3,7 +3,7 @@
#ifndef INVERTER_TOOLS_VOLTRONIC_TIME_H
#define INVERTER_TOOLS_VOLTRONIC_TIME_H
#include "../numeric_types.h"
#include "../types.h"
namespace voltronic {

View File

@ -12,24 +12,14 @@
namespace voltronic {
USBDevice::USBDevice(u16 vendorId, u16 productId) {
init();
if (hid_init() != 0)
throw DeviceError("hidapi initialization failure");
device_ = hid_open(vendorId, productId, nullptr);
if (!device_)
throw DeviceError("failed to create hidapi device");
}
USBDevice::USBDevice(const std::string& path) {
init();
device_ = hid_open_path(path.c_str());
if (!device_)
throw DeviceError("failed to create hidapi device");
}
void USBDevice::init() {
if (hid_init() != 0)
throw DeviceError("hidapi initialization failure");
}
USBDevice::~USBDevice() {
if (device_)
hid_close(device_);