Skip to content

Commit

Permalink
Merge branch 'main' into feature/ocpp-integration-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Pietfried authored Nov 12, 2024
2 parents 2f993d5 + 23d3dc6 commit 025857d
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 17 deletions.
54 changes: 43 additions & 11 deletions modules/OCPP/OCPP.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
#include "OCPP.hpp"

#include <fstream>
#include <optional>
#include <sstream>
#include <string>

#include "generated/types/ocpp.hpp"
#include "ocpp/common/types.hpp"
#include "ocpp/v16/types.hpp"
#include <fmt/core.h>
#include <fstream>
#include <ocpp_conversions.hpp>

#include <conversions.hpp>
#include <error_mapping.hpp>
#include <evse_security_ocpp.hpp>
#include <external_energy_limits.hpp>
#include <optional>

namespace module {

Expand Down Expand Up @@ -48,16 +52,44 @@ static ocpp::v16::ErrorInfo get_error_info(const Everest::error::Error& error) {
"EVerest", "caused_by:" + error.message};
}

// check if is VendorError
if (error_type.find("VendorError") != std::string::npos) {
return ocpp::v16::ErrorInfo{
uuid, ocpp::v16::ChargePointErrorCode::OtherError, false, error.message, error.origin.to_string(),
error.sub_type};
}
const auto get_simplified_error_type = [](const std::string& error_type) {
// this function should return everything after the first '/'
// delimiter - if there is no delimiter or the delimiter is at
// the end, it should return the input itself
static constexpr auto TYPE_INTERFACE_DELIMITER = '/';

auto input = std::istringstream(error_type);
std::string tmp;

// move right after the first delimiter
std::getline(input, tmp, TYPE_INTERFACE_DELIMITER);

// Default case
return ocpp::v16::ErrorInfo{
uuid, ocpp::v16::ChargePointErrorCode::InternalError, false, error.description, std::nullopt, error_type};
if (!input) {
// no delimiter found or delimiter at the end
return error_type;
}

// get the rest of the input
std::getline(input, tmp);

return tmp;
};

const auto is_fault = [](const Everest::error::Error&) {
// NOTE (aw): this could be customized, depending on the error
return false;
};

static constexpr auto TYPE_DELIMITER = '/';

return {
uuid,
ocpp::v16::ChargePointErrorCode::OtherError,
is_fault(error),
error.origin.to_string(), // info
error.message, // vendor id
get_simplified_error_type(error.type) + TYPE_DELIMITER + error.sub_type, // vendor error code
};
}

void create_empty_user_config(const fs::path& user_config_path) {
Expand Down
49 changes: 43 additions & 6 deletions modules/OCPP/doc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -276,15 +276,52 @@ it initiates a **StatusNotification.req** that contains information about the er
The field **status** of the **StatusNotification.req** will be set to faulted only in case the error is of the special type
**evse_manager/Inoperative**. The field **connectorId** is set based on the mapping (for EVSE id and connector id) of the origin of the error.
If no mapping is provided, the error will be reported on connectorId 0. Note that the mapping can be configured per module inside the
EVerest config file. The field **errorCode** is set based on the **type** property of the error.
EVerest config file.

The fields **info**, **vendorId**, and **vendorErrorCode** are set based on the error type and the provided error properties. Please see the
definition of `get_error_info` to see how the **StatusNotification.req** is constructed based on the given error.
For all other errors, raised in EVerest, the following mapping to an
OCPP **StatusNotification.req** will be used:

The **StatusNotification.req** message has some limitations with respect to reporting errors:
* **StatusNotification.req** property ``errorCode`` will always be
``OtherError``
* **StatusNotification.req** property ``status`` will reflect the present status of the
charge point
* **StatusNotification.req** property ``info`` -> origin of EVerest error
* **StatusNotification.req** property ``vendorErrorCode`` -> EVerest error type and
subtype (the error type is simplified, meaning, that its leading part,
the interface name, is stripped)
* **StatusNotification.req** property ``vendorId`` -> EVerest error message

* Single errors cannot simply be cleared. If multiple errors are raised, it is not possible to clear individual errors.
* Some fields of the message have relatively small character limits (e.g., **info** with 50 characters, **vendorErrorCode** with 50 characters).
The reason for using the **StatusNotification.req** property property
``vendorId`` for the error message is that it can carry the largest
string (255 characters), whereas the other fields (``info`` and
``vendorErrorCode``) only allow up to 50 characters.

If for example the module with id `yeti_driver` within its
implementation with id `board_support` creates the following error:

.. code-block:: cpp
error_factory->create_error("evse_board_support/EnergyManagement",
"OutOfEnergy", "someone cut the wires")
the corresponding fields in the **StatusNotification.req** message will
look like this:

.. code-block:: JSON
{
"info": "yeti_driver->board_support",
"vendorErrorCode": "EnergyManagement/OutOfEnergy",
"vendorId": "someone cut the wires"
}
The **StatusNotification.req** message has some limitations with respect
to reporting errors:

* Single errors cannot simply be cleared. If multiple errors are raised,
it is not possible to clear individual errors.
* ``vendorId``, ``info`` and ``vendorErrorCode`` are limited in length
(see above).

This module attempts to follow the Minimum Required Error Codes (MRECS): https://inl.gov/chargex/mrec/. This proposes a unified
methodology to define and classify a minimum required set of error codes and how to report them via OCPP1.6.
Expand Down

0 comments on commit 025857d

Please sign in to comment.