X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/c00e81f9f50c4fe50ee32b02a689d68c9bc595b6..59fa9c93d72d1e065274dadc44a53595fd54648a:/projects/sgconf/tariffs.cpp diff --git a/projects/sgconf/tariffs.cpp b/projects/sgconf/tariffs.cpp index 3da1001a..23449bb6 100644 --- a/projects/sgconf/tariffs.cpp +++ b/projects/sgconf/tariffs.cpp @@ -2,17 +2,22 @@ #include "api_action.h" #include "options.h" +#include "makeproto.h" #include "config.h" +#include "utils.h" #include "stg/servconf.h" #include "stg/servconf_types.h" #include "stg/tariff_conf.h" -#include "stg/os_int.h" +#include "stg/common.h" #include +#include #include #include #include +#include +#include #include namespace @@ -25,34 +30,200 @@ if (level == 0) return dash ? std::string(level * 4 - 2, ' ') + "- " : std::string(level * 4, ' '); } -std::string PeriodToString(TARIFF::PERIOD period) +std::string ChangePolicyToString(STG::Tariff::ChangePolicy changePolicy) +{ +switch (changePolicy) + { + case STG::Tariff::ALLOW: return "allow"; + case STG::Tariff::TO_CHEAP: return "to_cheap"; + case STG::Tariff::TO_EXPENSIVE: return "to_expensive"; + case STG::Tariff::DENY: return "deny"; + } +return "unknown"; +} + +std::string PeriodToString(STG::Tariff::Period period) { switch (period) { - case TARIFF::DAY: + case STG::Tariff::DAY: return "daily"; - case TARIFF::MONTH: + case STG::Tariff::MONTH: return "monthly"; } return "unknown"; } -std::string TraffTypeToString(int traffType) +std::string TraffTypeToString(STG::Tariff::TraffType traffType) { switch (traffType) { - case TRAFF_UP: + case STG::Tariff::TRAFF_UP: return "upload"; - case TRAFF_DOWN: + case STG::Tariff::TRAFF_DOWN: return "download"; - case TRAFF_UP_DOWN: + case STG::Tariff::TRAFF_UP_DOWN: return "upload + download"; - case TRAFF_MAX: + case STG::Tariff::TRAFF_MAX: return "max(upload, download)"; } return "unknown"; } +void ConvPeriod(const std::string & value, std::optional & res) +{ +std::string lowered = ToLower(value); +if (lowered == "daily") + res = STG::Tariff::DAY; +else if (lowered == "monthly") + res = STG::Tariff::MONTH; +else + throw SGCONF::ACTION::ERROR("Period should be 'daily' or 'monthly'. Got: '" + value + "'"); +} + +void ConvChangePolicy(const std::string & value, std::optional & res) +{ +std::string lowered = ToLower(value); +if (lowered == "allow") + res = STG::Tariff::ALLOW; +else if (lowered == "to_cheap") + res = STG::Tariff::TO_CHEAP; +else if (lowered == "to_expensive") + res = STG::Tariff::TO_EXPENSIVE; +else if (lowered == "deny") + res = STG::Tariff::DENY; +else + throw SGCONF::ACTION::ERROR("Change policy should be 'allow', 'to_cheap', 'to_expensive' or 'deny'. Got: '" + value + "'"); +} + +void ConvChangePolicyTimeout(const std::string & value, std::optional & res) +{ +struct tm brokenTime; +if (stg_strptime(value.c_str(), "%Y-%m-%d %H:%M:%S", &brokenTime) == NULL) + throw SGCONF::ACTION::ERROR("Credit expiration should be in format 'YYYY-MM-DD HH:MM:SS'. Got: '" + value + "'"); +res = stg_timegm(&brokenTime); +} + +void ConvTraffType(const std::string & value, std::optional & res) +{ +std::string lowered = ToLower(value); +lowered.erase(std::remove(lowered.begin(), lowered.end(), ' '), lowered.end()); +if (lowered == "upload") + res = STG::Tariff::TRAFF_UP; +else if (lowered == "download") + res = STG::Tariff::TRAFF_DOWN; +else if (lowered == "upload+download") + res = STG::Tariff::TRAFF_UP_DOWN; +else if (lowered.substr(0, 3) == "max") + res = STG::Tariff::TRAFF_MAX; +else + throw SGCONF::ACTION::ERROR("Traff type should be 'upload', 'download', 'upload + download' or 'max'. Got: '" + value + "'"); +} + +STG::DirPriceDataOpt ConvTimeSpan(const std::string & value) +{ +size_t dashPos = value.find_first_of('-'); +if (dashPos == std::string::npos) + throw SGCONF::ACTION::ERROR("Time span should be in format 'hh:mm-hh:mm'. Got: '" + value + "'"); +size_t fromColon = value.find_first_of(':'); +if (fromColon == std::string::npos || fromColon > dashPos) + throw SGCONF::ACTION::ERROR("Time span should be in format 'hh:mm-hh:mm'. Got: '" + value + "'"); +size_t toColon = value.find_first_of(':', dashPos); +if (toColon == std::string::npos) + throw SGCONF::ACTION::ERROR("Time span should be in format 'hh:mm-hh:mm'. Got: '" + value + "'"); +STG::DirPriceDataOpt res; +res.hDay = FromString(value.substr(0, fromColon)); +if (res.hDay.value() < 0 || res.hDay.value() > 23) + throw SGCONF::ACTION::ERROR("Invalid 'from' hours. Got: '" + value.substr(0, fromColon) + "'"); +res.mDay = FromString(value.substr(fromColon + 1, dashPos - fromColon - 1)); +if (res.mDay.value() < 0 || res.mDay.value() > 59) + throw SGCONF::ACTION::ERROR("Invalid 'from' minutes. Got: '" + value.substr(fromColon + 1, dashPos - fromColon - 1) + "'"); +res.hNight = FromString(value.substr(dashPos + 1, toColon - dashPos - 1)); +if (res.hNight.value() < 0 || res.hNight.value() > 23) + throw SGCONF::ACTION::ERROR("Invalid 'to' hours. Got: '" + value.substr(dashPos + 1, toColon - dashPos - 1) + "'"); +res.mNight = FromString(value.substr(toColon + 1, value.length() - toColon)); +if (res.mNight.value() < 0 || res.mNight.value() > 59) + throw SGCONF::ACTION::ERROR("Invalid 'to' minutes. Got: '" + value.substr(toColon + 1, value.length() - toColon) + "'"); +return res; +} + +void splice(std::vector & lhs, const std::vector & rhs) +{ +for (size_t i = 0; i < lhs.size() && i < rhs.size(); ++i) + lhs[i].splice(rhs[i]); +} + +void ConvTimes(std::string value, std::vector & res) +{ +value.erase(std::remove(value.begin(), value.end(), ' '), value.end()); +splice(res, Split >(value, ',', ConvTimeSpan)); +} + +struct ConvPrice +{ + using MemPtr = std::optional (STG::DirPriceDataOpt::*); + ConvPrice(MemPtr before, MemPtr after) + : m_before(before), m_after(after) + {} + + STG::DirPriceDataOpt operator()(const std::string & value) + { + STG::DirPriceDataOpt res; + size_t slashPos = value.find_first_of('/'); + if (slashPos == std::string::npos) + { + double price = 0; + if (str2x(value, price) < 0) + throw SGCONF::ACTION::ERROR("Price should be a floating point number. Got: '" + value + "'"); + (res.*m_before) = (res.*m_after) = price; + res.noDiscount = true; + } + else + { + double price = 0; + if (str2x(value.substr(0, slashPos), price) < 0) + throw SGCONF::ACTION::ERROR("Price should be a floating point number. Got: '" + value.substr(0, slashPos) + "'"); + (res.*m_before) = price; + if (str2x(value.substr(slashPos + 1, value.length() - slashPos), price) < 0) + throw SGCONF::ACTION::ERROR("Price should be a floating point number. Got: '" + value.substr(slashPos + 1, value.length() - slashPos) + "'"); + (res.*m_after) = price; + res.noDiscount = false; + } + return res; + } + + MemPtr m_before; + MemPtr m_after; +}; + +void ConvDayPrices(std::string value, std::vector & res) +{ +value.erase(std::remove(value.begin(), value.end(), ' '), value.end()); +splice(res, Split >(value, ',', ConvPrice(&STG::DirPriceDataOpt::priceDayA, &STG::DirPriceDataOpt::priceDayB))); +} + +void ConvNightPrices(std::string value, std::vector & res) +{ +value.erase(std::remove(value.begin(), value.end(), ' '), value.end()); +splice(res, Split >(value, ',', ConvPrice(&STG::DirPriceDataOpt::priceNightA, &STG::DirPriceDataOpt::priceNightB))); +} + +STG::DirPriceDataOpt ConvThreshold(std::string value) +{ + STG::DirPriceDataOpt res; +double threshold = 0; +if (str2x(value, threshold) < 0) + throw SGCONF::ACTION::ERROR("Threshold should be a floating point value. Got: '" + value + "'"); +res.threshold = threshold; +return res; +} + +void ConvThresholds(std::string value, std::vector & res) +{ +value.erase(std::remove(value.begin(), value.end(), ' '), value.end()); +splice(res, Split >(value, ',', ConvThreshold)); +} + std::string TimeToString(int h, int m) { std::ostringstream stream; @@ -61,7 +232,7 @@ stream << (h < 10 ? "0" : "") << h << ":" return stream.str(); } -void PrintDirPriceData(size_t dir, const DIRPRICE_DATA & data, size_t level) +void PrintDirPriceData(size_t dir, const STG::DirPriceData & data, size_t level) { std::string night = TimeToString(data.hNight, data.mNight); std::string day = TimeToString(data.hDay, data.mDay); @@ -73,17 +244,19 @@ std::cout << Indent(level, true) << "dir: " << dir << "\n" << Indent(level) << "discount: " << (data.noDiscount ? "no" : "yes") << "\n"; // Attention! } -void PrintTariffConf(const TARIFF_CONF & conf, size_t level) +void PrintTariffConf(const STG::TariffConf & conf, size_t level) { std::cout << Indent(level, true) << "name: " << conf.name << "\n" << Indent(level) << "fee: " << conf.fee << "\n" << Indent(level) << "free mb: " << conf.free << "\n" << Indent(level) << "passive cost: " << conf.passiveCost << "\n" << Indent(level) << "traff type: " << TraffTypeToString(conf.traffType) << "\n" - << Indent(level) << "period: " << PeriodToString(conf.period) << "\n"; + << Indent(level) << "period: " << PeriodToString(conf.period) << "\n" + << Indent(level) << "change policy: " << ChangePolicyToString(conf.changePolicy) << "\n" + << Indent(level) << "change policy timeout: " << formatTime(conf.changePolicyTimeout) << "\n"; } -void PrintTariff(const STG::GET_TARIFF::INFO & info, size_t level = 0) +void PrintTariff(const STG::GetTariff::Info & info, size_t level = 0) { PrintTariffConf(info.tariffConf, level); std::cout << Indent(level) << "dir prices:\n"; @@ -95,13 +268,15 @@ std::vector GetTariffParams() { std::vector params; params.push_back(SGCONF::API_ACTION::PARAM("fee", "", "\t\ttariff fee")); -params.push_back(SGCONF::API_ACTION::PARAM("free", "", "\tprepaid traff")); +params.push_back(SGCONF::API_ACTION::PARAM("free", "", "\tprepaid traffic")); params.push_back(SGCONF::API_ACTION::PARAM("passive-cost", "", "\tpassive cost")); -params.push_back(SGCONF::API_ACTION::PARAM("traff-type", "", "\ttraff type (up, dow, up+down, max)")); +params.push_back(SGCONF::API_ACTION::PARAM("traff-type", "", "\ttraffic type (up, down, up+down, max)")); params.push_back(SGCONF::API_ACTION::PARAM("period", "", "\ttarification period (daily, monthly)")); +params.push_back(SGCONF::API_ACTION::PARAM("change-policy", "", "tariff change policy (allow, to_cheap, to_expensive, deny)")); +params.push_back(SGCONF::API_ACTION::PARAM("change-policy-timeout", "", "tariff change policy timeout")); params.push_back(SGCONF::API_ACTION::PARAM("times", "", "coma-separated day time-spans for each direction")); params.push_back(SGCONF::API_ACTION::PARAM("day-prices", "", "coma-separated day prices for each direction")); -params.push_back(SGCONF::API_ACTION::PARAM("night-prices", "", "coma-separated day prices for each direction")); +params.push_back(SGCONF::API_ACTION::PARAM("night-prices", "", "coma-separated night prices for each direction")); params.push_back(SGCONF::API_ACTION::PARAM("thresholds", "", "coma-separated thresholds for each direction")); return params; } @@ -120,7 +295,7 @@ std::cout << "Success.\n"; void GetTariffsCallback(bool result, const std::string & reason, - const std::vector & info, + const std::vector & info, void * /*data*/) { if (!result) @@ -135,7 +310,7 @@ for (size_t i = 0; i < info.size(); ++i) void GetTariffCallback(bool result, const std::string & reason, - const std::vector & info, + const std::vector & info, void * data) { assert(data != NULL && "Expecting pointer to std::string with the tariff's name."); @@ -154,54 +329,82 @@ bool GetTariffsFunction(const SGCONF::CONFIG & config, const std::string & /*arg*/, const std::map & /*options*/) { -STG::SERVCONF proto(config.server.data(), - config.port.data(), - config.userName.data(), - config.userPass.data()); -return proto.GetTariffs(GetTariffsCallback, NULL) == STG::st_ok; +return makeProto(config).GetTariffs(GetTariffsCallback, NULL) == STG::st_ok; } bool GetTariffFunction(const SGCONF::CONFIG & config, const std::string & arg, const std::map & /*options*/) { -STG::SERVCONF proto(config.server.data(), - config.port.data(), - config.userName.data(), - config.userPass.data()); // STG currently doesn't support . // So get a list of tariffs and filter it. 'data' param holds a pointer to 'name'. std::string name(arg); -return proto.GetTariffs(GetTariffCallback, &name) == STG::st_ok; +return makeProto(config).GetTariffs(GetTariffCallback, &name) == STG::st_ok; } bool DelTariffFunction(const SGCONF::CONFIG & config, const std::string & arg, const std::map & /*options*/) { -STG::SERVCONF proto(config.server.data(), - config.port.data(), - config.userName.data(), - config.userPass.data()); -return proto.DelTariff(arg, SimpleCallback, NULL) == STG::st_ok; +return makeProto(config).DelTariff(arg, SimpleCallback, NULL) == STG::st_ok; } bool AddTariffFunction(const SGCONF::CONFIG & config, const std::string & arg, - const std::map & /*options*/) + const std::map & options) { -// TODO -std::cerr << "Unimplemented.\n"; -return false; +STG::TariffDataOpt conf; +conf.tariffConf.name = arg; +SGCONF::MaybeSet(options, "fee", conf.tariffConf.fee); +SGCONF::MaybeSet(options, "free", conf.tariffConf.free); +SGCONF::MaybeSet(options, "passive-cost", conf.tariffConf.passiveCost); +SGCONF::MaybeSet(options, "traff-type", conf.tariffConf.traffType, ConvTraffType); +SGCONF::MaybeSet(options, "period", conf.tariffConf.period, ConvPeriod); +SGCONF::MaybeSet(options, "change-policy", conf.tariffConf.changePolicy, ConvChangePolicy); +SGCONF::MaybeSet(options, "change-policy-timeout", conf.tariffConf.changePolicyTimeout, ConvChangePolicyTimeout); +SGCONF::MaybeSet(options, "times", conf.dirPrice, ConvTimes); +SGCONF::MaybeSet(options, "day-prices", conf.dirPrice, ConvDayPrices); +SGCONF::MaybeSet(options, "night-prices", conf.dirPrice, ConvNightPrices); +SGCONF::MaybeSet(options, "thresholds", conf.dirPrice, ConvThresholds); +for (size_t i = 0; i < conf.dirPrice.size(); ++i) + { + if (conf.dirPrice[i].priceDayA || + conf.dirPrice[i].priceNightA || + conf.dirPrice[i].priceDayB || + conf.dirPrice[i].priceNightB) + conf.dirPrice[i].singlePrice = conf.dirPrice[i].priceDayA.value() == conf.dirPrice[i].priceNightA.value() && + conf.dirPrice[i].priceDayB.value() == conf.dirPrice[i].priceNightB.value(); + } +return makeProto(config).AddTariff(arg, conf, SimpleCallback, NULL) == STG::st_ok; } bool ChgTariffFunction(const SGCONF::CONFIG & config, const std::string & arg, const std::map & options) { -// TODO -std::cerr << "Unimplemented.\n"; -return false; +STG::TariffDataOpt conf; +conf.tariffConf.name = arg; +SGCONF::MaybeSet(options, "fee", conf.tariffConf.fee); +SGCONF::MaybeSet(options, "free", conf.tariffConf.free); +SGCONF::MaybeSet(options, "passive-cost", conf.tariffConf.passiveCost); +SGCONF::MaybeSet(options, "traff-type", conf.tariffConf.traffType, ConvTraffType); +SGCONF::MaybeSet(options, "period", conf.tariffConf.period, ConvPeriod); +SGCONF::MaybeSet(options, "change-policy", conf.tariffConf.changePolicy, ConvChangePolicy); +SGCONF::MaybeSet(options, "change-policy-timeout", conf.tariffConf.changePolicyTimeout, ConvChangePolicyTimeout); +SGCONF::MaybeSet(options, "times", conf.dirPrice, ConvTimes); +SGCONF::MaybeSet(options, "day-prices", conf.dirPrice, ConvDayPrices); +SGCONF::MaybeSet(options, "night-prices", conf.dirPrice, ConvNightPrices); +SGCONF::MaybeSet(options, "thresholds", conf.dirPrice, ConvThresholds); +for (size_t i = 0; i < conf.dirPrice.size(); ++i) + { + if (conf.dirPrice[i].priceDayA || + conf.dirPrice[i].priceNightA || + conf.dirPrice[i].priceDayB || + conf.dirPrice[i].priceNightB) + conf.dirPrice[i].singlePrice = conf.dirPrice[i].priceDayA.value() == conf.dirPrice[i].priceNightA.value() && + conf.dirPrice[i].priceDayB.value() == conf.dirPrice[i].priceNightB.value(); + } +return makeProto(config).ChgTariff(conf, SimpleCallback, NULL) == STG::st_ok; } } // namespace anonymous @@ -213,6 +416,6 @@ blocks.Add("Tariff management options") .Add("get-tariffs", SGCONF::MakeAPIAction(commands, GetTariffsFunction), "\tget tariff list") .Add("get-tariff", SGCONF::MakeAPIAction(commands, "", GetTariffFunction), "get tariff") .Add("add-tariff", SGCONF::MakeAPIAction(commands, "", params, AddTariffFunction), "add tariff") - .Add("del-tariff", SGCONF::MakeAPIAction(commands, "", DelTariffFunction), "del tariff") + .Add("del-tariff", SGCONF::MakeAPIAction(commands, "", DelTariffFunction), "delete tariff") .Add("chg-tariff", SGCONF::MakeAPIAction(commands, "", params, ChgTariffFunction), "change tariff"); }