// SPDX-License-Identifier: BSD-3-Clause #include #include #include #include #include #include #include "client.h" #include "types.h" #include "defines.h" #include "../protocol/exceptions.h" #include "response.h" #include "../voltronic/crc.h" #define MKRESPONSE(type) std::shared_ptr(new response_type::type(raw, rawSize)) #define RESPONSE_CASE(type) \ case CommandType::Get ## type: \ response = MKRESPONSE(type); \ break; \ namespace p18 { std::shared_ptr Client::execute(int commandType, std::vector& arguments) { auto type = static_cast(commandType); std::ostringstream buf; buf << std::setfill('0'); bool isSetCommand = commandType >= 100; auto pos = raw_commands.find(type); if (pos == raw_commands.end()) throw std::runtime_error("packedCommand " + std::to_string(commandType) + " not found"); std::string packedCommand = pos->second; std::string packedArguments = packArguments(type, arguments); size_t len = sizeof(voltronic::CRC) + 1 + packedCommand.size() + packedArguments.size(); buf << "^"; buf << (isSetCommand ? "S" : "P"); buf << std::setw(3) << len; buf << packedCommand; buf << packedArguments; std::string packed = buf.str(); auto result = runOnDevice(packed); std::shared_ptr response; const auto raw = result.first; const auto rawSize = result.second; switch (type) { RESPONSE_CASE(ProtocolID) RESPONSE_CASE(CurrentTime) RESPONSE_CASE(TotalGenerated) RESPONSE_CASE(YearGenerated) RESPONSE_CASE(MonthGenerated) RESPONSE_CASE(DayGenerated) RESPONSE_CASE(SeriesNumber) RESPONSE_CASE(CPUVersion) RESPONSE_CASE(RatedInformation) RESPONSE_CASE(GeneralStatus) RESPONSE_CASE(WorkingMode) RESPONSE_CASE(FaultsAndWarnings) RESPONSE_CASE(FlagsAndStatuses) RESPONSE_CASE(Defaults) RESPONSE_CASE(AllowedChargingCurrents) RESPONSE_CASE(AllowedACChargingCurrents) RESPONSE_CASE(ParallelRatedInformation) RESPONSE_CASE(ParallelGeneralStatus) RESPONSE_CASE(ACChargingTimeBucket) RESPONSE_CASE(ACLoadsSupplyTimeBucket) case CommandType::SetLoads: case CommandType::SetFlag: case CommandType::SetDefaults: case CommandType::SetBatteryMaxChargingCurrent: case CommandType::SetBatteryMaxACChargingCurrent: case CommandType::SetACOutputFreq: case CommandType::SetBatteryMaxChargingVoltage: case CommandType::SetACOutputRatedVoltage: case CommandType::SetOutputSourcePriority: case CommandType::SetBatteryChargingThresholds: case CommandType::SetChargingSourcePriority: case CommandType::SetSolarPowerPriority: case CommandType::SetACInputVoltageRange: case CommandType::SetBatteryType: case CommandType::SetOutputModel: case CommandType::SetBatteryCutOffVoltage: case CommandType::SetSolarConfig: case CommandType::ClearGenerated: case CommandType::SetDateTime: case CommandType::SetACChargingTimeBucket: case CommandType::SetACLoadsSupplyTimeBucket: response = MKRESPONSE(SetResponse); break; } if (!response->validate()) throw protocol::InvalidResponseError("validate() failed"); response->unpack(); return std::move(response); } std::string Client::packArguments(p18::CommandType commandType, std::vector& arguments) { std::ostringstream buf; buf << std::setfill('0'); switch (commandType) { case CommandType::GetYearGenerated: case CommandType::SetOutputSourcePriority: case CommandType::SetSolarPowerPriority: case CommandType::SetACInputVoltageRange: case CommandType::SetBatteryType: case CommandType::SetLoads: buf << arguments[0]; break; case CommandType::GetMonthGenerated: case CommandType::GetDayGenerated: buf << arguments[0]; for (int i = 1; i <= (commandType == CommandType::GetMonthGenerated ? 1 : 2); i++) buf << std::setw(2) << std::stoi(arguments[i]); break; case CommandType::GetParallelGeneralStatus: case CommandType::GetParallelRatedInformation: buf << std::stoi(arguments[0]); break; case CommandType::SetFlag: buf << (arguments[1] == "1" ? "E" : "D"); buf << arguments[0]; break; case CommandType::SetBatteryMaxChargingCurrent: case CommandType::SetBatteryMaxACChargingCurrent: buf << arguments[0] << ","; buf << std::setw(3) << std::stoi(arguments[1]); break; case CommandType::SetACOutputFreq: buf << std::setw(2) << std::stoi(arguments[0]); break; case CommandType::SetBatteryMaxChargingVoltage: case CommandType::SetBatteryChargingThresholds: { for (int i = 0; i < 2; i++) { double val = std::stod(arguments[i]); buf << std::setw(3) << (int)round(val*10); if (i == 0) buf << ","; } break; } case CommandType::SetACOutputRatedVoltage: { buf << std::setw(4) << (std::stoi(arguments[0])*10); break; } case CommandType::SetChargingSourcePriority: case CommandType::SetOutputModel: buf << arguments[0] << "," << arguments[1]; break; case CommandType::SetBatteryCutOffVoltage: { double v = std::stod(arguments[0]); buf << std::setw(3) << ((int)round(v*10)); break; } case CommandType::SetSolarConfig: { size_t len = arguments[0].size(); buf << std::setw(2) << len << arguments[0]; if (len < 20) { for (int i = 0; i < 20-len; i++) buf << "0"; } break; } case CommandType::SetDateTime: { for (int i = 0; i < 6; i++) { int val = std::stoi(arguments[i]); if (i == 0) val -= 2000; buf << std::setw(2) << val; } break; } case CommandType::SetACChargingTimeBucket: case CommandType::SetACLoadsSupplyTimeBucket: for (int i = 0; i < 4; i++) { buf << std::setw(2) << std::stoi(arguments[i]); if (i == 1) buf << ","; } break; default: break; } return buf.str(); } }