Skip to content

Commit

Permalink
Merge pull request EVerest#88 from US-JOET/migrate_departure_time_cha…
Browse files Browse the repository at this point in the history
…nges

✨ 🩹⏱️ Migrate departure time patches to the main repo
  • Loading branch information
shankari authored Nov 16, 2024
2 parents 927d9e8 + 0ddd024 commit a897c86
Show file tree
Hide file tree
Showing 24 changed files with 2,560 additions and 386 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
2 changes: 1 addition & 1 deletion docker-compose.ocpp201.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ services:
- 1880:1880
environment:
- MQTT_SERVER_ADDRESS=mqtt-server
- FLOWS=/config/config-sil-two-evse-flow.json
- FLOWS=/config/config-sil-iso15118-ac-flow.json
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
9 changes: 9 additions & 0 deletions manager/config-sil-ocpp201-pnc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,15 @@ active_modules:
evse_manager:
- module_id: evse_manager_1
implementation_id: evse
error_history:
- module_id: error_history
implementation_id: error_history
error_history:
module: ErrorHistory
config_implementation:
error_history:
database_path: /tmp/error_history.db

system:
module: System

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 a897c86

Please sign in to comment.