Skip to content

Commit

Permalink
Add new Error Handling functionality
Browse files Browse the repository at this point in the history
* subscribe error
* raise error
* request clear error

Signed-off-by: Andreas Heinrich <andreas.heinrich@rwth-aachen.de>
  • Loading branch information
andistorm committed Oct 18, 2023
1 parent 206473f commit 5a320bf
Show file tree
Hide file tree
Showing 17 changed files with 816 additions and 51 deletions.
2 changes: 1 addition & 1 deletion everestjs/everestjs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ static Napi::Value boot_module(const Napi::CallbackInfo& info) {
// connect to mqtt server and start mqtt mainloop thread
auto everest_handle = std::make_unique<Everest::Everest>(
module_id, *config, validate_schema, rs->mqtt_broker_host, rs->mqtt_broker_port, rs->mqtt_everest_prefix,
rs->mqtt_external_prefix, rs->telemetry_prefix, rs->telemetry_enabled);
rs->mqtt_external_prefix, rs->telemetry_prefix, rs->telemetry_enabled, rs->errors_dir);

ctx = new EvModCtx(std::move(everest_handle), module_manifest, env);

Expand Down
6 changes: 3 additions & 3 deletions everestpy/src/everest/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
std::unique_ptr<Everest::Everest> Module::create_everest_instance(const std::string& module_id,
const RuntimeSession& session) {
const auto& rs = session.get_runtime_settings();
return std::make_unique<Everest::Everest>(module_id, session.get_config(), rs->validate_schema,
rs->mqtt_broker_host, rs->mqtt_broker_port, rs->mqtt_everest_prefix,
rs->mqtt_external_prefix, rs->telemetry_prefix, rs->telemetry_enabled);
return std::make_unique<Everest::Everest>(
module_id, session.get_config(), rs->validate_schema, rs->mqtt_broker_host, rs->mqtt_broker_port,
rs->mqtt_everest_prefix, rs->mqtt_external_prefix, rs->telemetry_prefix, rs->telemetry_enabled, rs->errors_dir);
}

static std::string get_ev_module_from_env() {
Expand Down
12 changes: 12 additions & 0 deletions include/framework/ModuleAdapter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <everest/logging.hpp>
#include <utils/conversions.hpp>
#include <utils/date.hpp>
#include <utils/error.hpp>

#include <iomanip>
#include <iostream>
Expand Down Expand Up @@ -71,6 +72,12 @@ struct ModuleAdapter {
using CallFunc = std::function<Result(const Requirement&, const std::string&, Parameters)>;
using PublishFunc = std::function<void(const std::string&, const std::string&, Value)>;
using SubscribeFunc = std::function<void(const Requirement&, const std::string&, ValueCallback)>;
using SubscribeErrorFunc = std::function<void(const Requirement&, const std::string&, error::ErrorCallback)>;
using SubscribeErrorClearedFunc = std::function<void(const Requirement&, const std::string&, error::ErrorCallback)>;
using RaiseErrorFunc = std::function<error::ErrorHandle(const std::string&, const std::string&, const std::string&,
const error::Severity&)>;
using RequestClearAllErrorsFunc = std::function<Result(const std::string)>;
using RequestClearErrorFunc = std::function<Result(const std::string&, const error::ErrorHandle&)>;
using ExtMqttPublishFunc = std::function<void(const std::string&, const std::string&)>;
using ExtMqttSubscribeFunc = std::function<void(const std::string&, StringHandler)>;
using TelemetryPublishFunc =
Expand All @@ -79,6 +86,11 @@ struct ModuleAdapter {
CallFunc call;
PublishFunc publish;
SubscribeFunc subscribe;
SubscribeErrorFunc subscribe_error;
SubscribeErrorClearedFunc subscribe_error_cleared;
RaiseErrorFunc raise_error;
RequestClearAllErrorsFunc request_clear_all_errors;
RequestClearErrorFunc request_clear_error;
ExtMqttPublishFunc ext_mqtt_publish;
ExtMqttSubscribeFunc ext_mqtt_subscribe;
std::vector<cmd> registered_commands;
Expand Down
31 changes: 30 additions & 1 deletion include/framework/everest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <everest/exceptions.hpp>

#include <utils/config.hpp>
#include <utils/error.hpp>
#include <utils/mqtt_abstraction.hpp>
#include <utils/types.hpp>

Expand All @@ -38,7 +39,8 @@ class Everest {
public:
Everest(std::string module_id, const Config& config, bool validate_data_with_schema,
const std::string& mqtt_server_address, int mqtt_server_port, const std::string& mqtt_everest_prefix,
const std::string& mqtt_external_prefix, const std::string& telemetry_prefix, bool telemetry_enabled);
const std::string& mqtt_external_prefix, const std::string& telemetry_prefix, bool telemetry_enabled,
const fs::path& errors_dir);

// forbid copy assignment and copy construction
// NOTE (aw): move assignment and construction are also not supported because we're creating explicit references to
Expand Down Expand Up @@ -73,6 +75,32 @@ class Everest {
///
void subscribe_var(const Requirement& req, const std::string& var_name, const JsonCallback& callback);

///
/// \brief Subscribes to an error of another module indentified by the given \p req and error type
/// \p error_type. The given \p callback is called when a new error is raised
///
void subscribe_error(const Requirement& req, const std::string& error_type, const JsonCallback& callback);

///
/// \brief Subscribes to an error cleared event of another module indentified by the given \p req and error type
/// \p error_type. The given \p callback is called when an error is cleared
///
void subscribe_error_cleared(const Requirement& req, const std::string& error_type, const JsonCallback& callback);

///
/// \brief Requests to clear the error with the given \p uuid of the given \p impl_id.
/// If \p clear_all is true, all errors of the given \p impl_id are cleared.
/// Return a response json with the uuid of the cleared error
///
json request_clear_error(const std::string& impl_id, const std::string& uuid = "", const bool clear_all = false);

///
/// \brief Raises an given \p error of the given \p impl_id, with the given \p error_type. Returns the uuid of the
/// raised error
///
std::string raise_error(const std::string& impl_id, const std::string& error_type, const std::string& message,
const std::string& severity);

///
/// \brief publishes the given \p data on the given \p topic
///
Expand Down Expand Up @@ -152,6 +180,7 @@ class Everest {
std::string telemetry_prefix;
std::optional<TelemetryConfig> telemetry_config;
bool telemetry_enabled;
error::ErrorTypeMap error_map;

void handle_ready(json data);

Expand Down
2 changes: 2 additions & 0 deletions include/framework/runtime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ inline constexpr auto DATAROOT_DIR = "share";

inline constexpr auto MODULES_DIR = "modules";
inline constexpr auto TYPES_DIR = "types";
inline constexpr auto ERRORS_DIR = "errors";
inline constexpr auto INTERFACES_DIR = "interfaces";
inline constexpr auto SCHEMAS_DIR = "schemas";
inline constexpr auto CONFIG_NAME = "default.yaml";
Expand Down Expand Up @@ -102,6 +103,7 @@ struct RuntimeSettings {
fs::path modules_dir;
fs::path interfaces_dir;
fs::path types_dir;
fs::path errors_dir;
fs::path logging_config_file;
fs::path config_file;
fs::path www_dir;
Expand Down
23 changes: 19 additions & 4 deletions include/utils/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,16 @@ struct schemas {
};

///
/// \brief Allowed format of a type URI, which are of a format like this /type_file_name#/TypeName
/// \brief Allowed format of a $ref, which can be any path to a yaml file with a json pointer to a json object
///
const static std::regex type_uri_regex{R"(^((?:\/[a-zA-Z0-9\-\_]+)+#\/[a-zA-Z0-9\-\_]+)$)"};
const static std::regex relative_ref_regex{
R"(^\.{1,2}\/[a-zA-Z0-9\-\_]+(?:\/[a-zA-Z0-9\-\_]+)*\.yaml#\/[a-zA-Z0-9\-\_]+(?:\/[a-zA-Z0-9\-\_]+)*$)"};

///
/// \brief Allowed format of a $ref, which can be any uri to a yaml file with a json pointer to a json object
///
const static std::regex absolute_ref_regex{
R"(^file:\/\/\/[a-zA-Z0-9\-\_]+(?:\/[a-zA-Z0-9\-\_]+)*\.yaml#\/[a-zA-Z0-9\-\_]+(?:\/[a-zA-Z0-9\-\_]+)*$)"};

///
/// \brief Contains config and manifest parsing
Expand All @@ -55,6 +62,7 @@ class Config {

std::unordered_map<std::string, std::optional<TelemetryConfig>> telemetry_configs;

static json replace_error_refs(json& interface_json);
///
/// \brief loads the contents of the interface file referenced by the give \p intf_name from disk and validates its
/// contents
Expand Down Expand Up @@ -169,11 +177,18 @@ class Config {
std::string mqtt_module_prefix(const std::string& module_id);

///
/// \brief A json schema loader that can handle type refs and otherwise uses the builtin draft7 schema of
/// \biref A json schema loader that can handle absolute refs and otherwise uses the builtin draft7 schema of the
/// json schema validator when it encounters it. Throws an exception
/// otherwise
///
static void abs_ref_loader(const json_uri& uri, json& schema);

///
/// \brief A json schema loader that can handle relative refs and otherwise uses the builtin draft7 schema of
/// the json schema validator when it encounters it. Throws an exception
/// otherwise
///
void ref_loader(const json_uri& uri, json& schema);
static void rel_ref_loader(const json_uri& uri, json& schema, const fs::path& base_path);

///
/// \brief loads the config.json and manifest.json in the schemes subfolder of
Expand Down
84 changes: 84 additions & 0 deletions include/utils/error.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
#ifndef UTILS_ERROR_HPP
#define UTILS_ERROR_HPP

#include <filesystem>
#include <fmt/format.h>
#include <map>
#include <string>

#include <utils/date.hpp>
#include <utils/types.hpp>

namespace Everest {
namespace error {

class NotValidErrorType : public std::exception {
public:
explicit NotValidErrorType(const std::string& error_type_) : error_type(error_type_){};
virtual const char* what() const throw() {
return fmt::format("Error type '{}' is not valid.", error_type).c_str();
}

private:
const std::string error_type;
};

enum class Severity {
Low,
Mid,
High
};

std::string severity_to_string(const Severity& s);
Severity string_to_severity(const std::string& s);

struct UUID {
UUID();
UUID(const std::string& uuid);

Check notice on line 39 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L39

Struct 'UUID' has a constructor with 1 argument that is not explicit.
bool operator<(const UUID& other) const;

std::string uuid;

Check notice on line 42 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L42

struct member 'UUID::uuid' is never used.
};

using ErrorHandle = UUID;
struct Error {
using time_point = date::utc_clock::time_point;
Error(const std::string& type, const std::string& message, const std::string& description,
const ImplementationIdentifier& from, const bool persistent, const Severity& severity,
const time_point& timestamp, const UUID& uuid);
Error(const std::string& type, const std::string& message, const std::string& description,
const ImplementationIdentifier& from, const Severity& severity = Severity::Low);
Error(const std::string& type, const std::string& message, const std::string& description,
const std::string& from_module, const std::string& from_implementation,
const Severity& severity = Severity::Low);

std::string type;

Check notice on line 57 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L57

struct member 'Error::type' is never used.
std::string description;

Check notice on line 58 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L58

struct member 'Error::description' is never used.
std::string message;

Check notice on line 59 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L59

struct member 'Error::message' is never used.
bool persistent;

Check notice on line 60 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L60

struct member 'Error::persistent' is never used.
Severity severity;

Check notice on line 61 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L61

struct member 'Error::severity' is never used.
ImplementationIdentifier from;
time_point timestamp;
UUID uuid;
};

class ErrorTypeMap {
public:
ErrorTypeMap() = default;
ErrorTypeMap(std::filesystem::path error_types_dir);

Check notice on line 70 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L70

Class 'ErrorTypeMap' has a constructor with 1 argument that is not explicit.
void load_error_types(std::filesystem::path error_types_dir);
std::string get_description(const std::string& error_type) const;
bool has(const std::string& error_type) const;

private:
std::map<std::string, std::string> error_types;

Check notice on line 76 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L76

class member 'ErrorTypeMap::error_types' is never used.
};

using ErrorCallback = std::function<void(Error)>;

} // namespace error
} // namespace Everest

#endif // UTILS_ERROR_HPP
17 changes: 17 additions & 0 deletions include/utils/error_json.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
#ifndef UTILS_ERROR_JSON_HPP
#define UTILS_ERROR_JSON_HPP

#include <utils/error.hpp>

namespace Everest {
namespace error {

Error json_to_error(const json& j);
json error_to_json(const Error& e);

} // namespace error
} // namespace Everest

#endif // UTILS_ERROR_JSON_HPP
9 changes: 9 additions & 0 deletions include/utils/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ enum class HandlerType {
Call,
Result,
SubscribeVar,
SubscribeError,
ClearErrorRequest,
ExternalMQTT,
Unknown
};
Expand Down Expand Up @@ -102,6 +104,13 @@ struct Requirement {
size_t index;
};

struct ImplementationIdentifier {
ImplementationIdentifier(const std::string& module_id_, const std::string& implementation_id_) :
module_id(module_id_), implementation_id(implementation_id_){};
std::string module_id;
std::string implementation_id;
};

#define EVCALLBACK(function) [](auto&& PH1) { function(std::forward<decltype(PH1)>(PH1)); }

#endif // UTILS_TYPES_HPP
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ set_target_properties(framework PROPERTIES SOVERSION ${PROJECT_VERSION})

target_sources(framework
PRIVATE
error.cpp
config.cpp
everest.cpp
message_queue.cpp
Expand Down
Loading

0 comments on commit 5a320bf

Please sign in to comment.