From 3aed59276c2b7275603f6da18377b77718948667 Mon Sep 17 00:00:00 2001 From: Evgeny Zinoviev Date: Mon, 7 Jun 2021 01:40:32 +0300 Subject: wip --- src/protocol_18/input.cc | 341 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 src/protocol_18/input.cc (limited to 'src/protocol_18/input.cc') diff --git a/src/protocol_18/input.cc b/src/protocol_18/input.cc new file mode 100644 index 0000000..5647ee6 --- /dev/null +++ b/src/protocol_18/input.cc @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include "input.h" +#include "defines.h" +#include "functions.h" +#include "../util.h" + +namespace p18 { + +using namespace protocol; + +const std::map client_commands = { + {"get-protocol-id", CommandType::GetProtocolID}, + {"get-date-time", CommandType::GetCurrentTime}, + {"get-total-generated", CommandType::GetTotalGenerated}, + {"get-year-generated", CommandType::GetYearGenerated}, + {"get-month-generated", CommandType::GetMonthGenerated}, + {"get-day-generated", CommandType::GetDayGenerated}, + {"get-series-number", CommandType::GetSeriesNumber}, + {"get-cpu-version", CommandType::GetCPUVersion}, + {"get-rated", CommandType::GetRatedInformation}, + {"get-status", CommandType::GetGeneralStatus}, + {"get-mode", CommandType::GetWorkingMode}, + {"get-errors", CommandType::GetFaultsAndWarnings}, + {"get-flags", CommandType::GetFlagsAndStatuses}, + {"get-rated-defaults", CommandType::GetDefaults}, + {"get-allowed-charging-currents", CommandType::GetAllowedChargingCurrents}, + {"get-allowed-ac-charging-currents", CommandType::GetAllowedACChargingCurrents}, + {"get-p-rated", CommandType::GetParallelRatedInformation}, + {"get-p-status", CommandType::GetParallelGeneralStatus}, + {"get-ac-charging-time", CommandType::GetACChargingTimeBucket}, + {"get-ac-loads-supply-time", CommandType::GetACLoadsSupplyTimeBucket}, + {"set-loads-supply", CommandType::SetLoads}, + {"set-flag", CommandType::SetFlag}, + {"set-rated-defaults", CommandType::SetDefaults}, + {"set-max-charging-current", CommandType::SetBatteryMaxChargingCurrent}, + {"set-max-ac-charging-current", CommandType::SetBatteryMaxACChargingCurrent}, + {"set-ac-output-freq", CommandType::SetACOutputFreq}, + {"set-max-charging-voltage", CommandType::SetBatteryMaxChargingVoltage}, + {"set-ac-output-voltage", CommandType::SetACOutputRatedVoltage}, + {"set-output-source-priority", CommandType::SetOutputSourcePriority}, + {"set-charging-thresholds", CommandType::SetBatteryChargingThresholds}, /* Battery re-charging and re-discharging voltage when utility is available */ + {"set-charging-source-priority", CommandType::SetChargingSourcePriority}, + {"set-solar-power-priority", CommandType::SetSolarPowerPriority}, + {"set-ac-input-voltage-range", CommandType::SetACInputVoltageRange}, + {"set-battery-type", CommandType::SetBatteryType}, + {"set-output-model", CommandType::SetOutputModel}, + {"set-battery-cut-off-voltage", CommandType::SetBatteryCutOffVoltage}, + {"set-solar-configuration", CommandType::SetSolarConfig}, + {"clear-generated-data", CommandType::ClearGenerated}, + {"set-date-time", CommandType::SetDateTime}, + {"set-ac-charging-time", CommandType::SetACChargingTimeBucket}, + {"set-ac-loads-supply-time", CommandType::SetACLoadsSupplyTimeBucket}, +}; + +CommandType validate_input(std::string& command, + std::vector& arguments, + void* input) { + auto it = client_commands.find(command); + if (it == client_commands.end()) + throw std::invalid_argument("invalid command"); + + auto commandType = it->second; + switch (commandType) { + case CommandType::GetYearGenerated: + GET_ARGS(1); + validate_date_args(&arguments[0], nullptr, nullptr); + break; + + case CommandType::GetMonthGenerated: + GET_ARGS(2); + validate_date_args(&arguments[0], &arguments[1], nullptr); + break; + + case CommandType::GetDayGenerated: + GET_ARGS(3); + validate_date_args(&arguments[0], &arguments[1], &arguments[2]); + break; + + case CommandType::GetParallelRatedInformation: + case CommandType::GetParallelGeneralStatus: + GET_ARGS(1); + if (!is_numeric(arguments[0]) || arguments[0].size() > 1) + throw std::invalid_argument("invalid argument"); + break; + + case CommandType::SetLoads: { + GET_ARGS(1); + std::string &arg = arguments[0]; + if (arg != "0" && arg != "1") + throw std::invalid_argument("invalid argument, only 0 or 1 allowed"); + break; + } + + case CommandType::SetFlag: { + GET_ARGS(2); + + bool match_found = false; + for (auto const& item: flags) { + if (arguments[0] == item.flag) { + arguments[0] = item.letter; + match_found = true; + break; + } + } + + if (!match_found) + throw std::invalid_argument("invalid flag"); + + if (arguments[1] != "0" && arguments[1] != "1") + throw std::invalid_argument("invalid flag state, only 0 or 1 allowed"); + + break; + } + + case CommandType::SetBatteryMaxChargingCurrent: + case CommandType::SetBatteryMaxACChargingCurrent: { + GET_ARGS(2); + + auto id = static_cast(std::stoul(arguments[0])); + auto amps = static_cast(std::stoul(arguments[1])); + + if (!is_valid_parallel_id(id)) + throw std::invalid_argument("invalid id"); + + // 3 characters max + if (amps > 999) + throw std::invalid_argument("invalid amps"); + + break; + } + + case CommandType::SetACOutputFreq: { + GET_ARGS(1); + std::string &freq = arguments[0]; + if (freq != "50" && freq != "60") + throw std::invalid_argument("invalid frequency, only 50 or 60 allowed"); + break; + } + + case CommandType::SetBatteryMaxChargingVoltage: { + GET_ARGS(2); + + float cv = std::stof(arguments[0]); + float fv = std::stof(arguments[1]); + + if (cv < 48.0 || cv > 58.4) + throw std::invalid_argument("invalid CV"); + + if (fv < 48.0 || fv > 58.4) + throw std::invalid_argument("invalid FV"); + + break; + } + + case CommandType::SetACOutputRatedVoltage: { + GET_ARGS(1); + + auto v = static_cast(std::stoul(arguments[0])); + + bool matchFound = false; + for (const auto &item: ac_output_rated_voltages) { + if (v == item) { + matchFound = true; + break; + } + } + + if (!matchFound) + throw std::invalid_argument("invalid voltage"); + + break; + } + + case CommandType::SetOutputSourcePriority: { + GET_ARGS(1); + + std::array priorities({"SUB", "SBU"}); + + long index = index_of(priorities, arguments[0]); + if (index == -1) + throw std::invalid_argument("invalid argument"); + + arguments[0] = std::to_string(index); + break; + } + + case CommandType::SetBatteryChargingThresholds: { + GET_ARGS(2); + + float cv = std::stof(arguments[0]); + float dv = std::stof(arguments[1]); + + if (index_of(bat_ac_recharging_voltages_12v, cv) == -1 && + index_of(bat_ac_recharging_voltages_24v, cv) == -1 && + index_of(bat_ac_recharging_voltages_48v, cv) == -1) + throw std::invalid_argument("invalid CV"); + + if (index_of(bat_ac_redischarging_voltages_12v, dv) == -1 && + index_of(bat_ac_redischarging_voltages_24v, dv) == -1 && + index_of(bat_ac_redischarging_voltages_48v, dv) == -1) + throw std::invalid_argument("invalid DV"); + + break; + } + + case CommandType::SetChargingSourcePriority: { + GET_ARGS(2); + + auto id = static_cast(std::stoul(arguments[0])); + if (!is_valid_parallel_id(id)) + throw std::invalid_argument("invalid id"); + + std::array priorities({"SF", "SU", "S"}); + long index = index_of(priorities, arguments[1]); + if (index == -1) + throw std::invalid_argument("invalid argument"); + + arguments[1] = std::to_string(index); + break; + } + + case CommandType::SetSolarPowerPriority: { + GET_ARGS(1); + + std::array allowed({"BLU", "LBU"}); + long index = index_of(allowed, arguments[0]); + if (index == -1) + throw std::invalid_argument("invalid priority"); + + arguments[0] = std::to_string(index); + break; + } + + case CommandType::SetACInputVoltageRange: { + GET_ARGS(1); + std::array allowed({"APPLIANCE", "UPS"}); + long index = index_of(allowed, arguments[0]); + if (index == -1) + throw std::invalid_argument("invalid argument"); + arguments[0] = std::to_string(index); + break; + } + + case CommandType::SetBatteryType: { + GET_ARGS(1); + + std::array allowed({"AGM", "FLOODED", "USER"}); + long index = index_of(allowed, arguments[0]); + if (index == -1) + throw std::invalid_argument("invalid type"); + arguments[0] = std::to_string(index); + + break; + } + + case CommandType::SetOutputModel: { + GET_ARGS(2); + + auto id = static_cast(std::stoul(arguments[0])); + if (!is_valid_parallel_id(id)) + throw std::invalid_argument("invalid id"); + + std::array allowed({"SM", "P", "P1", "P2", "P3"}); + long index = index_of(allowed, arguments[1]); + if (index == -1) + throw std::invalid_argument("invalid model"); + arguments[1] = std::to_string(index); + + break; + } + + case CommandType::SetBatteryCutOffVoltage: { + GET_ARGS(1); + + float v = std::stof(arguments[0]); + if (v < 40.0 || v > 48.0) + throw std::invalid_argument("invalid voltage"); + + break; + } + + case CommandType::SetSolarConfig: { + GET_ARGS(1); + + if (!is_numeric(arguments[0]) || arguments[0].size() > 20) + throw std::invalid_argument("invalid argument"); + + break; + } + + case CommandType::SetDateTime: { + GET_ARGS(6); + + validate_date_args(&arguments[0], &arguments[1], &arguments[2]); + validate_time_args(&arguments[3], &arguments[4], &arguments[5]); + + break; + } + + case CommandType::SetACChargingTimeBucket: + case CommandType::SetACLoadsSupplyTimeBucket: { + GET_ARGS(2); + + std::vector start = split(arguments[0], ':'); + if (start.size() != 2) + throw std::invalid_argument("invalid start time"); + + std::vector end = split(arguments[1], ':'); + if (end.size() != 2) + throw std::invalid_argument("invalid end time"); + + auto startHour = static_cast(std::stoul(start[0])); + auto startMinute = static_cast(std::stoul(start[1])); + if (startHour > 23 || startMinute > 59) + throw std::invalid_argument("invalid start time"); + + auto endHour = static_cast(std::stoul(end[0])); + auto endMinute = static_cast(std::stoul(end[1])); + if (endHour > 23 || endMinute > 59) + throw std::invalid_argument("invalid end time"); + + arguments.clear(); + + arguments.emplace_back(std::to_string(startHour)); + arguments.emplace_back(std::to_string(startMinute)); + + arguments.emplace_back(std::to_string(endHour)); + arguments.emplace_back(std::to_string(endMinute)); + + break; + } + + default: + break; + } + + return commandType; +} + +} \ No newline at end of file -- cgit v1.2.3