Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
2
tools/EVerest-main/lib/everest/framework/everestjs/.gitignore
vendored
Normal file
2
tools/EVerest-main/lib/everest/framework/everestjs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*node_modules
|
||||
package-lock.json
|
||||
@@ -0,0 +1,120 @@
|
||||
find_program(
|
||||
NODE
|
||||
node
|
||||
REQUIRED
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${NODE} --version
|
||||
OUTPUT_VARIABLE NODE_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# We need Node-API version 6 or higher
|
||||
set(NODE_API_VERSION_REQUIRED 6)
|
||||
include(NodeApiVersion)
|
||||
require_node_api_version("${NODE_VERSION}" "${NODE_API_VERSION_REQUIRED}")
|
||||
|
||||
find_program(
|
||||
NPM
|
||||
npm
|
||||
REQUIRED
|
||||
)
|
||||
|
||||
# Include Node-API wrappers
|
||||
# FIXME (aw): we want this as an requirement, not implicitely install it by ourself
|
||||
execute_process(
|
||||
COMMAND
|
||||
${NPM} list -p node-addon-api --loglevel=error | grep node-addon-api
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
OUTPUT_VARIABLE
|
||||
NODE_ADDON_API_PACKAGE_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
set(NODE_ADDON_API_INSTALL_VERSION "8.1")
|
||||
message(STATUS "Adding node-addon-api@${NODE_ADDON_API_INSTALL_VERSION}")
|
||||
if (NOT NODE_ADDON_API_PACKAGE_DIR)
|
||||
execute_process(
|
||||
COMMAND
|
||||
${NPM} install node-addon-api@${NODE_ADDON_API_INSTALL_VERSION}
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
OUTPUT_QUIET
|
||||
RESULT_VARIABLE
|
||||
NPM_INSTALL_NODE_ADDON_API_FAILED
|
||||
)
|
||||
|
||||
if (NPM_INSTALL_NODE_ADDON_API_FAILED)
|
||||
message(FATAL_ERROR "Installation of node-addon-api failed")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${NODE} -p "require('node-addon-api').include"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
OUTPUT_VARIABLE NODE_ADDON_API_DIR
|
||||
)
|
||||
string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR "${NODE_ADDON_API_DIR}")
|
||||
|
||||
find_path(NODEJS_INCLUDE_DIR "node_api.h"
|
||||
PATH_SUFFIXES
|
||||
"node"
|
||||
"node16"
|
||||
"node17"
|
||||
"node18"
|
||||
"node19"
|
||||
"node20"
|
||||
"node21"
|
||||
"node22"
|
||||
"nodejs/src"
|
||||
HINTS
|
||||
"$ENV{HOME}/.nvm/versions/node/${NODE_VERSION}/include"
|
||||
REQUIRED
|
||||
)
|
||||
|
||||
add_library(everestjs SHARED)
|
||||
target_sources(everestjs
|
||||
PRIVATE
|
||||
everestjs.cpp
|
||||
conversions.cpp
|
||||
js_exec_ctx.cpp
|
||||
)
|
||||
|
||||
target_compile_options(everestjs PRIVATE ${COMPILER_WARNING_OPTIONS})
|
||||
|
||||
# define NAPI_VERSION
|
||||
target_compile_definitions(everestjs
|
||||
PRIVATE
|
||||
-DNAPI_VERSION=${NODE_API_VERSION_REQUIRED}
|
||||
-DNAPI_CPP_EXCEPTIONS
|
||||
)
|
||||
|
||||
set_target_properties(everestjs PROPERTIES PREFIX "" SUFFIX ".node")
|
||||
|
||||
target_include_directories(everestjs
|
||||
PRIVATE
|
||||
$<BUILD_INTERFACE:${NODE_ADDON_API_DIR}>
|
||||
$<BUILD_INTERFACE:${NODEJS_INCLUDE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
)
|
||||
|
||||
target_link_libraries(everestjs
|
||||
PRIVATE
|
||||
everest::framework
|
||||
everest::log
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS everestjs
|
||||
LIBRARY
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/everest/node_modules/everestjs
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
index.js
|
||||
package.json
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/everest/node_modules/everestjs
|
||||
)
|
||||
@@ -0,0 +1,213 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
#include "conversions.hpp"
|
||||
|
||||
#include <everest/exceptions.hpp>
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
#include <utils/error/error_json.hpp>
|
||||
|
||||
namespace EverestJs {
|
||||
|
||||
Everest::json convertToJson(const Napi::Value& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (value.IsNull() || value.IsUndefined()) {
|
||||
return Everest::json(nullptr);
|
||||
} else if (value.IsString()) {
|
||||
return Everest::json(std::string(value.As<Napi::String>()));
|
||||
} else if (value.IsNumber()) {
|
||||
int64_t intNumber = value.As<Napi::Number>();
|
||||
double floatNumber = value.As<Napi::Number>();
|
||||
if (floatNumber == intNumber)
|
||||
return Everest::json(intNumber);
|
||||
return Everest::json(floatNumber);
|
||||
} else if (value.IsBoolean()) {
|
||||
return Everest::json(bool(value.As<Napi::Boolean>()));
|
||||
} else if (value.IsArray()) {
|
||||
auto j = Everest::json::array();
|
||||
Napi::Array array = value.As<Napi::Array>();
|
||||
for (uint64_t i = 0; i < array.Length(); i++) {
|
||||
Napi::Value entry = Napi::Value(array[i]);
|
||||
j[i] = convertToJson(entry);
|
||||
}
|
||||
return j;
|
||||
} else if (value.IsObject() && value.Type() == napi_object) {
|
||||
auto j = Everest::json({});
|
||||
Napi::Object obj = value.As<Napi::Object>();
|
||||
Napi::Array keys = obj.GetPropertyNames();
|
||||
for (uint64_t i = 0; i < keys.Length(); i++) {
|
||||
Napi::Value key = keys[i];
|
||||
if (key.IsString()) {
|
||||
std::string k = key.As<Napi::String>();
|
||||
Napi::Value v = Napi::Value(obj[k]);
|
||||
j[k] = convertToJson(v);
|
||||
} else {
|
||||
EVTHROW(EVEXCEPTION(Everest::EverestApiError,
|
||||
"Javascript type of object key can not be converted to Everest::json: ",
|
||||
napi_valuetype_strings[key.Type()]));
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
EVTHROW(EVEXCEPTION(Everest::EverestApiError, "Javascript type can not be converted to Everest::json: ",
|
||||
napi_valuetype_strings[value.Type()]));
|
||||
}
|
||||
|
||||
Everest::TelemetryMap convertToTelemetryMap(const Napi::Object& obj) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
Everest::TelemetryMap telemetry;
|
||||
Napi::Array keys = obj.GetPropertyNames();
|
||||
for (uint64_t i = 0; i < keys.Length(); i++) {
|
||||
Napi::Value key = keys[i];
|
||||
if (key.IsString()) {
|
||||
std::string k = key.As<Napi::String>();
|
||||
Napi::Value value = Napi::Value(obj[k]);
|
||||
if (value.IsString()) {
|
||||
telemetry[k] = std::string(value.As<Napi::String>());
|
||||
} else if (value.IsNumber()) {
|
||||
int intNumber = value.As<Napi::Number>();
|
||||
double floatNumber = value.As<Napi::Number>();
|
||||
if (floatNumber == intNumber) {
|
||||
telemetry[k] = intNumber;
|
||||
} else {
|
||||
telemetry[k] = floatNumber;
|
||||
}
|
||||
} else if (value.IsBoolean()) {
|
||||
telemetry[k] = bool(value.As<Napi::Boolean>());
|
||||
}
|
||||
}
|
||||
}
|
||||
return telemetry;
|
||||
}
|
||||
|
||||
Napi::Value convertToNapiValue(const Napi::Env& env, const json& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (value.is_null()) {
|
||||
return env.Null();
|
||||
} else if (value.is_string()) {
|
||||
return Napi::String::New(env, std::string(value));
|
||||
} else if (value.is_number_integer()) {
|
||||
return Napi::Number::New(env, int64_t(value));
|
||||
} else if (value.is_number_float()) {
|
||||
return Napi::Number::New(env, double(value));
|
||||
} else if (value.is_boolean()) {
|
||||
return Napi::Boolean::New(env, bool(value));
|
||||
} else if (value.is_array()) {
|
||||
Napi::Array v = Napi::Array::New(env);
|
||||
for (uint64_t i = 0; i < value.size(); i++) {
|
||||
v.Set(i, convertToNapiValue(env, value[i]));
|
||||
}
|
||||
return v;
|
||||
} else if (value.is_object()) {
|
||||
Napi::Object v = Napi::Object::New(env);
|
||||
for (auto& el : value.items()) {
|
||||
v.Set(el.key(), convertToNapiValue(env, el.value()));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
EVTHROW(EVEXCEPTION(Everest::EverestApiError, "Javascript type can not be converted to Napi::Value: ", value));
|
||||
}
|
||||
|
||||
Everest::error::Error convertToError(const Napi::Value& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
Everest::json j = convertToJson(value);
|
||||
return j.get<Everest::error::Error>();
|
||||
}
|
||||
|
||||
Everest::error::ErrorType convertToErrorType(const Napi::Value& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (value.IsString()) {
|
||||
return Everest::error::ErrorType(std::string(value.As<Napi::String>()));
|
||||
}
|
||||
EVTHROW(EVEXCEPTION(Everest::EverestApiError, "Javascript type can not be converted to Everest::error::ErrorType: ",
|
||||
napi_valuetype_strings[value.Type()]));
|
||||
}
|
||||
|
||||
Everest::error::ErrorSubType convertToErrorSubType(const Napi::Value& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (value.IsString()) {
|
||||
return Everest::error::ErrorSubType(std::string(value.As<Napi::String>()));
|
||||
}
|
||||
EVTHROW(EVEXCEPTION(Everest::EverestApiError,
|
||||
"Javascript type can not be converted to Everest::error::ErrorSubType: ",
|
||||
napi_valuetype_strings[value.Type()]));
|
||||
}
|
||||
|
||||
Everest::error::Severity convertToErrorSeverity(const Napi::Value& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (value.IsString()) {
|
||||
return Everest::error::string_to_severity(std::string(value.As<Napi::String>()));
|
||||
}
|
||||
EVTHROW(EVEXCEPTION(Everest::EverestApiError, "Javascript type can not be converted to Everest::error::Severity: ",
|
||||
napi_valuetype_strings[value.Type()]));
|
||||
}
|
||||
|
||||
Everest::error::State convertToErrorState(const Napi::Value& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (value.IsString()) {
|
||||
return Everest::error::string_to_state(std::string(value.As<Napi::String>()));
|
||||
}
|
||||
EVTHROW(EVEXCEPTION(Everest::EverestApiError, "Javascript type can not be converted to Everest::error::State: ",
|
||||
napi_valuetype_strings[value.Type()]));
|
||||
}
|
||||
|
||||
Napi::Value convertToNapiValue(const Napi::Env& env, const Everest::error::Error& error) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
json j(error);
|
||||
Napi::Value res = convertToNapiValue(env, j);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isSingleErrorStateCondition(const Napi::Value& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (value.IsArray()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Everest::error::ErrorStateMonitor::StateCondition convertToErrorStateCondition(const Napi::Value& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (value.IsObject()) {
|
||||
Napi::Object obj = value.As<Napi::Object>();
|
||||
Napi::Value type = obj.Get("type");
|
||||
Napi::Value sub_type = obj.Get("sub_type");
|
||||
Napi::Value active = obj.Get("active");
|
||||
return Everest::error::ErrorStateMonitor::StateCondition(
|
||||
convertToErrorType(type), convertToErrorSubType(sub_type), bool(active.As<Napi::Boolean>()));
|
||||
}
|
||||
EVTHROW(EVEXCEPTION(Everest::EverestApiError,
|
||||
"Javascript type can not be converted to Everest::error::ErrorStateMonitor::StateCondition: ",
|
||||
napi_valuetype_strings[value.Type()]));
|
||||
}
|
||||
|
||||
std::list<Everest::error::ErrorStateMonitor::StateCondition>
|
||||
convertToErrorStateConditionList(const Napi::Value& value) {
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (value.IsArray()) {
|
||||
std::list<Everest::error::ErrorStateMonitor::StateCondition> conditions;
|
||||
Napi::Array array = value.As<Napi::Array>();
|
||||
for (uint64_t i = 0; i < array.Length(); i++) {
|
||||
Napi::Value entry = Napi::Value(array[i]);
|
||||
conditions.push_back(convertToErrorStateCondition(entry));
|
||||
}
|
||||
return conditions;
|
||||
}
|
||||
EVTHROW(EVEXCEPTION(
|
||||
Everest::EverestApiError,
|
||||
"Javascript type can not be converted to std::list<Everest::error::ErrorStateMonitor::StateCondition>: ",
|
||||
napi_valuetype_strings[value.Type()]));
|
||||
}
|
||||
|
||||
} // namespace EverestJs
|
||||
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef CONVERSIONS_HPP
|
||||
#define CONVERSIONS_HPP
|
||||
|
||||
#include <framework/everest.hpp>
|
||||
|
||||
#include <napi.h>
|
||||
|
||||
#include <utils/conversions.hpp>
|
||||
#include <utils/types.hpp>
|
||||
|
||||
#include <utils/error.hpp>
|
||||
#include <utils/error/error_state_monitor.hpp>
|
||||
|
||||
namespace EverestJs {
|
||||
|
||||
static const char* const napi_valuetype_strings[] = {
|
||||
"undefined", //
|
||||
"null", //
|
||||
"boolean", //
|
||||
"number", //
|
||||
"string", //
|
||||
"symbol", //
|
||||
"object", //
|
||||
"function", //
|
||||
"external", //
|
||||
"bigint", //
|
||||
};
|
||||
|
||||
Everest::json convertToJson(const Napi::Value& value);
|
||||
Everest::json convertToConfigMap(const Everest::json& json_config);
|
||||
Everest::TelemetryMap convertToTelemetryMap(const Napi::Object& obj);
|
||||
Napi::Value convertToNapiValue(const Napi::Env& env, const Everest::json& value);
|
||||
|
||||
// Error related
|
||||
Everest::error::Error convertToError(const Napi::Value& value);
|
||||
Everest::error::ErrorType convertToErrorType(const Napi::Value& value);
|
||||
Everest::error::ErrorSubType convertToErrorSubType(const Napi::Value& value);
|
||||
Everest::error::Severity convertToErrorSeverity(const Napi::Value& value);
|
||||
Everest::error::State convertToErrorState(const Napi::Value& value);
|
||||
Napi::Value convertToNapiValue(const Napi::Env& env, const Everest::error::Error& error);
|
||||
// ErrorStateCondition related
|
||||
bool isSingleErrorStateCondition(const Napi::Value& value);
|
||||
Everest::error::ErrorStateMonitor::StateCondition convertToErrorStateCondition(const Napi::Value& value);
|
||||
std::list<Everest::error::ErrorStateMonitor::StateCondition> convertToErrorStateConditionList(const Napi::Value& value);
|
||||
|
||||
} // namespace EverestJs
|
||||
|
||||
#endif // CONVERSIONS_HPP
|
||||
1009
tools/EVerest-main/lib/everest/framework/everestjs/everestjs.cpp
Normal file
1009
tools/EVerest-main/lib/everest/framework/everestjs/everestjs.cpp
Normal file
File diff suppressed because it is too large
Load Diff
118
tools/EVerest-main/lib/everest/framework/everestjs/index.js
Normal file
118
tools/EVerest-main/lib/everest/framework/everestjs/index.js
Normal file
@@ -0,0 +1,118 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
const util = require('util');
|
||||
const addon = require('./everestjs.node');
|
||||
|
||||
const helpers = {
|
||||
get_default: (obj, key, defaultValue) => ((obj[key] === undefined) ? defaultValue : obj[key]),
|
||||
to_string: (...values) => {
|
||||
let log_string = '';
|
||||
values.forEach((value) => {
|
||||
if (typeof value == 'string') {
|
||||
log_string += value;
|
||||
} else if (typeof value == 'number') {
|
||||
log_string += value;
|
||||
} else if (typeof value == 'boolean') {
|
||||
log_string += value ? 'true' : 'false';
|
||||
} else if (typeof value == 'object') {
|
||||
log_string += util.inspect(value, false, 6, true);
|
||||
} else {
|
||||
throw new Error(`The logging function cannot handle input of type ${typeof value}`);
|
||||
}
|
||||
});
|
||||
return log_string;
|
||||
},
|
||||
};
|
||||
|
||||
const EverestModule = function EverestModule(handler_setup, user_settings) {
|
||||
const env_settings = {
|
||||
module: process.env.EV_MODULE,
|
||||
prefix: process.env.EV_PREFIX,
|
||||
logging_config_file: process.env.EV_LOG_CONF_FILE,
|
||||
mqtt_everest_prefix: process.env.EV_MQTT_EVEREST_PREFIX,
|
||||
mqtt_external_prefix: process.env.EV_MQTT_EXTERNAL_PREFIX,
|
||||
mqtt_broker_socket_path: process.env.EV_MQTT_BROKER_SOCKET_PATH,
|
||||
mqtt_server_address: process.env.EV_MQTT_BROKER_HOST,
|
||||
mqtt_server_port: process.env.EV_MQTT_BROKER_PORT,
|
||||
validate_schema: process.env.EV_VALIDATE_SCHEMA,
|
||||
};
|
||||
|
||||
const settings = { ...env_settings, ...user_settings };
|
||||
|
||||
if (!settings.module) {
|
||||
throw new Error('parameter "module" is missing');
|
||||
}
|
||||
|
||||
const config = {
|
||||
module: settings.module,
|
||||
prefix: settings.prefix,
|
||||
logging_config_file: settings.logging_config_file,
|
||||
mqtt_everest_prefix: settings.mqtt_everest_prefix,
|
||||
mqtt_external_prefix: helpers.get_default(settings, 'mqtt_external_prefix', ''),
|
||||
mqtt_broker_socket_path: helpers.get_default(settings, 'mqtt_broker_socket_path', ''),
|
||||
mqtt_server_address: helpers.get_default(settings, 'mqtt_server_address', ''),
|
||||
mqtt_server_port: helpers.get_default(settings, 'mqtt_server_port', 0),
|
||||
validate_schema: helpers.get_default(settings, 'validate_schema', false),
|
||||
};
|
||||
|
||||
function callbackWrapper(on, request, ...args) {
|
||||
const result = request(...args);
|
||||
Promise.resolve(result).then(on.fulfill, on.reject);
|
||||
}
|
||||
|
||||
const available_handlers = addon.boot_module.call(this, config, callbackWrapper);
|
||||
|
||||
const module_setup = {
|
||||
setup: available_handlers,
|
||||
info: this.info,
|
||||
config: this.config,
|
||||
mqtt: this.mqtt,
|
||||
telemetry: this.telemetry,
|
||||
};
|
||||
|
||||
if (this.mqtt === undefined) {
|
||||
const missing_mqtt_getter = {
|
||||
get() { throw new Error('External mqtt not available - missing enable_external_mqtt in manifest?'); },
|
||||
};
|
||||
Object.defineProperty(module_setup, 'mqtt', missing_mqtt_getter);
|
||||
Object.defineProperty(this, 'mqtt', missing_mqtt_getter);
|
||||
}
|
||||
|
||||
if (this.telemetry === undefined) {
|
||||
const missing_telemetry_getter = {
|
||||
get() { throw new Error('Telemetry not available - missing enable_telemetry in manifest?'); },
|
||||
};
|
||||
Object.defineProperty(module_setup, 'telemetry', missing_telemetry_getter);
|
||||
Object.defineProperty(this, 'telemetry', missing_telemetry_getter);
|
||||
}
|
||||
|
||||
// check, if we need to register cmds
|
||||
if (typeof handler_setup === 'undefined') {
|
||||
if (Object.keys(available_handlers.provides).length !== 0) {
|
||||
throw new Error('handler setup callback is missing - you need to register at least one command');
|
||||
}
|
||||
return addon.signal_ready.call(this);
|
||||
}
|
||||
|
||||
if (typeof handler_setup === 'function') {
|
||||
return Promise.resolve(handler_setup.call({}, module_setup)).then(
|
||||
() => addon.signal_ready.call(this)
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error('handler setup callback needs to be of type function');
|
||||
};
|
||||
|
||||
// setup log handlers
|
||||
exports.evlog = {};
|
||||
Object.keys(addon.log).forEach((key) => {
|
||||
exports.evlog[key] = (...log_args) => addon.log[key](helpers.to_string(...log_args));
|
||||
});
|
||||
|
||||
let boot_module_called = false;
|
||||
|
||||
exports.boot_module = (handler_setup, settings) => {
|
||||
if (boot_module_called) throw Error('Calling initModule more than once is not supported right now');
|
||||
boot_module_called = true;
|
||||
return new EverestModule(handler_setup, settings);
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
#include "js_exec_ctx.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
void JsExecCtx::tramp(Napi::Env env, Napi::Function callback, std::nullptr_t* context, JsExecCtx* this_) {
|
||||
(void)context; // this is unused in this callback
|
||||
|
||||
try {
|
||||
std::vector<napi_value> args{this_->result_handler_ref.Value()};
|
||||
if (this_->arg_func != nullptr) {
|
||||
// append args from our arg function via move magic ...
|
||||
auto append_args = this_->arg_func(env);
|
||||
args.reserve(args.size() + append_args.size());
|
||||
std::move(std::begin(append_args), std::end(append_args), std::back_inserter(args));
|
||||
append_args.clear();
|
||||
}
|
||||
|
||||
callback.Call(args);
|
||||
} catch (std::exception& e) {
|
||||
EVLOG_AND_RETHROW(env);
|
||||
}
|
||||
}
|
||||
|
||||
Napi::Value JsExecCtx::on_fulfill(const Napi::CallbackInfo& info) {
|
||||
JsExecCtx* this_ = reinterpret_cast<JsExecCtx*>(info.Data());
|
||||
if (this_->res_func != nullptr) {
|
||||
this_->res_func(info, false);
|
||||
}
|
||||
this_->promise.set_value();
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
|
||||
Napi::Value JsExecCtx::on_reject(const Napi::CallbackInfo& info) {
|
||||
JsExecCtx* this_ = reinterpret_cast<JsExecCtx*>(info.Data());
|
||||
if (this_->res_func != nullptr) {
|
||||
this_->res_func(info, true);
|
||||
} else {
|
||||
// there is no catch handler registered, so we throw
|
||||
throw Napi::Error::New(
|
||||
info.Env(),
|
||||
"JsExecCtx call into javascript code got rejected and could not be handled (rejection handler not defined");
|
||||
}
|
||||
|
||||
this_->promise.set_value();
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
|
||||
void JsExecCtx::exec(const ArgFuncType& arg_func, const ResFuncType& res_func) {
|
||||
// FIXME (aw): we're blocking all other threads trying to call this function
|
||||
// a proper solution should be found
|
||||
std::unique_lock<std::mutex> lock(exec_mutex);
|
||||
this->arg_func = arg_func;
|
||||
this->res_func = res_func;
|
||||
|
||||
promise = std::promise<void>();
|
||||
|
||||
tsfn.BlockingCall(this);
|
||||
promise.get_future().get();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
//
|
||||
// author: aw@pionix.de
|
||||
//
|
||||
|
||||
#ifndef JS_EXEC_CTX_HPP
|
||||
#define JS_EXEC_CTX_HPP
|
||||
|
||||
#include <future>
|
||||
|
||||
#include <napi.h>
|
||||
|
||||
class JsExecCtx {
|
||||
private:
|
||||
static void tramp(Napi::Env env, Napi::Function callback, std::nullptr_t*, JsExecCtx* this_);
|
||||
static Napi::Value on_fulfill(const Napi::CallbackInfo& info);
|
||||
static Napi::Value on_reject(const Napi::CallbackInfo& info);
|
||||
|
||||
public:
|
||||
using TsfnType = Napi::TypedThreadSafeFunction<std::nullptr_t, JsExecCtx, JsExecCtx::tramp>;
|
||||
using ArgFuncType = std::function<std::vector<napi_value>(Napi::Env&)>;
|
||||
using ResFuncType = std::function<void(const Napi::CallbackInfo&, bool)>;
|
||||
|
||||
// FIXME (aw): proper module_instance handling if nullptr
|
||||
JsExecCtx(const Napi::Env& env, const Napi::Function& func, const std::string& res_name = "RequestDispatcher") :
|
||||
tsfn(TsfnType::New(env, func, res_name, 0, 1)),
|
||||
result_handler_ref(Napi::Persistent(Napi::Object::New(env))),
|
||||
func_ref(Napi::Persistent(func)) {
|
||||
|
||||
result_handler_ref.Value().DefineProperty(Napi::PropertyDescriptor::Value(
|
||||
"fulfill", Napi::Function::New(env, on_fulfill, nullptr, this), napi_enumerable));
|
||||
result_handler_ref.Value().DefineProperty(Napi::PropertyDescriptor::Value(
|
||||
"reject", Napi::Function::New(env, on_reject, nullptr, this), napi_enumerable));
|
||||
}
|
||||
|
||||
~JsExecCtx() {
|
||||
tsfn.Release();
|
||||
}
|
||||
|
||||
void exec(const ArgFuncType& arg_func, const ResFuncType& res_func);
|
||||
|
||||
private:
|
||||
ArgFuncType arg_func;
|
||||
ResFuncType res_func;
|
||||
// FIXME (aw): will the referenced object be GC'd when the references get destroyed?
|
||||
// and is okay to be destroyed in our async thread?
|
||||
TsfnType tsfn;
|
||||
Napi::ObjectReference result_handler_ref;
|
||||
Napi::FunctionReference func_ref{};
|
||||
std::mutex exec_mutex;
|
||||
std::promise<void> promise;
|
||||
};
|
||||
|
||||
#endif // JS_EXEC_CTX_HPP
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "everestjs",
|
||||
"main": "index.js",
|
||||
"version": "0.25.0",
|
||||
"description": "EVerest API for node.js",
|
||||
"dependencies": {
|
||||
"node-addon-api": "^3.2.1"
|
||||
}
|
||||
}
|
||||
56
tools/EVerest-main/lib/everest/framework/everestjs/utils.hpp
Normal file
56
tools/EVerest-main/lib/everest/framework/everestjs/utils.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_HPP
|
||||
#define UTILS_HPP
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
#include <everest/metamacros.hpp>
|
||||
|
||||
namespace EverestJs {
|
||||
|
||||
// this is needed to get javascript stacktraces whenever possible while still logging boost error information
|
||||
#define EVLOG_AND_RETHROW(...) \
|
||||
do { \
|
||||
try { \
|
||||
throw; \
|
||||
} catch (Napi::Error & e) { \
|
||||
try { \
|
||||
BOOST_THROW_EXCEPTION(boost::enable_error_info(e) \
|
||||
<< boost::log::BOOST_LOG_VERSION_NAMESPACE::current_scope()); \
|
||||
} catch (boost::exception & ex) { \
|
||||
char const* const* f = boost::get_error_info<boost::throw_file>(ex); \
|
||||
int const* l = boost::get_error_info<boost::throw_line>(ex); \
|
||||
char const* const* fn = boost::get_error_info<boost::throw_function>(ex); \
|
||||
EVLOG_critical << "Catched top level Napi::Error, forwarding to javascript..." << std::endl \
|
||||
<< (f ? *f : "") << ":" << (l ? std::to_string(*l) : "") << " in Function " \
|
||||
<< (fn ? *fn : "") << std::endl \
|
||||
<< boost::diagnostic_information(e, true) << boost::diagnostic_information(ex, false) \
|
||||
<< std::endl \
|
||||
<< "==============================" << std::endl \
|
||||
<< std::endl; \
|
||||
} \
|
||||
throw; /* this will forward the exception back to javascript and enable js backtraces */ \
|
||||
} catch (std::exception & e) { \
|
||||
try { \
|
||||
BOOST_THROW_EXCEPTION(boost::enable_error_info(e) \
|
||||
<< boost::log::BOOST_LOG_VERSION_NAMESPACE::current_scope()); \
|
||||
} catch (boost::exception & ex) { \
|
||||
char const* const* f = boost::get_error_info<boost::throw_file>(ex); \
|
||||
int const* l = boost::get_error_info<boost::throw_line>(ex); \
|
||||
char const* const* fn = boost::get_error_info<boost::throw_function>(ex); \
|
||||
EVLOG_critical << "Catched top level exception, forwarding to javascript..." << std::endl \
|
||||
<< (f ? *f : "") << ":" << (l ? std::to_string(*l) : "") << " in Function " \
|
||||
<< (fn ? *fn : "") << std::endl \
|
||||
<< boost::diagnostic_information(e, true) << boost::diagnostic_information(ex, false) \
|
||||
<< std::endl \
|
||||
<< "==============================" << std::endl \
|
||||
<< std::endl; \
|
||||
/* this will forward the exception to javascript and enable js backtraces */ \
|
||||
metamacro_if_eq(0, metamacro_argcount(__VA_ARGS__))(throw;)(EVTHROW(Napi::Error::New( \
|
||||
metamacro_at(0, __VA_ARGS__), Napi::String::New(metamacro_at(0, __VA_ARGS__), e.what())));) \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
} // namespace EverestJs
|
||||
|
||||
#endif // UTILS_HPP
|
||||
Reference in New Issue
Block a user