1.6.1. Error Framework

1.6.1.1. Purpose

The error framework is used to handle errors in the EVerest framework.

As not every module can “decide” by itself how to react to an error and how to handle it, the error framework provides functionality that allows modules to react to errors that are raised in required other modules.

The other main purpose of the error framework is to provide a way to monitor and report errors in the system. This can be used for example for displaying or reporting to an OCPP backend.

1.6.1.2. Usage

1.6.1.2.1. General

1.6.1.2.1.1. Raise an error

Each implementation of an interface can raise errors that are defined in the interface. There is one function raise_error that takes an error object as argument. The error object is an instance of the class Error. To create the initial error object, the ErrorFactory is used.

1.6.1.2.1.2. Clear an error

An error can be cleared by the same implementation that raised the error. There are multiple functions to clear an error.

The function clear_error provides two different signatures. The first signature takes the ErrorType and a boolean clear_all as arguments. clear_all is optional and defaults to False. If clear_all is True, all errors of the given ErrorType are cleared. If clear_all is False, only the Error with ErrorType = “” is cleared.

The second signature takes ErrorType and ErrorSubType as arguments. In this case, only the error with the given ErrorType and ErrorSubType is cleared.

The function clear_all_errors_of_impl clears all errors of the current implementation.

1.6.1.2.1.3. Subscribe to an error

A module can subscribe to errors of its requirements. This way the module can react to those errors. There are two functions to subscribe to errors.

The function subscribe_error takes the ErrorType and two callback functions as arguments. The ErrorType is the type of the error that the module wants to subscribe to. The first callback is called when the error is raised. The second callback is called when the error is cleared.

The function subscribe_all_errors takes again two callback functions as arguments. The first callback is called when an error of any type is raised by the requirement. The second callback is called when an error is cleared.

1.6.1.2.1.4. Subscribe globally to all errors

This feature is currently only available for C++ modules. It allows a module to susbcribe to globally all errors of all modules. This can be used for example for logging purposes or error reporting.

To enable this functionality, the flag enable_global_errors in the module’s manifest file must be set to true. With this, the function subscribe_global_all_errors is added to the autogenerated code. This way the function can be used as the other subscribe functions, with two callback functions as arguments.

1.6.1.2.1.5. The ErrorFactory

Since a module does not have direct access to some information that is required to create an error object, as for example the module_id, the class ErrorFactory is used, which is provided for each implementation of an interface, with correct default values.

The ErrorFactory is used to create the initial error object. This error object can be raised as it is, or can be modified before raising.

The ErrorFactory provides multiple signatures for the function create_error: The first signature takes no arguments and creates an error object with default values. The second signature takes the ErrorType, ErrorSubType and message as arguments. The third signature takes the ErrorType, ErrorSubType, message and severity as arguments. The fourth signature takes the ErrorType, ErrorSubType, message and state as arguments. The fifth signature takes the ErrorType, ErrorSubType, message, severity and state as arguments.

1.6.1.2.1.6. The ErrorStateMonitor

The ErrorStateMonitor is a class that is used to monitor the error state of implementations and requirements. It is provided for each implementation of an interface and for each requirement of an module.

To check if an error is currently active, the function is_error_active is used. This function takes the ErrorType and ErrorSubType as arguments and returns a boolean value. If the error is active, the function returns True, otherwise False.

To check if a specific set of errors is in a specific state, the struct StateCondition is defined. This struct has the members ErrorType, ErrorSubType and active: boolean. The function is_condition_satisfied can either take a single StateCondition or a list of StateCondition as argument. If a single StateCondition is passed, the function returns True if the error is in the state as defined in the StateCondition. If a list of StateCondition is passed, the function returns True if all conditions are satisfied.

1.6.1.2.2. Syntax in a C++ module

You can find two example modules written in C++ in the examples folder: ExampleErrorRaiser and ExampleErrorSubscriber.

1.6.1.2.2.1. Raise an error

Can be done in the implementation of an interface.

// Create an error object
Error error_object = this->error_factory->create_error(
    "example/ExampleErrorA",    // ErrorType
    "",                         // ErrorSubType
    "This is an example error"  // message
);
// Raise the error
raise_error(error_object);

1.6.1.2.2.2. Clear an error

Can be done in the implementation of an interface.

// Clear all errors of the ErrorType "example/ExampleErrorA"
clear_error(
    "example/ExampleErrorA",    // ErrorType
    true                        // clear_all
);

// Clear the error with ErrorType "example/ExampleErrorA" and ErrorSubType ""
clear_error(
    "example/ExampleErrorA",    // ErrorType
    ""                          // ErrorSubType
);
clear_error(
    "example/ExampleErrorA",    // ErrorType
    false                       // clear_all
);
clear_error(
    "example/ExampleErrorA"     // ErrorType
);                              // clear_all defaults to false

// Clear all errors of the current implementation
clear_all_errors_of_impl();

1.6.1.2.2.3. Subscribe to an error

May be done in the init function of the implementation.

// Subscribe to an error of the ErrorType "example/ExampleErrorA"
subscribe_error(
    "example/ExampleErrorA",                    // ErrorType
    [](Error error) {                           // callback
        // Do something when the error is raised
    },
    [](Error error) {                           // clear_callback
        // Do something when the error is cleared
    }
);

// Subscribe to all errors of the requirement
subscribe_all_errors(
    [](Error error) {                           // callback
        // Do something when an error is raised
    },
    [](Error error) {                           // clear_callback
        // Do something when an error is cleared
    }
);

1.6.1.2.2.4. Subscribe to global all errors

Needs to be enabled in the manifest file of the module. May be done in the init function of the implementation.

// Subscribe to global all errors
subscribe_global_all_errors(
    [](Error error) {                           // callback
        // Do something when an error is raised
    },
    [](Error error) {                           // clear_callback
        // Do something when an error is cleared
    }
);

1.6.1.2.2.5. The ErrorFactory

Is used to create an error object.

Error error_object_0 = this->error_factory->create_error();

Error error_object_1 = this->error_factory->create_error(
    "example/ExampleErrorA",        // ErrorType
    "",                             // ErrorSubType
    "This is an example error"      // message
);

Error error_object_2 = this->error_factory->create_error(
    "example/ExampleErrorA",        // ErrorType
    "",                             // ErrorSubType
    "This is an example error",     // message
    Everest::error::Severity::High  // severity
);

Error error_object_3 = this->error_factory->create_error(
    "example/ExampleErrorA",        // ErrorType
    "",                             // ErrorSubType
    "This is an example error",     // message
    Everest::error::State::Active   // state
);

Error error_object_4 = this->error_factory->create_error(
    "example/ExampleErrorA",        // ErrorType
    "",                             // ErrorSubType
    "This is an example error",     // message
    Everest::error::Severity::High, // severity
    Everest::error::State::Active   // state
);

1.6.1.2.2.6. The ErrorStateMonitor

Is used to monitor the error state of implementations and requirements. Can be accessed in the implementation of an interface / anytime for requirements.

Get the ErrorStateMonitor:

// Get the ErrorStateMonitor of an implementation
std::shared_ptr<ErrorStateMonitor>& monitor = this->error_state_monitor;

// Get the ErrorStateMonitor of a requirement
std::shared_ptr<ErrorStateMonitor>& monitor = this->mod->r_example_raiser->error_state_monitor;

Check if an error is active:

// Check if an error of the ErrorType "example/ExampleErrorA" is active
bool is_active = monitor->is_error_active(
    "example/ExampleErrorA",    // ErrorType
    ""                          // ErrorSubType
);

Check if a specific set of errors is in a specific state:

// Check if an error of the ErrorType "example/ExampleErrorA" is active
StateCondition condition = {
    "example/ExampleErrorA",        // ErrorType
    "",                             // ErrorSubType
    true                            // active
};
bool is_satisfied = monitor->is_condition_satisfied(condition);

// Check if multiple errors are active
std::list<StateCondition> conditions = {
    {
        "example/ExampleErrorA",    // ErrorType
        "",                         // ErrorSubType
        true                        // active
    },
    {
        "example/ExampleErrorB",    // ErrorType
        "",                         // ErrorSubType
        true                        // active
    }
};
bool are_satisfied = monitor->is_condition_satisfied(conditions);

1.6.1.2.3. Syntax in a Python module

You can find two example modules written in Python in the examples folder: PyExampleErrorRaiser and PyExampleErrorSubscriber.

The error related classes need to be imported from the everest module.

from everest.framework import error

1.6.1.2.3.1. Raise an error

Can be done in the implementation of an interface after initializing. In opposite to the C++ implementation, the raise function is called on the module object and takes additionally the implementation_id as argument.

# Create an error object
error_object = self._mod.get_error_factory("example_raiser").create_error(
    "example/ExampleErrorA",    # ErrorType
    "",                         # ErrorSubType
    "This is an example error"  # message
)
# Raise the error
self._mod.raise_error(
    "example_raiser",           # implementation_id
    error_object                # error
)

1.6.1.2.3.2. Clear an error

Can be done in the implementation of an interface after raising. In opposite to the C++ implementation, the clear function is called on the module object and takes additionally the implementation_id as argument.

# Clear all errors of the ErrorType "example/ExampleErrorA"
self._mod.clear_error(
    "example_raiser",           # implementation_id
    "example/ExampleErrorA",    # ErrorType
    True                        # clear_all
)

# Clear the error with ErrorType "example/ExampleErrorA" and ErrorSubType ""
self._mod.clear_error(
    "example_raiser",           # implementation_id
    "example/ExampleErrorA",    # ErrorType
    ""                          # ErrorSubType
)
self._mod.clear_error(
    "example_raiser",           # implementation_id
    "example/ExampleErrorA",    # ErrorType
    False                       # clear_all
)
self._mod.clear_error(
    "example_raiser",           # implementation_id
    "example/ExampleErrorA"     # ErrorType
)                               # clear_all defaults to false

# Clear all errors of the current implementation
self._mod.clear_all_errors_of_impl(
    "example_raiser"            # implementation_id
)

1.6.1.2.3.3. Subscribe to an error

Can be done in the init function of the implementation. In opposite to the C++ implementation, the subscribe function is called on the module object and takes additionally the requirement as argument.

# Subscribe to an error of the ErrorType "example/ExampleErrorA"
self._mod.subscribe_error(
    self._setup.connections["example_raiser"][0],   # requirement
    "example/ExampleErrorA",                        # ErrorType
    lambda error: print("Error raised: ", error),   # callback
    lambda error: print("Error cleared: ", error)   # clear_callback
)

# Subscribe to all errors of the requirement
self._mod.subscribe_all_errors(
    self._setup.connections["example_raiser"][0],   # implementation_id
    lambda error: print("Error raised: ", error),   # callback
    lambda error: print("Error cleared: ", error)   # clear_callback
)

1.6.1.2.3.4. Subscribe to global all errors

This feature is currently only available for C++ modules.

1.6.1.2.3.5. The ErrorFactory

Is used to create an error object.

error_object_0 = self._mod.get_error_factory("example_raiser").create_error()

error_object_1 = self._mod.get_error_factory("example_raiser").create_error(
    "example/ExampleErrorA",        # ErrorType
    "",                             # ErrorSubType
    "This is an example error"      # message
)

error_object_2 = self._mod.get_error_factory("example_raiser").create_error(
    "example/ExampleErrorA",        # ErrorType
    "",                             # ErrorSubType
    "This is an example error",     # message
    error.Severity.High             # severity
)

error_object_3 = self._mod.get_error_factory("example_raiser").create_error(
    "example/ExampleErrorA",        # ErrorType
    "",                             # ErrorSubType
    "This is an example error",     # message
    error.State.Active              # state
)

error_object_4 = self._mod.get_error_factory("example_raiser").create_error(
    "example/ExampleErrorA",        # ErrorType
    "",                             # ErrorSubType
    "This is an example error",     # message
    error.Severity.High,            # severity
    error.State.Active              # state
)

1.6.1.2.3.6. The ErrorStateMonitor

Get the ErrorStateMonitor:

# Get the ErrorStateMonitor of an implementation
monitor = self._mod.get_error_state_monitor_impl(
    "example_raiser"                                # implementation_id
)

# Get the ErrorStateMonitor of a requirement
monitor = self._mod.get_error_state_monitor_req(
    self._setup.connections["example_raiser"][0]    # requirement
)

Check if an error is active:

# Check if an error of the ErrorType "example/ExampleErrorA" is active
is_active = monitor.is_error_active(
    "example/ExampleErrorA",    # ErrorType
    ""                          # ErrorSubType
)

Check if a specific set of errors is in a specific state:

# Check if an error of the ErrorType "example/ExampleErrorA" is active
condition = error.StateCondition(
    "example/ExampleErrorA",    # ErrorType
    "",                         # ErrorSubType
    True                        # active
)
is_satisfied = monitor.is_condition_satisfied(condition)

# Check if multiple errors are active
conditions = [
    error.StateCondition(
        "example/ExampleErrorA",    # ErrorType
        "",                         # ErrorSubType
        True                        # active
    ),
    error.StateCondition(
        "example/ExampleErrorB",    # ErrorType
        "",                         # ErrorSubType
        True                        # active
    )
]
are_satisfied = monitor.is_condition_satisfied(conditions)

1.6.1.2.4. Syntax in a Javascript module

You can find two example modules written in Javascript in the examples folder: JsExampleErrorRaiser and JsExampleErrorSubscriber.

1.6.1.2.4.1. Raise an error

Can be done in the implementation of an interface after initializing.

// Create an error object
let error_object = mod.provides.example_raiser.error_factory.create_error(
    "example/ExampleErrorA",    // ErrorType
    "",                         // ErrorSubType
    "This is an example error"  // message
);
// Raise the error
mod.provides.example_raiser.raise_error(error_object);

1.6.1.2.4.2. Clear an error

Can be done in the implementation of an interface after raising.

// Clear all errors of the ErrorType "example/ExampleErrorA"
mod.provides.example_raiser.clear_error(
    "example/ExampleErrorA",    // ErrorType
    true                        // clear_all
);

// Clear the error with ErrorType "example/ExampleErrorA" and ErrorSubType ""
mod.provides.example_raiser.clear_error(
    "example/ExampleErrorA",    // ErrorType
    ""                          // ErrorSubType
);
mod.provides.example_raiser.clear_error(
    "example/ExampleErrorA",    // ErrorType
    false                       // clear_all
);
mod.provides.example_raiser.clear_error(
    "example/ExampleErrorA"     // ErrorType
);                              // clear_all defaults to false

// Clear all errors of the current implementation
mod.provides.example_raiser.clear_all_errors_of_impl();

1.6.1.2.4.3. Subscribe to an error

May be done in the init function of the implementation. In Javascript, the subscription is limited to only one callback per subscription. So the example below wouldn’t be possible since it would subscribe two times to example/ExampleErrorA.

// Subscribe to an error of the ErrorType "example/ExampleErrorA"
setup.uses.example_raiser.subscribe_error(
    "example/ExampleErrorA",                    // ErrorType
    (error) => {                                // callback
        // Do something when the error is raised
    },
    (error) => {                                // clear_callback
        // Do something when the error is cleared
    }
);

// Subscribe to all errors of the requirement
setup.uses.example_raiser.subscribe_all_errors(
    (error) => {                                // callback
        // Do something when an error is raised
    },
    (error) => {                                // clear_callback
        // Do something when an error is cleared
    }
);

1.6.1.2.4.4. Subscribe to global all errors

This feature is currently only available for C++ modules.

1.6.1.2.4.5. The ErrorFactory

Is used to create new error objects. The function signature of create_error with arguments ErrorType, ErrorSubType, message, and state is not available in Javascript.

let error_object_0 = mod.provides.example_raiser.error_factory.create_error();

let error_object_1 = mod.provides.example_raiser.error_factory.create_error(
    "example/ExampleErrorA",        // ErrorType
    "",                             // ErrorSubType
    "This is an example error"      // message
);

let error_object_2 = mod.provides.example_raiser.error_factory.create_error(
    "example/ExampleErrorA",        // ErrorType
    "",                             // ErrorSubType
    "This is an example error",     // message
    error.Severity.High             // severity
);

let error_object_4 = mod.provides.example_raiser.error_factory.create_error(
    "example/ExampleErrorA",        // ErrorType
    "",                             // ErrorSubType
    "This is an example error",     // message
    error.Severity.High,            // severity
    error.State.Active              // state
);

1.6.1.2.4.6. The ErrorStateMonitor

Get the ErrorStateMonitor:

// Get the ErrorStateMonitor of an implementation
let monitor = mod.provides.example_raiser.error_state_monitor;

// Get the ErrorStateMonitor of a requirement
let monitor = setup.uses.example_raiser.error_state_monitor;

Check if an error is active:

// Check if an error of the ErrorType "example/ExampleErrorA" is active
let is_active = monitor.is_error_active(
    "example/ExampleErrorA",    // ErrorType
    ""                          // ErrorSubType
);

Check if a specific set of errors is in a specific state:

// Check if an error of the ErrorType "example/ExampleErrorA" is active
let condition = {
    ErrorType: "example/ExampleErrorA",         // ErrorType
    ErrorSubType: "",                           // ErrorSubType
    active: true                                // active
};
let is_satisfied = monitor.is_condition_satisfied(condition);

// Check if multiple errors are active
let conditions = [
    {
        ErrorType: "example/ExampleErrorA",     // ErrorType
        ErrorSubType: "",                       // ErrorSubType
        active: true                            // active
    },
    {
        ErrorType: "example/ExampleErrorB",     // ErrorType
        ErrorSubType: "",                       // ErrorSubType
        active: true                            // active
    }
];
let are_satisfied = monitor.is_condition_satisfied(conditions);

1.6.1.3. Usage Guide

1.6.1.3.1. Creating Error objects

Error objects may always be created using the ErrorFactory of the implementation.

Error objects can be edited after creation, before raising them.

The following attributes may not be changed after creation: - timestamp - origin.module_id - origin.implementation_id - uuid

1.6.1.3.2. The global subscription

If a module is subscribed to global all errors, it may do only “reporting” actions, but no “handling” actions. This means that the module does not change its behavior based on the error, but only reports the error for example to a log file.

1.6.1.3.3. Side effects of raising errors

The error framework allow module implementations to get notified about an error from one of their requirements by subscribing to the error. This can be used for reporting purposes (e.g. via OCPP) or it can be used to adjust the control flow of the module based on the raised error.

It is important to note that raising errors can therefore lead to side effects within other modules. The side effects shall be documented as part of the module documentation (see e.g. EvseManager or OCPP).

1.6.1.4. Architecture

t.b.d.

This section is still under construction. The most is implemented in the directories include/utils/error/ and lib/error/ in the everest-framework repository.