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/error handling #92

Merged
merged 15 commits into from
Nov 3, 2023
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
14 changes: 14 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,13 @@ 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 RequestClearErrorUUIDFunc = std::function<Result(const std::string&, const error::ErrorHandle&)>;
using RequestClearAllErrorsOfMouduleFunc = std::function<Result(const std::string&)>;
using RequestClearAllErrorsOfTypeOfModuleFunc = std::function<Result(const std::string&, const std::string&)>;
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 +87,12 @@ struct ModuleAdapter {
CallFunc call;
PublishFunc publish;
SubscribeFunc subscribe;
SubscribeErrorFunc subscribe_error;
SubscribeErrorClearedFunc subscribe_error_cleared;
RaiseErrorFunc raise_error;
RequestClearErrorUUIDFunc request_clear_error_uuid;
RequestClearAllErrorsOfMouduleFunc request_clear_all_errors_of_module;
RequestClearAllErrorsOfTypeOfModuleFunc request_clear_all_errors_of_type_of_module;
ExtMqttPublishFunc ext_mqtt_publish;
ExtMqttSubscribeFunc ext_mqtt_subscribe;
std::vector<cmd> registered_commands;
Expand Down
32 changes: 32 additions & 0 deletions 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 Down Expand Up @@ -73,6 +74,37 @@ 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 errors
/// If \p request_type is RequestClearErrorOption::ClearUUID, the error with the given \p uuid of the given \p
/// impl_id is cleared. \p error_type is ignored. If \p request_type is
/// RequestClearErrorOption::ClearAllOfTypeOfModule, all errors of the given \p impl_id with the given \p error_type
/// are cleared. \p uuid is ignored. If \p request_type is RequestClearErrorOption::ClearAllOfModule, all errors of
/// the given \p impl_id are cleared. \p uuid and \p error_type are ignored. Return a response json with the uuids
/// of the cleared errors
///
json request_clear_error(const error::RequestClearErrorOption request_type, const std::string& impl_id,
const std::string& uuid, const std::string& error_type);

///
/// \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
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
27 changes: 23 additions & 4 deletions include/utils/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#define UTILS_CONFIG_HPP

#include <filesystem>
#include <list>
#include <optional>
#include <regex>
#include <set>
Expand All @@ -13,6 +14,7 @@
#include <nlohmann/json-schema.hpp>

#include <utils/config_cache.hpp>
#include <utils/error.hpp>
#include <utils/types.hpp>

namespace Everest {
Expand All @@ -26,10 +28,11 @@ struct RuntimeSettings;
/// \brief A structure that contains all available schemas
///
struct schemas {
json config; ///< The config schema
json manifest; ///< The manifest scheme
json interface; ///< The interface schema
json type; ///< The type schema
json config; ///< The config schema
json manifest; ///< The manifest scheme
json interface; ///< The interface schema
json type; ///< The type schema
json error_declaration_list; ///< The error-declaration-list schema
};

///
Expand All @@ -51,10 +54,23 @@ class Config {
json interfaces;
json interface_definitions;
json types;
json errors;
schemas _schemas;

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

///
/// \brief loads the contents of an error or an error list referenced by the given \p reference.
///
/// \returns a list of json objects containing the error definitions
std::list<json> resolve_error_ref(const std::string& reference);

///
/// \brief replaces all error references in the given \p interface_json with the actual error definitions
///
/// \returns the interface_json with replaced error references
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 @@ -90,7 +106,10 @@ class Config {

void load_and_validate_manifest(const std::string& module_id, const json& module_config);

error::ErrorTypeMap error_map;

public:
error::ErrorTypeMap get_error_map() const;
std::string get_module_name(const std::string& module_id);
bool module_provides(const std::string& module_name, const std::string& impl_id);
json get_module_cmds(const std::string& module_name, const std::string& impl_id);
Expand Down
93 changes: 93 additions & 0 deletions include/utils/error.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// 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 {

enum class RequestClearErrorOption {
ClearUUID,
ClearAllOfTypeOfModule,
ClearAllOfModule
};
std::string request_clear_error_option_to_string(const RequestClearErrorOption& o);
RequestClearErrorOption string_to_request_clear_error_option(const std::string& s);

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();
explicit UUID(const std::string& uuid);
bool operator<(const UUID& other) const;
std::string to_string() const;

std::string uuid;

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

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L51

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 66 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L66

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

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

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L67

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

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

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L68

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

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

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L69

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

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

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

class ErrorTypeMap {
public:
ErrorTypeMap() = default;
explicit ErrorTypeMap(std::filesystem::path error_types_dir);
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 85 in include/utils/error.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error.hpp#L85

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

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

} // namespace error
} // namespace Everest

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

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

#include <list>
#include <map>
#include <memory>
#include <mutex>

#include <everest/exceptions.hpp>

namespace Everest {
namespace error {

class EverestErrorAlreadyExistsError : public EverestBaseLogicError {
public:
explicit EverestErrorAlreadyExistsError(const ErrorHandle& handle) :
EverestBaseLogicError("Error with handle '" + handle.to_string() + "' already exists."){};
};

class EverestErrorDoesNotExistsError : public EverestBaseLogicError {
public:
explicit EverestErrorDoesNotExistsError(const ErrorHandle& handle) :
EverestBaseLogicError("Error with handle '" + handle.to_string() + "' does not exists."){};
};

class EverestFalseRequestorError : public EverestBaseLogicError {
public:
explicit EverestFalseRequestorError(const ImplementationIdentifier& impl_req,
const ImplementationIdentifier& impl_val) :
EverestBaseLogicError("Request came from " + impl_req.to_string() + ", but should be from " +
impl_val.to_string()){};
};

class ErrorDatabase {
public:
ErrorDatabase() = default;
ErrorHandle add_error(std::shared_ptr<Error> error);

std::shared_ptr<Error> clear_error_handle(const ImplementationIdentifier& impl, const ErrorHandle& handle);
std::list<std::shared_ptr<Error>> clear_all_errors_of_type_of_module(const ImplementationIdentifier& impl,
const std::string& type);
std::list<std::shared_ptr<Error>> clear_all_errors_of_module(const ImplementationIdentifier& impl);

private:
bool has_error(const ErrorHandle& handle) const;

std::map<ErrorHandle, std::shared_ptr<Error>> errors;

Check notice on line 52 in include/utils/error_database.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error_database.hpp#L52

class member 'ErrorDatabase::errors' is never used.
std::mutex errors_mutex;
};

} // namespace error
} // namespace Everest

#endif // UTILS_ERROR_DATABASE_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
42 changes: 42 additions & 0 deletions include/utils/error_manager.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
#ifndef UTILS_ERROR_MANAGER_HPP
#define UTILS_ERROR_MANAGER_HPP

#include <functional>

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

namespace Everest {
namespace error {

class ErrorManager {
public:
using HandlerFunc = std::function<void(const json&)>;
using SendMessageFunc = std::function<void(const std::string&, const json&)>;
using RegisterHandlerFunc = std::function<void(const std::string&, HandlerFunc&)>;
using RegisterCallHandlerFunc = RegisterHandlerFunc;
using RegisterErrorHandlerFunc = RegisterHandlerFunc;

public:
void add_error_topic(const std::string& topic);
ErrorManager(SendMessageFunc send_json_message_, RegisterCallHandlerFunc register_call_handler_,
RegisterErrorHandlerFunc register_error_handler_, const std::string& request_clear_error_topic_);

private:
void handle_error(const json& data);
void handle_request_clear_error(const json& data);

ErrorDatabase error_database;
std::string request_clear_error_topic;

Check notice on line 32 in include/utils/error_manager.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/utils/error_manager.hpp#L32

class member 'ErrorManager::request_clear_error_topic' is never used.

RegisterCallHandlerFunc register_call_handler;
RegisterErrorHandlerFunc register_error_handler;
SendMessageFunc send_json_message;
};

} // namespace error
} // namespace Everest

#endif // UTILS_ERROR_MANAGER_HPP
Loading