From 9e12a64e956da2729dfab4c5ab3ec8069db1352b Mon Sep 17 00:00:00 2001 From: Rui Rocha <rui.rocha@ctw.bmwgroup.com> Date: Mon, 27 Jan 2025 12:31:09 +0000 Subject: [PATCH] feat: better structure --- .devcontainer/devcontainer.json | 3 +- com-middleware/BUILD | 7 +-- com-middleware/{ => src}/can/BUILD | 6 +-- com-middleware/{ => src}/can/CanDriver.cpp | 19 +++++-- com-middleware/{ => src}/can/CanDriver.hpp | 17 +++++-- com-middleware/src/can/ICanDriver.hpp | 25 +++++++++ com-middleware/{ => src}/exceptions/BUILD | 0 .../exceptions/CanCloseException.hpp | 2 +- .../{ => src}/exceptions/CanInitException.hpp | 2 +- com-middleware/{ => src}/main.cpp | 51 ++++++++++--------- com-middleware/src/mq/BUILD | 10 ++++ com-middleware/src/mq/IMQPublisher.hpp | 28 ++++++++++ com-middleware/src/mq/IMQSubscriber.hpp | 27 ++++++++++ com-middleware/src/mq/ZeroMQPublisher.cpp | 29 +++++++++++ com-middleware/src/mq/ZeroMQPublisher.hpp | 34 +++++++++++++ com-middleware/src/mq/ZeroMQSubscriber.cpp | 19 +++++++ com-middleware/src/mq/ZeroMQSubscriber.hpp | 35 +++++++++++++ 17 files changed, 271 insertions(+), 43 deletions(-) rename com-middleware/{ => src}/can/BUILD (53%) rename com-middleware/{ => src}/can/CanDriver.cpp (79%) rename com-middleware/{ => src}/can/CanDriver.hpp (59%) create mode 100644 com-middleware/src/can/ICanDriver.hpp rename com-middleware/{ => src}/exceptions/BUILD (100%) rename com-middleware/{ => src}/exceptions/CanCloseException.hpp (92%) rename com-middleware/{ => src}/exceptions/CanInitException.hpp (93%) rename com-middleware/{ => src}/main.cpp (65%) create mode 100644 com-middleware/src/mq/BUILD create mode 100644 com-middleware/src/mq/IMQPublisher.hpp create mode 100644 com-middleware/src/mq/IMQSubscriber.hpp create mode 100644 com-middleware/src/mq/ZeroMQPublisher.cpp create mode 100644 com-middleware/src/mq/ZeroMQPublisher.hpp create mode 100644 com-middleware/src/mq/ZeroMQSubscriber.cpp create mode 100644 com-middleware/src/mq/ZeroMQSubscriber.hpp diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6331d00b..f9647189 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -19,7 +19,8 @@ "xaver.clang-format", "BazelBuild.vscode-bazel", "llvm-vs-code-extensions.vscode-clangd", - "ms-azuretools.vscode-docker" + "ms-azuretools.vscode-docker", + "cschlosser.doxdocgen" ], "settings": { "remote.autoForwardPorts": false diff --git a/com-middleware/BUILD b/com-middleware/BUILD index d0f61154..8106915e 100644 --- a/com-middleware/BUILD +++ b/com-middleware/BUILD @@ -1,13 +1,14 @@ cc_binary( name = "bin", - srcs = ["main.cpp"], + srcs = ["src/main.cpp"], features = ["warnings_critical_code_gcc"], linkopts = [ "-lzmq", ], deps = [ - "//com-middleware/can", - "//com-middleware/exceptions", + "//com-middleware/src/can", + "//com-middleware/src/exceptions", + "//com-middleware/src/mq", "@cppzmq", ], ) diff --git a/com-middleware/can/BUILD b/com-middleware/src/can/BUILD similarity index 53% rename from com-middleware/can/BUILD rename to com-middleware/src/can/BUILD index e80a6630..e542ac06 100644 --- a/com-middleware/can/BUILD +++ b/com-middleware/src/can/BUILD @@ -1,8 +1,8 @@ cc_library( name = "can", - srcs = ["CanDriver.cpp"], - hdrs = ["CanDriver.hpp"], + srcs = glob(["*.cpp"]), + hdrs = glob(["*.hpp"]), features = ["warnings_critical_code_gcc"], visibility = ["//visibility:public"], - deps = ["//com-middleware/exceptions"], + deps = ["//com-middleware/src/exceptions"], ) diff --git a/com-middleware/can/CanDriver.cpp b/com-middleware/src/can/CanDriver.cpp similarity index 79% rename from com-middleware/can/CanDriver.cpp rename to com-middleware/src/can/CanDriver.cpp index 3404f5bc..60726ba2 100644 --- a/com-middleware/can/CanDriver.cpp +++ b/com-middleware/src/can/CanDriver.cpp @@ -9,8 +9,8 @@ #include <cstring> #include <iostream> -#include "com-middleware/exceptions/CanCloseException.hpp" -#include "com-middleware/exceptions/CanInitException.hpp" +#include "com-middleware/src/exceptions/CanCloseException.hpp" +#include "com-middleware/src/exceptions/CanInitException.hpp" namespace candriver { @@ -19,6 +19,14 @@ CanDriver::CanDriver(std::string can_interface, int32_t timeout_s) initializeCan(); }; +CanDriver::~CanDriver() { + try { + uninitializeCan(); + } catch (const std::exception &exception) { + std::cout << exception.what() << "\n"; + } +}; + void CanDriver::initializeCan() { if (m_canInterface.empty()) { throw exceptions::CanInitException("Can interface is empty"); @@ -38,6 +46,7 @@ void CanDriver::initializeCan() { // Setting the can interface strcpy(ifr.ifr_name, m_canInterface.c_str()); // To determine the interface index an appropriate ioctl() has to be used (0 for all) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) ioctl(m_can_socket_fd, SIOCGIFINDEX, &ifr); memset(&addr, 0, sizeof(addr)); @@ -63,7 +72,7 @@ void CanDriver::uninitializeCan() const { } } -auto CanDriver::readMessage(void *buffer) const -> int32_t { - return static_cast<int32_t>(read(m_can_socket_fd, buffer, sizeof(struct can_frame))); +auto CanDriver::receive(can_frame *frame) const -> int32_t { + return static_cast<int32_t>(::read(m_can_socket_fd, frame, sizeof(can_frame))); } -} // namespace candriver \ No newline at end of file +} // namespace candriver diff --git a/com-middleware/can/CanDriver.hpp b/com-middleware/src/can/CanDriver.hpp similarity index 59% rename from com-middleware/can/CanDriver.hpp rename to com-middleware/src/can/CanDriver.hpp index a4931314..0380a236 100644 --- a/com-middleware/can/CanDriver.hpp +++ b/com-middleware/src/can/CanDriver.hpp @@ -1,11 +1,15 @@ #ifndef CAN_DRIVER_HPP #define CAN_DRIVER_HPP +#include <linux/can.h> + #include <string> +#include "ICanDriver.hpp" + namespace candriver { -class CanDriver { +class CanDriver : public ICanDriver { private: std::string m_canInterface; int32_t m_timeout_s; @@ -13,7 +17,12 @@ class CanDriver { public: CanDriver(std::string can_interface, int32_t timeout_s); - virtual ~CanDriver() noexcept(false) { uninitializeCan(); } + ~CanDriver() override; + CanDriver() = delete; + CanDriver(const CanDriver&) = default; + auto operator=(const CanDriver&) -> CanDriver& = default; + CanDriver(CanDriver&&) = default; + auto operator=(CanDriver&&) -> CanDriver& = default; private: /** @@ -28,8 +37,8 @@ class CanDriver { void uninitializeCan() const; public: - auto readMessage(void* buffer) const -> int32_t; + auto receive(can_frame* frame) const -> int32_t override; }; } // namespace candriver -#endif \ No newline at end of file +#endif diff --git a/com-middleware/src/can/ICanDriver.hpp b/com-middleware/src/can/ICanDriver.hpp new file mode 100644 index 00000000..6a286ddf --- /dev/null +++ b/com-middleware/src/can/ICanDriver.hpp @@ -0,0 +1,25 @@ +#include <linux/can.h> + +#include <cstdint> + +/** + * @brief Asbtracts the Linux CAN Socket logic + * + */ +class ICanDriver { + public: + ICanDriver() = default; + ICanDriver(const ICanDriver&) = default; + auto operator=(const ICanDriver&) -> ICanDriver& = default; + ICanDriver(ICanDriver&&) = default; + auto operator=(ICanDriver&&) -> ICanDriver& = default; + virtual ~ICanDriver() = default; + + /** + * @brief Receives CAN message + * + * @param frame CAN frame + * @return int32_t number of bytes received + */ + virtual auto receive(can_frame* frame) const -> int32_t = 0; +}; diff --git a/com-middleware/exceptions/BUILD b/com-middleware/src/exceptions/BUILD similarity index 100% rename from com-middleware/exceptions/BUILD rename to com-middleware/src/exceptions/BUILD diff --git a/com-middleware/exceptions/CanCloseException.hpp b/com-middleware/src/exceptions/CanCloseException.hpp similarity index 92% rename from com-middleware/exceptions/CanCloseException.hpp rename to com-middleware/src/exceptions/CanCloseException.hpp index 432d5b8a..74b80dd8 100644 --- a/com-middleware/exceptions/CanCloseException.hpp +++ b/com-middleware/src/exceptions/CanCloseException.hpp @@ -25,4 +25,4 @@ class CanCloseException : public exception { } // namespace candriver::exceptions -#endif // CAN_DRIVER_EXCEPTIONS_CANCLOSEEXCEPTION_HPP \ No newline at end of file +#endif // CAN_DRIVER_EXCEPTIONS_CANCLOSEEXCEPTION_HPP diff --git a/com-middleware/exceptions/CanInitException.hpp b/com-middleware/src/exceptions/CanInitException.hpp similarity index 93% rename from com-middleware/exceptions/CanInitException.hpp rename to com-middleware/src/exceptions/CanInitException.hpp index 04025a96..52d67edc 100644 --- a/com-middleware/exceptions/CanInitException.hpp +++ b/com-middleware/src/exceptions/CanInitException.hpp @@ -25,4 +25,4 @@ class CanInitException : public exception { } // namespace candriver::exceptions -#endif // CAN_DRIVER_EXCEPTIONS_CANINITEXCEPTION_HPP \ No newline at end of file +#endif // CAN_DRIVER_EXCEPTIONS_CANINITEXCEPTION_HPP diff --git a/com-middleware/main.cpp b/com-middleware/src/main.cpp similarity index 65% rename from com-middleware/main.cpp rename to com-middleware/src/main.cpp index 2c8dfe7a..abc17a18 100644 --- a/com-middleware/main.cpp +++ b/com-middleware/src/main.cpp @@ -14,28 +14,30 @@ #include <zmq.hpp> #include "can/CanDriver.hpp" +#include "mq/ZeroMQPublisher.hpp" namespace { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) volatile std::sig_atomic_t interrupted{0}; } // namespace -void signal_handler(int signum) { +void signalHandler(int signum) { (void)signum; // ignore unused variable interrupted = 1; } -void catch_signals() { - (void)std::signal(SIGINT, signal_handler); - (void)std::signal(SIGTERM, signal_handler); - (void)std::signal(SIGSEGV, signal_handler); - (void)std::signal(SIGABRT, signal_handler); +void catchSignals() { + (void)std::signal(SIGINT, signalHandler); + (void)std::signal(SIGTERM, signalHandler); + (void)std::signal(SIGSEGV, signalHandler); + (void)std::signal(SIGABRT, signalHandler); } auto main(int argc, char **argv) -> int { int opt{}; std::string interface = "can0"; + // NOLINTNEXTLINE(concurrency-mt-unsafe) while ((opt = getopt(argc, argv, "i:")) != -1) { // "i:" means -i takes an argument switch (opt) { case 'i': @@ -52,25 +54,28 @@ auto main(int argc, char **argv) -> int { std::unique_ptr<candriver::CanDriver> can; try { can = std::make_unique<candriver::CanDriver>(interface, 5); - } catch (const std::exception &myCustomException) { - std::cout << myCustomException.what() << std::endl; + } catch (const std::exception &exception) { + std::cout << exception.what() << "\n"; return 1; } struct can_frame frame {}; // Classical can frame - int32_t numBytes{}; + int32_t num_bytes{}; + // Connect to zmq - zmq::context_t ctx(1); - zmq::socket_t publisher(ctx, zmq::socket_type::pub); - publisher.bind("ipc:///tmp/speed.ipc"); + zmq::context_t context(1); + std::unique_ptr<zmq::socket_t> pub_socket = + std::make_unique<zmq::socket_t>(context, zmq::socket_type::pub); + mq::ZeroMQPublisher publisher{std::move(pub_socket)}; + publisher.subscribe("ipc:///tmp/speed.ipc"); - catch_signals(); + catchSignals(); while (true) { - numBytes = can->readMessage(&frame); + num_bytes = can->receive(&frame); - if (numBytes < 0) { + if (num_bytes < 0) { if (errno == EINTR) { - std::cout << "interrupt received, exiting gracefully..." << std::endl; + std::cout << "interrupt received, exiting gracefully..." << "\n"; break; } if (errno != EAGAIN) { @@ -81,19 +86,21 @@ auto main(int argc, char **argv) -> int { // The len element contains the payload length in bytes and should be used instead of // can_dlc + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) printf("0x%03X [%d] ", frame.can_id, frame.len); for (int i = 0; i < frame.len; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) std::cout << printf("%02X ", frame.data[i]); } std::cout << "\n"; - zmq::message_t msg{static_cast<size_t>(numBytes)}; - memcpy(msg.data(), frame.data, static_cast<size_t>(numBytes)); + std::vector<uint8_t> message(frame.len); + memcpy(message.data(), frame.data, static_cast<size_t>(num_bytes)); try { - publisher.send(msg, zmq::send_flags::none); + publisher.publish(message); } catch (zmq::error_t &e) { std::cout << "interrupt received, proceeding...\n"; } @@ -104,11 +111,5 @@ auto main(int argc, char **argv) -> int { } } - // Closing ZeroMQ - std::cout << "Closing publisher\n"; - publisher.close(); - std::cout << "Closing context\n"; - ctx.close(); - return 0; } diff --git a/com-middleware/src/mq/BUILD b/com-middleware/src/mq/BUILD new file mode 100644 index 00000000..1bc3a734 --- /dev/null +++ b/com-middleware/src/mq/BUILD @@ -0,0 +1,10 @@ +cc_library( + name = "mq", + srcs = glob(["*.cpp"]), + hdrs = glob(["*.hpp"]), + features = ["warnings_critical_code_gcc"], + visibility = ["//visibility:public"], + deps = [ + "@cppzmq", + ], +) diff --git a/com-middleware/src/mq/IMQPublisher.hpp b/com-middleware/src/mq/IMQPublisher.hpp new file mode 100644 index 00000000..d01fe15d --- /dev/null +++ b/com-middleware/src/mq/IMQPublisher.hpp @@ -0,0 +1,28 @@ +#ifndef MQ_PUBLISHER_HPP +#define MQ_PUBLISHER_HPP + +#include <cstdint> +#include <string> +#include <vector> + +namespace mq { + +class IMQPublisher { + protected: + // Constructors + IMQPublisher() = default; + IMQPublisher(const IMQPublisher&) = default; + IMQPublisher(IMQPublisher&&) = default; + + // Asignment operators + auto operator=(const IMQPublisher&) -> IMQPublisher& = default; + auto operator=(IMQPublisher&&) -> IMQPublisher& = default; + + public: + virtual ~IMQPublisher() = default; + virtual void subscribe(const std::string& topic) = 0; + virtual auto publish(const std::vector<uint8_t>& data) -> bool = 0; +}; +} // namespace mq + +#endif diff --git a/com-middleware/src/mq/IMQSubscriber.hpp b/com-middleware/src/mq/IMQSubscriber.hpp new file mode 100644 index 00000000..37bf3508 --- /dev/null +++ b/com-middleware/src/mq/IMQSubscriber.hpp @@ -0,0 +1,27 @@ +#ifndef MQ_SUBSCRIBER_HPP +#define MQ_SUBSCRIBER_HPP + +#include <string> +#include <vector> + +namespace mq { + +class IMQSubscriber { + protected: + // Constructors + IMQSubscriber() = default; + IMQSubscriber(const IMQSubscriber&) = default; + IMQSubscriber(IMQSubscriber&&) = default; + + // Asignment operators + auto operator=(const IMQSubscriber&) -> IMQSubscriber& = default; + auto operator=(IMQSubscriber&&) -> IMQSubscriber& = default; + + public: + virtual ~IMQSubscriber() = 0; + virtual void subscribe(const std::string& topic) = 0; + virtual auto receive() -> std::vector<uint8_t> = 0; +}; +} // namespace mq + +#endif diff --git a/com-middleware/src/mq/ZeroMQPublisher.cpp b/com-middleware/src/mq/ZeroMQPublisher.cpp new file mode 100644 index 00000000..83b215a0 --- /dev/null +++ b/com-middleware/src/mq/ZeroMQPublisher.cpp @@ -0,0 +1,29 @@ +#include "ZeroMQPublisher.hpp" + +#include <iostream> + +namespace mq { +ZeroMQPublisher::ZeroMQPublisher(std::unique_ptr<zmq::socket_t> socket) + : m_socket(std::move(socket)) {}; + +ZeroMQPublisher::~ZeroMQPublisher() { + if (m_socket) { + std::cout << "Closing publisher\n"; + m_socket->close(); + } +} + +void ZeroMQPublisher::subscribe(const std::string& topic) { m_socket->bind(topic); } + +auto ZeroMQPublisher::publish(const std::vector<uint8_t>& data) -> bool { + zmq::message_t msg(data.size()); + memcpy(msg.data(), data.data(), data.size()); + std::cout << m_socket.get() << "\n"; + zmq::send_result_t res = m_socket->send(msg, zmq::send_flags::none); + if (res.has_value()) { + std::cout << "!sent\n"; + return true; + } + return false; +} +} // namespace mq diff --git a/com-middleware/src/mq/ZeroMQPublisher.hpp b/com-middleware/src/mq/ZeroMQPublisher.hpp new file mode 100644 index 00000000..455eb3bc --- /dev/null +++ b/com-middleware/src/mq/ZeroMQPublisher.hpp @@ -0,0 +1,34 @@ +#ifndef ZERO_MQ_PUBLISHER_HPP +#define ZERO_MQ_PUBLISHER_HPP + +#include <memory> +#include <string> +#include <zmq.hpp> + +#include "IMQPublisher.hpp" + +namespace mq { + +class ZeroMQPublisher : public IMQPublisher { + protected: + // Constructors + ZeroMQPublisher() = default; + ZeroMQPublisher(ZeroMQPublisher&&) = default; + + // Asignment operators + auto operator=(ZeroMQPublisher&&) -> ZeroMQPublisher& = default; + + public: + virtual ~ZeroMQPublisher() override; + explicit ZeroMQPublisher(std::unique_ptr<zmq::socket_t> socket); + ZeroMQPublisher(const ZeroMQPublisher&) = delete; + auto operator=(const ZeroMQPublisher&) -> ZeroMQPublisher& = delete; + void subscribe(const std::string& topic) override; + auto publish(const std::vector<uint8_t>& data) -> bool override; + + private: + std::unique_ptr<zmq::socket_t> m_socket; +}; +} // namespace mq + +#endif diff --git a/com-middleware/src/mq/ZeroMQSubscriber.cpp b/com-middleware/src/mq/ZeroMQSubscriber.cpp new file mode 100644 index 00000000..457ee2a9 --- /dev/null +++ b/com-middleware/src/mq/ZeroMQSubscriber.cpp @@ -0,0 +1,19 @@ +#include "ZeroMQSubscriber.hpp" + +namespace mq { +ZeroMQSubscriber::ZeroMQSubscriber(const std::shared_ptr<zmq::context_t>& context) + : m_context(context), m_socket(*context, zmq::socket_type::sub) {}; + +void ZeroMQSubscriber::subscribe(const std::string& topic) { m_socket.bind(topic); } + +auto ZeroMQSubscriber::receive() -> std::vector<uint8_t> { + zmq::message_t msg; + if (!m_socket.recv(msg, zmq::recv_flags::none)) { + return {}; + } + + std::vector<uint8_t> data(msg.size()); + memcpy(data.data(), msg.data(), msg.size()); + return data; +} +} // namespace mq diff --git a/com-middleware/src/mq/ZeroMQSubscriber.hpp b/com-middleware/src/mq/ZeroMQSubscriber.hpp new file mode 100644 index 00000000..6c28bc9a --- /dev/null +++ b/com-middleware/src/mq/ZeroMQSubscriber.hpp @@ -0,0 +1,35 @@ +#ifndef ZERO_MQ_SUBSCRIBER_HPP +#define ZERO_MQ_SUBSCRIBER_HPP + +#include <memory> +#include <string> +#include <zmq.hpp> + +#include "IMQSubscriber.hpp" + +namespace mq { + +class ZeroMQSubscriber : public IMQSubscriber { + protected: + // Constructors + ZeroMQSubscriber() = default; + ZeroMQSubscriber(ZeroMQSubscriber&&) = default; + + // Asignment operators + auto operator=(ZeroMQSubscriber&&) -> ZeroMQSubscriber& = default; + + public: + ~ZeroMQSubscriber(); + explicit ZeroMQSubscriber(const std::shared_ptr<zmq::context_t>& context); + ZeroMQSubscriber(const ZeroMQSubscriber&) = delete; + auto operator=(const ZeroMQSubscriber&) -> ZeroMQSubscriber& = delete; + void subscribe(const std::string& topic) override; + auto receive() -> std::vector<uint8_t> override; + + private: + std::shared_ptr<zmq::context_t> m_context; + zmq::socket_t m_socket; +}; +} // namespace mq + +#endif