Skip to content

Commit 690e3d9

Browse files
committed
add price plan controller
1 parent 00dc0e9 commit 690e3d9

File tree

9 files changed

+149
-98
lines changed

9 files changed

+149
-98
lines changed

rest/configuration.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ constexpr auto CHARLIES_SMART_METER_ID = "smart-meter-2";
2020
constexpr auto ANDREAS_SMART_METER_ID = "smart-meter-3";
2121
constexpr auto ALEXS_SMART_METER_ID = "smart-meter-4";
2222

23-
std::vector<PricePlan> price_plans() {
24-
std::vector<PricePlan> pricePlans;
25-
pricePlans.push_back(PricePlan(MOST_EVIL_PRICE_PLAN_ID, DR_EVILS_DARK_ENERGY_ENERGY_SUPPLIER, 10, {}));
26-
pricePlans.push_back(PricePlan(RENEWABLES_PRICE_PLAN_ID, THE_GREEN_ECO_ENERGY_SUPPLIER, 2, {}));
27-
pricePlans.push_back(PricePlan(STANDARD_PRICE_PLAN_ID, POWER_FOR_EVERYONE_ENERGY_SUPPLIER, 1, {}));
28-
return pricePlans;
23+
std::vector<PricePlan> pricePlans() {
24+
std::vector<PricePlan> price_plans;
25+
price_plans.push_back(PricePlan(MOST_EVIL_PRICE_PLAN_ID, DR_EVILS_DARK_ENERGY_ENERGY_SUPPLIER, 10, {}));
26+
price_plans.push_back(PricePlan(RENEWABLES_PRICE_PLAN_ID, THE_GREEN_ECO_ENERGY_SUPPLIER, 2, {}));
27+
price_plans.push_back(PricePlan(STANDARD_PRICE_PLAN_ID, POWER_FOR_EVERYONE_ENERGY_SUPPLIER, 1, {}));
28+
return price_plans;
2929
}
3030

3131
std::unordered_map<std::string, std::string> smart_meter_to_price_plan_accounts() {
@@ -39,7 +39,7 @@ std::unordered_map<std::string, std::string> smart_meter_to_price_plan_accounts(
3939
auto readings() {
4040
std::unordered_map<std::string, std::vector<ElectricityReading>> result;
4141
for (auto &[meter, plan] : smart_meter_to_price_plan_accounts()) {
42-
result[meter] = generator{}.generate(20);
42+
result[meter] = generator{}.generate(21);
4343
}
4444
return result;
4545
}

rest/controller/MeterReadingController.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ namespace detail {
2323
return ss.str();
2424
}
2525

26+
auto fromRfc3339(const std::string &time) {
27+
std::tm tm = {};
28+
std::stringstream ss(time);
29+
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
30+
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
31+
return tp;
32+
}
33+
2634
auto renderReadingAsJson(const ElectricityReading &r) {
2735
return nlohmann::json{{"time", toRfc3339(r.getTime())},
2836
{"reading", r.getReading()}};
@@ -60,18 +68,11 @@ class MeterReadingController {
6068
auto body = nlohmann::json::parse(req.body());
6169
auto smartMeterId = body["smartMeterId"];
6270
std::vector<ElectricityReading> electricityReadings;
63-
6471
for (auto &electricityReading : body["electricityReadings"]) {
65-
std::string tmp = electricityReading["time"];
66-
std::tm tm = {};
67-
std::stringstream ss(tmp);
68-
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
69-
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
70-
electricityReadings.emplace_back(tp, electricityReading["reading"]);
72+
electricityReadings.emplace_back(detail::fromRfc3339(electricityReading["time"]),
73+
electricityReading["reading"]);
7174
}
7275
meterReadingService.storeReadings(smartMeterId, electricityReadings);
73-
74-
std::cout << electricityReadings.size() << std::endl;
7576
return {};
7677
}
7778

rest/controller/PricePlanComparatorController.h

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,77 @@
22
#define DEVELOPER_JOYOFENERGY_CPP_BEAST_PRICEPLANCOMPARATORCONTROLLER_H
33

44
#include <service/ElectricityReadingService.h>
5+
#include <service/PricePlanService.h>
6+
#include <configuration.h>
57

68
#include <ctime>
79
#include <iomanip>
810

911
#include <boost/beast/http.hpp>
1012
#include <nlohmann/json.hpp>
1113

12-
#include <service/PricePlanService.h>
14+
1315
#include <iostream>
1416

1517
namespace http = boost::beast::http;
1618

17-
namespace price_detail {
18-
auto renderCostsAsJson(const ElectricityReading &r) {
19-
return nlohmann::json{{"hehe"},
20-
{"reading"}};
19+
class PricePlanComparatorController {
20+
public:
21+
PricePlanComparatorController(PricePlanService &pricePlanService) : pricePlanService(pricePlanService) {}
22+
23+
http::response<http::string_body>
24+
Compare(const http::request<http::string_body> &req, const std::vector<std::string> &queries) {
25+
const auto &meterId = queries[0];
26+
auto costs = pricePlanService.getConsumptionCostOfElectricityReadingsForEachPricePlan(meterId);
27+
28+
if (!costs) {
29+
return {http::status::not_found, req.version()};
2130
}
22-
} // namespace price_detail
23-
24-
class PricePlanComparatorController {
25-
public:
26-
PricePlanComparatorController(PricePlanService &pricePlanService) : pricePlanService(pricePlanService) {}
27-
28-
// http::response<http::string_body>
29-
// Read(const http::request<http::string_body> &req, const std::vector<std::string> &queries) {
30-
// const auto &meterId = queries[0];
31-
// auto costs = pricePlanService.getConsumptionCostOfElectricityReadingsForEachPricePlan(meterId);
32-
// for(auto &i:*costs){
33-
// std::cout<<i.first<<" "<<i.second<<std::endl;
34-
// }
35-
//
36-
// if (!costs) {
37-
// return {http::status::not_found, req.version()};
38-
// }
39-
// http::response<http::string_body> res{http::status::ok, req.version()};
40-
// res.set(http::field::content_type, "application/json");
41-
// res.keep_alive(req.keep_alive());
42-
// auto results = nlohmann::json::array();
43-
// std::transform(costs->begin(), costs->end(), std::back_inserter(results), &price_detail::renderCostsAsJson);
44-
// nlohmann::json j;
45-
// j["readings"] = results;
46-
// res.body() = j.dump();
47-
// res.prepare_payload();
48-
// return res;
49-
// }
50-
51-
private:
52-
PricePlanService &pricePlanService;
53-
};
31+
auto current_price_plans = smart_meter_to_price_plan_accounts();
32+
http::response<http::string_body> res{http::status::ok, req.version()};
33+
res.set(http::field::content_type, "application/json");
34+
res.keep_alive(req.keep_alive());
35+
nlohmann::json j;
36+
j["pricePlanComparisons"] = {{"price-plan-0", double(costs.value()["price-plan-0"])/10000},
37+
{"price-plan-1", double(costs.value()["price-plan-1"])/10000},
38+
{"price-plan-2", double(costs.value()["price-plan-2"])/10000}};
39+
j["pricePlanId"] = current_price_plans[meterId];
40+
res.body() = j.dump();
41+
res.prepare_payload();
42+
return res;
43+
}
44+
45+
http::response<http::string_body>
46+
Recommend(const http::request<http::string_body> &req, const std::vector<std::string> &queries) {
47+
const auto &meterId = queries[0];
48+
int limit = std::stoi(queries[2]);
49+
auto costs = pricePlanService.getConsumptionCostOfElectricityReadingsForEachPricePlan(meterId);
50+
51+
if (!costs) {
52+
return {http::status::not_found, req.version()};
53+
}
54+
55+
std::vector<std::pair<std::string, float>> ordered_costs{costs->begin(), costs->end()};
56+
std::sort(ordered_costs.begin(), ordered_costs.end(), [](auto &cost_a, auto &cost_b) {
57+
return cost_a.second < cost_b.second;
58+
});
59+
ordered_costs.resize(std::min(limit, int(ordered_costs.size())));
60+
61+
http::response<http::string_body> res{http::status::ok, req.version()};
62+
res.set(http::field::content_type, "application/json");
63+
res.keep_alive(req.keep_alive());
64+
auto results = nlohmann::json::array();
65+
std::transform(ordered_costs.begin(), ordered_costs.end(), std::back_inserter(results),
66+
[](auto &cost) { return nlohmann::json{{cost.first, cost.second}}; });
67+
nlohmann::json j;
68+
j["recommend"] = results;
69+
res.body() = j.dump();
70+
res.prepare_payload();
71+
return res;
72+
}
73+
74+
private:
75+
PricePlanService &pricePlanService;
76+
};
5477

5578
#endif //DEVELOPER_JOYOFENERGY_CPP_BEAST_PRICEPLANCOMPARATORCONTROLLER_H

rest/domain/PricePlan.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ class PricePlan {
2424
: planName(planName), energySupplier(energySupplier), unitRate(unitRate),
2525
peakTimeMultipliers(peakTimeMultipliers) {}
2626

27-
const std::string &getEnergySupplier() const { return energySupplier; }
27+
std::string getEnergySupplier() const { return energySupplier; }
2828

29-
const std::string &getPlanName() const { return planName; }
29+
std::string getPlanName() const { return planName; }
3030

31-
const int getUnitRate() const { return unitRate; }
31+
int getUnitRate() const { return unitRate; }
3232

3333
private:
3434
const std::string energySupplier;

rest/generator.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class generator {
1212
readings.reserve(number);
1313
auto now = std::chrono::system_clock::now();
1414
for (int i = number; i > 0; i--) {
15-
auto r = std::abs(std::rand()) % 4;
16-
readings.emplace_back(now - std::chrono::seconds(i * 10), r);
15+
auto r = std::abs(std::rand()) % 4000;
16+
readings.emplace_back(now - std::chrono::minutes(i * 3), 4000);
1717
}
1818
return readings;
1919
}

rest/server.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ namespace server_detail {
1212
public:
1313
explicit impl(int concurrency) : ioc(concurrency) {
1414
using reading = MeterReadingController;
15+
using price_plan = PricePlanComparatorController;
1516
router.to<reading, &reading::Read>(R"(/readings/read/([a-zA-Z0-9_-]+))", electricityReadingService, meterReadingService);
1617
router.to<reading, &reading::Store>(R"(/readings/store)", electricityReadingService, meterReadingService);
18+
router.to<price_plan, &price_plan::Compare>(R"(/price-plans/compare-all/([a-zA-Z0-9_-]+))", pricePlanService);
19+
router.to<price_plan, &price_plan::Recommend>(R"(/price-plans/recommend/([a-zA-Z0-9_-]+)\?(limit)=([0-9]+))", pricePlanService);
1720
}
1821

1922
void launch(const char *address, unsigned short port) {
@@ -31,6 +34,8 @@ namespace server_detail {
3134
std::unordered_map<std::string, std::vector<ElectricityReading>> meterAssociatedReadings{readings()};
3235
ElectricityReadingService electricityReadingService{meterAssociatedReadings};
3336
MeterReadingService meterReadingService{meterAssociatedReadings};
37+
std::vector<PricePlan> price_plans{pricePlans()};
38+
PricePlanService pricePlanService{price_plans, meterReadingService};
3439
router router;
3540
std::function<http::response<http::string_body>(
3641
const http::request<http::string_body> &)> handler = router.handler();

rest/service/MeterReadingService.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,27 @@
1515

1616
class MeterReadingService {
1717
public:
18-
std::optional<std::vector<ElectricityReading>> getReadings(std::string smartMeterId) {
18+
std::optional<std::vector<ElectricityReading>> getReadings(const std::string &smartMeterId) {
1919
if (meterAssociatedReadings.find(smartMeterId) == meterAssociatedReadings.end()) {
2020
return {};
2121
}
2222
return meterAssociatedReadings[smartMeterId];
2323
}
2424

25-
void storeReadings(std::string smartMeterId, std::vector<ElectricityReading> electricityReadings) {
25+
void storeReadings(const std::string &smartMeterId, std::vector<ElectricityReading> &electricityReadings) {
2626
if (meterAssociatedReadings.find(smartMeterId) == meterAssociatedReadings.end()) {
2727
meterAssociatedReadings[smartMeterId] = {};
2828
}
2929
meterAssociatedReadings[smartMeterId].insert(meterAssociatedReadings[smartMeterId].end(),
3030
electricityReadings.begin(), electricityReadings.end());
31-
32-
std::cout<<meterAssociatedReadings.size()<<std::endl;
3331
}
3432

35-
MeterReadingService(std::unordered_map<std::string, std::vector<ElectricityReading>> meterAssociatedReadings) :
33+
explicit MeterReadingService(
34+
std::unordered_map<std::string, std::vector<ElectricityReading>> &meterAssociatedReadings) :
3635
meterAssociatedReadings(meterAssociatedReadings) {}
3736

3837
private:
39-
std::unordered_map<std::string, std::vector<ElectricityReading>> meterAssociatedReadings;
38+
std::unordered_map<std::string, std::vector<ElectricityReading>> &meterAssociatedReadings;
4039
};
4140

4241
#endif //DEVELOPER_JOYOFENERGY_CPP_BEAST_METERREADINGSERVICE_H

rest/service/PricePlanService.h

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,63 +11,69 @@
1111
#include <string>
1212
#include <configuration.h>
1313

14+
#include <controller/MeterReadingController.h>
15+
1416
class PricePlanService {
1517
public:
1618
using time_point_type = std::chrono::time_point<std::chrono::system_clock>;
1719

18-
std::optional<std::map<std::string, float>> getConsumptionCostOfElectricityReadingsForEachPricePlan(std::string smartMeterId){
19-
std::optional<std::vector<ElectricityReading>> electricityReadings = meterReadingService.getReadings(smartMeterId);
20+
std::optional<std::map<std::string, int>>
21+
getConsumptionCostOfElectricityReadingsForEachPricePlan(std::string smartMeterId) {
22+
std::optional<std::vector<ElectricityReading>> electricityReadings = meterReadingService.getReadings(
23+
smartMeterId);
2024
if (!electricityReadings.has_value()) {
2125
return {};
2226
}
2327

24-
std::map<std::string, float> consumptionCostOfElectricityReadingsForEachPricePlan;
25-
for(auto pricePlan:pricePlans){
26-
consumptionCostOfElectricityReadingsForEachPricePlan.insert(std::make_pair(pricePlan.getPlanName(), calculateCost(electricityReadings.value(), pricePlan)));
28+
std::map<std::string, int> consumptionCostOfElectricityReadingsForEachPricePlan;
29+
for (auto pricePlan:pricePlans) {
30+
consumptionCostOfElectricityReadingsForEachPricePlan.insert(
31+
std::make_pair(pricePlan.getPlanName(), calculateCost(electricityReadings.value(), pricePlan)));
2732
}
2833
return consumptionCostOfElectricityReadingsForEachPricePlan;
2934
}
3035

31-
PricePlanService(std::vector<PricePlan> pricePlans, MeterReadingService meterReadingService) :
36+
PricePlanService(std::vector<PricePlan> &pricePlans, MeterReadingService &meterReadingService) :
3237
pricePlans(pricePlans), meterReadingService(meterReadingService) {}
3338

3439
private:
35-
const std::vector<PricePlan> pricePlans;
36-
MeterReadingService meterReadingService;
37-
38-
int calculateCost(std::vector<ElectricityReading> electricityReadings, PricePlan pricePlan) {
39-
float average = calculateAverageReading(electricityReadings);
40-
float timeElapsed = calculateTimeElapsed(electricityReadings);
41-
42-
float averagedCost = round(average / timeElapsed);
43-
return averagedCost * pricePlan.getUnitRate();
44-
}
45-
46-
float calculateAverageReading(std::vector<ElectricityReading> electricityReadings) {
47-
float summedReadings = 0;
48-
for (ElectricityReading electricityReading : electricityReadings) {
49-
summedReadings += electricityReading.getReading();
50-
}
51-
52-
return round(summedReadings / electricityReadings.size());
53-
}
40+
const std::vector<PricePlan> &pricePlans;
41+
MeterReadingService &meterReadingService;
5442

55-
float calculateTimeElapsed(std::vector<ElectricityReading> electricityReadings) {
43+
static auto calculateTimeElapsed(std::vector<ElectricityReading> electricityReadings) {
5644
ElectricityReading first = *electricityReadings.begin();
5745
ElectricityReading last = *electricityReadings.begin();
5846
std::vector<ElectricityReading>::iterator it;
59-
for(it = electricityReadings.begin(); it != electricityReadings.end(); it++){
60-
if (it->getTime() < first.getTime()){
47+
for (it = electricityReadings.begin(); it != electricityReadings.end(); it++) {
48+
if (it->getTime() < first.getTime()) {
6149
first = *it;
6250
}
63-
if (it->getTime() > first.getTime()){
51+
if (it->getTime() > first.getTime()) {
6452
last = *it;
6553
}
6654
}
6755

68-
std::chrono::steady_clock::duration duration = std::chrono::duration_cast<std::chrono::hours>(
69-
last.getTime() - first.getTime());
70-
return duration.count();
56+
std::chrono::duration duration = last.getTime() - first.getTime();
57+
58+
return duration;
59+
}
60+
61+
static int calculateCost(const std::vector<ElectricityReading> &electricityReadings, const PricePlan &pricePlan) {
62+
int average = calculateAverageReading(electricityReadings);
63+
auto secondsElapsed = std::chrono::duration_cast<std::chrono::seconds>(
64+
calculateTimeElapsed(electricityReadings));
65+
66+
int averagedCost = average * 3600 / secondsElapsed.count();
67+
return averagedCost * pricePlan.getUnitRate();
68+
}
69+
70+
static int calculateAverageReading(const std::vector<ElectricityReading> &electricityReadings) {
71+
int summedReadings = 0;
72+
for (auto &electricityReading : electricityReadings) {
73+
summedReadings += electricityReading.getReading();
74+
}
75+
76+
return summedReadings / int(electricityReadings.size());
7177
}
7278
};
7379

0 commit comments

Comments
 (0)