Skip to content

Commit

Permalink
🩹⏱️ Initial set of patches for the departure time demo
Browse files Browse the repository at this point in the history
These are primarily from @the-bay-kay's fork

The main changes here are:
- move them from the demo script to the dockerfile so that the compilation is
  done upfront, and not at runtime. this makes startup much faster and is more
  consistent with the ethos of containerization
- create new scripts to apply the patches, since there are *so many* of them
- move the existing patches for the auth method from the demo script to the new
  mechanism for consistency
- these patches were not being applied earlier, so we also had to change the
  node-red flow to pass in the payment method
- add new python packages to support generating curves properly
#74 (comment)
- fix the paths for the packages so that they work properly
#74 (comment)

Signed-off-by: Shankari <[email protected]>
  • Loading branch information
Shankari committed Nov 16, 2024
1 parent edd85b8 commit 0ddd024
Show file tree
Hide file tree
Showing 21 changed files with 853 additions and 110 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
TAG=0.0.19
TAG=0.0.20

EVEREST_MANAGER_CPUS='1.0'
EVEREST_MANAGER_MEMORY='1024mb'
18 changes: 1 addition & 17 deletions demo-iso15118-2-ocpp-201.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,25 +130,9 @@ docker exec everest-ac-demo-manager-1 rm /ext/dist/share/everest/modules/OCPP201
docker exec everest-ac-demo-manager-1 rm /ext/dist/share/everest/modules/OCPP201/component_config/custom/Connector_2_1.json

echo "Configuring and restarting nodered"
docker cp nodered/config/config-sil-iso15118-ac-flow.json everest-ac-demo-nodered-1:/config/config-sil-two-evse-flow.json
docker cp nodered/config/config-sil-iso15118-ac-flow.json everest-ac-demo-nodered-1:/config/config-sil-iso15118-ac-flow.json
docker restart everest-ac-demo-nodered-1

echo "Installing patch and vim and cleaning up the cache"
docker exec everest-ac-demo-manager-1 /bin/bash -c "apt-get -qq -o=Dpkg::Use-Pty=0 update \
&& apt-get install -y -qq -o=Dpkg::Use-Pty=0 patch \
&& apt-get install -y -qq -o=Dpkg::Use-Pty=0 vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*"

echo "Copying over EVerest patches"
docker cp manager/enable_payment_method_in_python.patch everest-ac-demo-manager-1:/tmp/
docker cp manager/support_payment_in_jsevmanager.patch everest-ac-demo-manager-1:/tmp/

echo "Now applying the patches"
docker cp manager/enable_evcc_logging.cfg everest-ac-demo-manager-1:/ext/dist/etc/everest/default_logging.cfg
docker exec everest-ac-demo-manager-1 /bin/bash -c "cd /ext && patch -p0 -i /tmp/enable_payment_method_in_python.patch"
docker exec everest-ac-demo-manager-1 /bin/bash -c "cd /ext/dist/libexec/everest && patch -p1 -i /tmp/support_payment_in_jsevmanager.patch"

if [[ "$DEMO_VERSION" =~ sp2 || "$DEMO_VERSION" =~ sp3 ]]; then
docker cp manager/cached_certs_correct_name_emaid.tar.gz everest-ac-demo-manager-1:/ext/source/build
docker exec everest-ac-demo-manager-1 /bin/bash -c "pushd /ext/source/build && tar xf cached_certs_correct_name_emaid.tar.gz"
Expand Down
39 changes: 38 additions & 1 deletion manager/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ FROM ghcr.io/everest/everest-ci/build-kit-base:v1.4.2
ARG EVEREST_VERSION=2024.9.0
ENV EVEREST_VERSION=${EVEREST_VERSION}

RUN echo "Copying compile-time patches before starting compile"

COPY demo-patches/ /tmp/demo-patches/
COPY demo-patch-scripts/ /tmp/demo-patch-scripts/

RUN echo "Installing patch and vim"
RUN apt-get -y -qq update
RUN apt-get install -y -qq patch
RUN apt-get install -y -qq vim

# Cloning the repo now and copying files over
RUN git clone https://github.com/EVerest/everest-core.git \
&& cd everest-core \
Expand All @@ -11,10 +21,37 @@ RUN git clone https://github.com/EVerest/everest-core.git \
&& mkdir -p /ext/scripts \
&& cp -r everest-core/.ci/build-kit/scripts/* /ext/scripts/ \
&& mv everest-core /ext/source \
&& bash /tmp/demo-patch-scripts/apply-compile-patches.sh \
&& /entrypoint.sh run-script compile \
&& /entrypoint.sh run-script install

# Copy over the custom config *after* compilation and installation
# The previous approach works for code patches to the
# modules in everest-core, which are checked out as part
# of the build. However, it does not work for patches to the
# libraries that the modules use because the modules are
# downloaded as part of the build

# so we need to apply them post-build and then recompile and
# re-install. If there was a way to split the prep and the
# build (e.g. between the cmake and the ninja, we could apply
# it there. But this is what we have to work with :(

RUN bash /tmp/demo-patch-scripts/apply-library-patches.sh
RUN /entrypoint.sh run-script compile

# cleanup
RUN apt-get -y remove --purge build-essential
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /var/tmp/*

# Copy over the non-compiled patches *after* compilation and installation
RUN echo "Applying Post-Build patches..."
RUN bash /tmp/demo-patch-scripts/apply-runtime-patches.sh

# Setup python stuff for the more complex simulator
# and for testing if needed
RUN pip install --break-system-packages numpy==2.1.3
RUN pip install --break-system-packages control==0.10.1
RUN pip install --break-system-packages paho-mqtt==2.1.0

COPY run-test.sh /ext/source/tests/run-test.sh

Expand Down
Binary file not shown.
5 changes: 5 additions & 0 deletions manager/demo-patch-scripts/apply-compile-patches.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

echo "Applying compile-time patches"

cd / && patch -p0 -i /tmp/demo-patches/enable_iso_dt.patch
5 changes: 5 additions & 0 deletions manager/demo-patch-scripts/apply-library-patches.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

echo "Applying library patches"

cd / && patch -p0 -i /tmp/demo-patches/enable_ocpp_logging.patch
24 changes: 24 additions & 0 deletions manager/demo-patch-scripts/apply-runtime-patches.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

echo "Applying comm_session_handler_enabledt.patch"
cd / && patch -p0 -N -i /tmp/demo-patches/comm_session_handler_enabledt.patch
echo "Applying ev_state_enabledt.patch"
cd / && patch -p0 -N -i /tmp/demo-patches/ev_state_enabledt.patch
echo "Applying iso15118_2_states_enabledt.patch"
cd / && patch -p0 -N -i /tmp/demo-patches/iso15118_2_states_enabledt.patch
echo "Applying jsevmanager_index_enabledt.patch"
cd / && patch -p0 -N -i /tmp/demo-patches/jsevmanager_index_enabledt.patch
echo "Applying pyjosev_module_enabledt.patch"
cd / && patch -p0 -N -i /tmp/demo-patches/pyjosev_module_enabledt.patch
echo "Applying simulator_enabledt.patch"
cd / && patch -p0 -N -i /tmp/demo-patches/simulator_enabledt.patch

echo "Applying enabled_payment_method_in_python.patch"
cd /ext && patch -p0 -i /tmp/demo-patches/enable_payment_method_in_python.patch
echo "Applying support_payment_in_jsevmanager.patch"
cd /ext/dist/libexec/everest && patch -p1 -i /tmp/demo-patches/support_payment_in_jsevmanager.patch

cp /tmp/demo-patches/power_curve.py \
/ext/dist/libexec/everest/3rd_party/josev/iso15118/evcc/states/

cp /tmp/demo-patches/enable_evcc_logging.cfg /ext/dist/etc/everest/default_logging.cfg
24 changes: 24 additions & 0 deletions manager/demo-patches/comm_session_handler_enabledt.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--- /ext/source/build/dist/libexec/everest/3rd_party/josev/iso15118/evcc/comm_session_handler.py
+++ /ext/dist/libexec/everest/3rd_party/josev/iso15118/evcc/comm_session_handler.py
@@ -114,6 +114,21 @@
# Once the timer is up, the EV will terminate the communication session.
# A value >= 0 means the timer is running, a value < 0 means it stopped.
self.ongoing_timer: float = -1
+
+ # The Charge timer (in seconds) starts running once the EVCC
+ # receives a PowerDeliveryRes with EVSEProcessing set to 'Finished'.
+ # This timer counts up during a charge session, recording the duration.
+ # When a charge session is paused or stopped, the timer is reset.
+ # A value >= 0 means the timer is running, a value < 0 means it stopped.
+ self.charging_session_timer: float = -1
+ # The end of profile schedule marks the final departure time within
+ # the profile_entry_schedule created for a PowerDeliveryReq. This
+ # value, is used to mark when the 24 entry schedule has terminated.
+ # See ISO 15118-2 Subclause 8.5.2.10 for details
+ self.end_of_profile_schedule: int= -1
+ self.sim_speed: int = -1
+ self.departure_time: int = -1
+
# Temporarily save the ScheduleExchangeReq, which need to be resent to the SECC
# if the response message's EVSEProcessing field is set to "Ongoing"
self.ongoing_schedule_exchange_req: Optional[ScheduleExchangeReq] = None
File renamed without changes.
141 changes: 141 additions & 0 deletions manager/demo-patches/enable_iso_dt.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
--- /ext/source/modules/EvseV2G/iso_server.cpp
+++ /ext/source/modules/EvseV2G/iso_server.cpp
@@ -37,6 +37,7 @@
#define MQTT_MAX_PAYLOAD_SIZE 268435455
#define V2G_SECC_MSG_CERTINSTALL_TIME 4500
#define GEN_CHALLENGE_SIZE 16
+#define CEIL(x, y) (x)/(y) + ((x) % (y) != 0);

constexpr uint16_t SAE_V2H = 28472;
constexpr uint16_t SAE_V2G = 28473;
@@ -1072,6 +1073,7 @@
}
res->EVSEProcessing = (iso2_EVSEProcessingType)conn->ctx->evse_v2g_data.evse_processing[PHASE_AUTH];

+
if (conn->ctx->evse_v2g_data.evse_processing[PHASE_AUTH] != iso2_EVSEProcessingType_Finished) {
if (((is_payment_option_contract == false) && (conn->ctx->session.auth_timeout_eim == 0)) ||
((is_payment_option_contract == true) && (conn->ctx->session.auth_timeout_pnc == 0))) {
@@ -1130,32 +1132,84 @@
res->EVSEChargeParameter_isUsed = 0;
res->EVSEProcessing = (iso2_EVSEProcessingType)conn->ctx->evse_v2g_data.evse_processing[PHASE_PARAMETER];

+ struct linked_ac_params {
+ float max_current;
+ int64_t voltage;
+ int64_t pmax;
+ };
+
+ linked_ac_params sel_params;
+
/* Configure SA-schedules*/
if (res->EVSEProcessing == iso2_EVSEProcessingType_Finished) {
/* If processing is finished, configure SASchedule list */
if (conn->ctx->evse_v2g_data.evse_sa_schedule_list_is_used == false) {
+ int64_t departure_time_duration = req->AC_EVChargeParameter.DepartureTime;
+
/* If not configured, configure SA-schedule automatically for AC charging */
if (conn->ctx->is_dc_charger == false) {
/* Determin max current and nominal voltage */
- float max_current = conn->ctx->basic_config.evse_ac_current_limit;
- int64_t nom_voltage =
+ linked_ac_params default_params;
+ /* Setup default params (before the departure time overrides) */
+ default_params.max_current = conn->ctx->basic_config.evse_ac_current_limit;
+ default_params.voltage =
conn->ctx->evse_v2g_data.evse_nominal_voltage.Value *
pow(10, conn->ctx->evse_v2g_data.evse_nominal_voltage.Multiplier); /* nominal voltage */

/* Calculate pmax based on max current, nominal voltage and phase count (which the car has selected
* above) */
- int64_t pmax =
- max_current * nom_voltage *
+ default_params.pmax =
+ default_params.max_current * default_params.voltage *
((req->RequestedEnergyTransferMode == iso2_EnergyTransferModeType_AC_single_phase_core) ? 1 : 3);
- populate_physical_value(&conn->ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
+ dlog(DLOG_LEVEL_WARNING, "before adjusting for departure time, max_current %f, nom_voltage %d, pmax %d, departure_duration %d",
+ default_params.max_current, default_params.voltage,
+ default_params.pmax, departure_time_duration);
+ double req_eamount = calc_physical_value(req->AC_EVChargeParameter.EAmount.Value,
+ req->AC_EVChargeParameter.EAmount.Multiplier);
+ dlog(DLOG_LEVEL_WARNING, "Requested departure time %u, requested energy %f",
+ departure_time_duration, req_eamount);
+ if (departure_time_duration == 0) {
+ dlog(DLOG_LEVEL_WARNING, "No departure time specified, not modifying defaults");
+ sel_params = default_params;
+ } else {
+ dlog(DLOG_LEVEL_WARNING, "Departure time specified, checking to see if we can lower requirements");
+ int64_t departure_time_duration_hours = departure_time_duration / 3600;
+ float min_hours_to_charge = req_eamount / default_params.pmax;
+ float min_secs_to_charge = min_hours_to_charge * 60 * 60;
+ if (min_secs_to_charge >= departure_time_duration) {
+ dlog(DLOG_LEVEL_WARNING,
+ "Min hours to charge %f, requested departure time in hours %f, pmax is unchanged",
+ min_hours_to_charge, departure_time_duration_hours);
+ sel_params = default_params;
+ } else {
+ /* In general, while computing these values, we use integer division
+ which rounds down. We then check if there is a reminder and add one to round up.
+ The rationale is that, from a user perspective, it is better to finish charging
+ a teeny bit early rather than not provide all the energy requested */
+ sel_params.pmax = (int)std::ceil(req_eamount / departure_time_duration_hours);
+ sel_params.voltage = default_params.voltage;
+ sel_params.max_current = CEIL(sel_params.pmax, (sel_params.voltage *
+ ((req->RequestedEnergyTransferMode == iso2_EnergyTransferModeType_AC_single_phase_core) ? 1 : 3)));
+ dlog(DLOG_LEVEL_WARNING,
+ "Min hours to charge %f, requested departure time in hours %d, plenty of time to charge",
+ min_hours_to_charge, departure_time_duration_hours);
+ dlog(DLOG_LEVEL_WARNING, "lowering pmax = %d, max_current = %f, pmax float = %f",
+ sel_params.pmax, sel_params.max_current, ((float)req_eamount / (float)departure_time_duration_hours));
+ }
+ }
+ populate_physical_value(&conn->ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
.PMax,
- pmax, iso2_unitSymbolType_W);
+ sel_params.pmax, iso2_unitSymbolType_W);
} else {
conn->ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
.PMax = conn->ctx->evse_v2g_data.evse_maximum_power_limit;
}
+ if (departure_time_duration == 0) {
+ departure_time_duration = SA_SCHEDULE_DURATION; // one day, per spec
+ }
+
conn->ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
.RelativeTimeInterval.start = 0;
@@ -1164,7 +1218,7 @@
.RelativeTimeInterval.duration_isUsed = 1;
conn->ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
- .RelativeTimeInterval.duration = SA_SCHEDULE_DURATION;
+ .RelativeTimeInterval.duration = departure_time_duration;
conn->ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.arrayLen = 1;
conn->ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.arrayLen = 1;
@@ -1207,19 +1261,15 @@

populate_ac_evse_status(conn->ctx, &res->AC_EVSEChargeParameter.AC_EVSEStatus);

+ // We have already calculated all of these above, so let's not duplicate code here
/* Max current */
- float max_current = conn->ctx->basic_config.evse_ac_current_limit;
- populate_physical_value_float(&res->AC_EVSEChargeParameter.EVSEMaxCurrent, max_current, 1,
+ populate_physical_value_float(&res->AC_EVSEChargeParameter.EVSEMaxCurrent, sel_params.max_current, 1,
iso2_unitSymbolType_A);
-
/* Nominal voltage */
res->AC_EVSEChargeParameter.EVSENominalVoltage = conn->ctx->evse_v2g_data.evse_nominal_voltage;
- int64_t nom_voltage = conn->ctx->evse_v2g_data.evse_nominal_voltage.Value *
- pow(10, conn->ctx->evse_v2g_data.evse_nominal_voltage.Multiplier);

/* Calculate pmax based on max current, nominal voltage and phase count (which the car has selected above) */
- int64_t pmax = max_current * nom_voltage *
- ((iso2_EnergyTransferModeType_AC_single_phase_core == req->RequestedEnergyTransferMode) ? 1 : 3);
+ int64_t pmax = sel_params.pmax;

/* Check the SASchedule */
if (res->SAScheduleList_isUsed == (unsigned int)1) {
33 changes: 33 additions & 0 deletions manager/demo-patches/enable_ocpp_logging.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--- /ext/cache/cpm/libocpp/56452082640eeee05feec42f2f502d6beb8e684c/libocpp/lib/ocpp/v201/charge_point.cpp
+++ /ext/cache/cpm/libocpp/56452082640eeee05feec42f2f502d6beb8e684c/libocpp/lib/ocpp/v201/charge_point.cpp
@@ -3358,7 +3358,7 @@
}

void ChargePoint::handle_set_charging_profile_req(Call<SetChargingProfileRequest> call) {
- EVLOG_debug << "Received SetChargingProfileRequest: " << call.msg << "\nwith messageId: " << call.uniqueId;
+ EVLOG_info << "Received SetChargingProfileRequest: " << call.msg << "\nwith messageId: " << call.uniqueId;
auto msg = call.msg;
SetChargingProfileResponse response;
response.status = ChargingProfileStatusEnum::Rejected;
@@ -3383,7 +3383,7 @@
response.statusInfo = StatusInfo();
response.statusInfo->reasonCode = "InvalidValue";
response.statusInfo->additionalInfo = "ChargingStationExternalConstraintsInSetChargingProfileRequest";
- EVLOG_debug << "Rejecting SetChargingProfileRequest:\n reasonCode: " << response.statusInfo->reasonCode.get()
+ EVLOG_info << "Rejecting SetChargingProfileRequest:\n reasonCode: " << response.statusInfo->reasonCode.get()
<< "\nadditionalInfo: " << response.statusInfo->additionalInfo->get();

ocpp::CallResult<SetChargingProfileResponse> call_result(response, call.uniqueId);
@@ -3394,10 +3394,10 @@

response = this->smart_charging_handler->validate_and_add_profile(msg.chargingProfile, msg.evseId);
if (response.status == ChargingProfileStatusEnum::Accepted) {
- EVLOG_debug << "Accepting SetChargingProfileRequest";
+ EVLOG_info << "Accepting SetChargingProfileRequest";
this->callbacks.set_charging_profiles_callback();
} else {
- EVLOG_debug << "Rejecting SetChargingProfileRequest:\n reasonCode: " << response.statusInfo->reasonCode.get()
+ EVLOG_info << "Rejecting SetChargingProfileRequest:\n reasonCode: " << response.statusInfo->reasonCode.get()
<< "\nadditionalInfo: " << response.statusInfo->additionalInfo->get();
}

Loading

0 comments on commit 0ddd024

Please sign in to comment.