From 02f70550771914304b890de0d189cdfdb52f6fff Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Tue, 2 Aug 2022 13:47:36 +0200 Subject: [PATCH 01/24] linux/EddieEndpoint: parse arguments and receive port number receive port number and flag to enable example resources as arguments. Parse arguments using argparse. Signed-off-by: Francesco Pham --- common/include/EddieEndpoint.h | 2 +- linux/CMakeLists.txt | 6 +- linux/communication/src/EddieEndpoint.cpp | 4 +- linux/communication/src/ResourceDirectory.cpp | 5 +- linux/examples/eddie_endpoint.cpp | 49 +- linux/include/argparse.hpp | 1249 +++++++++++++++++ 6 files changed, 1297 insertions(+), 18 deletions(-) create mode 100644 linux/include/argparse.hpp diff --git a/common/include/EddieEndpoint.h b/common/include/EddieEndpoint.h index f8f1622..4c35f63 100644 --- a/common/include/EddieEndpoint.h +++ b/common/include/EddieEndpoint.h @@ -21,7 +21,7 @@ private: public: - EddieEndpoint(); + explicit EddieEndpoint(const std::string& port = "5683"); ~EddieEndpoint(); diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 171bf38..c40591b 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -4,8 +4,7 @@ project(eddie-linux) set(VERSION_MAJOR 0) set(VERSION_MINOR 1) -# GoogleTest requires at least C++11 -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) find_package(PkgConfig REQUIRED) find_package(Threads) @@ -38,9 +37,10 @@ target_link_libraries(resource-directory PkgConfig::Coap ${CMAKE_THREAD_LIBS_INI set_property(TARGET resource-directory PROPERTY C_STANDARD 11) install(TARGETS resource-directory RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -# resource directory executable +# eddie endpoint executable add_executable(eddie-endpoint examples/eddie_endpoint.cpp ${SOURCES}) target_link_libraries(eddie-endpoint PkgConfig::Coap ${CMAKE_THREAD_LIBS_INIT} eddie-communication) +target_include_directories(eddie-endpoint PUBLIC ${PROJECT_SOURCE_DIR}/include) set_property(TARGET eddie-endpoint PROPERTY C_STANDARD 11) install(TARGETS eddie-endpoint RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index a2781ef..ccc01f1 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -54,10 +54,10 @@ CoapServer* EddieEndpoint::get_server() { return this->server; } -EddieEndpoint::EddieEndpoint() { +EddieEndpoint::EddieEndpoint(const std::string& port) { std::string ip = get_local_node_ip(); - server = new CoapServer(ip.c_str(), std::to_string(COAP_DEFAULT_PORT).c_str(), 0); + server = new CoapServer(ip.c_str(), port.c_str(), 0); } EddieEndpoint::~EddieEndpoint() { diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp index 9e1a0a7..597df40 100644 --- a/linux/communication/src/ResourceDirectory.cpp +++ b/linux/communication/src/ResourceDirectory.cpp @@ -14,10 +14,11 @@ #include #include #include + #define COAP_IP4_MULTICAST "224.0.1.187" #define COAP_IP6_MULTICAST_LOCAL_LINK "FF02::FD" #define COAP_IP6_MULTICAST_SITE_LOCAL "FF05::FD" -#define RD_PORT "5683" +#define RD_DEFAULT_PORT "5683" #define DEFAULT_SECTOR_NAME "sectoreddie" ThreadSafeMap *ResourceDirectory::rd_endpoints = new ThreadSafeMap(); @@ -670,7 +671,7 @@ int ResourceDirectory::init_server_context() { coap_startup(); coap_address_t dst; - if (resolve_address("::", RD_PORT, &dst) < 0) { + if (resolve_address("::", RD_DEFAULT_PORT, &dst) < 0) { LOG_ERR("Failed to resolve address"); coap_free_context(this->context); coap_cleanup(); diff --git a/linux/examples/eddie_endpoint.cpp b/linux/examples/eddie_endpoint.cpp index 86ec739..89db03c 100644 --- a/linux/examples/eddie_endpoint.cpp +++ b/linux/examples/eddie_endpoint.cpp @@ -6,6 +6,7 @@ #include "EddieEndpoint.h" #include "EddieResource.h" +#include "argparse.hpp" class EddieLamp : public EddieResource { private: @@ -72,17 +73,45 @@ public: } }; -int main() +int main(int argc, char *argv[]) { - EddieEndpoint node = EddieEndpoint(); - EddieLamp lamp_resource; - EddieTemperature temperature_resource; - - node.discover_rd(); - node.add_resource(&lamp_resource); - node.add_resource(&temperature_resource); - node.publish_resources(); - node.get_resources_from_rd(); + argparse::ArgumentParser program("program_name"); + + program.add_argument("--port", "-p") + .help("Port number") + .scan<'i', int>() + .default_value(5683); + + program.add_argument("--exampleres", "-e") + .help("Publish example lamp and temperature resources") + .default_value(false) + .implicit_value(true); + + try { + program.parse_args(argc, argv); + } + catch (const std::runtime_error& err) { + std::cerr << err.what() << std::endl; + std::cerr << program; + std::exit(1); + } + + auto port_number = program.get("port"); + + coap_set_log_level(LOG_DEBUG); + + EddieEndpoint node = EddieEndpoint(std::to_string(port_number)); + + node.discover_rd(); + + if (program["--exampleres"] == true) { + EddieLamp lamp_resource; + EddieTemperature temperature_resource; + node.add_resource(&lamp_resource); + node.add_resource(&temperature_resource); + node.publish_resources(); + } + node.start_server(); return 0; diff --git a/linux/include/argparse.hpp b/linux/include/argparse.hpp new file mode 100644 index 0000000..569704e --- /dev/null +++ b/linux/include/argparse.hpp @@ -0,0 +1,1249 @@ +/* + __ _ _ __ __ _ _ __ __ _ _ __ ___ ___ + / _` | '__/ _` | '_ \ / _` | '__/ __|/ _ \ Argument Parser for Modern C++ +| (_| | | | (_| | |_) | (_| | | \__ \ __/ http://github.com/p-ranav/argparse + \__,_|_| \__, | .__/ \__,_|_| |___/\___| + |___/|_| + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2019-2022 Pranav Srinivas Kumar +and other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace argparse { + +namespace details { // namespace for helper methods + +template +struct HasContainerTraits : std::false_type {}; + +template <> struct HasContainerTraits : std::false_type {}; + +template +struct HasContainerTraits< + T, std::void_t().begin()), + decltype(std::declval().end()), + decltype(std::declval().size())>> : std::true_type {}; + +template +static constexpr bool IsContainer = HasContainerTraits::value; + +template +struct HasStreamableTraits : std::false_type {}; + +template +struct HasStreamableTraits< + T, + std::void_t() << std::declval())>> + : std::true_type {}; + +template +static constexpr bool IsStreamable = HasStreamableTraits::value; + +constexpr std::size_t repr_max_container_size = 5; + +template std::string repr(T const &val) { + if constexpr (std::is_same_v) { + return val ? "true" : "false"; + } else if constexpr (std::is_convertible_v) { + return '"' + std::string{std::string_view{val}} + '"'; + } else if constexpr (IsContainer) { + std::stringstream out; + out << "{"; + const auto size = val.size(); + if (size > 1) { + out << repr(*val.begin()); + std::for_each( + std::next(val.begin()), + std::next(val.begin(), + std::min(size, repr_max_container_size) - 1), + [&out](const auto &v) { out << " " << repr(v); }); + if (size <= repr_max_container_size) { + out << " "; + } else { + out << "..."; + } + } + if (size > 0) { + out << repr(*std::prev(val.end())); + } + out << "}"; + return out.str(); + } else if constexpr (IsStreamable) { + std::stringstream out; + out << val; + return out.str(); + } else { + return ""; + } +} + +namespace { + +template constexpr bool standard_signed_integer = false; +template <> constexpr bool standard_signed_integer = true; +template <> constexpr bool standard_signed_integer = true; +template <> constexpr bool standard_signed_integer = true; +template <> constexpr bool standard_signed_integer = true; +template <> constexpr bool standard_signed_integer = true; + +template constexpr bool standard_unsigned_integer = false; +template <> constexpr bool standard_unsigned_integer = true; +template <> constexpr bool standard_unsigned_integer = true; +template <> constexpr bool standard_unsigned_integer = true; +template <> constexpr bool standard_unsigned_integer = true; +template <> +constexpr bool standard_unsigned_integer = true; + +} // namespace + +constexpr int radix_8 = 8; +constexpr int radix_10 = 10; +constexpr int radix_16 = 16; + +template +constexpr bool standard_integer = + standard_signed_integer || standard_unsigned_integer; + +template +constexpr decltype(auto) apply_plus_one_impl(F &&f, Tuple &&t, Extra &&x, + std::index_sequence /*unused*/) { + return std::invoke(std::forward(f), std::get(std::forward(t))..., + std::forward(x)); +} + +template +constexpr decltype(auto) apply_plus_one(F &&f, Tuple &&t, Extra &&x) { + return details::apply_plus_one_impl( + std::forward(f), std::forward(t), std::forward(x), + std::make_index_sequence< + std::tuple_size_v>>{}); +} + +constexpr auto pointer_range(std::string_view s) noexcept { + return std::tuple(s.data(), s.data() + s.size()); +} + +template +constexpr bool starts_with(std::basic_string_view prefix, + std::basic_string_view s) noexcept { + return s.substr(0, prefix.size()) == prefix; +} + +enum class chars_format { + scientific = 0x1, + fixed = 0x2, + hex = 0x4, + general = fixed | scientific +}; + +struct ConsumeHexPrefixResult { + bool is_hexadecimal; + std::string_view rest; +}; + +using namespace std::literals; + +constexpr auto consume_hex_prefix(std::string_view s) + -> ConsumeHexPrefixResult { + if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) { + s.remove_prefix(2); + return {true, s}; + } + return {false, s}; +} + +template +inline auto do_from_chars(std::string_view s) -> T { + T x; + auto [first, last] = pointer_range(s); + auto [ptr, ec] = std::from_chars(first, last, x, Param); + if (ec == std::errc()) { + if (ptr == last) { + return x; + } + throw std::invalid_argument{"pattern does not match to the end"}; + } + if (ec == std::errc::invalid_argument) { + throw std::invalid_argument{"pattern not found"}; + } + if (ec == std::errc::result_out_of_range) { + throw std::range_error{"not representable"}; + } + return x; // unreachable +} + +template struct parse_number { + auto operator()(std::string_view s) -> T { + return do_from_chars(s); + } +}; + +template struct parse_number { + auto operator()(std::string_view s) -> T { + if (auto [ok, rest] = consume_hex_prefix(s); ok) { + return do_from_chars(rest); + } + throw std::invalid_argument{"pattern not found"}; + } +}; + +template struct parse_number { + auto operator()(std::string_view s) -> T { + auto [ok, rest] = consume_hex_prefix(s); + if (ok) { + return do_from_chars(rest); + } + if (starts_with("0"sv, s)) { + return do_from_chars(rest); + } + return do_from_chars(rest); + } +}; + +namespace { + +template constexpr auto generic_strtod = nullptr; +template <> constexpr auto generic_strtod = strtof; +template <> constexpr auto generic_strtod = strtod; +template <> constexpr auto generic_strtod = strtold; + +} // namespace + +template inline auto do_strtod(std::string const &s) -> T { + if (isspace(static_cast(s[0])) || s[0] == '+') { + throw std::invalid_argument{"pattern not found"}; + } + + auto [first, last] = pointer_range(s); + char *ptr; + + errno = 0; + auto x = generic_strtod(first, &ptr); + if (errno == 0) { + if (ptr == last) { + return x; + } + throw std::invalid_argument{"pattern does not match to the end"}; + } + if (errno == ERANGE) { + throw std::range_error{"not representable"}; + } + return x; // unreachable +} + +template struct parse_number { + auto operator()(std::string const &s) -> T { + if (auto r = consume_hex_prefix(s); r.is_hexadecimal) { + throw std::invalid_argument{ + "chars_format::general does not parse hexfloat"}; + } + + return do_strtod(s); + } +}; + +template struct parse_number { + auto operator()(std::string const &s) -> T { + if (auto r = consume_hex_prefix(s); !r.is_hexadecimal) { + throw std::invalid_argument{"chars_format::hex parses hexfloat"}; + } + + return do_strtod(s); + } +}; + +template struct parse_number { + auto operator()(std::string const &s) -> T { + if (auto r = consume_hex_prefix(s); r.is_hexadecimal) { + throw std::invalid_argument{ + "chars_format::scientific does not parse hexfloat"}; + } + if (s.find_first_of("eE") == std::string::npos) { + throw std::invalid_argument{ + "chars_format::scientific requires exponent part"}; + } + + return do_strtod(s); + } +}; + +template struct parse_number { + auto operator()(std::string const &s) -> T { + if (auto r = consume_hex_prefix(s); r.is_hexadecimal) { + throw std::invalid_argument{ + "chars_format::fixed does not parse hexfloat"}; + } + if (s.find_first_of("eE") != std::string::npos) { + throw std::invalid_argument{ + "chars_format::fixed does not parse exponent part"}; + } + + return do_strtod(s); + } +}; + +} // namespace details + +enum class nargs_pattern { + optional, + any, + at_least_one +}; + +enum class default_arguments : unsigned int { + none = 0, + help = 1, + version = 2, + all = help | version, +}; + +inline default_arguments operator&(const default_arguments &a, + const default_arguments &b) { + return static_cast( + static_cast::type>(a) & + static_cast::type>(b)); +} + +class ArgumentParser; + +class Argument { + friend class ArgumentParser; + friend auto operator<<(std::ostream &stream, const ArgumentParser &parser) + -> std::ostream &; + + template + explicit Argument(std::array &&a, + std::index_sequence /*unused*/) + : m_is_optional((is_optional(a[I]) || ...)), m_is_required(false), + m_is_repeatable(false), m_is_used(false) { + ((void)m_names.emplace_back(a[I]), ...); + std::sort( + m_names.begin(), m_names.end(), [](const auto &lhs, const auto &rhs) { + return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size(); + }); + } + +public: + template + explicit Argument(std::array &&a) + : Argument(std::move(a), std::make_index_sequence{}) {} + + Argument &help(std::string help_text) { + m_help = std::move(help_text); + return *this; + } + + template Argument &default_value(T &&value) { + m_default_value_repr = details::repr(value); + m_default_value = std::forward(value); + return *this; + } + + Argument &required() { + m_is_required = true; + return *this; + } + + Argument &implicit_value(std::any value) { + m_implicit_value = std::move(value); + m_num_args_range = NArgsRange{0, 0}; + return *this; + } + + template + auto action(F &&callable, Args &&...bound_args) + -> std::enable_if_t, + Argument &> { + using action_type = std::conditional_t< + std::is_void_v>, + void_action, valued_action>; + if constexpr (sizeof...(Args) == 0) { + m_action.emplace(std::forward(callable)); + } else { + m_action.emplace( + [f = std::forward(callable), + tup = std::make_tuple(std::forward(bound_args)...)]( + std::string const &opt) mutable { + return details::apply_plus_one(f, tup, opt); + }); + } + return *this; + } + + auto &append() { + m_is_repeatable = true; + return *this; + } + + template + auto scan() -> std::enable_if_t, Argument &> { + static_assert(!(std::is_const_v || std::is_volatile_v), + "T should not be cv-qualified"); + auto is_one_of = [](char c, auto... x) constexpr { + return ((c == x) || ...); + }; + + if constexpr (is_one_of(Shape, 'd') && details::standard_integer) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'i') && details::standard_integer) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'u') && + details::standard_unsigned_integer) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'o') && + details::standard_unsigned_integer) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'x', 'X') && + details::standard_unsigned_integer) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'a', 'A') && + std::is_floating_point_v) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'e', 'E') && + std::is_floating_point_v) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'f', 'F') && + std::is_floating_point_v) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'g', 'G') && + std::is_floating_point_v) { + action(details::parse_number()); + } else { + static_assert(alignof(T) == 0, "No scan specification for T"); + } + + return *this; + } + + Argument &nargs(std::size_t num_args) { + m_num_args_range = NArgsRange{num_args, num_args}; + return *this; + } + + Argument &nargs(std::size_t num_args_min, std::size_t num_args_max) { + m_num_args_range = NArgsRange{num_args_min, num_args_max}; + return *this; + } + + Argument &nargs(nargs_pattern pattern) { + switch (pattern) { + case nargs_pattern::optional: + m_num_args_range = NArgsRange{0, 1}; + break; + case nargs_pattern::any: + m_num_args_range = NArgsRange{0, std::numeric_limits::max()}; + break; + case nargs_pattern::at_least_one: + m_num_args_range = NArgsRange{1, std::numeric_limits::max()}; + break; + } + return *this; + } + + Argument &remaining() { + m_accepts_optional_like_value = true; + return nargs(nargs_pattern::any); + } + + template + Iterator consume(Iterator start, Iterator end, + std::string_view used_name = {}) { + if (!m_is_repeatable && m_is_used) { + throw std::runtime_error("Duplicate argument"); + } + m_is_used = true; + m_used_name = used_name; + + const auto num_args_max = m_num_args_range.get_max(); + const auto num_args_min = m_num_args_range.get_min(); + std::size_t dist = 0; + if (num_args_max == 0) { + m_values.emplace_back(m_implicit_value); + std::visit([](const auto &f) { f({}); }, m_action); + return start; + } + if ((dist = static_cast(std::distance(start, end))) >= num_args_min) { + if (num_args_max < dist) { + end = std::next(start, num_args_max); + } + if (!m_accepts_optional_like_value) { + end = std::find_if(start, end, Argument::is_optional); + dist = static_cast(std::distance(start, end)); + if (dist < num_args_min) { + throw std::runtime_error("Too few arguments"); + } + } + + struct ActionApply { + void operator()(valued_action &f) { + std::transform(first, last, std::back_inserter(self.m_values), f); + } + + void operator()(void_action &f) { + std::for_each(first, last, f); + if (!self.m_default_value.has_value()) { + if (!self.m_accepts_optional_like_value) { + self.m_values.resize(std::distance(first, last)); + } + } + } + + Iterator first, last; + Argument &self; + }; + std::visit(ActionApply{start, end, *this}, m_action); + return end; + } + if (m_default_value.has_value()) { + return start; + } + throw std::runtime_error("Too few arguments for '" + + std::string(m_used_name) + "'."); + } + + /* + * @throws std::runtime_error if argument values are not valid + */ + void validate() const { + if (m_is_optional) { + // TODO: check if an implicit value was programmed for this argument + if (!m_is_used && !m_default_value.has_value() && m_is_required) { + throw_required_arg_not_used_error(); + } + if (m_is_used && m_is_required && m_values.empty()) { + throw_required_arg_no_value_provided_error(); + } + } else { + if (!m_num_args_range.contains(m_values.size()) && !m_default_value.has_value()) { + throw_nargs_range_validation_error(); + } + } + } + + std::size_t get_arguments_length() const { + return std::accumulate(std::begin(m_names), std::end(m_names), + std::size_t(0), [](const auto &sum, const auto &s) { + return sum + s.size() + + 1; // +1 for space between names + }); + } + + friend std::ostream &operator<<(std::ostream &stream, + const Argument &argument) { + std::stringstream name_stream; + std::copy(std::begin(argument.m_names), std::end(argument.m_names), + std::ostream_iterator(name_stream, " ")); + stream << name_stream.str() << "\t" << argument.m_help; + if (argument.m_default_value.has_value()) { + if (!argument.m_help.empty()) { + stream << " "; + } + stream << "[default: " << argument.m_default_value_repr << "]"; + } else if (argument.m_is_required) { + if (!argument.m_help.empty()) { + stream << " "; + } + stream << "[required]"; + } + stream << "\n"; + return stream; + } + + template bool operator!=(const T &rhs) const { + return !(*this == rhs); + } + + /* + * Compare to an argument value of known type + * @throws std::logic_error in case of incompatible types + */ + template bool operator==(const T &rhs) const { + if constexpr (!details::IsContainer) { + return get() == rhs; + } else { + using ValueType = typename T::value_type; + auto lhs = get(); + return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs), + std::end(rhs), [](const auto &lhs, const auto &rhs) { + return std::any_cast(lhs) == rhs; + }); + } + } + +private: + + class NArgsRange { + std::size_t m_min; + std::size_t m_max; + + public: + NArgsRange(std::size_t minimum, std::size_t maximum) + : m_min(minimum), m_max(maximum) { + if (minimum > maximum) { + throw std::logic_error("Range of number of arguments is invalid"); + } + } + + bool contains(std::size_t value) const { + return value >= m_min && value <= m_max; + } + + bool is_exact() const { + return m_min == m_max; + } + + bool is_right_bounded() const { + return m_max < std::numeric_limits::max(); + } + + std::size_t get_min() const { + return m_min; + } + + std::size_t get_max() const { + return m_max; + } + }; + + void throw_nargs_range_validation_error() const { + std::stringstream stream; + if (!m_used_name.empty()) { + stream << m_used_name << ": "; + } + if (m_num_args_range.is_exact()) { + stream << m_num_args_range.get_min(); + } else if (m_num_args_range.is_right_bounded()) { + stream << m_num_args_range.get_min() << " to " << m_num_args_range.get_max(); + } else { + stream << m_num_args_range.get_min() << " or more"; + } + stream << " argument(s) expected. " << m_values.size() << " provided."; + throw std::runtime_error(stream.str()); + } + + void throw_required_arg_not_used_error() const { + std::stringstream stream; + stream << m_names[0] << ": required."; + throw std::runtime_error(stream.str()); + } + + void throw_required_arg_no_value_provided_error() const { + std::stringstream stream; + stream << m_used_name << ": no value provided."; + throw std::runtime_error(stream.str()); + } + + static constexpr int eof = std::char_traits::eof(); + + static auto lookahead(std::string_view s) -> int { + if (s.empty()) { + return eof; + } + return static_cast(static_cast(s[0])); + } + + /* + * decimal-literal: + * '0' + * nonzero-digit digit-sequence_opt + * integer-part fractional-part + * fractional-part + * integer-part '.' exponent-part_opt + * integer-part exponent-part + * + * integer-part: + * digit-sequence + * + * fractional-part: + * '.' post-decimal-point + * + * post-decimal-point: + * digit-sequence exponent-part_opt + * + * exponent-part: + * 'e' post-e + * 'E' post-e + * + * post-e: + * sign_opt digit-sequence + * + * sign: one of + * '+' '-' + */ + static bool is_decimal_literal(std::string_view s) { + auto is_digit = [](auto c) constexpr { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return true; + default: + return false; + } + }; + + // precondition: we have consumed or will consume at least one digit + auto consume_digits = [=](std::string_view s) { + // NOLINTNEXTLINE(readability-qualified-auto) + auto it = std::find_if_not(std::begin(s), std::end(s), is_digit); + return s.substr(it - std::begin(s)); + }; + + switch (lookahead(s)) { + case '0': { + s.remove_prefix(1); + if (s.empty()) { + return true; + } + goto integer_part; + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + s = consume_digits(s); + if (s.empty()) { + return true; + } + goto integer_part_consumed; + } + case '.': { + s.remove_prefix(1); + goto post_decimal_point; + } + default: + return false; + } + + integer_part: + s = consume_digits(s); + integer_part_consumed: + switch (lookahead(s)) { + case '.': { + s.remove_prefix(1); + if (is_digit(lookahead(s))) { + goto post_decimal_point; + } else { + goto exponent_part_opt; + } + } + case 'e': + case 'E': { + s.remove_prefix(1); + goto post_e; + } + default: + return false; + } + + post_decimal_point: + if (is_digit(lookahead(s))) { + s = consume_digits(s); + goto exponent_part_opt; + } + return false; + + exponent_part_opt: + switch (lookahead(s)) { + case eof: + return true; + case 'e': + case 'E': { + s.remove_prefix(1); + goto post_e; + } + default: + return false; + } + + post_e: + switch (lookahead(s)) { + case '-': + case '+': + s.remove_prefix(1); + } + if (is_digit(lookahead(s))) { + s = consume_digits(s); + return s.empty(); + } + return false; + } + + static bool is_optional(std::string_view name) { + return !is_positional(name); + } + + /* + * positional: + * _empty_ + * '-' + * '-' decimal-literal + * !'-' anything + */ + static bool is_positional(std::string_view name) { + switch (lookahead(name)) { + case eof: + return true; + case '-': { + name.remove_prefix(1); + if (name.empty()) { + return true; + } + return is_decimal_literal(name); + } + default: + return true; + } + } + + /* + * Get argument value given a type + * @throws std::logic_error in case of incompatible types + */ + template T get() const { + if (!m_values.empty()) { + if constexpr (details::IsContainer) { + return any_cast_container(m_values); + } else { + return std::any_cast(m_values.front()); + } + } + if (m_default_value.has_value()) { + return std::any_cast(m_default_value); + } + if constexpr (details::IsContainer) { + if (!m_accepts_optional_like_value) { + return any_cast_container(m_values); + } + } + + throw std::logic_error("No value provided for '" + m_names.back() + "'."); + } + + /* + * Get argument value given a type. + * @pre The object has no default value. + * @returns The stored value if any, std::nullopt otherwise. + */ + template auto present() const -> std::optional { + if (m_default_value.has_value()) { + throw std::logic_error("Argument with default value always presents"); + } + if (m_values.empty()) { + return std::nullopt; + } + if constexpr (details::IsContainer) { + return any_cast_container(m_values); + } + return std::any_cast(m_values.front()); + } + + template + static auto any_cast_container(const std::vector &operand) -> T { + using ValueType = typename T::value_type; + + T result; + std::transform( + std::begin(operand), std::end(operand), std::back_inserter(result), + [](const auto &value) { return std::any_cast(value); }); + return result; + } + + std::vector m_names; + std::string_view m_used_name; + std::string m_help; + std::any m_default_value; + std::string m_default_value_repr; + std::any m_implicit_value; + using valued_action = std::function; + using void_action = std::function; + std::variant m_action{ + std::in_place_type, + [](const std::string &value) { return value; }}; + std::vector m_values; + NArgsRange m_num_args_range {1, 1}; + bool m_accepts_optional_like_value = false; + bool m_is_optional : true; + bool m_is_required : true; + bool m_is_repeatable : true; + bool m_is_used : true; // True if the optional argument is used by user +}; + +class ArgumentParser { +public: + explicit ArgumentParser(std::string program_name = {}, + std::string version = "1.0", + default_arguments add_args = default_arguments::all) + : m_program_name(std::move(program_name)), m_version(std::move(version)) { + if ((add_args & default_arguments::help) == default_arguments::help) { + add_argument("-h", "--help") + .action([&](const auto &/*unused*/) { + std::cout << help().str(); + std::exit(0); + }) + .default_value(false) + .help("shows help message and exits") + .implicit_value(true) + .nargs(0); + } + if ((add_args & default_arguments::version) == default_arguments::version) { + add_argument("-v", "--version") + .action([&](const auto &/*unused*/) { + std::cout << m_version << std::endl; + std::exit(0); + }) + .default_value(false) + .help("prints version information and exits") + .implicit_value(true) + .nargs(0); + } + } + + ArgumentParser(ArgumentParser &&) noexcept = default; + ArgumentParser &operator=(ArgumentParser &&) = default; + + ArgumentParser(const ArgumentParser &other) + : m_program_name(other.m_program_name), + m_version(other.m_version), + m_description(other.m_description), + m_epilog(other.m_epilog), + m_is_parsed(other.m_is_parsed), + m_positional_arguments(other.m_positional_arguments), + m_optional_arguments(other.m_optional_arguments) { + for (auto it = std::begin(m_positional_arguments); + it != std::end(m_positional_arguments); ++it) { + index_argument(it); + } + for (auto it = std::begin(m_optional_arguments); + it != std::end(m_optional_arguments); ++it) { + index_argument(it); + } + } + + ~ArgumentParser() = default; + + ArgumentParser &operator=(const ArgumentParser &other) { + auto tmp = other; + std::swap(*this, tmp); + return *this; + } + + // Parameter packing + // Call add_argument with variadic number of string arguments + template Argument &add_argument(Targs... f_args) { + using array_of_sv = std::array; + auto argument = m_optional_arguments.emplace( + std::cend(m_optional_arguments), array_of_sv{f_args...}); + + if (!argument->m_is_optional) { + m_positional_arguments.splice(std::cend(m_positional_arguments), + m_optional_arguments, argument); + } + + index_argument(argument); + return *argument; + } + + // Parameter packed add_parents method + // Accepts a variadic number of ArgumentParser objects + template + ArgumentParser &add_parents(const Targs &...f_args) { + for (const ArgumentParser &parent_parser : {std::ref(f_args)...}) { + for (const auto &argument : parent_parser.m_positional_arguments) { + auto it = m_positional_arguments.insert( + std::cend(m_positional_arguments), argument); + index_argument(it); + } + for (const auto &argument : parent_parser.m_optional_arguments) { + auto it = m_optional_arguments.insert(std::cend(m_optional_arguments), + argument); + index_argument(it); + } + } + return *this; + } + + ArgumentParser &add_description(std::string description) { + m_description = std::move(description); + return *this; + } + + ArgumentParser &add_epilog(std::string epilog) { + m_epilog = std::move(epilog); + return *this; + } + + /* Call parse_args_internal - which does all the work + * Then, validate the parsed arguments + * This variant is used mainly for testing + * @throws std::runtime_error in case of any invalid argument + */ + void parse_args(const std::vector &arguments) { + parse_args_internal(arguments); + // Check if all arguments are parsed + for ([[maybe_unused]] const auto& [unused, argument] : m_argument_map) { + argument->validate(); + } + } + + /* Main entry point for parsing command-line arguments using this + * ArgumentParser + * @throws std::runtime_error in case of any invalid argument + */ + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) + void parse_args(int argc, const char *const argv[]) { + std::vector arguments; + std::copy(argv, argv + argc, std::back_inserter(arguments)); + parse_args(arguments); + } + + /* Getter for options with default values. + * @throws std::logic_error if parse_args() has not been previously called + * @throws std::logic_error if there is no such option + * @throws std::logic_error if the option has no value + * @throws std::bad_any_cast if the option is not of type T + */ + template T get(std::string_view arg_name) const { + if (!m_is_parsed) { + throw std::logic_error("Nothing parsed, no arguments are available."); + } + return (*this)[arg_name].get(); + } + + /* Getter for options without default values. + * @pre The option has no default value. + * @throws std::logic_error if there is no such option + * @throws std::bad_any_cast if the option is not of type T + */ + template + auto present(std::string_view arg_name) const -> std::optional { + return (*this)[arg_name].present(); + } + + /* Getter that returns true for user-supplied options. Returns false if not + * user-supplied, even with a default value. + */ + auto is_used(std::string_view arg_name) const { + return (*this)[arg_name].m_is_used; + } + + /* Indexing operator. Return a reference to an Argument object + * Used in conjuction with Argument.operator== e.g., parser["foo"] == true + * @throws std::logic_error in case of an invalid argument name + */ + Argument &operator[](std::string_view arg_name) const { + auto it = m_argument_map.find(arg_name); + if (it != m_argument_map.end()) { + return *(it->second); + } + if (arg_name.front() != '-') { + std::string name(arg_name); + // "-" + arg_name + name = "-" + name; + it = m_argument_map.find(name); + if (it != m_argument_map.end()) { + return *(it->second); + } + // "--" + arg_name + name = "-" + name; + it = m_argument_map.find(name); + if (it != m_argument_map.end()) { + return *(it->second); + } + } + throw std::logic_error("No such argument: " + std::string(arg_name)); + } + + // Print help message + friend auto operator<<(std::ostream &stream, const ArgumentParser &parser) + -> std::ostream & { + stream.setf(std::ios_base::left); + stream << "Usage: " << parser.m_program_name << " [options] "; + std::size_t longest_arg_length = parser.get_length_of_longest_argument(); + + for (const auto &argument : parser.m_positional_arguments) { + stream << argument.m_names.front() << " "; + } + stream << "\n\n"; + + if (!parser.m_description.empty()) { + stream << parser.m_description << "\n\n"; + } + + if (!parser.m_positional_arguments.empty()) { + stream << "Positional arguments:\n"; + } + + for (const auto &argument : parser.m_positional_arguments) { + stream.width(longest_arg_length); + stream << argument; + } + + if (!parser.m_optional_arguments.empty()) { + stream << (parser.m_positional_arguments.empty() ? "" : "\n") + << "Optional arguments:\n"; + } + + for (const auto &argument : parser.m_optional_arguments) { + stream.width(longest_arg_length); + stream << argument; + } + + if (!parser.m_epilog.empty()) { + stream << '\n'; + stream << parser.m_epilog << "\n\n"; + } + + return stream; + } + + // Format help message + auto help() const -> std::stringstream { + std::stringstream out; + out << *this; + return out; + } + + // Printing the one and only help message + // I've stuck with a simple message format, nothing fancy. + [[deprecated("Use cout << program; instead. See also help().")]] std::string + print_help() const { + auto out = help(); + std::cout << out.rdbuf(); + return out.str(); + } + +private: + /* + * @throws std::runtime_error in case of any invalid argument + */ + void parse_args_internal(const std::vector &arguments) { + if (m_program_name.empty() && !arguments.empty()) { + m_program_name = arguments.front(); + } + auto end = std::end(arguments); + auto positional_argument_it = std::begin(m_positional_arguments); + for (auto it = std::next(std::begin(arguments)); it != end;) { + const auto ¤t_argument = *it; + if (Argument::is_positional(current_argument)) { + if (positional_argument_it == std::end(m_positional_arguments)) { + throw std::runtime_error( + "Maximum number of positional arguments exceeded"); + } + auto argument = positional_argument_it++; + it = argument->consume(it, end); + continue; + } + + auto arg_map_it = m_argument_map.find(current_argument); + if (arg_map_it != m_argument_map.end()) { + auto argument = arg_map_it->second; + it = argument->consume(std::next(it), end, arg_map_it->first); + } else if (const auto &compound_arg = current_argument; + compound_arg.size() > 1 && compound_arg[0] == '-' && + compound_arg[1] != '-') { + ++it; + for (std::size_t j = 1; j < compound_arg.size(); j++) { + auto hypothetical_arg = std::string{'-', compound_arg[j]}; + auto arg_map_it2 = m_argument_map.find(hypothetical_arg); + if (arg_map_it2 != m_argument_map.end()) { + auto argument = arg_map_it2->second; + it = argument->consume(it, end, arg_map_it2->first); + } else { + throw std::runtime_error("Unknown argument: " + current_argument); + } + } + } else { + throw std::runtime_error("Unknown argument: " + current_argument); + } + } + m_is_parsed = true; + } + + // Used by print_help. + std::size_t get_length_of_longest_argument() const { + if (m_argument_map.empty()) { + return 0; + } + std::size_t max_size = 0; + for ([[maybe_unused]] const auto& [unused, argument] : m_argument_map) { + max_size = std::max(max_size, argument->get_arguments_length()); + } + return max_size; + } + + using list_iterator = std::list::iterator; + + void index_argument(list_iterator it) { + for (const auto &name : std::as_const(it->m_names)) { + m_argument_map.insert_or_assign(name, it); + } + } + + std::string m_program_name; + std::string m_version; + std::string m_description; + std::string m_epilog; + bool m_is_parsed = false; + std::list m_positional_arguments; + std::list m_optional_arguments; + std::map> m_argument_map; +}; + +} // namespace argparse -- GitLab From 8e0d5156bee92b8638094200c993064943a4ac19 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Tue, 2 Aug 2022 14:19:42 +0200 Subject: [PATCH 02/24] linux/CoapClient: start thread in CoapClient that spins coap_io_process Signed-off-by: Francesco Pham --- linux/communication/include/CoapClient.h | 8 ++++--- linux/communication/src/CoapClient.cpp | 13 +++++++++-- linux/communication/src/EddieEndpoint.cpp | 28 +---------------------- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/linux/communication/include/CoapClient.h b/linux/communication/include/CoapClient.h index bb79663..9cfcfa5 100644 --- a/linux/communication/include/CoapClient.h +++ b/linux/communication/include/CoapClient.h @@ -23,11 +23,13 @@ class CoapClient { private: - int quit = 0; + bool quit = false; coap_context_t *context = nullptr; coap_session_t *open_session(const char *dst_host, const char *dst_port, coap_proto_t protocol); + std::thread client_thread; + public: // TODO: move into private static ThreadSafeMap *messages; @@ -39,9 +41,9 @@ public: void destroy_client(); - int get_quit() const; + bool get_quit() const; - void set_quit(int value); + void set_quit(bool value); coap_context_t *get_context(); diff --git a/linux/communication/src/CoapClient.cpp b/linux/communication/src/CoapClient.cpp index ea44283..ecc7ff5 100644 --- a/linux/communication/src/CoapClient.cpp +++ b/linux/communication/src/CoapClient.cpp @@ -535,6 +535,9 @@ CoapClient::CoapClient() { } void CoapClient::destroy_client() { + set_quit(true); + client_thread.join(); + auto coap_messages = messages->get_all(); auto m_cast_messages = multicast_messages->get_valid_keys(); @@ -557,11 +560,11 @@ void CoapClient::destroy_client() { coap_cleanup(); } -int CoapClient::get_quit() const { +bool CoapClient::get_quit() const { return this->quit; } -void CoapClient::set_quit(int value) { +void CoapClient::set_quit(bool value) { this->quit = value; } @@ -582,6 +585,12 @@ int CoapClient::init_client() { coap_register_response_handler(this->context, message_handler); + auto client_run = [this]() { + while (!this->get_quit()) + coap_io_process(this->context, COAP_IO_WAIT); + }; + client_thread = std::thread(client_run); + return 0; } diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index ccc01f1..9ef3a29 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -144,33 +144,7 @@ int EddieEndpoint::add_resource(EddieResource *resource) { } int EddieEndpoint::start_server() { - auto server_run = [this]() { - if (!this->get_server()) { - LOG_DBG("[CoapNode::run]: server is NULL"); - exit(EXIT_FAILURE); - } - - while (!this->get_server()->get_quit()) - coap_io_process(this->get_server()->get_context(), COAP_IO_WAIT); - }; - - auto client_run = [this]() { - if (!this->get_server()) { - LOG_DBG("[CoapNode::run]: server is NULL"); - exit(EXIT_FAILURE); - } - - while (this->get_server()->get_quit() == 0) - coap_io_process(this->get_client()->get_context(), COAP_IO_WAIT); - }; - - std::thread server_thread(server_run); - std::thread client_thread(client_run); - - server_thread.join(); - client_thread.join(); - - return 0; + return get_server()->run(); } int EddieEndpoint::publish_resources() { -- GitLab From 1c04adfadb002eebd04c47869c16890f01abba67 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Tue, 2 Aug 2022 14:50:08 +0200 Subject: [PATCH 03/24] ResourceDirectory: add base attr, move get_local_node_ip to common - add base attr - move get_local_node_ip to common - rt for rd-lookup/res now is core.rd-lookup-res and rd-lookup/ep now is core.rd-lookup-ep Signed-off-by: Francesco Pham --- .../communication/include/ResourceDirectory.h | 2 +- linux/communication/include/common.h | 2 + linux/communication/src/CoapServer.cpp | 4 +- linux/communication/src/EddieEndpoint.cpp | 38 ------------------- linux/communication/src/ResourceDirectory.cpp | 7 ++-- linux/communication/src/common.cpp | 37 ++++++++++++++++++ linux/examples/resource_directory.cpp | 7 +--- 7 files changed, 48 insertions(+), 49 deletions(-) diff --git a/linux/communication/include/ResourceDirectory.h b/linux/communication/include/ResourceDirectory.h index d6495d2..e8af0dc 100644 --- a/linux/communication/include/ResourceDirectory.h +++ b/linux/communication/include/ResourceDirectory.h @@ -62,7 +62,7 @@ public: * * @param server_context A preinitialized server context. If not specified, the server context will be created. */ - explicit ResourceDirectory(coap_context_t *server_context = nullptr); + explicit ResourceDirectory(const std::string& ip, coap_context_t *server_context = nullptr); ~ResourceDirectory(); diff --git a/linux/communication/include/common.h b/linux/communication/include/common.h index e5d3fa4..9bad6d4 100644 --- a/linux/communication/include/common.h +++ b/linux/communication/include/common.h @@ -22,6 +22,8 @@ int resolve_address(const char *host, const char *service, coap_address_t *dst); +std::string get_local_node_ip(); + int match(const std::string &src, const std::string &dst, int is_prefix); /** diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index e899e9e..0c906e8 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -122,7 +122,7 @@ CoapServer::CoapServer(const char *host, const char *port, int is_resource_dir) this->client = new CoapClient(); this->client->init_client(); if (is_resource_dir) { - this->resource_directory = new ResourceDirectory(this->context); + this->resource_directory = new ResourceDirectory(my_ip, this->context); } auto now = std::chrono::duration_cast( @@ -435,7 +435,7 @@ int CoapServer::unpublish_resources() { } void CoapServer::init_resource_dir_local() { - this->resource_directory = new ResourceDirectory(this->context); + this->resource_directory = new ResourceDirectory(my_ip, this->context); } std::list CoapServer::rd_resources_to_string_list_filtered(const std::map &filters) { diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index 9ef3a29..a690b98 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -6,46 +6,8 @@ */ #include "EddieEndpoint.h" -#include - #define NOW std::chrono::system_clock::now() -std::string get_local_node_ip() { - std::string ip; - struct ifaddrs *if_addresses; - int rc; - char host[NI_MAXHOST]; - - if (getifaddrs(&if_addresses) == -1) { - perror("[CoapNode::connect]: getifaddrs"); - exit(EXIT_FAILURE); - } - - for (struct ifaddrs *ifa = if_addresses; ifa != nullptr; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6 || strcmp(ifa->ifa_name, "lo") == 0) - continue; - - rc = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, - nullptr, 0, NI_NUMERICHOST); - - if (rc != 0) { - LOG_DBG("[CoapNode::connect]: getnameinfo failed: %s", gai_strerror(rc)); - exit(EXIT_FAILURE); - } - - std::string host_str = host; - - ip = host_str.substr(0, host_str.length() - (strlen(ifa->ifa_name) + 1)); - - if (ip.empty()) { - LOG_DBG("[CoapNode::connect]: no valid ip address found for this machine"); - exit(EXIT_FAILURE); - } - } - - return ip; -} - CoapClient* EddieEndpoint::get_client(){ return this->server ? this->server->get_client() : nullptr; } diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp index 597df40..f8d4cbd 100644 --- a/linux/communication/src/ResourceDirectory.cpp +++ b/linux/communication/src/ResourceDirectory.cpp @@ -689,7 +689,7 @@ int ResourceDirectory::init_server_context() { return 0; } -ResourceDirectory::ResourceDirectory(coap_context_t *server_context) { +ResourceDirectory::ResourceDirectory(const std::string& ip, coap_context_t *server_context) { if (server_context != nullptr) this->context = server_context; @@ -707,18 +707,19 @@ ResourceDirectory::ResourceDirectory(coap_context_t *server_context) { coap_register_handler(resource_dir, COAP_REQUEST_POST, hnd_post_rd); coap_add_attr(resource_dir, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); coap_add_attr(resource_dir, coap_make_str_const("ct"), coap_make_str_const("40"), 0); + coap_add_attr(resource_dir, coap_make_str_const("base"), coap_make_str_const(ip.c_str()), 0); coap_add_resource(this->context, resource_dir); rd_lookup_res = coap_resource_init(coap_make_str_const("rd-lookup/res"), 0); coap_register_handler(rd_lookup_res, COAP_REQUEST_GET, hnd_get_lookup_res); - coap_add_attr(rd_lookup_res, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); + coap_add_attr(rd_lookup_res, coap_make_str_const("rt"), coap_make_str_const("\"core.rd-lookup-res\""), 0); coap_add_attr(rd_lookup_res, coap_make_str_const("ct"), coap_make_str_const("40"), 0); coap_resource_set_get_observable(rd_lookup_res, 1); coap_add_resource(this->context, rd_lookup_res); rd_lookup_ep = coap_resource_init(coap_make_str_const("rd-lookup/ep"), 0); coap_register_handler(rd_lookup_ep, COAP_REQUEST_GET, hnd_get_lookup_ep); - coap_add_attr(rd_lookup_ep, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); + coap_add_attr(rd_lookup_ep, coap_make_str_const("rt"), coap_make_str_const("\"core.rd-lookup-ep\""), 0); coap_add_attr(rd_lookup_ep, coap_make_str_const("ct"), coap_make_str_const("40"), 0); coap_add_resource(this->context, rd_lookup_ep); } diff --git a/linux/communication/src/common.cpp b/linux/communication/src/common.cpp index bdba77f..36af74d 100644 --- a/linux/communication/src/common.cpp +++ b/linux/communication/src/common.cpp @@ -10,6 +10,7 @@ #include #include #include +#include int resolve_address(const char *host, const char *service, coap_address_t *dst) { struct addrinfo *res, *aInfo; @@ -66,4 +67,40 @@ std::vector split(const std::string &s, char delimiter) { } return tokens; +} + +std::string get_local_node_ip() { + std::string ip; + struct ifaddrs *if_addresses; + int rc; + char host[NI_MAXHOST]; + + if (getifaddrs(&if_addresses) == -1) { + perror("[CoapNode::connect]: getifaddrs"); + exit(EXIT_FAILURE); + } + + for (struct ifaddrs *ifa = if_addresses; ifa != nullptr; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6 || strcmp(ifa->ifa_name, "lo") == 0) + continue; + + rc = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, + nullptr, 0, NI_NUMERICHOST); + + if (rc != 0) { + LOG_DBG("[CoapNode::connect]: getnameinfo failed: %s", gai_strerror(rc)); + exit(EXIT_FAILURE); + } + + std::string host_str = host; + + ip = host_str.substr(0, host_str.length() - (strlen(ifa->ifa_name) + 1)); + + if (ip.empty()) { + LOG_DBG("[CoapNode::connect]: no valid ip address found for this machine"); + exit(EXIT_FAILURE); + } + } + + return ip; } \ No newline at end of file diff --git a/linux/examples/resource_directory.cpp b/linux/examples/resource_directory.cpp index 207f27d..1eb8df2 100644 --- a/linux/examples/resource_directory.cpp +++ b/linux/examples/resource_directory.cpp @@ -8,9 +8,6 @@ #include "ResourceDirectory.h" int main() { - ResourceDirectory *resource_directory = new ResourceDirectory(); - - resource_directory->run(); - - delete resource_directory; + ResourceDirectory resource_directory(get_local_node_ip()); + resource_directory.run(); } \ No newline at end of file -- GitLab From a5bacffb6b5a245ad4bb560f966a26bf18f3315f Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Tue, 2 Aug 2022 15:11:59 +0200 Subject: [PATCH 04/24] linux/CoapServer: use parse_link_format to parse response - add some logs Signed-off-by: Francesco Pham --- linux/communication/src/CoapServer.cpp | 25 +++++++++++++++-------- linux/communication/src/EddieEndpoint.cpp | 5 +++-- linux/examples/resource_directory.cpp | 1 + 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index 0c906e8..01a8385 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -8,6 +8,7 @@ #include "CoapServer.h" #include "common.h" #include "ResourceDirectory.h" +#include "LinkFormat.h" #include #include @@ -250,6 +251,7 @@ int CoapServer::add_resource(EddieResource *resource) { } int CoapServer::discover_rd() { + LOG_DBG("Discovering resource directory"); auto m_token = this->client->send_get_nc_request(".well-known/core", "rt=core.rd", nullptr, 0); std::list list; @@ -267,7 +269,10 @@ int CoapServer::discover_rd() { std::thread t(f); t.join(); - if (list.empty()) return 0; + if (list.empty()) { + LOG_DBG("Could not find resource directory"); + return -1; + } auto pdu = list.front(); size_t data_len; @@ -279,16 +284,20 @@ int CoapServer::discover_rd() { return -1; } - std::string data_str = std::string(reinterpret_cast(*data_pdu)); - auto rd_start = data_str.find("coap://"); - auto rd_end = data_str.find(';', rd_start); + std::string payload = std::string(reinterpret_cast(data_pdu), data_len); + std::map> parsed = parse_link_format(payload); - this->resource_dir_ip = data_str.substr(rd_start + 7, (rd_end - 5) - (rd_start + 7)); - this->resource_dir_port = data_str.substr(rd_end - 4, 4); + if (parsed.empty()) return -1; - CoapClient::multicast_messages->remove_valid_key(m_token); + auto first_link = parsed.begin()->second; + auto base = first_link.find("base"); - return 1; + this->resource_dir_ip = base->second; + this->resource_dir_port = "5683"; + + LOG_DBG("Found resource directory: %s:%s", this->resource_dir_ip.c_str(), this->resource_dir_port.c_str()); + + return 0; } int CoapServer::publish_resources(int lt) { diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index a690b98..b720f80 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -61,7 +61,7 @@ std::string EddieEndpoint::get_resources_from_rd() { // this node is not rd, doesn't know who is rd std::string resource_str; if (get_server()->get_rd_ip().empty()) { - if (get_server()->discover_rd() != 1) { + if (!get_server()->discover_rd()) { coap_log(LOG_ERR, "[CoapNode::discover]: error looking up rd node\n"); exit(-1); } @@ -100,8 +100,8 @@ std::string EddieEndpoint::get_resources_from_rd() { } int EddieEndpoint::add_resource(EddieResource *resource) { + LOG_DBG("Adding resource: %s", resource->get_path_str().c_str()); get_server()->add_resource(resource); - this->publish_resources(); return 0; } @@ -110,6 +110,7 @@ int EddieEndpoint::start_server() { } int EddieEndpoint::publish_resources() { + LOG_DBG("Publishing resources"); get_server()->publish_resources(); return 0; } \ No newline at end of file diff --git a/linux/examples/resource_directory.cpp b/linux/examples/resource_directory.cpp index 1eb8df2..8306d6f 100644 --- a/linux/examples/resource_directory.cpp +++ b/linux/examples/resource_directory.cpp @@ -8,6 +8,7 @@ #include "ResourceDirectory.h" int main() { + coap_set_log_level(LOG_DEBUG); ResourceDirectory resource_directory(get_local_node_ip()); resource_directory.run(); } \ No newline at end of file -- GitLab From b814b1919dcdccba24f934d22804e8db65db5145 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Tue, 2 Aug 2022 16:07:04 +0200 Subject: [PATCH 05/24] linux: move destroy_server into destructor, do not block coap_io_process - move destroy_server into destructor, - do not block coap_io_process in CoapClient - add some logs - move call to destroy_client in CoapServer destructor - fix tests Signed-off-by: Francesco Pham --- linux/communication/include/CoapServer.h | 6 +-- linux/communication/src/CoapClient.cpp | 2 +- linux/communication/src/CoapServer.cpp | 15 +++++--- linux/communication/src/EddieEndpoint.cpp | 4 -- linux/communication/src/ResourceDirectory.cpp | 2 + linux/tests/communication_tests.cpp | 38 +++++-------------- 6 files changed, 25 insertions(+), 42 deletions(-) diff --git a/linux/communication/include/CoapServer.h b/linux/communication/include/CoapServer.h index f110b8f..f5a44ee 100644 --- a/linux/communication/include/CoapServer.h +++ b/linux/communication/include/CoapServer.h @@ -48,10 +48,12 @@ private: static void resource_handler(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response); + int init_server(const char *host, const char *port); + public: explicit CoapServer(const char *host, const char *port, int is_resource_dir); - void destroy_server(); + ~CoapServer(); int get_quit() const; @@ -67,8 +69,6 @@ public: std::string get_rd_port(); - int init_server(const char *host, const char *port); - void init_resource_dir_local(); int discover_rd(); diff --git a/linux/communication/src/CoapClient.cpp b/linux/communication/src/CoapClient.cpp index ecc7ff5..cfb342a 100644 --- a/linux/communication/src/CoapClient.cpp +++ b/linux/communication/src/CoapClient.cpp @@ -587,7 +587,7 @@ int CoapClient::init_client() { auto client_run = [this]() { while (!this->get_quit()) - coap_io_process(this->context, COAP_IO_WAIT); + coap_io_process(this->context, 100); }; client_thread = std::thread(client_run); diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index 01a8385..d34b02c 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -119,10 +119,12 @@ CoapServer::CoapServer(const char *host, const char *port, int is_resource_dir) my_port = port; default_lt = "90000"; + LOG_DBG("Initializing CoapServer"); init_server("::", port); this->client = new CoapClient(); this->client->init_client(); if (is_resource_dir) { + LOG_DBG("Starting resource directory"); this->resource_directory = new ResourceDirectory(my_ip, this->context); } @@ -134,19 +136,22 @@ CoapServer::CoapServer(const char *host, const char *port, int is_resource_dir) ep = oss.str(); d = ep; -} -void CoapServer::destroy_server() { - resources->delete_all(); + LOG_DBG("Initialized CoapServer"); +} - if (!this->is_rd()) +CoapServer::~CoapServer() { + if (!this->is_rd()) unpublish_resources(); else delete this->resource_directory; - delete client; + resources->delete_all(); delete resources; + this->client->destroy_client(); + delete client; + coap_free_context(this->context); } diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index b720f80..264ee99 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -23,10 +23,6 @@ EddieEndpoint::EddieEndpoint(const std::string& port) { } EddieEndpoint::~EddieEndpoint() { - // TODO: client is owned by the server therefore server has to destroy it not here - if (get_client() != nullptr) get_client()->destroy_client(); - if (get_server() != nullptr) get_server()->destroy_server(); - delete server; coap_cleanup(); diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp index f8d4cbd..bb38c82 100644 --- a/linux/communication/src/ResourceDirectory.cpp +++ b/linux/communication/src/ResourceDirectory.cpp @@ -722,6 +722,8 @@ ResourceDirectory::ResourceDirectory(const std::string& ip, coap_context_t *serv coap_add_attr(rd_lookup_ep, coap_make_str_const("rt"), coap_make_str_const("\"core.rd-lookup-ep\""), 0); coap_add_attr(rd_lookup_ep, coap_make_str_const("ct"), coap_make_str_const("40"), 0); coap_add_resource(this->context, rd_lookup_ep); + + LOG_DBG("Initialized resource directory"); } ResourceDirectory::~ResourceDirectory() { diff --git a/linux/tests/communication_tests.cpp b/linux/tests/communication_tests.cpp index b18306c..9be630f 100644 --- a/linux/tests/communication_tests.cpp +++ b/linux/tests/communication_tests.cpp @@ -57,19 +57,9 @@ public: TEST(Communication, Client_Init) { CoapClient client; client.init_client(); -} - -TEST(Communication, Client_QuitGetSet) { - CoapClient client; - client.init_client(); - - /* Test initial set value */ - EXPECT_EQ(client.get_quit(), 0); - - /* Test API for setting and getting quit class value */ - client.set_quit(42); - EXPECT_EQ(client.get_quit(), 42); - EXPECT_NE(client.get_quit(), 43); + EXPECT_EQ(client.get_quit(), false); + client.destroy_client(); + EXPECT_EQ(client.get_quit(), true); } TEST(Communication, Client_Requests) { @@ -97,19 +87,18 @@ TEST(Communication, Client_Destroy) { client.destroy_client(); } -TEST(Communication, Server_Init) { +TEST(Communication, Server_Init_No_Rd) { + /* Resource dir disabled */ + CoapServer server = CoapServer("0.0.0.0", "5683", 0); +} + +TEST(Communication, Server_Init_With_Rd) { /* Resource dir enabled */ CoapServer server = CoapServer("0.0.0.0", "5683", 1); - server.init_server("0.0.0.0", "5683"); - - /* Resource dir disabled */ - server = CoapServer("0.0.0.0", "5683", 0); - server.init_server("0.0.0.0", "5683"); } TEST(Communication, Server_QuitGetSet) { CoapServer server = CoapServer("0.0.0.0", "5683", 1); - server.init_server("0.0.0.0", "5683"); /* Test initial set value */ EXPECT_EQ(server.get_quit(), 0); @@ -125,17 +114,8 @@ TEST(Communication, Server_Resource) { EddieLamp lamp_resource; server.add_resource(&lamp_resource); - - server.destroy_server(); } -TEST(Communication, Server_Destroy) { - CoapServer server = CoapServer("0.0.0.0", "5683", 1); - - server.destroy_server(); -} - - TEST(Communication, Endpoint_Init) { EddieEndpoint endpoint; } -- GitLab From dec9693b5caa65bb9f1c74cc4079fbc3a599ca1d Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Tue, 2 Aug 2022 17:17:49 +0200 Subject: [PATCH 06/24] linux: do not initialize rd in publish_resources remove rd initialization and rd discovery in publish_resources. Add local rd initialization in EddieEndpoint::discover_rd() when CoapServer::discover_rd() does not find remote rd. Signed-off-by: Francesco Pham --- linux/communication/src/CoapServer.cpp | 31 ++++++++++--------- linux/communication/src/EddieEndpoint.cpp | 7 +++-- linux/communication/src/ResourceDirectory.cpp | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index d34b02c..2e515b4 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -123,10 +123,7 @@ CoapServer::CoapServer(const char *host, const char *port, int is_resource_dir) init_server("::", port); this->client = new CoapClient(); this->client->init_client(); - if (is_resource_dir) { - LOG_DBG("Starting resource directory"); - this->resource_directory = new ResourceDirectory(my_ip, this->context); - } + if (is_resource_dir) init_resource_dir_local(); auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); @@ -319,13 +316,17 @@ int CoapServer::publish_resources(int lt) { if (data.empty() && !res_list.empty()) { LOG_DBG("[CoapServer::publish_resources]: unable to create string representation of resource"); - return 0; + return -1; } - if (!this->is_rd()) this->discover_rd(); - - if (!resource_dir_ip.empty()) { - int success = 0; + if (is_rd() && this->resource_directory) { + if (this->resource_directory->add_endpoint(ep_d_key, params, data) != 0) { + LOG_ERR("failed to add endpoint to local resource directory"); + return -1; + } + } + else if (!resource_dir_ip.empty()) { + bool success = false; while (!success) { int token = this->client->send_post_request(resource_dir_ip.c_str(), resource_dir_port.c_str(), @@ -345,7 +346,7 @@ int CoapServer::publish_resources(int lt) { if (option) { option = coap_option_next(&opt_iter); this->rd_endpoint = "rd/" + std::string(reinterpret_cast(coap_opt_value(option))); - success = 1; + success = true; } lock.unlock(); @@ -356,11 +357,12 @@ int CoapServer::publish_resources(int lt) { t.join(); } } - else if (!this->is_rd()) init_resource_dir_local(); - - if (this->resource_directory) this->resource_directory->add_endpoint(ep_d_key, params, data); + else { + LOG_ERR("Resource directory is neither local nor remote"); + return -1; + } - return 1; + return 0; } /** @@ -449,6 +451,7 @@ int CoapServer::unpublish_resources() { } void CoapServer::init_resource_dir_local() { + LOG_DBG("Initializing local resource directory"); this->resource_directory = new ResourceDirectory(my_ip, this->context); } diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index 264ee99..2a6eeb4 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -29,7 +29,9 @@ EddieEndpoint::~EddieEndpoint() { } int EddieEndpoint::discover_rd() { - return get_server()->discover_rd(); + int r = get_server()->discover_rd(); + if (r != 0) get_server()->init_resource_dir_local(); + return 0; } std::string EddieEndpoint::get_resources_from_rd() { @@ -107,6 +109,5 @@ int EddieEndpoint::start_server() { int EddieEndpoint::publish_resources() { LOG_DBG("Publishing resources"); - get_server()->publish_resources(); - return 0; + return get_server()->publish_resources(); } \ No newline at end of file diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp index bb38c82..9a941f2 100644 --- a/linux/communication/src/ResourceDirectory.cpp +++ b/linux/communication/src/ResourceDirectory.cpp @@ -749,7 +749,7 @@ int ResourceDirectory::add_endpoint( const std::string &data ) { std::string ep_name = ResourceDirectory::add_endpoint_to_rd(ep_d_key, this->context, params, data); - return ep_name.empty(); + return ep_name.empty() ? -1 : 0; } int ResourceDirectory::remove_endpoint(const std::string& rd_endpoint, const std::string& ep, const std::string& d) { -- GitLab From b2d68f9b1afe44f987e2eadefc1dfb179f11aece Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Tue, 2 Aug 2022 18:19:24 +0200 Subject: [PATCH 07/24] linux/EddieEndpoint: add ip parameter to constructor and parse from args receiving ip as program argument to manually specify ip instead of automatically finding it. if --auto parameter is not specified the ip will still be get automatically Signed-off-by: Francesco Pham --- common/include/EddieEndpoint.h | 2 +- linux/communication/src/CoapServer.cpp | 2 +- linux/communication/src/EddieEndpoint.cpp | 8 ++++---- linux/examples/eddie_endpoint.cpp | 8 +++++++- linux/tests/communication_tests.cpp | 2 +- zephyr/src/EddieEndpoint.cpp | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/common/include/EddieEndpoint.h b/common/include/EddieEndpoint.h index 4c35f63..f8db312 100644 --- a/common/include/EddieEndpoint.h +++ b/common/include/EddieEndpoint.h @@ -21,7 +21,7 @@ private: public: - explicit EddieEndpoint(const std::string& port = "5683"); + explicit EddieEndpoint(const std::string& port = "5683", const std::string& ip = ""); ~EddieEndpoint(); diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index 2e515b4..0795166 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -297,7 +297,7 @@ int CoapServer::discover_rd() { this->resource_dir_ip = base->second; this->resource_dir_port = "5683"; - LOG_DBG("Found resource directory: %s:%s", this->resource_dir_ip.c_str(), this->resource_dir_port.c_str()); + LOG_DBG("Found resource directory: ip=%s port=%s", this->resource_dir_ip.c_str(), this->resource_dir_port.c_str()); return 0; } diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index 2a6eeb4..e5ad14b 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -16,10 +16,10 @@ CoapServer* EddieEndpoint::get_server() { return this->server; } -EddieEndpoint::EddieEndpoint(const std::string& port) { - std::string ip = get_local_node_ip(); - - server = new CoapServer(ip.c_str(), port.c_str(), 0); +EddieEndpoint::EddieEndpoint(const std::string& port, const std::string& ip) { + server = new CoapServer(ip.empty() ? get_local_node_ip().c_str() : ip.c_str(), + port.c_str(), + 0); } EddieEndpoint::~EddieEndpoint() { diff --git a/linux/examples/eddie_endpoint.cpp b/linux/examples/eddie_endpoint.cpp index 89db03c..89945ae 100644 --- a/linux/examples/eddie_endpoint.cpp +++ b/linux/examples/eddie_endpoint.cpp @@ -77,6 +77,10 @@ int main(int argc, char *argv[]) { argparse::ArgumentParser program("program_name"); + program.add_argument("--ip", "-a") + .help("Ip address") + .default_value(std::string{"auto"}); + program.add_argument("--port", "-p") .help("Port number") .scan<'i', int>() @@ -97,10 +101,12 @@ int main(int argc, char *argv[]) } auto port_number = program.get("port"); + auto ip = program.get("ip"); + if (ip == "auto") ip = ""; coap_set_log_level(LOG_DEBUG); - EddieEndpoint node = EddieEndpoint(std::to_string(port_number)); + EddieEndpoint node = EddieEndpoint(std::to_string(port_number), ip); node.discover_rd(); diff --git a/linux/tests/communication_tests.cpp b/linux/tests/communication_tests.cpp index 9be630f..41dd4b8 100644 --- a/linux/tests/communication_tests.cpp +++ b/linux/tests/communication_tests.cpp @@ -117,5 +117,5 @@ TEST(Communication, Server_Resource) { } TEST(Communication, Endpoint_Init) { - EddieEndpoint endpoint; + EddieEndpoint endpoint(); } diff --git a/zephyr/src/EddieEndpoint.cpp b/zephyr/src/EddieEndpoint.cpp index fcb24df..cc2aa73 100644 --- a/zephyr/src/EddieEndpoint.cpp +++ b/zephyr/src/EddieEndpoint.cpp @@ -62,7 +62,7 @@ int EddieEndpoint::start_server() return get_server()->start_server(); } -EddieEndpoint::EddieEndpoint() { +EddieEndpoint::EddieEndpoint(const std::string& port, const std::string& ip) { server = new CoapServer(); } -- GitLab From bb2808dbbc9f4b253316353fd5636b52e25b07b4 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Wed, 3 Aug 2022 12:18:02 +0200 Subject: [PATCH 08/24] linux/CoapClient: single send_message method removing duplicate code - remove duplicate code in CoapClient by having a single send_message method - store token instead of message id in message responses - remove duplicate code in message_handler - adapt all request to new CoapClient send_message - remove client example executable Signed-off-by: Francesco Pham --- linux/CMakeLists.txt | 6 - linux/communication/include/CoapClient.h | 36 +- linux/communication/include/common.h | 6 + linux/communication/src/CoapClient.cpp | 424 ++---------------- linux/communication/src/CoapServer.cpp | 57 +-- linux/communication/src/EddieEndpoint.cpp | 8 +- linux/communication/src/common.cpp | 33 ++ linux/examples/client.cpp | 53 --- linux/tests/communication_tests.cpp | 16 +- .../src/VirtualizationReceiver.cpp | 5 +- 10 files changed, 136 insertions(+), 508 deletions(-) delete mode 100644 linux/examples/client.cpp diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index c40591b..a8c3bed 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -25,12 +25,6 @@ endif() include(GNUInstallDirs) -# eddie client executable -add_executable(eddie-client examples/client.cpp ${SOURCES}) -target_link_libraries(eddie-client PkgConfig::Coap ${CMAKE_THREAD_LIBS_INIT} eddie-communication) -set_property(TARGET eddie-client PROPERTY C_STANDARD 11) -install(TARGETS eddie-client RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - # resource directory executable add_executable(resource-directory examples/resource_directory.cpp ${SOURCES}) target_link_libraries(resource-directory PkgConfig::Coap ${CMAKE_THREAD_LIBS_INIT} eddie-communication) diff --git a/linux/communication/include/CoapClient.h b/linux/communication/include/CoapClient.h index 9cfcfa5..01e4162 100644 --- a/linux/communication/include/CoapClient.h +++ b/linux/communication/include/CoapClient.h @@ -32,7 +32,7 @@ private: public: // TODO: move into private - static ThreadSafeMap *messages; + static ThreadSafeMap *messages; static ThreadSafeStorage *multicast_messages; std::mutex client_mutex; std::condition_variable client_condition; @@ -53,29 +53,17 @@ public: message_handler(coap_session_t *session, const coap_pdu_t *sent, const coap_pdu_t *received, coap_mid_t id); - coap_mid_t - send_get_request(const char *dst_host, const char *dst_port, const char *dst_resource, - const char *query); - - std::string - send_get_nc_request(const char *dst_host, const char *query, unsigned char *data, - size_t data_len); - - coap_mid_t - send_post_request(const char *dst_host, const char *dst_port, const char *dst_resource, - const char *query, const char *data); - - coap_mid_t - send_delete_request(const char *dst_host, const char *dst_port, const char *res_to_delete); - - coap_mid_t - send_put_request(const char *dst_host, const char *dst_port, const char *dst_resource, - const char *query, const char *data); - - int send_message(method_t method, const char *dst_ip, const char *dst_port, - const char *dst_res, const char *query, const char *data); - - std::string receive_message(int token, int max_timeout); + std::string send_message(method_t method, + const char *dst_host, + const char *dst_port, + const char *dst_resource = nullptr, + const char *query = nullptr, + const uint8_t *data = nullptr, + size_t data_len = 0, + bool confirmable = true, + bool multicast = false); + + std::string receive_message(std::string token, int max_timeout); }; #endif //EDDIE_COAPCLIENT_H diff --git a/linux/communication/include/common.h b/linux/communication/include/common.h index 9bad6d4..da02b54 100644 --- a/linux/communication/include/common.h +++ b/linux/communication/include/common.h @@ -8,6 +8,8 @@ #ifndef COMMON_H #define COMMON_H +#include "EddieResource.h" + #include #include #include @@ -24,6 +26,10 @@ int resolve_address(const char *host, const char *service, coap_address_t *dst); std::string get_local_node_ip(); +method_t coap_method_to_method(coap_pdu_code_t method); + +coap_pdu_code_t method_to_coap_method(method_t method); + int match(const std::string &src, const std::string &dst, int is_prefix); /** diff --git a/linux/communication/src/CoapClient.cpp b/linux/communication/src/CoapClient.cpp index cfb342a..7082b31 100644 --- a/linux/communication/src/CoapClient.cpp +++ b/linux/communication/src/CoapClient.cpp @@ -19,7 +19,7 @@ #define min(a, b) ((a) < (b) ? (a) : (b)) /** Container for all response received from the various CoAP nodes */ -ThreadSafeMap *CoapClient::messages = new ThreadSafeMap(); +ThreadSafeMap *CoapClient::messages = new ThreadSafeMap(); /** Container for all multicast messages received */ ThreadSafeStorage *CoapClient::multicast_messages = new ThreadSafeStorage(); @@ -469,44 +469,27 @@ std::string pdu_to_string(coap_pdu_t *pdu) { coap_response_t CoapClient::message_handler(coap_session_t *session, const coap_pdu_t *sent, const coap_pdu_t *received, const coap_mid_t id) { - if (sent) { - coap_pdu_t *copy = coap_pdu_duplicate(received, session, - coap_pdu_get_token(received).length, - coap_pdu_get_token(received).s, - nullptr); - - coap_pdu_set_mid(copy, id); - - auto *len = static_cast(malloc(sizeof(size_t))); - const auto **data = static_cast(malloc(sizeof(const uint8_t *))); - coap_get_data(received, len, data); - coap_add_data(copy, *len, *data); - messages->set(id, copy); - - return COAP_RESPONSE_OK; - } else { - auto token = coap_pdu_get_token(received); - std::string token_str = std::string(reinterpret_cast(token.s)).substr(0, token.length); + auto token = coap_pdu_get_token(received); + std::string token_str = std::string(reinterpret_cast(token.s), token.length); - if (multicast_messages->contains_key(token_str)) { - auto *len = static_cast(malloc(sizeof(size_t))); - const auto **data = static_cast(malloc(sizeof(const uint8_t *))); + coap_pdu_t *copy = coap_pdu_duplicate(received, session, + coap_pdu_get_token(received).length, + coap_pdu_get_token(received).s, + nullptr); - coap_pdu_t *copy = coap_pdu_duplicate(received, session, - coap_pdu_get_token(received).length, - coap_pdu_get_token(received).s, - nullptr); + auto *len = static_cast(malloc(sizeof(size_t))); + const auto **data = static_cast(malloc(sizeof(const uint8_t *))); - coap_get_data(received, len, data); - coap_add_data(copy, *len, *data); - multicast_messages->insert_new_message(token_str, copy); + coap_get_data(received, len, data); + coap_add_data(copy, *len, *data); - return COAP_RESPONSE_OK; - } else { - return COAP_RESPONSE_OK; - } + if (sent) { + messages->set(token_str, copy); + } else if (multicast_messages->contains_key(token_str)) { + multicast_messages->insert_new_message(token_str, copy); } + return COAP_RESPONSE_OK; } coap_session_t * @@ -594,93 +577,9 @@ int CoapClient::init_client() { return 0; } -coap_mid_t -CoapClient::send_get_request(const char *dst_host, const char *dst_port, const char *dst_resource, - const char *query) { - coap_session_t *session; - uint8_t buf[1024]; - size_t buflen; - uint8_t *sbuf = buf; - coap_pdu_t *pdu; - int res; - coap_optlist_t *optlist = nullptr; - coap_mid_t message_id; - - if (!dst_port) { - dst_port = "5683"; - } - - // BEGIN CRITICAL CALLS TO CoAP APIs - std::unique_lockclient_mutex)> lock(this->client_mutex); - - session = open_session(dst_host, dst_port, COAP_PROTO_UDP); - - if (!session) { - LOG_ERR("Error creating remote session"); - coap_session_release(session); - return -1; - } - - // Initialize the PDU to send - message_id = coap_new_message_id(session); - pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, - message_id, - coap_session_max_pdu_size(session)); - - // Add token to the request - coap_session_new_token(session, &buflen, buf); - coap_add_token(pdu, buflen, buf); - - // Add destination as option to the PDU - if (dst_resource) { - buflen = sizeof(buf); - res = coap_split_path((const uint8_t *) dst_resource, - strlen(dst_resource), - sbuf, - &buflen); - - while (res--) { - coap_insert_optlist(&optlist, - coap_new_optlist(COAP_OPTION_URI_PATH, - coap_opt_length(sbuf), - coap_opt_value(sbuf))); - sbuf += coap_opt_size(sbuf); - } - } else { - coap_delete_pdu(pdu); - coap_delete_optlist(optlist); - return -1; - } - - // Add query as option to the PDU - if (query) { - buflen = sizeof(buf); - res = coap_split_query((const uint8_t *) query, strlen(query), sbuf, &buflen); - - while (res--) { - coap_insert_optlist(&optlist, - coap_new_optlist(COAP_OPTION_URI_QUERY, - coap_opt_length(sbuf), - coap_opt_value(sbuf))); - - sbuf += coap_opt_size(sbuf); - } - } - - coap_add_optlist_pdu(pdu, &optlist); - - coap_send(session, pdu); - - // END OF CRITICAL LINES - // unlock the mutex - lock.unlock(); - - return message_id; -} - -std::string -CoapClient::send_get_nc_request(const char *dst_host, const char *query, unsigned char *data, - size_t data_len) { +std::string CoapClient::send_message(method_t method, const char *dst_host, const char *dst_port, + const char *dst_resource, const char *query, + const uint8_t *data, size_t data_len, bool confirmable, bool multicast) { coap_session_t *session; uint8_t buf[1024]; size_t buflen; @@ -694,7 +593,10 @@ CoapClient::send_get_nc_request(const char *dst_host, const char *query, unsigne size_t token_len; uint8_t *token = token_buf; - session = open_session("224.0.1.187", "5683", COAP_PROTO_UDP); + // BEGIN CRITICAL CALLS TO CoAP APIs + std::unique_lockclient_mutex)> lock(this->client_mutex); + + session = open_session(dst_host, dst_port, COAP_PROTO_UDP); if (!session) { LOG_ERR("Error creating remote session"); @@ -703,7 +605,8 @@ CoapClient::send_get_nc_request(const char *dst_host, const char *query, unsigne } // Initialize the PDU to send - pdu = coap_pdu_init(COAP_MESSAGE_NON, COAP_REQUEST_CODE_GET, + pdu = coap_pdu_init(confirmable ? COAP_MESSAGE_CON : COAP_MESSAGE_NON, + method_to_coap_method(method), coap_new_message_id(session), coap_session_max_pdu_size(session)); @@ -711,11 +614,10 @@ CoapClient::send_get_nc_request(const char *dst_host, const char *query, unsigne coap_session_new_token(session, &token_len, token); coap_add_token(pdu, token_len, token); - // Add destination as option to the PDU - if (dst_host) { + if (dst_resource) { buflen = sizeof(buf); - res = coap_split_path((const uint8_t *) dst_host, - strlen(dst_host), + res = coap_split_path((const uint8_t *) dst_resource, + strlen(dst_resource), sbuf, &buflen); @@ -727,6 +629,7 @@ CoapClient::send_get_nc_request(const char *dst_host, const char *query, unsigne sbuf += coap_opt_size(sbuf); } } else { + // error, destination needed coap_delete_pdu(pdu); coap_delete_optlist(optlist); return ""; @@ -755,92 +658,9 @@ CoapClient::send_get_nc_request(const char *dst_host, const char *query, unsigne } // add token string as valid token - std::string token_str = std::string(reinterpret_cast(token)); - multicast_messages->insert_valid_key(token_str); - - coap_send(session, pdu); + std::string token_str = std::string(reinterpret_cast(token), token_len); - return token_str; -} - -coap_mid_t -CoapClient::send_post_request(const char *dst_host, const char *dst_port, const char *dst_resource, - const char *query, const char *data) { - coap_mid_t message_id; - coap_session_t *session; - uint8_t buf[1024]; - size_t buflen; - uint8_t *sbuf = buf; - coap_pdu_t *pdu; - int res; - coap_optlist_t *optlist = nullptr; - - // BEGIN CRITICAL CALLS TO CoAP APIs - std::unique_lockclient_mutex)> lock(this->client_mutex); - - session = open_session(dst_host, dst_port, COAP_PROTO_UDP); - - if (!session) { - LOG_ERR("Error creating remote session"); - coap_session_release(session); - return -1; - } - - // Initialize the PDU to send - message_id = coap_new_message_id(session); - pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_POST, - message_id, - coap_session_max_pdu_size(session)); - - // Add token to the request - coap_session_new_token(session, &buflen, buf); - coap_add_token(pdu, buflen, buf); - - // Add destination as option to the PDU - if (dst_resource) { - buflen = sizeof(buf); - res = coap_split_path((const uint8_t *) dst_resource, - strlen(dst_resource), - sbuf, - &buflen); - - while (res--) { - coap_insert_optlist(&optlist, - coap_new_optlist(COAP_OPTION_URI_PATH, - coap_opt_length(sbuf), - coap_opt_value(sbuf))); - sbuf += coap_opt_size(sbuf); - } - } else { - coap_delete_pdu(pdu); - coap_delete_optlist(optlist); - return -1; - } - - // Add query as option to the PDU - if (query) { - buflen = sizeof(buf); - res = coap_split_query((const uint8_t *) query, strlen(query), sbuf, &buflen); - - while (res--) { - coap_insert_optlist(&optlist, - coap_new_optlist(COAP_OPTION_URI_QUERY, - coap_opt_length(sbuf), - coap_opt_value(sbuf))); - - sbuf += coap_opt_size(sbuf); - } - } else { - coap_delete_pdu(pdu); - coap_delete_optlist(optlist); - return -1; - } - - coap_add_optlist_pdu(pdu, &optlist); - - if (data) { - coap_add_data(pdu, strlen(data), reinterpret_cast(data)); - } + if (multicast) multicast_messages->insert_valid_key(token_str); coap_send(session, pdu); @@ -848,188 +668,10 @@ CoapClient::send_post_request(const char *dst_host, const char *dst_port, const // unlock the mutex lock.unlock(); - return message_id; -} - -coap_mid_t -CoapClient::send_delete_request(const char *dst_host, const char *dst_port, const char *res_to_delete) { - if (!res_to_delete) { - LOG_DBG("[send_delete_request]: `res_to_delete` can not be empty"); - return -1; - } - - coap_mid_t message_id; - coap_session_t *session; - uint8_t buf[1024]; - size_t buflen; - uint8_t *sbuf = buf; - coap_pdu_t *pdu; - int res; - coap_optlist_t *optlist = nullptr; - - // BEGIN CRITICAL CALLS TO CoAP APIs - std::unique_lockclient_mutex)> lock(this->client_mutex); - - session = open_session(dst_host, dst_port, COAP_PROTO_UDP); - - if (!session) { - LOG_ERR("Error creating remote session"); - coap_session_release(session); - return -1; - } - - message_id = coap_new_message_id(session); - - // Initialize the PDU to send - pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_DELETE, - message_id, - coap_session_max_pdu_size(session)); - - // Add token to the request - coap_session_new_token(session, &buflen, buf); - coap_add_token(pdu, buflen, buf); - - buflen = sizeof(buf); - res = coap_split_path((const uint8_t *) res_to_delete, - strlen(res_to_delete), - sbuf, - &buflen); - while (res--) { - coap_insert_optlist(&optlist, - coap_new_optlist(COAP_OPTION_URI_PATH, - coap_opt_length(sbuf), - coap_opt_value(sbuf))); - sbuf += coap_opt_size(sbuf); - } - - coap_add_optlist_pdu(pdu, &optlist); - - coap_send(session, pdu); - - // END OF CRITICAL LINES - // unlock the mutex - lock.unlock(); - - return message_id; -} - -coap_mid_t -CoapClient::send_put_request(const char *dst_host, const char *dst_port, const char *dst_resource, - const char *query, const char *data) { - coap_mid_t message_id; - coap_session_t *session; - uint8_t buf[1024]; - size_t buflen; - uint8_t *sbuf = buf; - coap_pdu_t *pdu; - int res; - coap_optlist_t *optlist = nullptr; - - // BEGIN CRITICAL CALLS TO CoAP APIs - std::unique_lockclient_mutex)> lock(this->client_mutex); - - session = open_session(dst_host, dst_port, COAP_PROTO_UDP); - - if (!session) { - LOG_ERR("Error creating remote session"); - coap_session_release(session); - return -1; - } - - message_id = coap_new_message_id(session); - - pdu = coap_pdu_init(COAP_MESSAGE_CON, - COAP_REQUEST_CODE_PUT, - message_id, - coap_session_max_pdu_size(session)); - - // Add token to the request - coap_session_new_token(session, &buflen, buf); - coap_add_token(pdu, buflen, buf); - - if (dst_resource) { - buflen = sizeof(buf); - res = coap_split_path((const uint8_t *) dst_resource, - strlen(dst_resource), - sbuf, - &buflen); - - while (res--) { - coap_insert_optlist(&optlist, - coap_new_optlist(COAP_OPTION_URI_PATH, - coap_opt_length(sbuf), - coap_opt_value(sbuf))); - sbuf += coap_opt_size(sbuf); - } - } else { - // error, destination needed - coap_delete_pdu(pdu); - coap_delete_optlist(optlist); - return -1; - } - - // Add query as option to the PDU - if (query) { - buflen = sizeof(buf); - res = coap_split_query((const uint8_t *) query, strlen(query), sbuf, &buflen); - - while (res--) { - coap_insert_optlist(&optlist, - coap_new_optlist(COAP_OPTION_URI_QUERY, - coap_opt_length(sbuf), - coap_opt_value(sbuf))); - - sbuf += coap_opt_size(sbuf); - } - } - - coap_add_optlist_pdu(pdu, &optlist); - - coap_add_data(pdu, strlen(data), reinterpret_cast(data)); - - coap_send(session, pdu); - - // END OF CRITICAL LINES - // unlock the mutex - lock.unlock(); - - return message_id; -} - -int CoapClient::send_message(method_t method, const char *dst_ip, const char *dst_port, - const char *dst_res, const char *query, const char *data) { - int token; - - switch (method) { - case GET: { - token = this->send_get_request(dst_ip, dst_port, dst_res, query); - break; - } - - case DELETE: { - token = this->send_delete_request(dst_ip, dst_port, dst_res); - break; - } - - case POST: { - token = this->send_post_request(dst_ip, dst_port, dst_res, query, data); - break; - } - - case PUT: { - token = this->send_put_request(dst_ip, dst_port, dst_res, query, data); - break; - } - - default: { - token = -1; - } - } - - return token; + return token_str; } -std::string CoapClient::receive_message(int token, int max_timeout) { +std::string CoapClient::receive_message(std::string token, int max_timeout) { coap_pdu_t *pdu; auto function = [this, &max_timeout, &token, &pdu]() { std::unique_lockclient_mutex)> lock(this->client_mutex); diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index 0795166..748bbb3 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -25,22 +25,6 @@ class CoapClient; ThreadSafeMap *CoapServer::resources = new ThreadSafeMap(); -method_t coap_method_to_method(uint8_t method) { - switch (method) { - case COAP_REQUEST_CODE_GET: - return GET; - case COAP_REQUEST_CODE_POST: - return POST; - case COAP_REQUEST_CODE_PUT: - return PUT; - case COAP_REQUEST_CODE_DELETE: - return DELETE; - default: - LOG_ERR("Invalid coap method"); - return GET; - } -} - std::string resource_list_to_string(const std::list &resources) { size_t buf_size = 1000; @@ -254,7 +238,15 @@ int CoapServer::add_resource(EddieResource *resource) { int CoapServer::discover_rd() { LOG_DBG("Discovering resource directory"); - auto m_token = this->client->send_get_nc_request(".well-known/core", "rt=core.rd", nullptr, 0); + auto m_token = this->client->send_message(GET, + "::", + "5683", + ".well-known/core", + "rt=core.rd", + nullptr, + 0, + false, + true); std::list list; auto f = [this, m_token, &list]() { @@ -294,6 +286,11 @@ int CoapServer::discover_rd() { auto first_link = parsed.begin()->second; auto base = first_link.find("base"); + if (base == first_link.end()) { + LOG_ERR("could not find base attribute"); + return -1; + } + this->resource_dir_ip = base->second; this->resource_dir_port = "5683"; @@ -328,9 +325,13 @@ int CoapServer::publish_resources(int lt) { else if (!resource_dir_ip.empty()) { bool success = false; while (!success) { - int token = this->client->send_post_request(resource_dir_ip.c_str(), + std::string token = this->client->send_message(POST, + resource_dir_ip.c_str(), resource_dir_port.c_str(), - "rd", query.c_str(), data.c_str()); + "rd", + query.c_str(), + reinterpret_cast(data.c_str()), + data.length()); auto post_resources_lambda = [token, this, &success]() { std::unique_lockclient_mutex)> lock(client->client_mutex); @@ -382,9 +383,11 @@ int CoapServer::unpublish_resources() { if (res.empty()) { int return_val = 0; auto ep_ = this->rd_endpoint; - auto token = client->send_delete_request(resource_dir_ip.c_str(), - resource_dir_port.c_str(), - ep_.c_str()); + + std::string token = this->client->send_message(DELETE, + resource_dir_ip.c_str(), + resource_dir_port.c_str(), + ep_.c_str()); auto f = [this, token, &return_val]() { std::unique_lockclient_mutex)> lock(client->client_mutex); @@ -417,9 +420,13 @@ int CoapServer::unpublish_resources() { std::string query = "ep=" + ep + "&d=" + d + "<=" + default_lt + "&base=coap://" + my_ip + ":" + my_port; - auto token = client->send_post_request(resource_dir_ip.c_str(), - resource_dir_port.c_str(), - "rd", query.c_str(), payload.c_str()); + std::string token = this->client->send_message(POST, + resource_dir_ip.c_str(), + resource_dir_port.c_str(), + "rd", + query.c_str(), + reinterpret_cast(payload.c_str()), + payload.length()); auto f = [this, token, &return_val]() { std::unique_lockclient_mutex)> lock(client->client_mutex); diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index e5ad14b..3f922df 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -67,9 +67,11 @@ std::string EddieEndpoint::get_resources_from_rd() { if (!resource_str.empty()) return resource_str; - auto token = get_client()->send_get_request(get_server()->get_rd_ip().c_str(), - get_server()->get_rd_port().c_str(), - "rd-lookup/res", nullptr); + std::string token = this->get_client()->send_message(GET, + get_server()->get_rd_ip().c_str(), + get_server()->get_rd_port().c_str(), + "rd", + "rd-lookup/res"); auto resource_loop = [this, &token, &resource_str]() { std::unique_lockget_client()->client_mutex)> lock(this->get_client()->client_mutex); diff --git a/linux/communication/src/common.cpp b/linux/communication/src/common.cpp index 36af74d..c1c0323 100644 --- a/linux/communication/src/common.cpp +++ b/linux/communication/src/common.cpp @@ -6,6 +6,7 @@ */ #include "common.h" +#include "EddieResource.h" #include #include @@ -103,4 +104,36 @@ std::string get_local_node_ip() { } return ip; +} + +method_t coap_method_to_method(coap_pdu_code_t method) { + switch (method) { + case COAP_REQUEST_CODE_GET: + return GET; + case COAP_REQUEST_CODE_POST: + return POST; + case COAP_REQUEST_CODE_PUT: + return PUT; + case COAP_REQUEST_CODE_DELETE: + return DELETE; + default: + LOG_ERR("Invalid coap method"); + return GET; + } +} + +coap_pdu_code_t method_to_coap_method(method_t method) { + switch (method) { + case GET: + return COAP_REQUEST_CODE_GET; + case POST: + return COAP_REQUEST_CODE_POST; + case PUT: + return COAP_REQUEST_CODE_PUT; + case DELETE: + return COAP_REQUEST_CODE_DELETE; + default: + LOG_ERR("Invalid coap method"); + return COAP_REQUEST_CODE_GET; + } } \ No newline at end of file diff --git a/linux/examples/client.cpp b/linux/examples/client.cpp deleted file mode 100644 index 7f3eb6e..0000000 --- a/linux/examples/client.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * SPDX-FileCopyrightText: Huawei Inc. - * SPDX-FileCopyrightText: Politecnico Di Milano - */ - -#include "CoapClient.h" - -int main() { - int key = 3; - coap_set_log_level(LOG_DEBUG); - CoapClient client; - client.init_client(); - - auto main_loop = [&client, &key]() { - while (key) { - coap_io_process(client.get_context(), COAP_IO_WAIT * 1000); - key--; - } - }; - - std::thread t(main_loop); - - client.send_post_request("::", "5683", "rd", "ep=sazz&base=pazz", nullptr); - - auto token = client.send_get_nc_request(".well-known/core", - nullptr, - nullptr, - 0); - - auto f1 = [&client, token]() { - std::unique_lock lock(client.client_mutex); - auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(COAP_DEFAULT_LEISURE * 2); - client.client_condition.wait_until(lock, timeout); - - auto answers = CoapClient::multicast_messages->get(token); - - while (answers.empty()) { - answers = CoapClient::multicast_messages->get(token); - client.client_condition.wait_until(lock, timeout); - } - - auto pdu = answers.front(); - - coap_show_pdu(LOG_EMERG, pdu); - }; - - std::thread t1(f1); - t1.join(); - - t.join(); -} diff --git a/linux/tests/communication_tests.cpp b/linux/tests/communication_tests.cpp index 41dd4b8..3c46ca0 100644 --- a/linux/tests/communication_tests.cpp +++ b/linux/tests/communication_tests.cpp @@ -66,10 +66,18 @@ TEST(Communication, Client_Requests) { CoapClient client; client.init_client(); - client.send_get_request("::", "5683", "rd", "ep=sazz&base=pazz"); - client.send_get_nc_request(".well-known/core", nullptr, nullptr, 0); - client.send_post_request("::", "5683", "rd", "ep=sazz&base=pazz", nullptr); - client.send_delete_request("::", "5683", "rd"); + client.send_message(GET, "::", "5683", "rd", "ep=sazz&base=pazz"); + client.send_message(POST, "::", "5683", "rd", "ep=sazz&base=pazz"); + client.send_message(DELETE, "::", "5683", "rd"); + client.send_message(GET, + "::", + "5683", + ".well-known/core", + nullptr, + nullptr, + 0, + false, + true); client.destroy_client(); } diff --git a/linux/virtualization/src/VirtualizationReceiver.cpp b/linux/virtualization/src/VirtualizationReceiver.cpp index 9cc69f2..8cc0d5e 100644 --- a/linux/virtualization/src/VirtualizationReceiver.cpp +++ b/linux/virtualization/src/VirtualizationReceiver.cpp @@ -164,7 +164,7 @@ void VirtualizationReceiver::handle_method_call(GDBusConnection *connection, con } std::string ip, port, resource, query; - int token; + std::basic_string token; update_resources(); @@ -174,7 +174,8 @@ void VirtualizationReceiver::handle_method_call(GDBusConnection *connection, con port.c_str(), resource.c_str(), nullptr, - payload); + reinterpret_cast(payload), + strlen(payload)); } else { g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", "ERROR forging the message")); return; -- GitLab From 99c9bb49ccdcb994790abe9ee1f19fdc492033fa Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Thu, 4 Aug 2022 17:32:46 +0200 Subject: [PATCH 09/24] linux/ResourceDirectory: use coap_print_addr to format base url instead of using inet_ntop and htons, use coap_print_addr instead. This fixes ipv6 addresses not properly formatted Signed-off-by: Francesco Pham --- linux/communication/src/CoapServer.cpp | 15 +++++++++------ linux/communication/src/ResourceDirectory.cpp | 17 ++++------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index 748bbb3..dccb540 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -304,12 +304,6 @@ int CoapServer::publish_resources(int lt) { std::string data = resource_list_to_string(res_list); std::string base_address = "coap://" + my_ip + ":" + my_port; - std::string ep_d_key = this->ep + "@" + this->d; - std::string query = "ep=" + ep + "&d=" + d + "<=" + std::to_string(lt) + "&base=" + base_address; - std::unordered_map params = {{"base", base_address}, - {"ep", this->ep}, - {"d", this->d}, - {"lt", std::to_string(lt)}}; if (data.empty() && !res_list.empty()) { LOG_DBG("[CoapServer::publish_resources]: unable to create string representation of resource"); @@ -317,12 +311,21 @@ int CoapServer::publish_resources(int lt) { } if (is_rd() && this->resource_directory) { + std::string ep_d_key = this->ep + "@" + this->d; + + std::unordered_map params = {{"base", base_address}, + {"ep", this->ep}, + {"d", this->d}, + {"lt", std::to_string(lt)}}; + if (this->resource_directory->add_endpoint(ep_d_key, params, data) != 0) { LOG_ERR("failed to add endpoint to local resource directory"); return -1; } } else if (!resource_dir_ip.empty()) { + std::string query = "ep=" + ep + "&d=" + d + "<=" + std::to_string(lt) + "&base=" + base_address; + bool success = false; while (!success) { std::string token = this->client->send_message(POST, diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp index 9a941f2..c34e0cb 100644 --- a/linux/communication/src/ResourceDirectory.cpp +++ b/linux/communication/src/ResourceDirectory.cpp @@ -184,7 +184,7 @@ ResourceDirectory::rd_resources_to_string_list_filtered(std::mapaddr.sa.sa_family) { - case AF_INET: - case AF_INET6: - inet_ntop(AF_INET6, &remote->addr.sin6.sin6_addr, ip, INET6_ADDRSTRLEN); - port = htons(remote->addr.sin6.sin6_port); - break; - } - - std::string base_address = "coap://" + std::string(ip) + ":" + std::to_string(port); + unsigned char buf[64]; + size_t length = coap_print_addr(remote, buf, 64); + std::string base_address = "coap://" + std::string(reinterpret_cast(buf), length); query_map.insert(std::make_pair("base", base_address)); } -- GitLab From 70ce681f45e7676f978060f9f7072f1488072b41 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Wed, 3 Aug 2022 18:27:43 +0200 Subject: [PATCH 10/24] EddieResource: store path and query as a single string, tokenize later store path and query as a single string, on zephyr tokenize in CoapClient instead by splitting by delimiter. Signed-off-by: Francesco Pham --- common/include/EddieResource.h | 14 +++++++------ zephyr/include/CoapClient.h | 4 ++-- zephyr/src/CoapClient.cpp | 37 +++++++++++++++++----------------- zephyr/src/EddieEndpoint.cpp | 32 +++++++++++++++++++---------- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/common/include/EddieResource.h b/common/include/EddieResource.h index f565535..6c6b299 100644 --- a/common/include/EddieResource.h +++ b/common/include/EddieResource.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include typedef enum {GET, POST, PUT, DELETE} method_t; @@ -44,9 +44,10 @@ typedef enum { typedef struct { char const * dst_host; + const char * dst_port; method_t method; - const char * const * path; - const char * const * query; + const char * path; + const char * query; const uint8_t * data; size_t data_length; bool confirmable; @@ -57,6 +58,7 @@ typedef struct { std::string data; response_code_t status_code; std::string src_ip; + std::string location_path; } message_t; typedef std::function response_handler_t; @@ -118,11 +120,11 @@ public: const char * const * path = get_path(); std::string res; - if (path[0] == NULL) return ""; + if (path[0] == nullptr) return ""; else res = path[0]; for (int i=1; path[i]; i++) - res = res + "/" + std::string(path[i]); + res += "/" + std::string(path[i]); return res; } @@ -131,7 +133,7 @@ public: const char * const * attributes = get_attributes(); std::vector res; for (int i=0; attributes[i]; i++) { - res.push_back(std::string(attributes[i])); + res.emplace_back(attributes[i]); } return res; } diff --git a/zephyr/include/CoapClient.h b/zephyr/include/CoapClient.h index 68f3470..313940a 100644 --- a/zephyr/include/CoapClient.h +++ b/zephyr/include/CoapClient.h @@ -23,8 +23,8 @@ private: int process_simple_coap_reply(response_handler_t response_handler); int send_simple_coap_request(uint8_t method, - const char * const path[], - const char * const query[], + const char * path, + const char * query, const uint8_t data[], size_t data_length, bool confirmable, diff --git a/zephyr/src/CoapClient.cpp b/zephyr/src/CoapClient.cpp index 7f4cbc9..e4ef182 100644 --- a/zephyr/src/CoapClient.cpp +++ b/zephyr/src/CoapClient.cpp @@ -13,6 +13,7 @@ LOG_MODULE_REGISTER(coap_client, LOG_LEVEL_DBG); #include #include +#include #define PEER_PORT 5683 @@ -101,17 +102,22 @@ int CoapClient::process_simple_coap_reply(response_handler_t response_handler) } int CoapClient::send_simple_coap_request(uint8_t method, - const char * const path[], - const char * const query[], + const char * path, + const char * query, const uint8_t payload[], size_t payload_length, bool confirmable, unsigned int content_format) { struct coap_packet request; - const char * const *p; uint8_t *data; int r = 0; + char *token; + + char path_copy[strlen(path)+1]; + char query_copy[strlen(query)+1]; + strcpy(path_copy, path); + strcpy(query_copy, query); data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN); if (!data) { @@ -127,13 +133,15 @@ int CoapClient::send_simple_coap_request(uint8_t method, goto end; } - for (p = path; p && *p; p++) { + token = strtok(path_copy, "/"); + while(token) { r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH, - (const uint8_t*) *p, strlen(*p)); + (const uint8_t*)token, strlen(token)); if (r < 0) { LOG_ERR("Unable add path option to request"); goto end; } + token = strtok(nullptr, "/"); } r = coap_append_option_int(&request, COAP_OPTION_CONTENT_FORMAT, content_format); @@ -141,22 +149,18 @@ int CoapClient::send_simple_coap_request(uint8_t method, return -EINVAL; } - for (p = query; p && *p; p++) { + token = strtok(query_copy, "&"); + while(token) { r = coap_packet_append_option(&request, COAP_OPTION_URI_QUERY, - (const uint8_t*) *p, strlen(*p)); + (const uint8_t*)token, strlen(token)); if (r < 0) { LOG_ERR("Unable add query option to request"); goto end; } + token = strtok(nullptr, "&"); } - switch (method) { - case COAP_METHOD_GET: - case COAP_METHOD_DELETE: - break; - - case COAP_METHOD_PUT: - case COAP_METHOD_POST: + if (payload && payload_length>0) { r = coap_packet_append_payload_marker(&request); if (r < 0) { LOG_ERR("Unable to append payload marker"); @@ -168,11 +172,6 @@ int CoapClient::send_simple_coap_request(uint8_t method, LOG_ERR("Not able to append payload"); goto end; } - - break; - default: - r = -EINVAL; - goto end; } net_hexdump("Request", request.data, request.offset); diff --git a/zephyr/src/EddieEndpoint.cpp b/zephyr/src/EddieEndpoint.cpp index cc2aa73..f9a027c 100644 --- a/zephyr/src/EddieEndpoint.cpp +++ b/zephyr/src/EddieEndpoint.cpp @@ -27,13 +27,14 @@ int EddieEndpoint::discover_rd() { int r; - const char * const path[] = { ".well-known", "core", NULL }; - const char * const query[] = { "rt=core.rd", NULL }; + const char* path = ".well-known/core"; + const char* query = "rt=core.rd"; const uint8_t payload[] = {}; char const * dst_host = "FF05::FD"; request_t get_resources_request = { dst_host, + "5683", GET, path, query, @@ -88,16 +89,16 @@ int EddieEndpoint::publish_resources() { } // define request path and query - const char * const path[] = { "rd", NULL }; - const char * const query[] = { - "base=coap://[" CONFIG_NET_CONFIG_MY_IPV6_ADDR "]:5683" , - "ep=zephyrnode", // TODO: use device id as endpoint name - "lt=90000", - NULL - }; + const char* path = "rd"; + + // TODO: use device id as endpoint name + const char* query = "base=coap://[" CONFIG_NET_CONFIG_MY_IPV6_ADDR "]:5683&" + "ep=zephyrnode&" + "lt=90000"; request_t publish_resources_request = { this->resource_directory_ip.c_str(), //dst_host + "5683", //port POST, //method path, //path query, //query @@ -124,13 +125,14 @@ int EddieEndpoint::publish_resources() { std::string EddieEndpoint::get_resources_from_rd() { int r; - const char * const path[] = { "rd-lookup", "res", NULL }; - const char * const query[] = { NULL }; + const char * path = "rd-lookup/res"; + const char * query = ""; const uint8_t payload[] = {}; char const * dst_host = this->resource_directory_ip.c_str(); request_t get_resources_request = { dst_host, + "5683", GET, path, query, @@ -143,6 +145,14 @@ std::string EddieEndpoint::get_resources_from_rd() { auto response_handler = [](message_t response_message) { LOG_DBG("Response: %s", response_message.data.c_str()); auto links = parse_link_format(response_message.data); + + for (auto link: links) { + LOG_DBG("uri: %s", link.first.c_str()); + for(auto param: link.second) { + LOG_DBG("%s = %s", param.first.c_str(), param.second.c_str()); + } + } + return response_message.data; // TODO: returned parsed links instead }; -- GitLab From 89e27e2a948dbeab5ddd2d4439023d412327cb22 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Thu, 4 Aug 2022 17:44:25 +0200 Subject: [PATCH 11/24] linux/CoapClient: add send_message_and_wait_response - implement send_message_and_wait_response in CoapClient for sending request and wait for first reply - use message_t and request_t defined in EddieResource.h - adapt all requests to use send_message_and_wait_response - remove resource publishing code in unpublish_resources method Signed-off-by: Francesco Pham --- linux/communication/include/CoapClient.h | 8 +- linux/communication/src/CoapClient.cpp | 85 ++++---- linux/communication/src/CoapServer.cpp | 206 +++++------------- linux/communication/src/EddieEndpoint.cpp | 54 ++--- .../src/VirtualizationReceiver.cpp | 24 +- 5 files changed, 134 insertions(+), 243 deletions(-) diff --git a/linux/communication/include/CoapClient.h b/linux/communication/include/CoapClient.h index 01e4162..63530f1 100644 --- a/linux/communication/include/CoapClient.h +++ b/linux/communication/include/CoapClient.h @@ -32,8 +32,8 @@ private: public: // TODO: move into private - static ThreadSafeMap *messages; - static ThreadSafeStorage *multicast_messages; + static ThreadSafeMap *messages; + static ThreadSafeStorage *multicast_messages; std::mutex client_mutex; std::condition_variable client_condition; @@ -63,7 +63,9 @@ public: bool confirmable = true, bool multicast = false); - std::string receive_message(std::string token, int max_timeout); + message_t receive_message(std::string token, int max_timeout); + + message_t send_message_and_wait_response(request_t request, int max_timeout); }; #endif //EDDIE_COAPCLIENT_H diff --git a/linux/communication/src/CoapClient.cpp b/linux/communication/src/CoapClient.cpp index 7082b31..74654bb 100644 --- a/linux/communication/src/CoapClient.cpp +++ b/linux/communication/src/CoapClient.cpp @@ -19,10 +19,10 @@ #define min(a, b) ((a) < (b) ? (a) : (b)) /** Container for all response received from the various CoAP nodes */ -ThreadSafeMap *CoapClient::messages = new ThreadSafeMap(); +ThreadSafeMap *CoapClient::messages = new ThreadSafeMap(); /** Container for all multicast messages received */ -ThreadSafeStorage *CoapClient::multicast_messages = new ThreadSafeStorage(); +ThreadSafeStorage *CoapClient::multicast_messages = new ThreadSafeStorage(); std::string pdu_type_to_string(coap_pdu_type_t type) { switch (type) { @@ -473,21 +473,32 @@ CoapClient::message_handler(coap_session_t *session, const coap_pdu_t *sent, con auto token = coap_pdu_get_token(received); std::string token_str = std::string(reinterpret_cast(token.s), token.length); - coap_pdu_t *copy = coap_pdu_duplicate(received, session, - coap_pdu_get_token(received).length, - coap_pdu_get_token(received).s, - nullptr); + size_t len; + const uint8_t* data; + coap_get_data(received, &len, &data); + coap_pdu_code_t code = coap_pdu_get_code(received); - auto *len = static_cast(malloc(sizeof(size_t))); - const auto **data = static_cast(malloc(sizeof(const uint8_t *))); - - coap_get_data(received, len, data); - coap_add_data(copy, *len, *data); + coap_opt_iterator_t opt_iter; + coap_opt_t *option = coap_check_option(received, COAP_OPTION_LOCATION_PATH, &opt_iter); + std::string location_path; + if (option) location_path = std::string(reinterpret_cast(coap_opt_value(option))); + + auto remote = coap_session_get_addr_remote(session); + unsigned char src_addr[64]; + size_t src_addr_len = coap_print_addr(remote, src_addr, 64); + + message_t response_message { + std::string(reinterpret_cast(data), len), + static_cast(code), + std::string(reinterpret_cast(src_addr), src_addr_len), + location_path + }; - if (sent) { - messages->set(token_str, copy); - } else if (multicast_messages->contains_key(token_str)) { - multicast_messages->insert_new_message(token_str, copy); + if (multicast_messages->contains_key(token_str)) { + multicast_messages->insert_new_message(token_str, response_message); + } + else { + messages->set(token_str, response_message); } return COAP_RESPONSE_OK; } @@ -524,15 +535,6 @@ void CoapClient::destroy_client() { auto coap_messages = messages->get_all(); auto m_cast_messages = multicast_messages->get_valid_keys(); - for (auto message: coap_messages) coap_delete_pdu(message); - - for (const auto& token: m_cast_messages) { - auto pdus = multicast_messages->get(token); - for (auto pdu: pdus) { - coap_delete_pdu(pdu); - } - } - multicast_messages->delete_all(); messages->delete_all(); @@ -671,29 +673,34 @@ std::string CoapClient::send_message(method_t method, const char *dst_host, cons return token_str; } -std::string CoapClient::receive_message(std::string token, int max_timeout) { - coap_pdu_t *pdu; - auto function = [this, &max_timeout, &token, &pdu]() { +message_t CoapClient::receive_message(std::string token, int max_timeout) { + message_t response; + auto task = [this, &max_timeout, &token, &response]() { std::unique_lockclient_mutex)> lock(this->client_mutex); int seconds = max_timeout > COAP_DEFAULT_LEISURE ? max_timeout : COAP_DEFAULT_LEISURE_UNSIGNED; auto timeout = NOW + std::chrono::seconds(seconds); this->client_condition.wait_until(lock, timeout); - auto answer = CoapClient::messages->get(token); - - while (answer == nullptr) { - this->client_condition.wait_until(lock, timeout); - answer = CoapClient::messages->get(token); - } - - pdu = answer; + response = CoapClient::messages->get(token); }; - std::thread t(function); - + std::thread t(task); t.join(); CoapClient::messages->erase(token); + return response; +} - return pdu_to_string(pdu); -} \ No newline at end of file +message_t CoapClient::send_message_and_wait_response(request_t request, int max_timeout) { + std::string token = this->send_message(request.method, + request.dst_host, + request.dst_port, + request.path, + request.query, + request.data, + request.data_length, + request.confirmable, + false); + + return this->receive_message(token, max_timeout); +} diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index dccb540..1b6333c 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -238,48 +238,22 @@ int CoapServer::add_resource(EddieResource *resource) { int CoapServer::discover_rd() { LOG_DBG("Discovering resource directory"); - auto m_token = this->client->send_message(GET, - "::", - "5683", - ".well-known/core", - "rt=core.rd", - nullptr, - 0, - false, - true); - std::list list; - - auto f = [this, m_token, &list]() { - std::unique_lockclient->client_mutex)> lock(this->client->client_mutex); - auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(SERVER_LEISURE_TIME); - this->client->client_condition.wait_until(lock, timeout); - - auto answers = CoapClient::multicast_messages->get(m_token); - - list = answers; - lock.unlock(); - }; - - std::thread t(f); - t.join(); - - if (list.empty()) { - LOG_DBG("Could not find resource directory"); - return -1; - } - auto pdu = list.front(); - size_t data_len; - const uint8_t *data_pdu; - coap_get_data(pdu, &data_len, &data_pdu); + request_t request { + "::", + "5683", + GET, + ".well-known/core", + "rt=core.rd", + nullptr, + 0, + false, + 40 + }; - if (!data_pdu) { - LOG_ERR("[CoapServer::publish_resources]: empty payload from resource directory"); - return -1; - } + message_t response = get_client()->send_message_and_wait_response(request, 5U); - std::string payload = std::string(reinterpret_cast(data_pdu), data_len); - std::map> parsed = parse_link_format(payload); + std::map> parsed = parse_link_format(response.data); if (parsed.empty()) return -1; @@ -325,40 +299,25 @@ int CoapServer::publish_resources(int lt) { } else if (!resource_dir_ip.empty()) { std::string query = "ep=" + ep + "&d=" + d + "<=" + std::to_string(lt) + "&base=" + base_address; - - bool success = false; - while (!success) { - std::string token = this->client->send_message(POST, - resource_dir_ip.c_str(), - resource_dir_port.c_str(), - "rd", - query.c_str(), - reinterpret_cast(data.c_str()), - data.length()); - - auto post_resources_lambda = [token, this, &success]() { - std::unique_lockclient_mutex)> lock(client->client_mutex); - auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(COAP_DEFAULT_LEISURE * 2); - client->client_condition.wait_until(lock, timeout); - - auto post_pdu = CoapClient::messages->get(token); - - // extract and set endpoint string - coap_opt_iterator_t opt_iter; - coap_opt_t *option = coap_check_option(post_pdu, COAP_OPTION_LOCATION_PATH, &opt_iter); - - if (option) { - option = coap_option_next(&opt_iter); - this->rd_endpoint = "rd/" + std::string(reinterpret_cast(coap_opt_value(option))); - success = true; - } - - lock.unlock(); - CoapClient::messages->erase(token); - }; - - std::thread t(post_resources_lambda); - t.join(); + request_t request { + resource_dir_ip.c_str(), + resource_dir_port.c_str(), + POST, + "rd", + query.c_str(), + reinterpret_cast(data.c_str()), + data.length(), + true, + 40 + }; + + message_t response = get_client()->send_message_and_wait_response(request, 5U); + if (!response.location_path.empty()) { + this->rd_endpoint = "rd/" + response.location_path; + } + else { + LOG_ERR("Location path option not found in response"); + return -1; } } else { @@ -370,94 +329,35 @@ int CoapServer::publish_resources(int lt) { } /** - * Ask the RD node to delete all registered resources, if no resource is present in the local node. - * Otherwise, update the list of resources present in the RD node. - * @return 1 on success, 0 on failure + * Ask the RD node to delete all registered resources + * @return 0 non success. Non zero integer on failure */ int CoapServer::unpublish_resources() { if (this->resource_directory) { ResourceDirectory::remove_endpoint(rd_endpoint, ep, d); - return 1; + return 0; } - if (!rd_endpoint.empty()) { - std::list res = resources->get_all(); - - if (res.empty()) { - int return_val = 0; - auto ep_ = this->rd_endpoint; - - std::string token = this->client->send_message(DELETE, - resource_dir_ip.c_str(), - resource_dir_port.c_str(), - ep_.c_str()); - - auto f = [this, token, &return_val]() { - std::unique_lockclient_mutex)> lock(client->client_mutex); - auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(COAP_DEFAULT_LEISURE * 2); - - auto answer = CoapClient::messages->get(token); - - while (answer == nullptr) { - client->client_condition.wait_until(lock, timeout); - answer = CoapClient::messages->get(token); - } - - auto answer_type = coap_pdu_get_code(answer); - - lock.unlock(); - CoapClient::messages->erase(token); - - return_val = (answer_type == COAP_RESPONSE_CODE_DELETED); - }; - - std::thread t(f); - t.join(); - - return return_val; - } else { - int return_val = 0; - - // get all the resources in this node as a string - auto payload = resource_list_to_string(res); - - std::string query = "ep=" + ep + "&d=" + d + "<=" + default_lt + "&base=coap://" + my_ip + ":" + my_port; - - std::string token = this->client->send_message(POST, - resource_dir_ip.c_str(), - resource_dir_port.c_str(), - "rd", - query.c_str(), - reinterpret_cast(payload.c_str()), - payload.length()); - - auto f = [this, token, &return_val]() { - std::unique_lockclient_mutex)> lock(client->client_mutex); - auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(COAP_DEFAULT_LEISURE * 2); - - auto answer = CoapClient::messages->get(token); - - while (answer == nullptr) { - client->client_condition.wait_until(lock, timeout); - answer = CoapClient::messages->get(token); - } - - auto answer_type = coap_pdu_get_code(answer); - - lock.unlock(); - CoapClient::messages->erase(token); - - return_val = (answer_type == COAP_RESPONSE_CODE_CREATED); - }; - - std::thread t(f); - t.join(); - - return return_val; - } + if (rd_endpoint.empty()) { + LOG_ERR("rd_endpoint is empty"); + return -1; } - return 1; + request_t request { + resource_dir_ip.c_str(), + resource_dir_port.c_str(), + DELETE, + this->rd_endpoint.c_str(), + nullptr, + nullptr, + 0, + true, + 40 + }; + message_t response = get_client()->send_message_and_wait_response(request, 5U); + if (response.status_code != DELETED) return -1; + + return 0; } void CoapServer::init_resource_dir_local() { diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index 3f922df..b577b13 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -56,47 +56,23 @@ std::string EddieEndpoint::get_resources_from_rd() { return to_return; } - // this node is not rd, doesn't know who is rd - std::string resource_str; - if (get_server()->get_rd_ip().empty()) { - if (!get_server()->discover_rd()) { - coap_log(LOG_ERR, "[CoapNode::discover]: error looking up rd node\n"); - exit(-1); - } - } - - if (!resource_str.empty()) return resource_str; - - std::string token = this->get_client()->send_message(GET, - get_server()->get_rd_ip().c_str(), - get_server()->get_rd_port().c_str(), - "rd", - "rd-lookup/res"); - - auto resource_loop = [this, &token, &resource_str]() { - std::unique_lockget_client()->client_mutex)> lock(this->get_client()->client_mutex); - auto timeout = NOW + std::chrono::seconds(COAP_DEFAULT_LEISURE); - this->get_client()->client_condition.wait_until(lock, timeout); - - auto answer = CoapClient::messages->get(token); - - size_t data_len; - const uint8_t *data; - coap_get_data(answer, &data_len, &data); - - if (!data) { - resource_str = ""; - } else { - resource_str = std::string(reinterpret_cast(data)); - } - - free((void *) data); + std::string rd_ip = get_server()->get_rd_ip(); + std::string rd_port = get_server()->get_rd_port(); + + request_t request { + rd_ip.c_str(), + rd_port.c_str(), + GET, + "rd", + "rd-lookup/res", + nullptr, + 0, + true, + 40 }; + message_t response = get_client()->send_message_and_wait_response(request, 5U); - std::thread resource_thread(resource_loop); - resource_thread.join(); - - return resource_str; + return response.data; } int EddieEndpoint::add_resource(EddieResource *resource) { diff --git a/linux/virtualization/src/VirtualizationReceiver.cpp b/linux/virtualization/src/VirtualizationReceiver.cpp index 8cc0d5e..8d90fe8 100644 --- a/linux/virtualization/src/VirtualizationReceiver.cpp +++ b/linux/virtualization/src/VirtualizationReceiver.cpp @@ -168,21 +168,27 @@ void VirtualizationReceiver::handle_method_call(GDBusConnection *connection, con update_resources(); + std::string comm_answer; + if (mock_ai_executor(method, query_parameters, ip, port, resource, query)) { - token = eddie_endpoint->get_client()->send_message(method_from_string(method), - ip.c_str(), - port.c_str(), - resource.c_str(), - nullptr, - reinterpret_cast(payload), - strlen(payload)); + request_t request { + ip.c_str(), + port.c_str(), + method_from_string(method), + resource.c_str(), + nullptr, + reinterpret_cast(payload), + strlen(payload), + true, + 0 + }; + message_t response = eddie_endpoint->get_client()->send_message_and_wait_response(request, 5U); + comm_answer = response.data; } else { g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", "ERROR forging the message")); return; } - auto comm_answer = eddie_endpoint->get_client()->receive_message(token, 0); - g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", comm_answer.c_str())); } } -- GitLab From 79b85f8b4912a798264558b7bda79a8673f4974e Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Wed, 3 Aug 2022 20:36:05 +0200 Subject: [PATCH 12/24] linux/communication: remove unused stuff and fix linter warnings Signed-off-by: Francesco Pham --- linux/communication/include/CoapClient.h | 4 +- linux/communication/include/CoapServer.h | 8 +- .../communication/include/ResourceDirectory.h | 2 +- linux/communication/src/CoapClient.cpp | 449 +----------------- linux/communication/src/CoapServer.cpp | 13 +- linux/communication/src/ResourceDirectory.cpp | 2 +- 6 files changed, 12 insertions(+), 466 deletions(-) diff --git a/linux/communication/include/CoapClient.h b/linux/communication/include/CoapClient.h index 63530f1..f7824a6 100644 --- a/linux/communication/include/CoapClient.h +++ b/linux/communication/include/CoapClient.h @@ -26,7 +26,7 @@ private: bool quit = false; coap_context_t *context = nullptr; - coap_session_t *open_session(const char *dst_host, const char *dst_port, coap_proto_t protocol); + coap_session_t *open_session(const char *dst_host, const char *dst_port); std::thread client_thread; @@ -41,7 +41,7 @@ public: void destroy_client(); - bool get_quit() const; + [[nodiscard]] bool get_quit() const; void set_quit(bool value); diff --git a/linux/communication/include/CoapServer.h b/linux/communication/include/CoapServer.h index f5a44ee..68e16cc 100644 --- a/linux/communication/include/CoapServer.h +++ b/linux/communication/include/CoapServer.h @@ -21,7 +21,6 @@ #include #include - #define SERVER_LEISURE_TIME (5U) class CoapClient; @@ -36,7 +35,6 @@ private: std::string my_port; std::string ep; // endpoint name std::string d; // sector name - std::string default_lt; std::string resource_dir_ip; std::string resource_dir_port; @@ -55,15 +53,13 @@ public: ~CoapServer(); - int get_quit() const; + [[nodiscard]] int get_quit() const; void set_quit(int val); - coap_context_t *get_context(); - CoapClient *get_client(); - bool is_rd() const; + [[nodiscard]] bool is_rd() const; std::string get_rd_ip(); diff --git a/linux/communication/include/ResourceDirectory.h b/linux/communication/include/ResourceDirectory.h index e8af0dc..8968d4d 100644 --- a/linux/communication/include/ResourceDirectory.h +++ b/linux/communication/include/ResourceDirectory.h @@ -78,7 +78,7 @@ public: const std::unordered_map ¶ms, const std::string &data); - bool get_quit() const { + [[nodiscard]] bool get_quit() const { return quit; } diff --git a/linux/communication/src/CoapClient.cpp b/linux/communication/src/CoapClient.cpp index 74654bb..6aecd38 100644 --- a/linux/communication/src/CoapClient.cpp +++ b/linux/communication/src/CoapClient.cpp @@ -16,7 +16,6 @@ #define COAP_DEFAULT_LEISURE_UNSIGNED 5 #define NOW std::chrono::system_clock::now() -#define min(a, b) ((a) < (b) ? (a) : (b)) /** Container for all response received from the various CoAP nodes */ ThreadSafeMap *CoapClient::messages = new ThreadSafeMap(); @@ -24,448 +23,6 @@ ThreadSafeMap *CoapClient::messages = new ThreadSafeMap< /** Container for all multicast messages received */ ThreadSafeStorage *CoapClient::multicast_messages = new ThreadSafeStorage(); -std::string pdu_type_to_string(coap_pdu_type_t type) { - switch (type) { - case COAP_MESSAGE_ACK: - return "ACK"; - case COAP_MESSAGE_NON: - return "NON"; - case COAP_MESSAGE_RST: - return "RST"; - case COAP_MESSAGE_CON: - return "CON"; - default: - return "???"; - } -} - -std::string pdu_code_to_string(coap_pdu_code_t code) { - static const char *methods[] = {"0.00", "GET", "POST", "PUT", - "DELETE", "FETCH", "PATCH", "iPATCH"}; - static const char *signals[] = {"7.00", "CSM", "Ping", "Pong", "Release", - "Abort"}; - - char buf[5]; - - if (code < sizeof(methods) / sizeof(const char *)) { - return methods[code]; - } else if (code >= 224 && code - 224 < (int) (sizeof(signals) / sizeof(const char *))) { - return signals[code - 224]; - } else { - snprintf(buf, sizeof(buf), "%u.%02u", (code >> 5) & 0x7, code & 0x1f); - return buf; - } -} - - -static const char *msg_option_string(uint8_t code, uint16_t option_type) { - struct option_desc_t { - uint16_t type; - const char *name; - }; - - static struct option_desc_t options[] = { - {COAP_OPTION_IF_MATCH, "If-Match"}, - {COAP_OPTION_URI_HOST, "Uri-Host"}, - {COAP_OPTION_ETAG, "ETag"}, - {COAP_OPTION_IF_NONE_MATCH, "If-None-Match"}, - {COAP_OPTION_OBSERVE, "Observe"}, - {COAP_OPTION_URI_PORT, "Uri-Port"}, - {COAP_OPTION_LOCATION_PATH, "Location-Path"}, - {COAP_OPTION_URI_PATH, "Uri-Path"}, - {COAP_OPTION_CONTENT_FORMAT, "Content-Format"}, - {COAP_OPTION_MAXAGE, "Max-Age"}, - {COAP_OPTION_URI_QUERY, "Uri-Query"}, - {COAP_OPTION_HOP_LIMIT, "Hop-Limit"}, - {COAP_OPTION_ACCEPT, "Accept"}, - {COAP_OPTION_LOCATION_QUERY, "Location-Query"}, - {COAP_OPTION_BLOCK2, "Block2"}, - {COAP_OPTION_BLOCK1, "Block1"}, - {COAP_OPTION_SIZE2, "Size2"}, - {COAP_OPTION_PROXY_URI, "Proxy-Uri"}, - {COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme"}, - {COAP_OPTION_SIZE1, "Size1"}, - {COAP_OPTION_NORESPONSE, "No-Response"} - }; - - static struct option_desc_t options_csm[] = { - {COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size"}, - {COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-wise-Transfer"} - }; - - static struct option_desc_t options_pingpong[] = { - {COAP_SIGNALING_OPTION_CUSTODY, "Custody"} - }; - - static struct option_desc_t options_release[] = { - {COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS, "Alternative-Address"}, - {COAP_SIGNALING_OPTION_HOLD_OFF, "Hold-Off"} - }; - - static struct option_desc_t options_abort[] = { - {COAP_SIGNALING_OPTION_BAD_CSM_OPTION, "Bad-CSM-Option"} - }; - - static char buf[6]; - size_t i; - - if (code == COAP_SIGNALING_CSM) { - for (i = 0; i < sizeof(options_csm) / sizeof(struct option_desc_t); i++) { - if (option_type == options_csm[i].type) { - return options_csm[i].name; - } - } - } else if (code == COAP_SIGNALING_PING || code == COAP_SIGNALING_PONG) { - for (i = 0; i < sizeof(options_pingpong) / sizeof(struct option_desc_t); i++) { - if (option_type == options_pingpong[i].type) { - return options_pingpong[i].name; - } - } - } else if (code == COAP_SIGNALING_RELEASE) { - for (i = 0; i < sizeof(options_release) / sizeof(struct option_desc_t); i++) { - if (option_type == options_release[i].type) { - return options_release[i].name; - } - } - } else if (code == COAP_SIGNALING_ABORT) { - for (i = 0; i < sizeof(options_abort) / sizeof(struct option_desc_t); i++) { - if (option_type == options_abort[i].type) { - return options_abort[i].name; - } - } - } else { - /* search option_type in list of known options */ - for (i = 0; i < sizeof(options) / sizeof(struct option_desc_t); i++) { - if (option_type == options[i].type) { - return options[i].name; - } - } - } - - /* unknown option type, just print to buf */ - snprintf(buf, sizeof(buf), "%u", option_type); - return buf; -} - -static unsigned int -print_content_format(unsigned int format_type, unsigned char *result, unsigned int buf_len) { - struct desc_t { - unsigned int type; - const char *name; - }; - - static struct desc_t formats[] = { - {COAP_MEDIATYPE_TEXT_PLAIN, "text/plain"}, - {COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, "application/link-format"}, - {COAP_MEDIATYPE_APPLICATION_XML, "application/xml"}, - {COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, "application/octet-stream"}, - {COAP_MEDIATYPE_APPLICATION_RDF_XML, "application/rdf+xml"}, - {COAP_MEDIATYPE_APPLICATION_EXI, "application/exi"}, - {COAP_MEDIATYPE_APPLICATION_JSON, "application/json"}, - {COAP_MEDIATYPE_APPLICATION_CBOR, "application/cbor"}, - {COAP_MEDIATYPE_APPLICATION_CWT, "application/cwt"}, - {COAP_MEDIATYPE_APPLICATION_COSE_SIGN, "application/cose; cose-type=\"cose-sign\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, "application/cose; cose-type=\"cose-sign1\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, "application/cose; cose-type=\"cose-encrypt\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, "application/cose; cose-type=\"cose-encrypt0\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_MAC, "application/cose; cose-type=\"cose-mac\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_MAC0, "application/cose; cose-type=\"cose-mac0\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_KEY, "application/cose-key"}, - {COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, "application/cose-key-set"}, - {COAP_MEDIATYPE_APPLICATION_SENML_JSON, "application/senml+json"}, - {COAP_MEDIATYPE_APPLICATION_SENSML_JSON, "application/sensml+json"}, - {COAP_MEDIATYPE_APPLICATION_SENML_CBOR, "application/senml+cbor"}, - {COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, "application/sensml+cbor"}, - {COAP_MEDIATYPE_APPLICATION_SENML_EXI, "application/senml-exi"}, - {COAP_MEDIATYPE_APPLICATION_SENSML_EXI, "application/sensml-exi"}, - {COAP_MEDIATYPE_APPLICATION_SENML_XML, "application/senml+xml"}, - {COAP_MEDIATYPE_APPLICATION_SENSML_XML, "application/sensml+xml"}, - {COAP_MEDIATYPE_APPLICATION_DOTS_CBOR, "application/dots+cbor"}, - {75, "application/dcaf+cbor"} - }; - - size_t i; - - /* search format_type in list of known content formats */ - for (i = 0; i < sizeof(formats) / sizeof(struct desc_t); i++) { - if (format_type == formats[i].type) { - return snprintf((char *) result, buf_len, "%s", formats[i].name); - } - } - - /* unknown content format, just print numeric value to buf */ - return snprintf((char *) result, buf_len, "%d", format_type); -} - -int is_binary(int content_format) { - return !(content_format == -1 || - content_format == COAP_MEDIATYPE_TEXT_PLAIN || - content_format == COAP_MEDIATYPE_APPLICATION_LINK_FORMAT || - content_format == COAP_MEDIATYPE_APPLICATION_XML || - content_format == COAP_MEDIATYPE_APPLICATION_JSON); -} - -static size_t print_readable(const uint8_t *data, size_t len, unsigned char *result, - size_t buf_len, int encode_always) { - const uint8_t hex[] = "0123456789ABCDEF"; - size_t cnt = 0; - assert(data || len == 0); - - if (buf_len == 0) return 0; - - while (len) { - if (!encode_always && isprint(*data)) { - if (cnt + 1 < buf_len) { - *result++ = *data; - ++cnt; - } else { - break; - } - } else { - if (cnt + 4 < buf_len) { - *result++ = '\\'; - *result++ = 'x'; - *result++ = hex[(*data & 0xf0) >> 4]; - *result++ = hex[*data & 0x0f]; - cnt += 4; - } else { - break; - } - } - - ++data; - --len; - } - - *result = '\0'; - return cnt; -} - -std::string pdu_to_string(coap_pdu_t *pdu) { - int COAP_DEBUG_BUF_SIZE = 4096; - int COAP_DEFAULT_VERSION = 1; - - /* Proxy-Uri: can be 1034 bytes long */ - unsigned char buf[min(COAP_DEBUG_BUF_SIZE, 1035)]; - char outbuf[COAP_DEBUG_BUF_SIZE]; - size_t buf_len = 0; /* takes the number of bytes written to buf */ - int encode = 0, have_options = 0, i; - coap_opt_iterator_t opt_iter; - coap_opt_t *option; - int content_format = -1; - size_t data_len; - const uint8_t *data; - uint32_t opt_len; - const uint8_t *opt_val; - size_t outbuflen = 0; - - auto pdu_type = coap_pdu_get_type(pdu); - auto pdu_code = coap_pdu_get_code(pdu); - auto pdu_mid = coap_pdu_get_mid(pdu); - auto pdu_token = coap_pdu_get_token(pdu); - - snprintf(outbuf, sizeof(outbuf), "v:%d t:%s c:%s i:%04x {", - COAP_DEFAULT_VERSION, pdu_type_to_string(pdu_type).c_str(), - pdu_code_to_string(pdu_code).c_str(), pdu_mid); - - for (i = 0; i < pdu_token.length; i++) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - "%02x", pdu_token.s[i]); - } - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, "}"); - - /* show options, if any */ - coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, " ["); - while ((option = coap_option_next(&opt_iter))) { - buf[0] = '\000'; - if (!have_options) { - have_options = 1; - } else { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, ","); - } - - if (pdu_code == COAP_SIGNALING_CODE_CSM || pdu_code == COAP_SIGNALING_CODE_ABORT) { - switch (opt_iter.number) { - case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE: - /* this is equal to COAP_SIGNALING_OPTION_BAD_CSM_OPTION */ - buf_len = snprintf((char *) buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - default: - buf_len = 0; - break; - } - } else if (pdu_code == COAP_SIGNALING_CODE_PING || pdu_code == COAP_SIGNALING_CODE_PONG) { - buf_len = 0; - } else if (pdu_code == COAP_SIGNALING_CODE_RELEASE) { - switch (opt_iter.number) { - case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS: - buf_len = print_readable(coap_opt_value(option), - coap_opt_length(option), - buf, sizeof(buf), 0); - break; - case COAP_SIGNALING_OPTION_HOLD_OFF: - buf_len = snprintf((char *) buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - default: - buf_len = 0; - break; - } - } else { - switch (opt_iter.number) { - case COAP_OPTION_CONTENT_FORMAT: - case COAP_OPTION_ACCEPT: - content_format = (int) coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option)); - - buf_len = print_content_format(content_format, buf, sizeof(buf)); - break; - - case COAP_OPTION_BLOCK1: - case COAP_OPTION_BLOCK2: - /* split block option into number/more/size where more is the - * letter M if set, the _ otherwise */ - buf_len = snprintf((char *) buf, sizeof(buf), "%u/%c/%u", - coap_opt_block_num(option), /* block number */ - COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */ - (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */ - - break; - - case COAP_OPTION_URI_PORT: - case COAP_OPTION_MAXAGE: - case COAP_OPTION_OBSERVE: - case COAP_OPTION_SIZE1: - case COAP_OPTION_SIZE2: - case COAP_OPTION_HOP_LIMIT: - /* show values as unsigned decimal value */ - buf_len = snprintf((char *) buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - - case COAP_OPTION_IF_MATCH: - case COAP_OPTION_ETAG: - opt_len = coap_opt_length(option); - opt_val = coap_opt_value(option); - snprintf((char *) buf, sizeof(buf), "0x"); - for (i = 0; (uint32_t) i < opt_len; i++) { - buf_len = strlen((char *) buf); - snprintf((char *) &buf[buf_len], sizeof(buf) - buf_len, - "%02x", opt_val[i]); - } - buf_len = strlen((char *) buf); - break; - default: - /* generic output function for all other option types */ - if (opt_iter.number == COAP_OPTION_URI_PATH || - opt_iter.number == COAP_OPTION_PROXY_URI || - opt_iter.number == COAP_OPTION_URI_HOST || - opt_iter.number == COAP_OPTION_LOCATION_PATH || - opt_iter.number == COAP_OPTION_LOCATION_QUERY || - opt_iter.number == COAP_OPTION_PROXY_SCHEME || - opt_iter.number == COAP_OPTION_URI_QUERY) { - encode = 0; - } else { - encode = 1; - } - - buf_len = print_readable(coap_opt_value(option), - coap_opt_length(option), - buf, sizeof(buf), encode); - } - } - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - " %s:%.*s", msg_option_string(pdu_code, opt_iter.number), - (int) buf_len, buf); - } - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, " ]"); - - if (coap_get_data(pdu, &data_len, &data)) { - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, " :: "); - - if (is_binary(content_format) || !isprint(data[0])) { - size_t keep_data_len = data_len; - const uint8_t *keep_data = data; - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - "binary data length %zu\n", data_len); - - /* Output hex dump of binary data as a continuous entry */ - outbuf[0] = '\000'; - snprintf(outbuf, sizeof(outbuf), "<<"); - while (data_len--) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - "%02x", *data++); - } - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, ">>"); - data_len = keep_data_len; - data = keep_data; - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, "\n"); - - /* - * Output ascii readable (if possible), immediately under the - * hex value of the character output above to help binary debugging - */ - outbuf[0] = '\000'; - snprintf(outbuf, sizeof(outbuf), "<<"); - while (data_len--) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - "%c ", isprint(*data) ? *data : '.'); - data++; - } - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, ">>"); - } else { - size_t max_length; - outbuflen = strlen(outbuf); - max_length = sizeof(outbuf) - outbuflen; - if (max_length > 1) { - outbuf[outbuflen++] = '\''; - outbuf[outbuflen] = '\000'; - max_length--; - } - if (max_length > 1) { - outbuflen += print_readable(data, data_len, - (unsigned char *) &outbuf[outbuflen], - max_length, 0); - } - /* print_readable may be handling unprintables - hence headroom of 4 */ - if (outbuflen < sizeof(outbuf) - 4 - 1) { - outbuf[outbuflen++] = '\''; - outbuf[outbuflen] = '\000'; - } - } - } - - outbuflen = strlen(outbuf); - if (outbuflen == sizeof(outbuf) - 1) outbuflen--; - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, "\n"); - - return outbuf; -} - coap_response_t CoapClient::message_handler(coap_session_t *session, const coap_pdu_t *sent, const coap_pdu_t *received, const coap_mid_t id) { @@ -504,7 +61,7 @@ CoapClient::message_handler(coap_session_t *session, const coap_pdu_t *sent, con } coap_session_t * -CoapClient::open_session(const char *dst_host, const char *dst_port, coap_proto_t protocol) { +CoapClient::open_session(const char *dst_host, const char *dst_port) { coap_session_t *session; coap_address_t dst; @@ -513,7 +70,7 @@ CoapClient::open_session(const char *dst_host, const char *dst_port, coap_proto_ return nullptr; } - session = coap_new_client_session(this->context, nullptr, &dst, protocol); + session = coap_new_client_session(this->context, nullptr, &dst, COAP_PROTO_UDP); if (!session) { LOG_ERR("Unable to initiate session"); @@ -598,7 +155,7 @@ std::string CoapClient::send_message(method_t method, const char *dst_host, cons // BEGIN CRITICAL CALLS TO CoAP APIs std::unique_lockclient_mutex)> lock(this->client_mutex); - session = open_session(dst_host, dst_port, COAP_PROTO_UDP); + session = open_session(dst_host, dst_port); if (!session) { LOG_ERR("Error creating remote session"); diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index 1b6333c..79870f8 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -15,11 +15,9 @@ #include #include #include -#include #include #include #include -#include class CoapClient; @@ -101,7 +99,6 @@ void CoapServer::resource_handler(coap_resource_t *resource, coap_session_t *ses CoapServer::CoapServer(const char *host, const char *port, int is_resource_dir) { my_ip = host; my_port = port; - default_lt = "90000"; LOG_DBG("Initializing CoapServer"); init_server("::", port); @@ -144,10 +141,6 @@ void CoapServer::set_quit(int val) { quit = val; } -coap_context_t *CoapServer::get_context() { - return context; -} - CoapClient *CoapServer::get_client() { return client; } @@ -251,7 +244,7 @@ int CoapServer::discover_rd() { 40 }; - message_t response = get_client()->send_message_and_wait_response(request, 5U); + message_t response = get_client()->send_message_and_wait_response(request, SERVER_LEISURE_TIME); std::map> parsed = parse_link_format(response.data); @@ -311,7 +304,7 @@ int CoapServer::publish_resources(int lt) { 40 }; - message_t response = get_client()->send_message_and_wait_response(request, 5U); + message_t response = get_client()->send_message_and_wait_response(request, SERVER_LEISURE_TIME); if (!response.location_path.empty()) { this->rd_endpoint = "rd/" + response.location_path; } @@ -354,7 +347,7 @@ int CoapServer::unpublish_resources() { true, 40 }; - message_t response = get_client()->send_message_and_wait_response(request, 5U); + message_t response = get_client()->send_message_and_wait_response(request, SERVER_LEISURE_TIME); if (response.status_code != DELETED) return -1; return 0; diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp index c34e0cb..26cfa6f 100644 --- a/linux/communication/src/ResourceDirectory.cpp +++ b/linux/communication/src/ResourceDirectory.cpp @@ -727,7 +727,7 @@ ResourceDirectory::~ResourceDirectory() { int ResourceDirectory::run() { LOG_INFO("Running resource directory"); - while (this->get_quit() == 0) { + while (!this->get_quit()) { coap_io_process(this->context, COAP_IO_WAIT); } -- GitLab From 421be182229dba96c3ddd33818e0bee1766d2339 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Thu, 4 Aug 2022 12:37:33 +0200 Subject: [PATCH 13/24] zephyr: fix bus fault error, close coap client on error,remove base attr - fix bus fault error caused by how common library was linked - close coap client on error - remove base attribute on resource publishing so that base is automatically detected from the request source address Signed-off-by: Francesco Pham --- common/CMakeLists.txt | 3 ++- zephyr/src/CoapClient.cpp | 1 + zephyr/src/EddieEndpoint.cpp | 4 +--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index c80aa71..cf59641 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -8,6 +8,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "DEBUG") add_compile_definitions(DEBUG) endif() -add_library(${PROJECT_NAME} src/LinkFormat.cpp) +add_library(${PROJECT_NAME}) +target_sources(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/src/LinkFormat.cpp) target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include) diff --git a/zephyr/src/CoapClient.cpp b/zephyr/src/CoapClient.cpp index e4ef182..e2b0496 100644 --- a/zephyr/src/CoapClient.cpp +++ b/zephyr/src/CoapClient.cpp @@ -216,6 +216,7 @@ int CoapClient::send_request( int r = process_simple_coap_reply(response_handler); if (r < 0) { LOG_ERR("Error processing coap reply"); + close_coap_client(); return -1; } diff --git a/zephyr/src/EddieEndpoint.cpp b/zephyr/src/EddieEndpoint.cpp index f9a027c..a68bf9b 100644 --- a/zephyr/src/EddieEndpoint.cpp +++ b/zephyr/src/EddieEndpoint.cpp @@ -92,9 +92,7 @@ int EddieEndpoint::publish_resources() { const char* path = "rd"; // TODO: use device id as endpoint name - const char* query = "base=coap://[" CONFIG_NET_CONFIG_MY_IPV6_ADDR "]:5683&" - "ep=zephyrnode&" - "lt=90000"; + const char* query = "ep=zephyrnode<=90000"; request_t publish_resources_request = { this->resource_directory_ip.c_str(), //dst_host -- GitLab From 66997216a7ce38bccb504a295142794ac51e45e6 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Thu, 4 Aug 2022 16:43:33 +0200 Subject: [PATCH 14/24] linux/CoapClient: move init_client into CoapClient constructor Signed-off-by: Francesco Pham --- linux/communication/include/CoapClient.h | 4 +-- linux/communication/src/CoapClient.cpp | 40 ++++++++++-------------- linux/communication/src/CoapServer.cpp | 3 +- linux/examples/eddie_endpoint.cpp | 2 +- linux/tests/communication_tests.cpp | 4 --- 5 files changed, 19 insertions(+), 34 deletions(-) diff --git a/linux/communication/include/CoapClient.h b/linux/communication/include/CoapClient.h index f7824a6..e3c2d4a 100644 --- a/linux/communication/include/CoapClient.h +++ b/linux/communication/include/CoapClient.h @@ -37,7 +37,7 @@ public: std::mutex client_mutex; std::condition_variable client_condition; - CoapClient(); + CoapClient(coap_context_t *context = nullptr); void destroy_client(); @@ -47,8 +47,6 @@ public: coap_context_t *get_context(); - int init_client(); - static coap_response_t message_handler(coap_session_t *session, const coap_pdu_t *sent, const coap_pdu_t *received, coap_mid_t id); diff --git a/linux/communication/src/CoapClient.cpp b/linux/communication/src/CoapClient.cpp index 6aecd38..4a764db 100644 --- a/linux/communication/src/CoapClient.cpp +++ b/linux/communication/src/CoapClient.cpp @@ -81,8 +81,22 @@ CoapClient::open_session(const char *dst_host, const char *dst_port) { return session; } -CoapClient::CoapClient() { - this->context = nullptr; +CoapClient::CoapClient(coap_context_t *context) { + this->context = context ? context : coap_new_context(nullptr); + + if (!this->context) { + LOG_ERR("Unable to create context"); + // TODO: throw exception + return; + } + + coap_register_response_handler(this->context, message_handler); + + auto client_run = [this]() { + while (!this->get_quit()) + coap_io_process(this->context, 100); + }; + client_thread = std::thread(client_run); } void CoapClient::destroy_client() { @@ -114,28 +128,6 @@ coap_context_t *CoapClient::get_context() { return this->context; } -int CoapClient::init_client() { - this->context = coap_new_context(nullptr); - - if (!this->context) { - LOG_ERR("Unable to create context"); - coap_free_context(this->context); - coap_cleanup(); - - return -1; - } - - coap_register_response_handler(this->context, message_handler); - - auto client_run = [this]() { - while (!this->get_quit()) - coap_io_process(this->context, 100); - }; - client_thread = std::thread(client_run); - - return 0; -} - std::string CoapClient::send_message(method_t method, const char *dst_host, const char *dst_port, const char *dst_resource, const char *query, const uint8_t *data, size_t data_len, bool confirmable, bool multicast) { diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index 79870f8..d98f9cf 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -103,7 +103,6 @@ CoapServer::CoapServer(const char *host, const char *port, int is_resource_dir) LOG_DBG("Initializing CoapServer"); init_server("::", port); this->client = new CoapClient(); - this->client->init_client(); if (is_resource_dir) init_resource_dir_local(); auto now = std::chrono::duration_cast( @@ -233,7 +232,7 @@ int CoapServer::discover_rd() { LOG_DBG("Discovering resource directory"); request_t request { - "::", + COAP_IP6_MULTICAST_SITE_LOCAL, "5683", GET, ".well-known/core", diff --git a/linux/examples/eddie_endpoint.cpp b/linux/examples/eddie_endpoint.cpp index 89945ae..eb24e22 100644 --- a/linux/examples/eddie_endpoint.cpp +++ b/linux/examples/eddie_endpoint.cpp @@ -75,7 +75,7 @@ public: int main(int argc, char *argv[]) { - argparse::ArgumentParser program("program_name"); + argparse::ArgumentParser program("eddie-endpoint"); program.add_argument("--ip", "-a") .help("Ip address") diff --git a/linux/tests/communication_tests.cpp b/linux/tests/communication_tests.cpp index 3c46ca0..60c7fb9 100644 --- a/linux/tests/communication_tests.cpp +++ b/linux/tests/communication_tests.cpp @@ -56,7 +56,6 @@ public: TEST(Communication, Client_Init) { CoapClient client; - client.init_client(); EXPECT_EQ(client.get_quit(), false); client.destroy_client(); EXPECT_EQ(client.get_quit(), true); @@ -64,7 +63,6 @@ TEST(Communication, Client_Init) { TEST(Communication, Client_Requests) { CoapClient client; - client.init_client(); client.send_message(GET, "::", "5683", "rd", "ep=sazz&base=pazz"); client.send_message(POST, "::", "5683", "rd", "ep=sazz&base=pazz"); @@ -84,14 +82,12 @@ TEST(Communication, Client_Requests) { TEST(Communication, Client_Context) { CoapClient client; - client.init_client(); client.get_context(); client.destroy_client(); } TEST(Communication, Client_Destroy) { CoapClient client; - client.init_client(); client.destroy_client(); } -- GitLab From d86de80d1053025d342137b21fadfc1d0901a136 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Thu, 4 Aug 2022 17:14:57 +0200 Subject: [PATCH 15/24] linux/CoapClient: move destroy_client into CoapClient destructor Signed-off-by: Francesco Pham --- linux/communication/include/CoapClient.h | 4 ++-- linux/communication/src/CoapClient.cpp | 2 +- linux/communication/src/CoapServer.cpp | 2 -- linux/tests/communication_tests.cpp | 5 ----- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/linux/communication/include/CoapClient.h b/linux/communication/include/CoapClient.h index e3c2d4a..dac6237 100644 --- a/linux/communication/include/CoapClient.h +++ b/linux/communication/include/CoapClient.h @@ -37,9 +37,9 @@ public: std::mutex client_mutex; std::condition_variable client_condition; - CoapClient(coap_context_t *context = nullptr); + explicit CoapClient(coap_context_t *context = nullptr); - void destroy_client(); + ~CoapClient(); [[nodiscard]] bool get_quit() const; diff --git a/linux/communication/src/CoapClient.cpp b/linux/communication/src/CoapClient.cpp index 4a764db..23ccf83 100644 --- a/linux/communication/src/CoapClient.cpp +++ b/linux/communication/src/CoapClient.cpp @@ -99,7 +99,7 @@ CoapClient::CoapClient(coap_context_t *context) { client_thread = std::thread(client_run); } -void CoapClient::destroy_client() { +CoapClient::~CoapClient() { set_quit(true); client_thread.join(); diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index d98f9cf..a930b86 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -125,8 +125,6 @@ CoapServer::~CoapServer() { resources->delete_all(); delete resources; - - this->client->destroy_client(); delete client; coap_free_context(this->context); diff --git a/linux/tests/communication_tests.cpp b/linux/tests/communication_tests.cpp index 60c7fb9..b689e99 100644 --- a/linux/tests/communication_tests.cpp +++ b/linux/tests/communication_tests.cpp @@ -57,8 +57,6 @@ public: TEST(Communication, Client_Init) { CoapClient client; EXPECT_EQ(client.get_quit(), false); - client.destroy_client(); - EXPECT_EQ(client.get_quit(), true); } TEST(Communication, Client_Requests) { @@ -77,18 +75,15 @@ TEST(Communication, Client_Requests) { false, true); - client.destroy_client(); } TEST(Communication, Client_Context) { CoapClient client; client.get_context(); - client.destroy_client(); } TEST(Communication, Client_Destroy) { CoapClient client; - client.destroy_client(); } TEST(Communication, Server_Init_No_Rd) { -- GitLab From 615862c285604fa5a2dbdaa1bc5cf674e229bf82 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Thu, 4 Aug 2022 21:23:34 +0200 Subject: [PATCH 16/24] linux/CoapServer: check ipv6 or ipv4 and properly format url Signed-off-by: Francesco Pham --- linux/communication/include/common.h | 4 ++++ linux/communication/src/CoapServer.cpp | 15 +++++++++++++-- linux/communication/src/ResourceDirectory.cpp | 2 +- linux/communication/src/common.cpp | 11 +++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/linux/communication/include/common.h b/linux/communication/include/common.h index da02b54..d821111 100644 --- a/linux/communication/include/common.h +++ b/linux/communication/include/common.h @@ -32,6 +32,10 @@ coap_pdu_code_t method_to_coap_method(method_t method); int match(const std::string &src, const std::string &dst, int is_prefix); +bool is_ipv4_address(const std::string& str); + +bool is_ipv6_address(const std::string& str); + /** * Split a string in different substrings, given the delimiter character. * @param s the string to analyze diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index a930b86..adbc3c0 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -267,7 +267,14 @@ int CoapServer::publish_resources(int lt) { auto res_list = resources->get_all(); std::string data = resource_list_to_string(res_list); - std::string base_address = "coap://" + my_ip + ":" + my_port; + std::string base_address; + if (is_ipv6_address(my_ip)) + base_address = "coap://[" + my_ip + "]:" + my_port; + else if (is_ipv4_address(my_ip)) + base_address = "coap://" + my_ip + ":" + my_port; + else { + LOG_ERR("Invalid ip address"); + } if (data.empty() && !res_list.empty()) { LOG_DBG("[CoapServer::publish_resources]: unable to create string representation of resource"); @@ -282,13 +289,17 @@ int CoapServer::publish_resources(int lt) { {"d", this->d}, {"lt", std::to_string(lt)}}; + if (!base_address.empty()) params["base"] = base_address; + if (this->resource_directory->add_endpoint(ep_d_key, params, data) != 0) { LOG_ERR("failed to add endpoint to local resource directory"); return -1; } } else if (!resource_dir_ip.empty()) { - std::string query = "ep=" + ep + "&d=" + d + "<=" + std::to_string(lt) + "&base=" + base_address; + std::string query = "ep=" + ep + "&d=" + d + "<=" + std::to_string(lt); + if (!base_address.empty()) query += + "&base=" + base_address; + request_t request { resource_dir_ip.c_str(), resource_dir_port.c_str(), diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp index 26cfa6f..0ce50c0 100644 --- a/linux/communication/src/ResourceDirectory.cpp +++ b/linux/communication/src/ResourceDirectory.cpp @@ -698,7 +698,7 @@ ResourceDirectory::ResourceDirectory(const std::string& ip, coap_context_t *serv coap_register_handler(resource_dir, COAP_REQUEST_POST, hnd_post_rd); coap_add_attr(resource_dir, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); coap_add_attr(resource_dir, coap_make_str_const("ct"), coap_make_str_const("40"), 0); - coap_add_attr(resource_dir, coap_make_str_const("base"), coap_make_str_const(ip.c_str()), 0); + coap_add_attr(resource_dir, coap_make_str_const("base"), coap_make_str_const(ip.c_str()), 0); //TODO: check ip is valid coap_add_resource(this->context, resource_dir); rd_lookup_res = coap_resource_init(coap_make_str_const("rd-lookup/res"), 0); diff --git a/linux/communication/src/common.cpp b/linux/communication/src/common.cpp index c1c0323..7655eac 100644 --- a/linux/communication/src/common.cpp +++ b/linux/communication/src/common.cpp @@ -12,6 +12,7 @@ #include #include #include +#include int resolve_address(const char *host, const char *service, coap_address_t *dst) { struct addrinfo *res, *aInfo; @@ -136,4 +137,14 @@ coap_pdu_code_t method_to_coap_method(method_t method) { LOG_ERR("Invalid coap method"); return COAP_REQUEST_CODE_GET; } +} + +bool is_ipv4_address(const std::string& str) { + struct sockaddr_in sa{}; + return inet_pton(AF_INET, str.c_str(), &(sa.sin_addr)) != 0; +} + +bool is_ipv6_address(const std::string& str) { + struct sockaddr_in6 sa{}; + return inet_pton(AF_INET6, str.c_str(), &(sa.sin6_addr)) != 0; } \ No newline at end of file -- GitLab From 65a5ebab79986d9c3fc4b56762e32b6fdea94798 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Thu, 4 Aug 2022 21:52:01 +0200 Subject: [PATCH 17/24] linux/ResourceDirectory: use coap_split_query to parse query coap_split_query also decodes percent encoded query string. This fixes ipv6 addresses in base that gets escaped because of square parenthesis in uri. Signed-off-by: Francesco Pham --- linux/communication/src/ResourceDirectory.cpp | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp index 0ce50c0..174bd9f 100644 --- a/linux/communication/src/ResourceDirectory.cpp +++ b/linux/communication/src/ResourceDirectory.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #define COAP_IP4_MULTICAST "224.0.1.187" #define COAP_IP6_MULTICAST_LOCAL_LINK "FF02::FD" @@ -334,10 +335,24 @@ void ResourceDirectory::hnd_post_rd(coap_resource_t *resource, coap_session_t *s return; } - std::string query_str = std::string(reinterpret_cast(query->s), query->length); - std::vector sub_queries = split(query_str, '&'); - std::unordered_map query_map; + size_t buf_len = query->length + 128; + unsigned char buf[buf_len]; + int n_opts = coap_split_query((const uint8_t *)query->s, query->length, buf, &buf_len); + + if (n_opts < 0) { + LOG_ERR("Problem parsing query"); + return; + } + + std::vector sub_queries; + unsigned char *p_buf = buf; + while (n_opts--) { + std::string option = std::string(reinterpret_cast(coap_opt_value(p_buf)), coap_opt_length(p_buf)); + sub_queries.emplace_back(std::string(option)); + p_buf += coap_opt_size(p_buf); + } + std::unordered_map query_map; for (const auto &token: sub_queries) { std::vector sub_query = split(token, '='); query_map.insert(std::make_pair(sub_query[0], sub_query[1])); -- GitLab From 3ab3c796fa87d93be604aefe0f510c70e885ea43 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Fri, 5 Aug 2022 13:51:52 +0200 Subject: [PATCH 18/24] linux/CoapServer: discover_rd get rd address from response ip instead of getting the ip of the resource directory from base attribute, use the src ip from the response. Signed-off-by: Francesco Pham --- linux/communication/src/CoapServer.cpp | 17 ++++++++++------- linux/communication/src/ResourceDirectory.cpp | 1 - 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index adbc3c0..dc9a823 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -247,16 +247,19 @@ int CoapServer::discover_rd() { if (parsed.empty()) return -1; - auto first_link = parsed.begin()->second; - auto base = first_link.find("base"); - - if (base == first_link.end()) { - LOG_ERR("could not find base attribute"); + std::string rd_uri = "coap://" + response.src_ip; + coap_uri_t parsed_rd_uri; + int r = coap_split_uri(reinterpret_cast(rd_uri.c_str()), + rd_uri.length(), + &parsed_rd_uri); + + if (r < 0) { + LOG_ERR("Error parsing src address"); return -1; } - this->resource_dir_ip = base->second; - this->resource_dir_port = "5683"; + this->resource_dir_ip = std::string(reinterpret_cast(parsed_rd_uri.host.s), parsed_rd_uri.host.length); + this->resource_dir_port = std::to_string(parsed_rd_uri.port); LOG_DBG("Found resource directory: ip=%s port=%s", this->resource_dir_ip.c_str(), this->resource_dir_port.c_str()); diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp index 174bd9f..4d98424 100644 --- a/linux/communication/src/ResourceDirectory.cpp +++ b/linux/communication/src/ResourceDirectory.cpp @@ -713,7 +713,6 @@ ResourceDirectory::ResourceDirectory(const std::string& ip, coap_context_t *serv coap_register_handler(resource_dir, COAP_REQUEST_POST, hnd_post_rd); coap_add_attr(resource_dir, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); coap_add_attr(resource_dir, coap_make_str_const("ct"), coap_make_str_const("40"), 0); - coap_add_attr(resource_dir, coap_make_str_const("base"), coap_make_str_const(ip.c_str()), 0); //TODO: check ip is valid coap_add_resource(this->context, resource_dir); rd_lookup_res = coap_resource_init(coap_make_str_const("rd-lookup/res"), 0); -- GitLab From 32579ff45b98dfcb778e614580230d9c32448f7b Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Fri, 5 Aug 2022 15:07:07 +0200 Subject: [PATCH 19/24] linux/CoapClient: stop waiting as soon as receive response - add predicate to wait_until to stop waiting as soon as a response is received, without waiting until timeout - make client mutex and condition static - set BAD_REQUEST to response when the request times out Signed-off-by: Francesco Pham --- linux/communication/include/CoapClient.h | 8 +++---- linux/communication/include/common.h | 5 +++++ linux/communication/src/CoapClient.cpp | 22 ++++++++++++++----- linux/communication/src/CoapServer.cpp | 6 ++--- linux/communication/src/EddieEndpoint.cpp | 11 +++++++--- .../src/VirtualizationReceiver.cpp | 2 +- 6 files changed, 37 insertions(+), 17 deletions(-) diff --git a/linux/communication/include/CoapClient.h b/linux/communication/include/CoapClient.h index dac6237..5563359 100644 --- a/linux/communication/include/CoapClient.h +++ b/linux/communication/include/CoapClient.h @@ -34,8 +34,8 @@ public: // TODO: move into private static ThreadSafeMap *messages; static ThreadSafeStorage *multicast_messages; - std::mutex client_mutex; - std::condition_variable client_condition; + static std::mutex client_mutex; + static std::condition_variable client_condition; explicit CoapClient(coap_context_t *context = nullptr); @@ -61,9 +61,9 @@ public: bool confirmable = true, bool multicast = false); - message_t receive_message(std::string token, int max_timeout); + message_t receive_message(std::string token, int max_timeout=5); - message_t send_message_and_wait_response(request_t request, int max_timeout); + message_t send_message_and_wait_response(request_t request, int max_timeout=5); }; #endif //EDDIE_COAPCLIENT_H diff --git a/linux/communication/include/common.h b/linux/communication/include/common.h index d821111..f6428a9 100644 --- a/linux/communication/include/common.h +++ b/linux/communication/include/common.h @@ -61,6 +61,11 @@ public: return elem; } + bool exists(Key const &k) { + std::unique_lock lock(m_); + return c_.find(k) != c_.end(); + } + std::list get_all() { std::unique_lock lock(m_); std::list return_list; diff --git a/linux/communication/src/CoapClient.cpp b/linux/communication/src/CoapClient.cpp index 23ccf83..64246f7 100644 --- a/linux/communication/src/CoapClient.cpp +++ b/linux/communication/src/CoapClient.cpp @@ -23,6 +23,9 @@ ThreadSafeMap *CoapClient::messages = new ThreadSafeMap< /** Container for all multicast messages received */ ThreadSafeStorage *CoapClient::multicast_messages = new ThreadSafeStorage(); +std::mutex CoapClient::client_mutex; +std::condition_variable CoapClient::client_condition; + coap_response_t CoapClient::message_handler(coap_session_t *session, const coap_pdu_t *sent, const coap_pdu_t *received, const coap_mid_t id) { @@ -57,6 +60,7 @@ CoapClient::message_handler(coap_session_t *session, const coap_pdu_t *sent, con else { messages->set(token_str, response_message); } + CoapClient::client_condition.notify_one(); return COAP_RESPONSE_OK; } @@ -145,7 +149,7 @@ std::string CoapClient::send_message(method_t method, const char *dst_host, cons uint8_t *token = token_buf; // BEGIN CRITICAL CALLS TO CoAP APIs - std::unique_lockclient_mutex)> lock(this->client_mutex); + std::unique_lock lock(CoapClient::client_mutex); session = open_session(dst_host, dst_port); @@ -225,12 +229,18 @@ std::string CoapClient::send_message(method_t method, const char *dst_host, cons message_t CoapClient::receive_message(std::string token, int max_timeout) { message_t response; auto task = [this, &max_timeout, &token, &response]() { - std::unique_lockclient_mutex)> lock(this->client_mutex); - int seconds = max_timeout > COAP_DEFAULT_LEISURE ? max_timeout : COAP_DEFAULT_LEISURE_UNSIGNED; + std::unique_lock lock(CoapClient::client_mutex); + int seconds = max_timeout > 0 ? max_timeout : COAP_DEFAULT_LEISURE_UNSIGNED; auto timeout = NOW + std::chrono::seconds(seconds); - this->client_condition.wait_until(lock, timeout); - - response = CoapClient::messages->get(token); + auto check_exist_response = [&token]{ + return CoapClient::messages->exists(token); + }; + CoapClient::client_condition.wait_until(lock, timeout, check_exist_response); + + if (check_exist_response()) + response = CoapClient::messages->get(token); + else + response.status_code = BAD_REQUEST; }; std::thread t(task); diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index dc9a823..d43071c 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -241,7 +241,7 @@ int CoapServer::discover_rd() { 40 }; - message_t response = get_client()->send_message_and_wait_response(request, SERVER_LEISURE_TIME); + message_t response = get_client()->send_message_and_wait_response(request); std::map> parsed = parse_link_format(response.data); @@ -315,7 +315,7 @@ int CoapServer::publish_resources(int lt) { 40 }; - message_t response = get_client()->send_message_and_wait_response(request, SERVER_LEISURE_TIME); + message_t response = get_client()->send_message_and_wait_response(request); if (!response.location_path.empty()) { this->rd_endpoint = "rd/" + response.location_path; } @@ -358,7 +358,7 @@ int CoapServer::unpublish_resources() { true, 40 }; - message_t response = get_client()->send_message_and_wait_response(request, SERVER_LEISURE_TIME); + message_t response = get_client()->send_message_and_wait_response(request); if (response.status_code != DELETED) return -1; return 0; diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index b577b13..ed4282a 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -58,8 +58,13 @@ std::string EddieEndpoint::get_resources_from_rd() { std::string rd_ip = get_server()->get_rd_ip(); std::string rd_port = get_server()->get_rd_port(); - - request_t request { + + if (rd_ip.empty() || rd_port.empty()) { + LOG_ERR("Could not found resource directory"); + return ""; + } + + request_t request{ rd_ip.c_str(), rd_port.c_str(), GET, @@ -70,7 +75,7 @@ std::string EddieEndpoint::get_resources_from_rd() { true, 40 }; - message_t response = get_client()->send_message_and_wait_response(request, 5U); + message_t response = get_client()->send_message_and_wait_response(request); return response.data; } diff --git a/linux/virtualization/src/VirtualizationReceiver.cpp b/linux/virtualization/src/VirtualizationReceiver.cpp index 8d90fe8..fa380c0 100644 --- a/linux/virtualization/src/VirtualizationReceiver.cpp +++ b/linux/virtualization/src/VirtualizationReceiver.cpp @@ -182,7 +182,7 @@ void VirtualizationReceiver::handle_method_call(GDBusConnection *connection, con true, 0 }; - message_t response = eddie_endpoint->get_client()->send_message_and_wait_response(request, 5U); + message_t response = eddie_endpoint->get_client()->send_message_and_wait_response(request); comm_answer = response.data; } else { g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", "ERROR forging the message")); -- GitLab From 0092dd312939db5cc14580d416cf70d2b07d8eb5 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Fri, 5 Aug 2022 15:28:07 +0200 Subject: [PATCH 20/24] linux/EddieEndpoint: fix get resources from rd sending wrong request Signed-off-by: Francesco Pham --- linux/communication/src/EddieEndpoint.cpp | 3 ++- linux/examples/eddie_endpoint.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index ed4282a..0a81e13 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -68,15 +68,16 @@ std::string EddieEndpoint::get_resources_from_rd() { rd_ip.c_str(), rd_port.c_str(), GET, - "rd", "rd-lookup/res", nullptr, + nullptr, 0, true, 40 }; message_t response = get_client()->send_message_and_wait_response(request); + LOG_DBG("Discovered resources: %s", response.data.c_str()); return response.data; } diff --git a/linux/examples/eddie_endpoint.cpp b/linux/examples/eddie_endpoint.cpp index eb24e22..9db7255 100644 --- a/linux/examples/eddie_endpoint.cpp +++ b/linux/examples/eddie_endpoint.cpp @@ -118,6 +118,7 @@ int main(int argc, char *argv[]) node.publish_resources(); } + node.get_resources_from_rd(); node.start_server(); return 0; -- GitLab From b5b5fc982fe977005ac4c762c7d8c4c55d609ced Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Thu, 2 Jun 2022 20:57:19 +0200 Subject: [PATCH 21/24] tests: Add fixture to have eddie-server running in background Some integration tests or even units test with communication part we might need to spin up a server for some particular test case and tear it down again afterwards. This commits shows an example how to implement this test fixtures in ctest by using the FIXUP_SETUP/_CLEANUP properties on tests. The fixture execution and teardown is handed over to scripts for tracking PID. Signed-off-by: Stefan Schmidt --- linux/tests/CMakeLists.txt | 23 ++++++++++++++++++++++- tests/fixture-start.sh | 7 +++++++ tests/fixture-stop.sh | 5 +++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100755 tests/fixture-start.sh create mode 100755 tests/fixture-stop.sh diff --git a/linux/tests/CMakeLists.txt b/linux/tests/CMakeLists.txt index 1e6029b..5c4634d 100644 --- a/linux/tests/CMakeLists.txt +++ b/linux/tests/CMakeLists.txt @@ -11,4 +11,25 @@ target_link_libraries( ) include(GoogleTest) -gtest_discover_tests(communication_tests) +gtest_discover_tests(communication_tests PROPERTIES FIXTURES_REQUIRED "eddie-server") + +add_test(NAME Fixture.Eddie_Server_Setup + COMMAND ${CMAKE_SOURCE_DIR}/tests/fixture-start.sh ./eddie-server) +set_tests_properties(Fixture.Eddie_Server_Setup + PROPERTIES WORKING_DIRECTORY . + FIXTURES_SETUP eddie-server) + +add_test(NAME Fixture.Eddie_Server_Teardown + COMMAND ${CMAKE_SOURCE_DIR}/tests/fixture-stop.sh eddie-server) +set_tests_properties(Fixture.Eddie_Server_Teardown + PROPERTIES WORKING_DIRECTORY . + FIXTURES_CLEANUP eddie-server) + +add_test(NAME eddie-client COMMAND ./eddie-client) +set_tests_properties(eddie-client PROPERTIES WORKING_DIRECTORY . FIXTURES_REQUIRED "eddie-server") + +#set_tests_properties(communication_tests PROPERTIES WORKING_DIRECTORY . FIXTURES_REQUIRED "eddie-server") + +#add_test(NAME eddie-node COMMAND ./eddie-node) +#set_tests_properties(eddie-node PROPERTIES WORKING_DIRECTORY .) +#set_tests_properties(eddie-node PROPERTIES WORKING_DIRECTORY . FIXTURES_REQUIRED "eddie-server" TIMEOUT 5) diff --git a/tests/fixture-start.sh b/tests/fixture-start.sh new file mode 100755 index 0000000..0c3dda5 --- /dev/null +++ b/tests/fixture-start.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +$1 2>/dev/null 1>/dev/null 0 fixture.pid +# Allow server to fully start before lauching clients with ctest +sleep 1 +exit 0 diff --git a/tests/fixture-stop.sh b/tests/fixture-stop.sh new file mode 100755 index 0000000..d3a551e --- /dev/null +++ b/tests/fixture-stop.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +kill $(cat fixture.pid) +rm fixture.pid +exit 0 -- GitLab From 1452029f17c7b638a22852f61b21232890332eb4 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Fri, 5 Aug 2022 15:32:01 +0200 Subject: [PATCH 22/24] linux/tests: fix fixture to run eddie-endpoint instead of eddie-server - add some tests - wait 5 seconds instead of 1 second to start server before running tests Signed-off-by: Francesco Pham --- linux/tests/CMakeLists.txt | 19 +++++-------------- linux/tests/communication_tests.cpp | 17 +++++++++++++++-- tests/fixture-start.sh | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/linux/tests/CMakeLists.txt b/linux/tests/CMakeLists.txt index 5c4634d..8b9a5cd 100644 --- a/linux/tests/CMakeLists.txt +++ b/linux/tests/CMakeLists.txt @@ -11,25 +11,16 @@ target_link_libraries( ) include(GoogleTest) -gtest_discover_tests(communication_tests PROPERTIES FIXTURES_REQUIRED "eddie-server") +gtest_discover_tests(communication_tests PROPERTIES FIXTURES_REQUIRED "eddie-endpoint") add_test(NAME Fixture.Eddie_Server_Setup - COMMAND ${CMAKE_SOURCE_DIR}/tests/fixture-start.sh ./eddie-server) + COMMAND ${CMAKE_SOURCE_DIR}/tests/fixture-start.sh ./linux/eddie-endpoint) set_tests_properties(Fixture.Eddie_Server_Setup PROPERTIES WORKING_DIRECTORY . - FIXTURES_SETUP eddie-server) + FIXTURES_SETUP eddie-endpoint) add_test(NAME Fixture.Eddie_Server_Teardown - COMMAND ${CMAKE_SOURCE_DIR}/tests/fixture-stop.sh eddie-server) + COMMAND ${CMAKE_SOURCE_DIR}/tests/fixture-stop.sh ./linux/eddie-endpoint) set_tests_properties(Fixture.Eddie_Server_Teardown PROPERTIES WORKING_DIRECTORY . - FIXTURES_CLEANUP eddie-server) - -add_test(NAME eddie-client COMMAND ./eddie-client) -set_tests_properties(eddie-client PROPERTIES WORKING_DIRECTORY . FIXTURES_REQUIRED "eddie-server") - -#set_tests_properties(communication_tests PROPERTIES WORKING_DIRECTORY . FIXTURES_REQUIRED "eddie-server") - -#add_test(NAME eddie-node COMMAND ./eddie-node) -#set_tests_properties(eddie-node PROPERTIES WORKING_DIRECTORY .) -#set_tests_properties(eddie-node PROPERTIES WORKING_DIRECTORY . FIXTURES_REQUIRED "eddie-server" TIMEOUT 5) + FIXTURES_CLEANUP eddie-endpoint) diff --git a/linux/tests/communication_tests.cpp b/linux/tests/communication_tests.cpp index b689e99..f497e2d 100644 --- a/linux/tests/communication_tests.cpp +++ b/linux/tests/communication_tests.cpp @@ -14,7 +14,7 @@ class EddieLamp : public EddieResource { private: - const char * const core_path[2] = { "linux-lamp", nullptr }; + const char * const core_path[2] = { "test-lamp", nullptr }; const char * const core_attributes[3] = { "rt=eddie.lamp", "ct=40", nullptr }; int lamp_status = 0; @@ -116,5 +116,18 @@ TEST(Communication, Server_Resource) { } TEST(Communication, Endpoint_Init) { - EddieEndpoint endpoint(); + EddieEndpoint endpoint("5684"); } + +TEST(Communication, Rd_Discovery) { + EddieEndpoint endpoint("5684"); + EXPECT_EQ(endpoint.discover_rd(), 0); +} + +TEST(Communication, Get_Resources_From_Rd) { + EddieEndpoint endpoint("5684"); + + EXPECT_EQ(endpoint.discover_rd(), 0); + std::string res = endpoint.get_resources_from_rd(); + EXPECT_EQ(res.length(), 0); +} \ No newline at end of file diff --git a/tests/fixture-start.sh b/tests/fixture-start.sh index 0c3dda5..bb6870e 100755 --- a/tests/fixture-start.sh +++ b/tests/fixture-start.sh @@ -3,5 +3,5 @@ $1 2>/dev/null 1>/dev/null 0 fixture.pid # Allow server to fully start before lauching clients with ctest -sleep 1 +sleep 5 exit 0 -- GitLab From 5b79b1de8e278c30bdbf8e9835fdda23da47cedc Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Fri, 5 Aug 2022 16:00:35 +0200 Subject: [PATCH 23/24] linux/communication_tests: add resource publishing and discovery tests Signed-off-by: Francesco Pham --- linux/communication/src/CoapServer.cpp | 8 +++----- linux/communication/src/EddieEndpoint.cpp | 3 +-- linux/tests/communication_tests.cpp | 23 ++++++++++++++++++++++- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index d43071c..e0f21a6 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -118,10 +118,8 @@ CoapServer::CoapServer(const char *host, const char *port, int is_resource_dir) } CoapServer::~CoapServer() { - if (!this->is_rd()) - unpublish_resources(); - else - delete this->resource_directory; + unpublish_resources(); + delete this->resource_directory; resources->delete_all(); delete resources; @@ -343,7 +341,7 @@ int CoapServer::unpublish_resources() { } if (rd_endpoint.empty()) { - LOG_ERR("rd_endpoint is empty"); + LOG_ERR("rd_endpoint is empty, nothing to unpublish"); return -1; } diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp index 0a81e13..a6089cf 100644 --- a/linux/communication/src/EddieEndpoint.cpp +++ b/linux/communication/src/EddieEndpoint.cpp @@ -83,8 +83,7 @@ std::string EddieEndpoint::get_resources_from_rd() { int EddieEndpoint::add_resource(EddieResource *resource) { LOG_DBG("Adding resource: %s", resource->get_path_str().c_str()); - get_server()->add_resource(resource); - return 0; + return get_server()->add_resource(resource); } int EddieEndpoint::start_server() { diff --git a/linux/tests/communication_tests.cpp b/linux/tests/communication_tests.cpp index f497e2d..6ac1683 100644 --- a/linux/tests/communication_tests.cpp +++ b/linux/tests/communication_tests.cpp @@ -11,6 +11,7 @@ #include "CoapClient.h" #include "CoapServer.h" #include "EddieEndpoint.h" +#include "LinkFormat.h" class EddieLamp : public EddieResource { private: @@ -112,7 +113,7 @@ TEST(Communication, Server_Resource) { CoapServer server = CoapServer("0.0.0.0", "5683", 1); EddieLamp lamp_resource; - server.add_resource(&lamp_resource); + EXPECT_EQ(server.add_resource(&lamp_resource), 0); } TEST(Communication, Endpoint_Init) { @@ -130,4 +131,24 @@ TEST(Communication, Get_Resources_From_Rd) { EXPECT_EQ(endpoint.discover_rd(), 0); std::string res = endpoint.get_resources_from_rd(); EXPECT_EQ(res.length(), 0); +} + +TEST(Communication, Publish_And_Get_Resources) { + EddieEndpoint endpoint("5684"); + + EXPECT_EQ(endpoint.discover_rd(), 0); + + EddieLamp lamp_resource; + EXPECT_EQ(endpoint.add_resource(&lamp_resource), 0); + EXPECT_EQ(endpoint.publish_resources(), 0); + + std::string res = endpoint.get_resources_from_rd(); + EXPECT_GT(res.length(), 0); + + auto parsed = parse_link_format(res); + EXPECT_GT(parsed.size(), 0); + + auto first_link = parsed.begin()->second; + std::string rt = first_link["rt"]; + EXPECT_EQ(rt, "eddie.lamp"); } \ No newline at end of file -- GitLab From 1f8444ffe8f6b0ddd90bd2ac933039debe54a030 Mon Sep 17 00:00:00 2001 From: Francesco Pham Date: Fri, 5 Aug 2022 16:23:24 +0200 Subject: [PATCH 24/24] linux/Virtualization: fix GVariant format string - add discover_rd() call - do not initialize resource directory when already initialized Signed-off-by: Francesco Pham --- linux/communication/src/CoapServer.cpp | 4 ++++ linux/virtualization/src/VirtualizationReceiver.cpp | 2 ++ linux/virtualization/src/VirtualizationSender.cpp | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index e0f21a6..2b5c700 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -364,6 +364,10 @@ int CoapServer::unpublish_resources() { void CoapServer::init_resource_dir_local() { LOG_DBG("Initializing local resource directory"); + if (this->resource_directory) { + LOG_ERR("local resource directory already initialized"); + return; + } this->resource_directory = new ResourceDirectory(my_ip, this->context); } diff --git a/linux/virtualization/src/VirtualizationReceiver.cpp b/linux/virtualization/src/VirtualizationReceiver.cpp index fa380c0..3b2af12 100644 --- a/linux/virtualization/src/VirtualizationReceiver.cpp +++ b/linux/virtualization/src/VirtualizationReceiver.cpp @@ -219,6 +219,7 @@ VirtualizationReceiver::VirtualizationReceiver() { LOG_ERR("\teddie_endpoint is null\n"); exit(-1); } + eddie_endpoint->discover_rd(); } void VirtualizationReceiver::update_resources() { @@ -256,6 +257,7 @@ int VirtualizationReceiver::run(std::vector uris, std::vectoradd_resource(new_resource); + VirtualizationReceiver::eddie_endpoint->publish_resources(); } guint owner_id; diff --git a/linux/virtualization/src/VirtualizationSender.cpp b/linux/virtualization/src/VirtualizationSender.cpp index accc572..da170aa 100644 --- a/linux/virtualization/src/VirtualizationSender.cpp +++ b/linux/virtualization/src/VirtualizationSender.cpp @@ -135,7 +135,7 @@ const gchar *send_message(const std::unordered_map &pa g_variant_builder_add(builder, "{ss}", pair.first.c_str(), pair.second.c_str()); } - GVariant *value = g_variant_new("(sa{ss}s", method->second.c_str(), builder, payload->second.c_str()); + GVariant *value = g_variant_new("(sa{ss}s)", method->second.c_str(), builder, payload->second.c_str()); GDBusMessage *method_call_message; GDBusMessage *method_reply_message; -- GitLab