Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(OCPP1.6): forward VendorWarning appropriately #949

Merged
merged 8 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading