// 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; } }