- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
108 lines
4.3 KiB
C++
108 lines
4.3 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright Pionix GmbH and Contributors to EVerest
|
|
|
|
#include "Offer.hpp"
|
|
#include <everest/logging.hpp>
|
|
#include <fmt/core.h>
|
|
|
|
namespace module {
|
|
|
|
std::ostream& operator<<(std::ostream& out, const Offer& self) {
|
|
/*out << "\nOffer:\n\nImport:\n------\n";
|
|
for (auto e : self.import_offer) {
|
|
out << e;
|
|
}
|
|
out << "\n\nExport:\n------\n";
|
|
for (auto e : self.export_offer) {
|
|
out << e;
|
|
}*/
|
|
|
|
const types::energy::NumberWithSource nonumber = {-9999.0, "Not set"};
|
|
|
|
out << fmt::format("\033[1;34mOffer[0]: Import {}A ({}) {}W ({}) Export {}A ({}) {}W ({})\033[1;0m",
|
|
self.import_offer[0].limits_to_root.ac_max_current_A.value_or(nonumber).value,
|
|
self.import_offer[0].limits_to_root.ac_max_current_A.value_or(nonumber).source,
|
|
self.import_offer[0].limits_to_root.total_power_W.value_or(nonumber).value,
|
|
self.import_offer[0].limits_to_root.total_power_W.value_or(nonumber).source,
|
|
self.export_offer[0].limits_to_root.ac_max_current_A.value_or(nonumber).value,
|
|
self.export_offer[0].limits_to_root.ac_max_current_A.value_or(nonumber).source,
|
|
self.export_offer[0].limits_to_root.total_power_W.value_or(nonumber).value,
|
|
self.export_offer[0].limits_to_root.total_power_W.value_or(nonumber).source);
|
|
return out;
|
|
}
|
|
|
|
template <class T> void apply_one_limit_if_smaller(std::optional<T>& a, const std::optional<T>& b) {
|
|
if (b.has_value()) {
|
|
if (a.has_value()) {
|
|
if (a.value().value > b.value().value) {
|
|
a = b.value();
|
|
} else if (a.value().value == b.value().value) {
|
|
a.value().source += "," + b.value().source;
|
|
}
|
|
} else {
|
|
a = b.value();
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class T> void apply_one_limit_if_greater(std::optional<T>& a, const std::optional<T>& b) {
|
|
if (b.has_value()) {
|
|
if (a.has_value()) {
|
|
if (a.value().value < b.value().value) {
|
|
a = b.value();
|
|
} else if (a.value().value == b.value().value) {
|
|
a.value().source += "," + b.value().source;
|
|
}
|
|
} else {
|
|
a = b.value();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void apply_limits(ScheduleReq& a, const ScheduleReq& b) {
|
|
if (a.size() != b.size()) {
|
|
EVLOG_error << fmt::format("apply_limits: a({}) and b({}) do not have the same size.", a.size(), b.size());
|
|
return;
|
|
}
|
|
for (ScheduleReq::size_type i = 0; i < a.size(); i++) {
|
|
// limits to leave are already merged to the root side, so we dont use them here
|
|
apply_one_limit_if_smaller(a[i].limits_to_root.ac_max_current_A, b[i].limits_to_root.ac_max_current_A);
|
|
apply_one_limit_if_smaller(a[i].limits_to_root.ac_max_phase_count, b[i].limits_to_root.ac_max_phase_count);
|
|
apply_one_limit_if_smaller(a[i].limits_to_root.total_power_W, b[i].limits_to_root.total_power_W);
|
|
apply_one_limit_if_greater(a[i].limits_to_root.ac_min_phase_count, b[i].limits_to_root.ac_min_phase_count);
|
|
apply_one_limit_if_greater(a[i].limits_to_root.ac_min_current_A, b[i].limits_to_root.ac_min_current_A);
|
|
|
|
// copy other information if any
|
|
a[i].price_per_kwh = b[i].price_per_kwh;
|
|
a[i].limits_to_root.ac_number_of_active_phases = b[i].limits_to_root.ac_number_of_active_phases;
|
|
}
|
|
}
|
|
|
|
Offer::Offer(Market& market) {
|
|
// create maximum offer for this market place
|
|
create_offer_for_local_market(market);
|
|
}
|
|
|
|
// Recursive: start at leaf, walk to root and create empty root offer. On the way back, apply all limits of local
|
|
// marketplaces until we are at the leaf again.
|
|
void Offer::create_offer_for_local_market(Market& market) {
|
|
|
|
if (!market.is_root()) {
|
|
create_offer_for_local_market(*market.parent());
|
|
} else {
|
|
// initialize time slots
|
|
import_offer = globals.empty_schedule_req;
|
|
export_offer = globals.empty_schedule_req;
|
|
}
|
|
|
|
// limit offer with limits at this market place
|
|
apply_limits(import_offer, market.get_available_energy_import());
|
|
|
|
// limit offer with limits at this market place
|
|
apply_limits(export_offer, market.get_available_energy_export());
|
|
|
|
optimizer_target = market.energy_flow_request.optimizer_target;
|
|
}
|
|
|
|
} // namespace module
|