aboutsummaryrefslogtreecommitdiff
path: root/src/p18/client.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/p18/client.cc')
-rw-r--r--src/p18/client.cc234
1 files changed, 234 insertions, 0 deletions
diff --git a/src/p18/client.cc b/src/p18/client.cc
new file mode 100644
index 0000000..9baae1a
--- /dev/null
+++ b/src/p18/client.cc
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <memory>
+#include <utility>
+#include <sstream>
+#include <iomanip>
+#include <cmath>
+#include <stdexcept>
+
+#include "client.h"
+#include "types.h"
+#include "defines.h"
+#include "exceptions.h"
+#include "response.h"
+#include "../voltronic/crc.h"
+
+#define MKRESPONSE(type) std::shared_ptr<response_type::BaseResponse>(new response_type::type(raw, rawSize))
+
+#define RESPONSE_CASE(type) \
+ case CommandType::Get ## type: \
+ response = MKRESPONSE(type); \
+ break; \
+
+
+namespace p18 {
+
+void Client::setDevice(std::shared_ptr<voltronic::Device> device) {
+ device_ = std::move(device);
+}
+
+std::shared_ptr<response_type::BaseResponse> Client::execute(p18::CommandType commandType, std::vector<std::string>& arguments) {
+ std::ostringstream buf;
+ buf << std::setfill('0');
+
+ int iCommandType = static_cast<int>(commandType);
+ bool isSetCommand = iCommandType >= 100;
+
+ auto pos = raw_commands.find(commandType);
+ if (pos == raw_commands.end())
+ throw std::runtime_error("packedCommand " + std::to_string(iCommandType) + " not found");
+
+ std::string packedCommand = pos->second;
+ std::string packedArguments = packArguments(commandType, arguments);
+
+ size_t len = sizeof(voltronic::CRC) + 1 + packedCommand.size() + packedArguments.size();
+
+ buf << "^";
+ buf << (isSetCommand ? "S" : "P");
+ buf << std::setw(3) << len;
+ buf << packedCommand;
+ buf << packedArguments;
+
+ std::string packed = buf.str();
+
+ auto result = runOnDevice(packed);
+ std::shared_ptr<response_type::BaseResponse> response;
+
+ const auto raw = result.first;
+ const auto rawSize = result.second;
+
+ switch (commandType) {
+ RESPONSE_CASE(ProtocolID)
+ RESPONSE_CASE(CurrentTime)
+ RESPONSE_CASE(TotalGenerated)
+ RESPONSE_CASE(YearGenerated)
+ RESPONSE_CASE(MonthGenerated)
+ RESPONSE_CASE(DayGenerated)
+ RESPONSE_CASE(SeriesNumber)
+ RESPONSE_CASE(CPUVersion)
+ RESPONSE_CASE(RatedInformation)
+ RESPONSE_CASE(GeneralStatus)
+ RESPONSE_CASE(WorkingMode)
+ RESPONSE_CASE(FaultsAndWarnings)
+ RESPONSE_CASE(FlagsAndStatuses)
+ RESPONSE_CASE(Defaults)
+ RESPONSE_CASE(AllowedChargingCurrents)
+ RESPONSE_CASE(AllowedACChargingCurrents)
+ RESPONSE_CASE(ParallelRatedInformation)
+ RESPONSE_CASE(ParallelGeneralStatus)
+ RESPONSE_CASE(ACChargingTimeBucket)
+ RESPONSE_CASE(ACLoadsSupplyTimeBucket)
+
+ case CommandType::SetLoads:
+ case CommandType::SetFlag:
+ case CommandType::SetDefaults:
+ case CommandType::SetBatteryMaxChargingCurrent:
+ case CommandType::SetBatteryMaxACChargingCurrent:
+ case CommandType::SetACOutputFreq:
+ case CommandType::SetBatteryMaxChargingVoltage:
+ case CommandType::SetACOutputRatedVoltage:
+ case CommandType::SetOutputSourcePriority:
+ case CommandType::SetBatteryChargingThresholds:
+ case CommandType::SetChargingSourcePriority:
+ case CommandType::SetSolarPowerPriority:
+ case CommandType::SetACInputVoltageRange:
+ case CommandType::SetBatteryType:
+ case CommandType::SetOutputModel:
+ case CommandType::SetBatteryCutOffVoltage:
+ case CommandType::SetSolarConfig:
+ case CommandType::ClearGenerated:
+ case CommandType::SetDateTime:
+ case CommandType::SetACChargingTimeBucket:
+ case CommandType::SetACLoadsSupplyTimeBucket:
+ response = MKRESPONSE(SetResponse);
+ break;
+ }
+
+ try {
+ if (!response->validate())
+ throw InvalidResponseError("validate() failed");
+
+ response->unpack();
+ } catch (InvalidResponseError& e) {
+ return std::make_unique<response_type::ErrorResponse>(e.what());
+ }
+
+ return std::move(response);
+}
+
+std::pair<std::shared_ptr<char>, size_t> Client::runOnDevice(std::string& raw) {
+ size_t bufSize = 256;
+ std::shared_ptr<char> buf(new char[bufSize]);
+ size_t responseSize = device_->run(
+ (const u8*)raw.c_str(), raw.size(),
+ (u8*)buf.get(), bufSize);
+
+ return std::pair<std::shared_ptr<char>, size_t>(buf, responseSize);
+}
+
+std::string Client::packArguments(p18::CommandType commandType, std::vector<std::string>& arguments) {
+ std::ostringstream buf;
+ buf << std::setfill('0');
+
+ switch (commandType) {
+ case CommandType::GetYearGenerated:
+ case CommandType::SetOutputSourcePriority:
+ case CommandType::SetSolarPowerPriority:
+ case CommandType::SetACInputVoltageRange:
+ case CommandType::SetBatteryType:
+ case CommandType::SetLoads:
+ buf << arguments[0];
+ break;
+
+ case CommandType::GetMonthGenerated:
+ case CommandType::GetDayGenerated:
+ buf << arguments[0];
+ for (int i = 1; i <= (commandType == CommandType::GetMonthGenerated ? 1 : 2); i++)
+ buf << std::setw(2) << std::stoi(arguments[i]);
+ break;
+
+ case CommandType::GetParallelGeneralStatus:
+ case CommandType::GetParallelRatedInformation:
+ buf << std::stoi(arguments[0]);
+ break;
+
+ case CommandType::SetFlag:
+ buf << (arguments[1] == "1" ? "E" : "D");
+ buf << arguments[0];
+ break;
+
+ case CommandType::SetBatteryMaxChargingCurrent:
+ case CommandType::SetBatteryMaxACChargingCurrent:
+ buf << arguments[0] << ",";
+ buf << std::setw(3) << std::stoi(arguments[1]);
+ break;
+
+ case CommandType::SetACOutputFreq:
+ buf << std::setw(2) << std::stoi(arguments[0]);
+ break;
+
+ case CommandType::SetBatteryMaxChargingVoltage:
+ case CommandType::SetBatteryChargingThresholds: {
+ for (int i = 0; i < 2; i++) {
+ double val = std::stod(arguments[i]);
+ buf << std::setw(3) << (int)round(val*10);
+ if (i == 0)
+ buf << ",";
+ }
+ break;
+ }
+
+ case CommandType::SetACOutputRatedVoltage: {
+ buf << std::setw(4) << (std::stoi(arguments[0])*10);
+ break;
+ }
+
+ case CommandType::SetChargingSourcePriority:
+ case CommandType::SetOutputModel:
+ buf << arguments[0] << "," << arguments[1];
+ break;
+
+ case CommandType::SetBatteryCutOffVoltage: {
+ double v = std::stod(arguments[0]);
+ buf << std::setw(3) << ((int)round(v*10));
+ break;
+ }
+
+ case CommandType::SetSolarConfig: {
+ size_t len = arguments[0].size();
+ buf << std::setw(2) << len << arguments[0];
+ if (len < 20) {
+ for (int i = 0; i < 20-len; i++)
+ buf << "0";
+ }
+ break;
+ }
+
+ case CommandType::SetDateTime: {
+ for (int i = 0; i < 6; i++) {
+ int val = std::stoi(arguments[0]);
+ if (i == 0)
+ val -= 2000;
+ buf << std::setw(2) << val;
+ }
+ break;
+ }
+
+ case CommandType::SetACChargingTimeBucket:
+ case CommandType::SetACLoadsSupplyTimeBucket:
+ for (int i = 0; i < 4; i++) {
+ buf << std::setw(2) << std::stoi(arguments[i]);
+ if (i == 1)
+ buf << ",";
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return buf.str();
+}
+
+} \ No newline at end of file