Files
Eric F d398a6ced2 Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- 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
2026-06-08 00:38:27 -04:00

650 lines
28 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2023 chargebyte GmbH
// Copyright (C) 2023 Contributors to EVerest
#include "v2g_server.hpp"
#include <cstdint>
#include <cstdlib>
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <cbv2g/app_handshake/appHand_Decoder.h>
#include <cbv2g/app_handshake/appHand_Encoder.h>
#include <cbv2g/common/exi_basetypes.h>
#include <cbv2g/din/din_msgDefDecoder.h>
#include <cbv2g/din/din_msgDefEncoder.h>
#include <cbv2g/exi_v2gtp.h>
#include <cbv2g/iso_2/iso2_msgDefDecoder.h>
#include <cbv2g/iso_2/iso2_msgDefEncoder.h>
#include "connection.hpp"
#include "din_server.hpp"
#include "iso_server.hpp"
#include "log.hpp"
#include "tools.hpp"
#define MAX_RES_TIME 98
static types::iso15118::V2gMessageId get_v2g_message_id(enum V2gMsgTypeId v2g_msg, enum v2g_protocol selected_protocol,
bool is_req) {
switch (v2g_msg) {
case V2G_SUPPORTED_APP_PROTOCOL_MSG:
return is_req == true ? types::iso15118::V2gMessageId::SupportedAppProtocolReq
: types::iso15118::V2gMessageId::SupportedAppProtocolRes;
case V2G_SESSION_SETUP_MSG:
return is_req == true ? types::iso15118::V2gMessageId::SessionSetupReq
: types::iso15118::V2gMessageId::SessionSetupRes;
case V2G_SERVICE_DISCOVERY_MSG:
return is_req == true ? types::iso15118::V2gMessageId::ServiceDiscoveryReq
: types::iso15118::V2gMessageId::ServiceDiscoveryRes;
case V2G_SERVICE_DETAIL_MSG:
return is_req == true ? types::iso15118::V2gMessageId::ServiceDetailReq
: types::iso15118::V2gMessageId::ServiceDetailRes;
case V2G_PAYMENT_SERVICE_SELECTION_MSG:
return is_req == true ? selected_protocol == V2G_PROTO_DIN70121
? types::iso15118::V2gMessageId::ServicePaymentSelectionReq
: types::iso15118::V2gMessageId::PaymentServiceSelectionReq
: selected_protocol == V2G_PROTO_DIN70121 ? types::iso15118::V2gMessageId::ServicePaymentSelectionRes
: types::iso15118::V2gMessageId::PaymentServiceSelectionRes;
case V2G_PAYMENT_DETAILS_MSG:
return is_req == true ? types::iso15118::V2gMessageId::PaymentDetailsReq
: types::iso15118::V2gMessageId::PaymentDetailsRes;
case V2G_AUTHORIZATION_MSG:
return is_req == true ? selected_protocol == V2G_PROTO_DIN70121
? types::iso15118::V2gMessageId::ContractAuthenticationReq
: types::iso15118::V2gMessageId::AuthorizationReq
: selected_protocol == V2G_PROTO_DIN70121 ? types::iso15118::V2gMessageId::ContractAuthenticationRes
: types::iso15118::V2gMessageId::AuthorizationRes;
case V2G_CHARGE_PARAMETER_DISCOVERY_MSG:
return is_req == true ? types::iso15118::V2gMessageId::ChargeParameterDiscoveryReq
: types::iso15118::V2gMessageId::ChargeParameterDiscoveryRes;
case V2G_METERING_RECEIPT_MSG:
return is_req == true ? types::iso15118::V2gMessageId::MeteringReceiptReq
: types::iso15118::V2gMessageId::MeteringReceiptRes;
case V2G_CERTIFICATE_UPDATE_MSG:
return is_req == true ? types::iso15118::V2gMessageId::CertificateUpdateReq
: types::iso15118::V2gMessageId::CertificateUpdateRes;
case V2G_CERTIFICATE_INSTALLATION_MSG:
return is_req == true ? types::iso15118::V2gMessageId::CertificateInstallationReq
: types::iso15118::V2gMessageId::CertificateInstallationRes;
case V2G_CHARGING_STATUS_MSG:
return is_req == true ? types::iso15118::V2gMessageId::ChargingStatusReq
: types::iso15118::V2gMessageId::ChargingStatusRes;
case V2G_CABLE_CHECK_MSG:
return is_req == true ? types::iso15118::V2gMessageId::CableCheckReq
: types::iso15118::V2gMessageId::CableCheckRes;
case V2G_PRE_CHARGE_MSG:
return is_req == true ? types::iso15118::V2gMessageId::PreChargeReq
: types::iso15118::V2gMessageId::PreChargeRes;
case V2G_POWER_DELIVERY_MSG:
return is_req == true ? types::iso15118::V2gMessageId::PowerDeliveryReq
: types::iso15118::V2gMessageId::PowerDeliveryRes;
case V2G_CURRENT_DEMAND_MSG:
return is_req == true ? types::iso15118::V2gMessageId::CurrentDemandReq
: types::iso15118::V2gMessageId::CurrentDemandRes;
case V2G_WELDING_DETECTION_MSG:
return is_req == true ? types::iso15118::V2gMessageId::WeldingDetectionReq
: types::iso15118::V2gMessageId::WeldingDetectionRes;
case V2G_SESSION_STOP_MSG:
return is_req == true ? types::iso15118::V2gMessageId::SessionStopReq
: types::iso15118::V2gMessageId::SessionStopRes;
case V2G_UNKNOWN_MSG:
default:
return types::iso15118::V2gMessageId::UnknownMessage;
}
}
/*!
* \brief publish_var_V2G_Message This function fills a V2gMessages type with the V2G EXI message as HEX and Base64
* \param conn hold the context of the V2G-connection.
* \param is_req if it is a V2G request or response: 'true' if a request, and 'false' if a response
*/
static void publish_var_V2G_Message(v2g_connection* conn, bool is_req) {
types::iso15118::V2gMessages v2g_message;
u_int8_t* tempbuff = conn->buffer;
std::string msg_as_hex_string;
for (int i = 0; ((tempbuff != NULL) && (i < conn->payload_len + V2GTP_HEADER_LENGTH)); i++) {
char hex[4];
snprintf(hex, 4, "%x", *tempbuff); // to hex
if (std::string(hex).size() == 1)
msg_as_hex_string += '0';
msg_as_hex_string += hex;
tempbuff++;
}
std::string EXI_Base64;
EXI_Base64 = openssl::base64_encode(conn->buffer, conn->payload_len + V2GTP_HEADER_LENGTH);
if (EXI_Base64.size() == 0) {
dlog(DLOG_LEVEL_WARNING, "Unable to base64 encode EXI buffer");
}
v2g_message.exi_base64 = EXI_Base64;
v2g_message.id = get_v2g_message_id(conn->ctx->current_v2g_msg, conn->ctx->selected_protocol, is_req);
v2g_message.exi = msg_as_hex_string;
conn->ctx->p_charger->publish_v2g_messages(v2g_message);
}
/*!
* \brief v2g_incoming_v2gtp This function reads the V2G transport header
* \param conn hold the context of the V2G-connection.
* \return Returns 0 if the V2G-session was successfully stopped, 1 if connection was closed unexpectedly, otherwise -1.
*/
static int v2g_incoming_v2gtp(struct v2g_connection* conn) {
assert(conn != nullptr);
assert(conn->read != nullptr);
int rv;
/* read and process header */
rv = conn->read(conn, conn->buffer, V2GTP_HEADER_LENGTH);
if (rv < 0) {
dlog(DLOG_LEVEL_ERROR, "connection_read(header) failed: %s",
(rv == -1) ? strerror(errno) : "connection terminated");
return -1;
}
/* connection was closed unexpectedly (timeout or closed by peer) */
if (rv == 0)
return 1;
if (rv != V2GTP_HEADER_LENGTH) {
dlog(DLOG_LEVEL_ERROR, "connection_read(header) too short: expected %d, got %d", V2GTP_HEADER_LENGTH, rv);
return -1;
}
rv = V2GTP_ReadHeader(conn->buffer, &conn->payload_len);
if (rv == -1) {
dlog(DLOG_LEVEL_ERROR, "Invalid v2gtp header");
return -1;
}
if (conn->payload_len >= UINT32_MAX - V2GTP_HEADER_LENGTH) {
dlog(DLOG_LEVEL_ERROR, "Prevent integer overflow - payload too long: have %d, would need %u",
DEFAULT_BUFFER_SIZE, conn->payload_len);
return -1;
}
if (conn->payload_len + V2GTP_HEADER_LENGTH > DEFAULT_BUFFER_SIZE) {
dlog(DLOG_LEVEL_ERROR, "payload too long: have %d, would need %u", DEFAULT_BUFFER_SIZE,
conn->payload_len + V2GTP_HEADER_LENGTH);
/* we have no way to flush/discard remaining unread data from the socket without reading it in chunks,
* but this opens the chance to bind us in a "endless" read loop; so to protect us, simply close the connection
*/
return -1;
}
/* read request */
rv = conn->read(conn, &conn->buffer[V2GTP_HEADER_LENGTH], conn->payload_len);
if (rv < 0) {
dlog(DLOG_LEVEL_ERROR, "connection_read(payload) failed: %s",
(rv == -1) ? strerror(errno) : "connection terminated");
return -1;
}
if (rv != conn->payload_len) {
dlog(DLOG_LEVEL_ERROR, "connection_read(payload) too short: expected %d, got %d", conn->payload_len, rv);
return -1;
}
/* adjust buffer pos to decode request */
conn->stream.byte_pos = V2GTP_HEADER_LENGTH;
conn->stream.data_size = conn->payload_len + V2GTP_HEADER_LENGTH;
return 0;
}
/*!
* \brief v2g_outgoing_v2gtp This function creates the v2g transport header
* \param conn hold the context of the v2g-connection.
* \return Returns 0 if the v2g-session was successfully stopped, otherwise -1.
*/
int v2g_outgoing_v2gtp(struct v2g_connection* conn) {
assert(conn != nullptr);
assert(conn->write != nullptr);
/* fixup/create header */
const auto len = exi_bitstream_get_length(&conn->stream);
V2GTP_WriteHeader(conn->buffer, len - V2GTP_HEADER_LENGTH);
if (conn->write(conn, conn->buffer, len) == -1) {
dlog(DLOG_LEVEL_ERROR, "connection_write(header) failed: %s", strerror(errno));
return -1;
}
return 0;
}
/*!
* \brief v2g_handle_apphandshake After receiving a supportedAppProtocolReq message,
* the SECC shall process the received information. DIN [V2G-DC-436] ISO [V2G2-540]
* \param conn hold the context of the v2g-connection.
* \return Returns a v2g-event of type enum v2g_event.
*/
static enum v2g_event v2g_handle_apphandshake(struct v2g_connection* conn) {
enum v2g_event next_event = V2G_EVENT_NO_EVENT;
int i;
uint8_t ev_app_priority = 20; // lowest priority
/* validate handshake request and create response */
init_appHand_exiDocument(&conn->handshake_resp);
conn->handshake_resp.supportedAppProtocolRes_isUsed = 1;
conn->handshake_resp.supportedAppProtocolRes.ResponseCode =
appHand_responseCodeType_Failed_NoNegotiation; // [V2G2-172]
dlog(DLOG_LEVEL_INFO, "Handling SupportedAppProtocolReq");
conn->ctx->current_v2g_msg = V2G_SUPPORTED_APP_PROTOCOL_MSG;
if (decode_appHand_exiDocument(&conn->stream, &conn->handshake_req) != 0) {
dlog(DLOG_LEVEL_ERROR, "decode_appHandExiDocument() failed");
return V2G_EVENT_TERMINATE_CONNECTION; // If the mesage can't be decoded we have to terminate the tcp-connection
// (e.g. after an unexpected message)
}
types::iso15118::AppProtocols app_protocols; // to publish supported app protocol array
for (i = 0; i < conn->handshake_req.supportedAppProtocolReq.AppProtocol.arrayLen; i++) {
struct appHand_AppProtocolType* app_proto = &conn->handshake_req.supportedAppProtocolReq.AppProtocol.array[i];
char* proto_ns = strndup(static_cast<const char*>(app_proto->ProtocolNamespace.characters),
app_proto->ProtocolNamespace.charactersLen);
if (!proto_ns) {
dlog(DLOG_LEVEL_ERROR, "out-of-memory condition");
return V2G_EVENT_TERMINATE_CONNECTION;
}
dlog(DLOG_LEVEL_TRACE,
"handshake_req: Namespace: %s, Version: %" PRIu32 ".%" PRIu32 ", SchemaID: %" PRIu8 ", Priority: %" PRIu8,
proto_ns, app_proto->VersionNumberMajor, app_proto->VersionNumberMinor, app_proto->SchemaID,
app_proto->Priority);
if ((conn->ctx->supported_protocols & (1 << V2G_PROTO_DIN70121)) &&
(strcmp(proto_ns, DIN_70121_MSG_DEF) == 0) && (app_proto->VersionNumberMajor == DIN_70121_MAJOR) &&
(ev_app_priority >= app_proto->Priority)) {
conn->handshake_resp.supportedAppProtocolRes.ResponseCode =
(app_proto->VersionNumberMinor == DIN_70121_MINOR)
? appHand_responseCodeType_OK_SuccessfulNegotiation
: appHand_responseCodeType_OK_SuccessfulNegotiationWithMinorDeviation;
ev_app_priority = app_proto->Priority;
conn->handshake_resp.supportedAppProtocolRes.SchemaID = app_proto->SchemaID;
conn->ctx->selected_protocol = V2G_PROTO_DIN70121;
} else if ((conn->ctx->supported_protocols & (1 << V2G_PROTO_ISO15118_2013)) &&
(strcmp(proto_ns, ISO_15118_2013_MSG_DEF) == 0) &&
(app_proto->VersionNumberMajor == ISO_15118_2013_MAJOR) &&
(ev_app_priority >= app_proto->Priority)) {
conn->handshake_resp.supportedAppProtocolRes.ResponseCode =
(app_proto->VersionNumberMinor == ISO_15118_2013_MINOR)
? appHand_responseCodeType_OK_SuccessfulNegotiation
: appHand_responseCodeType_OK_SuccessfulNegotiationWithMinorDeviation;
ev_app_priority = app_proto->Priority;
conn->handshake_resp.supportedAppProtocolRes.SchemaID = app_proto->SchemaID;
conn->ctx->selected_protocol = V2G_PROTO_ISO15118_2013;
}
if (conn->ctx->debugMode == true) {
const types::iso15118::AppProtocol protocol = {
std::string(proto_ns), static_cast<int32_t>(app_proto->VersionNumberMajor),
static_cast<int32_t>(app_proto->VersionNumberMinor), static_cast<int32_t>(app_proto->SchemaID),
static_cast<int32_t>(app_proto->Priority)};
app_protocols.Protocols.push_back(protocol);
}
// TODO: ISO15118v2
free(proto_ns);
}
if (conn->ctx->debugMode == true) {
conn->ctx->p_charger->publish_ev_app_protocol(app_protocols);
/* form the content of V2G_Message type and publish the request*/
publish_var_V2G_Message(conn, true);
}
std::string selected_protocol_str;
if (conn->handshake_resp.supportedAppProtocolRes.ResponseCode ==
appHand_responseCodeType_OK_SuccessfulNegotiation ||
conn->handshake_resp.supportedAppProtocolRes.ResponseCode ==
appHand_responseCodeType_OK_SuccessfulNegotiationWithMinorDeviation) {
conn->handshake_resp.supportedAppProtocolRes.SchemaID_isUsed = (unsigned int)1;
if (V2G_PROTO_DIN70121 == conn->ctx->selected_protocol) {
dlog(DLOG_LEVEL_INFO, "Protocol negotiation was successful. Selected protocol is DIN70121");
selected_protocol_str = "DIN70121";
} else if (V2G_PROTO_ISO15118_2013 == conn->ctx->selected_protocol) {
dlog(DLOG_LEVEL_INFO, "Protocol negotiation was successful. Selected protocol is ISO15118");
selected_protocol_str = "ISO15118-2-2013";
} else if (V2G_PROTO_ISO15118_2010 == conn->ctx->selected_protocol) {
dlog(DLOG_LEVEL_INFO, "Protocol negotiation was successful. Selected protocol is ISO15118-2010");
selected_protocol_str = "ISO15118-2-2010";
}
} else {
dlog(DLOG_LEVEL_ERROR, "No compatible protocol found");
selected_protocol_str = "None";
next_event = V2G_EVENT_SEND_AND_TERMINATE; // Send response and terminate tcp-connection
}
if (conn->ctx->debugMode == true) {
conn->ctx->p_charger->publish_selected_protocol(selected_protocol_str);
}
if (conn->ctx->is_connection_terminated == true) {
dlog(DLOG_LEVEL_ERROR, "Connection is terminated. Abort charging");
return V2G_EVENT_TERMINATE_CONNECTION; // Abort charging without sending a response
}
/* Validate response code */
if ((conn->ctx->intl_emergency_shutdown == true) || (conn->ctx->stop_hlc == true) ||
(V2G_EVENT_SEND_AND_TERMINATE == next_event)) {
conn->handshake_resp.supportedAppProtocolRes.ResponseCode = appHand_responseCodeType_Failed_NoNegotiation;
dlog(DLOG_LEVEL_ERROR, "Abort charging session");
if (conn->ctx->terminate_connection_on_failed_response == true) {
next_event = V2G_EVENT_SEND_AND_TERMINATE; // send response and terminate the TCP-connection
}
}
/* encode response at the right buffer location */
conn->stream.byte_pos = V2GTP_HEADER_LENGTH;
conn->stream.bit_count = 0;
if (encode_appHand_exiDocument(&conn->stream, &conn->handshake_resp) != 0) {
dlog(DLOG_LEVEL_ERROR, "Encoding of the protocol handshake message failed");
next_event = V2G_EVENT_SEND_AND_TERMINATE;
}
return next_event;
}
int v2g_handle_connection(struct v2g_connection* conn) {
int rv = -1;
enum v2g_event rvAppHandshake = V2G_EVENT_NO_EVENT;
bool stop_receiving_loop = false;
int64_t start_time = 0; // in ms
enum v2g_protocol selected_protocol = V2G_UNKNOWN_PROTOCOL;
v2g_ctx_init_charging_state(conn->ctx, false);
conn->buffer = static_cast<uint8_t*>(malloc(DEFAULT_BUFFER_SIZE));
if (!conn->buffer)
return -1;
/* static setup */
conn->stream.data = conn->buffer;
/* Here is a good point to wait until the customer is ready for a resumed session,
* because we are waiting for the incoming message of the ev */
if (conn->d_link_action == dLinkAction::D_LINK_ACTION_PAUSE) {
// TODO: D_LINK pause
}
do {
/* setup for receive */
conn->stream.data[0] = 0;
conn->payload_len = 0;
exi_bitstream_init(&conn->stream, conn->buffer, 0, 0, nullptr);
/* next call return -1 on error, 1 if connection was closed unexpectedly, 0 on success */
rv = v2g_incoming_v2gtp(conn);
if (rv != 0) {
dlog(DLOG_LEVEL_ERROR, "v2g_incoming_v2gtp() failed");
goto error_out;
}
if (conn->ctx->is_connection_terminated == true) {
rv = -1;
goto error_out;
}
/* next call return -1 on non-recoverable errors, 1 on recoverable errors, 0 on success */
rvAppHandshake = v2g_handle_apphandshake(conn);
if (rvAppHandshake == V2G_EVENT_IGNORE_MSG) {
dlog(DLOG_LEVEL_WARNING, "v2g_handle_apphandshake() failed, ignoring packet");
}
} while ((rv == 1) && (rvAppHandshake == V2G_EVENT_IGNORE_MSG));
/* stream setup for sending is done within v2g_handle_apphandshake */
/* send supportedAppRes message */
if ((rvAppHandshake == V2G_EVENT_SEND_AND_TERMINATE) || (rvAppHandshake == V2G_EVENT_NO_EVENT)) {
/* form the content of V2G_Message type and publish the response for debugging*/
if (conn->ctx->debugMode == true) {
publish_var_V2G_Message(conn, false);
}
rv = v2g_outgoing_v2gtp(conn);
if (rv == -1) {
dlog(DLOG_LEVEL_ERROR, "v2g_outgoing_v2gtp() failed");
goto error_out;
}
}
/* terminate connection, if supportedApp handshake has failed */
if ((rvAppHandshake == V2G_EVENT_SEND_AND_TERMINATE) || (rvAppHandshake == V2G_EVENT_TERMINATE_CONNECTION)) {
rv = -1;
goto error_out;
}
/* Backup the selected protocol, because this value is shared and can be reseted while unplugging. */
selected_protocol = conn->ctx->selected_protocol;
/* allocate in/out documents dynamically */
switch (selected_protocol) {
case V2G_PROTO_DIN70121:
case V2G_PROTO_ISO15118_2010:
conn->exi_in.dinEXIDocument = static_cast<struct din_exiDocument*>(calloc(1, sizeof(struct din_exiDocument)));
if (conn->exi_in.dinEXIDocument == NULL) {
dlog(DLOG_LEVEL_ERROR, "out-of-memory");
goto error_out;
}
conn->exi_out.dinEXIDocument = static_cast<struct din_exiDocument*>(calloc(1, sizeof(struct din_exiDocument)));
if (conn->exi_out.dinEXIDocument == NULL) {
dlog(DLOG_LEVEL_ERROR, "out-of-memory");
goto error_out;
}
break;
case V2G_PROTO_ISO15118_2013:
conn->exi_in.iso2EXIDocument =
static_cast<struct iso2_exiDocument*>(calloc(1, sizeof(struct iso2_exiDocument)));
if (conn->exi_in.iso2EXIDocument == NULL) {
dlog(DLOG_LEVEL_ERROR, "out-of-memory");
goto error_out;
}
conn->exi_out.iso2EXIDocument =
static_cast<struct iso2_exiDocument*>(calloc(1, sizeof(struct iso2_exiDocument)));
if (conn->exi_out.iso2EXIDocument == NULL) {
dlog(DLOG_LEVEL_ERROR, "out-of-memory");
goto error_out;
}
break;
default:
goto error_out; // if protocol is unknown
}
do {
/* setup for receive */
conn->stream.data[0] = 0;
conn->stream.bit_count = 0;
conn->stream.byte_pos = 0;
conn->payload_len = 0;
/* next call return -1 on error, 1 connection was closed unexpectedly, 0 on success */
rv = v2g_incoming_v2gtp(conn);
if (rv == 1) {
dlog(DLOG_LEVEL_ERROR, "Timeout waiting for next request or peer closed connection");
break;
} else if (rv == -1) {
dlog(DLOG_LEVEL_ERROR, "v2g_incoming_v2gtp() (previous message \"%s\") failed",
v2g_msg_type[conn->ctx->last_v2g_msg]);
break;
}
start_time = getmonotonictime(); // To calc the duration of req msg configuration
/* according to agreed protocol decode the stream */
enum v2g_event v2gEvent = V2G_EVENT_NO_EVENT;
switch (selected_protocol) {
case V2G_PROTO_DIN70121:
case V2G_PROTO_ISO15118_2010:
memset(conn->exi_in.dinEXIDocument, 0, sizeof(struct din_exiDocument));
rv = decode_din_exiDocument(&conn->stream, conn->exi_in.dinEXIDocument);
if (rv != 0) {
dlog(DLOG_LEVEL_ERROR, "decode_dinExiDocument() (previous message \"%s\") failed: %d",
v2g_msg_type[conn->ctx->last_v2g_msg], rv);
/* we must ignore packet which we cannot decode, so reset rv to zero to stay in loop */
rv = 0;
v2gEvent = V2G_EVENT_IGNORE_MSG;
break;
}
memset(conn->exi_out.dinEXIDocument, 0, sizeof(struct din_exiDocument));
v2gEvent = din_handle_request(conn);
break;
case V2G_PROTO_ISO15118_2013:
memset(conn->exi_in.iso2EXIDocument, 0, sizeof(struct iso2_exiDocument));
rv = decode_iso2_exiDocument(&conn->stream, conn->exi_in.iso2EXIDocument);
if (rv != 0) {
dlog(DLOG_LEVEL_ERROR, "decode_iso2_exiDocument() (previous message \"%s\") failed: %d",
v2g_msg_type[conn->ctx->last_v2g_msg], rv);
/* we must ignore packet which we cannot decode, so reset rv to zero to stay in loop */
rv = 0;
v2gEvent = V2G_EVENT_IGNORE_MSG;
break;
}
conn->stream.byte_pos = 0; // Reset pos for the case if exi msg will be configured over mqtt
memset(conn->exi_out.iso2EXIDocument, 0, sizeof(struct iso2_exiDocument));
v2gEvent = iso_handle_request(conn);
break;
default:
goto error_out; // if protocol is unknown
}
/* form the content of V2G_Message type and publish the request*/
if (conn->ctx->debugMode == true) {
publish_var_V2G_Message(conn, true);
}
switch (v2gEvent) {
case V2G_EVENT_SEND_AND_TERMINATE:
stop_receiving_loop = true;
case V2G_EVENT_NO_EVENT: { // fall-through intended
/* Reset v2g-buffer */
conn->stream.data[0] = 0;
conn->stream.bit_count = 0;
conn->stream.byte_pos = V2GTP_HEADER_LENGTH;
conn->stream.data_size = DEFAULT_BUFFER_SIZE;
/* Configure msg and send */
switch (selected_protocol) {
case V2G_PROTO_DIN70121:
case V2G_PROTO_ISO15118_2010:
if ((rv = encode_din_exiDocument(&conn->stream, conn->exi_out.dinEXIDocument)) != 0) {
dlog(DLOG_LEVEL_ERROR, "encode_dinExiDocument() (message \"%s\") failed: %d",
v2g_msg_type[conn->ctx->current_v2g_msg], rv);
}
break;
case V2G_PROTO_ISO15118_2013:
if ((rv = encode_iso2_exiDocument(&conn->stream, conn->exi_out.iso2EXIDocument)) != 0) {
dlog(DLOG_LEVEL_ERROR, "encode_iso2_exiDocument() (message \"%s\") failed: %d",
v2g_msg_type[conn->ctx->current_v2g_msg], rv);
}
break;
default:
goto error_out; // if protocol is unknown
}
/* Wait max. res-time before sending the next response */
int64_t time_to_conf_res = getmonotonictime() - start_time;
if (time_to_conf_res < MAX_RES_TIME) {
// dlog(DLOG_LEVEL_ERROR,"time_to_conf_res %llu", time_to_conf_res);
std::this_thread::sleep_for(std::chrono::microseconds((MAX_RES_TIME - time_to_conf_res) * 1000));
} else {
dlog(DLOG_LEVEL_WARNING, "Response message (type %d) not configured within %d ms (took %" PRIi64 " ms)",
conn->ctx->current_v2g_msg, MAX_RES_TIME, time_to_conf_res);
}
}
case V2G_EVENT_SEND_RECV_EXI_MSG: { // fall-through intended
/* form the content of V2G_Message type and publish the response for debugging*/
if (conn->ctx->debugMode == true) {
publish_var_V2G_Message(conn, false);
}
/* Write header and send next res-msg */
if ((rv != 0) || ((rv = v2g_outgoing_v2gtp(conn)) == -1)) {
dlog(DLOG_LEVEL_ERROR, "v2g_outgoing_v2gtp() \"%s\" failed: %d",
v2g_msg_type[conn->ctx->current_v2g_msg], rv);
break;
}
break;
}
case V2G_EVENT_IGNORE_MSG:
dlog(DLOG_LEVEL_ERROR, "Ignoring V2G request message \"%s\". Waiting for next request",
v2g_msg_type[conn->ctx->current_v2g_msg]);
break;
case V2G_EVENT_TERMINATE_CONNECTION: // fall-through intended
default:
dlog(DLOG_LEVEL_ERROR, "Failed to handle V2G request message \"%s\"",
v2g_msg_type[conn->ctx->current_v2g_msg]);
stop_receiving_loop = true;
break;
}
} while ((rv == 0) && (stop_receiving_loop == false));
error_out:
switch (selected_protocol) {
case V2G_PROTO_DIN70121:
case V2G_PROTO_ISO15118_2010:
if (conn->exi_in.dinEXIDocument != NULL)
free(conn->exi_in.dinEXIDocument);
if (conn->exi_out.dinEXIDocument != NULL)
free(conn->exi_out.dinEXIDocument);
break;
case V2G_PROTO_ISO15118_2013:
if (conn->exi_in.iso2EXIDocument != NULL)
free(conn->exi_in.iso2EXIDocument);
if (conn->exi_out.iso2EXIDocument != NULL)
free(conn->exi_out.iso2EXIDocument);
break;
default:
break;
}
if (conn->buffer != NULL) {
free(conn->buffer);
}
conn->last_v2g_msg_at_disconnect = conn->ctx->current_v2g_msg;
v2g_ctx_init_charging_state(conn->ctx, true);
return rv ? -1 : 0;
}
uint64_t v2g_session_id_from_exi(bool is_iso, void* exi_in) {
uint64_t session_id = 0;
if (is_iso) {
struct iso2_exiDocument* req = static_cast<struct iso2_exiDocument*>(exi_in);
struct iso2_MessageHeaderType* hdr = &req->V2G_Message.Header;
/* the provided session id could be smaller (error) in case that the peer did not
* send our full session id back to us; this is why we init the id with 0 above
* and only copy the provided byte len
*/
memcpy(&session_id, &hdr->SessionID.bytes, std::min((int)sizeof(session_id), (int)hdr->SessionID.bytesLen));
} else {
struct din_exiDocument* req = static_cast<struct din_exiDocument*>(exi_in);
struct din_MessageHeaderType* hdr = &req->V2G_Message.Header;
/* see comment above */
memcpy(&session_id, &hdr->SessionID.bytes, std::min((int)sizeof(session_id), (int)hdr->SessionID.bytesLen));
}
return session_id;
}