From e190b7b3069d86fce25831c366bb0cd3fa615289 Mon Sep 17 00:00:00 2001 From: Romain Forlot Date: Mon, 10 Dec 2018 18:08:47 +0100 Subject: Simpler handling of binding subscriptions Don't use a child class for subscription, only use one because there isn't other different subscriptions type to be implemented about now and if so then we could split again. Remove functions no longer useful and move afb events and afb subscriptions part to low-can-subscription Change-Id: Ie3e4255961ac557465098cdb48730098a950461a Signed-off-by: Romain Forlot --- low-can-binding/binding/application.cpp | 6 +- low-can-binding/binding/application.hpp | 4 +- low-can-binding/binding/low-can-cb.cpp | 85 +++--- low-can-binding/binding/low-can-socket.cpp | 336 --------------------- low-can-binding/binding/low-can-socket.hpp | 96 ------ low-can-binding/binding/low-can-subscription.cpp | 365 ++++++++++++++++++++++- low-can-binding/binding/low-can-subscription.hpp | 75 ++++- 7 files changed, 474 insertions(+), 493 deletions(-) delete mode 100644 low-can-binding/binding/low-can-socket.cpp delete mode 100644 low-can-binding/binding/low-can-socket.hpp (limited to 'low-can-binding/binding') diff --git a/low-can-binding/binding/application.cpp b/low-can-binding/binding/application.cpp index 153821c7..a85b5184 100644 --- a/low-can-binding/binding/application.cpp +++ b/low-can-binding/binding/application.cpp @@ -20,7 +20,7 @@ #include "../utils/signals.hpp" #include "../utils/openxc-utils.hpp" -#include "low-can-socket.hpp" +#include "low-can-subscription.hpp" #define MICROSECONDS_IN_SECOND 1000000 #define ENGINE_VALUE_TIMEOUT 5 @@ -37,7 +37,7 @@ can_bus_t& application_t::get_can_bus_manager() return can_bus_manager_; } -std::map >& application_t::get_can_devices() +std::map >& application_t::get_can_devices() { return can_devices_; } @@ -136,4 +136,4 @@ bool application_t::isEngineOn() } return engine_on; -} \ No newline at end of file +} diff --git a/low-can-binding/binding/application.hpp b/low-can-binding/binding/application.hpp index 92aca382..e017685b 100644 --- a/low-can-binding/binding/application.hpp +++ b/low-can-binding/binding/application.hpp @@ -47,7 +47,7 @@ class application_t std::vector > can_message_set_; ///< Vector holding all message set from JSON signals description file - std::map > can_devices_; ///< Map containing all independant opened CAN sockets, key is the socket int value. + std::map > can_devices_; ///< Map containing all independant opened CAN sockets, key is the socket int value. application_t(); ///< Private constructor with implementation generated by the AGL generator. @@ -56,7 +56,7 @@ class application_t can_bus_t& get_can_bus_manager(); - std::map >& get_can_devices(); + std::map >& get_can_devices(); const std::string get_diagnostic_bus() const; diff --git a/low-can-binding/binding/low-can-cb.cpp b/low-can-binding/binding/low-can-cb.cpp index d25f0683..daeb5a0b 100644 --- a/low-can-binding/binding/low-can-cb.cpp +++ b/low-can-binding/binding/low-can-cb.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015, 2016 "IoT.bzh" + * Copyright (C) 2015, 2018 "IoT.bzh" * Author "Romain Forlot" * Author "Loic Collignon" * @@ -109,35 +109,6 @@ int read_message(sd_event_source *event_source, int fd, uint32_t revents, void * /// ///*******************************************************************************/ -static int make_subscription_unsubscription(afb_req_t request, - std::shared_ptr& can_subscription, - std::map >& s, - bool subscribe) -{ - /* Make the subscription or unsubscription to the event (if request is not null) */ - if(request && - ((subscribe ? afb_req_subscribe : afb_req_unsubscribe)(request, s[can_subscription->get_index()]->get_event())) < 0) - { - AFB_ERROR("Operation goes wrong for signal: %s", can_subscription->get_name().c_str()); - return -1; - } - return 0; -} - -static int create_event_handle(std::shared_ptr& can_subscription, - std::map >& s) -{ - int sub_index = can_subscription->get_index(); - can_subscription->set_event(afb_daemon_make_event(can_subscription->get_name().c_str())); - s[sub_index] = can_subscription; - if (!afb_event_is_valid(s[sub_index]->get_event())) - { - AFB_ERROR("Can't create an event for %s, something goes wrong.", can_subscription->get_name().c_str()); - return -1; - } - return 0; -} - /// @brief This will determine if an event handle needs to be created and checks if /// we got a valid afb_event to get subscribe or unsubscribe. After that launch the subscription or unsubscription /// against the application framework using that event handle. @@ -146,30 +117,52 @@ static int subscribe_unsubscribe_signal(afb_req_t request, std::shared_ptr& can_subscription, std::map >& s) { - int ret = -1; + int ret = 0; int sub_index = can_subscription->get_index(); + bool subscription_exists = s.count(sub_index); - if (can_subscription && s.find(sub_index) != s.end()) + // Susbcription part + if(subscribe) { - if (!afb_event_is_valid(s[sub_index]->get_event()) && !subscribe) + /* There is no valid request to subscribe so this must be an + * internal permanent diagnostic request. Skip the subscription + * part and don't register it into the current "low-can" + * subsciptions. + */ + if(! request) { - AFB_NOTICE("Event isn't valid, no need to unsubscribed."); - ret = -1; + return 0; } - ret = 0; + + // Event doesn't exist , so let's create it + if (! subscription_exists && + (ret = can_subscription->subscribe(request)) < 0) + return ret; + + if(! subscription_exists) + s[sub_index] = can_subscription; + + return ret; } - else + + // Unsubscrition part + if(! subscription_exists) { - /* Event doesn't exist , so let's create it */ - s[sub_index] = can_subscription; - ret = create_event_handle(can_subscription, s); + AFB_NOTICE("There isn't any valid subscriptions for that request."); + return ret; + } + else if (subscription_exists && + ! afb_event_is_valid(s[sub_index]->get_event()) ) + { + AFB_NOTICE("Event isn't valid, no need to unsubscribed."); + return ret; } - // Checks if the event handler is correctly created, if it is, it - // performs the subscription or unsubscription operations. - if (ret < 0) + if( (ret = s[sub_index]->unsubscribe(request)) < 0) return ret; - return make_subscription_unsubscription(request, can_subscription, s, subscribe); + s.erase(sub_index); + + return ret; } static int add_to_event_loop(std::shared_ptr& can_subscription) @@ -411,10 +404,10 @@ static int send_frame(struct canfd_frame& cfd, const std::string& bus_name) return -1; } - std::map >& cd = application_t::instance().get_can_devices(); + std::map >& cd = application_t::instance().get_can_devices(); if( cd.count(bus_name) == 0) - {cd[bus_name] = std::make_shared(low_can_socket_t());} + {cd[bus_name] = std::make_shared(low_can_subscription_t());} return cd[bus_name]->tx_send(cfd, bus_name); } diff --git a/low-can-binding/binding/low-can-socket.cpp b/low-can-binding/binding/low-can-socket.cpp deleted file mode 100644 index cd827b6b..00000000 --- a/low-can-binding/binding/low-can-socket.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2015, 2016 "IoT.bzh" - * Author "Romain Forlot" - * Author "Loic Collignon" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "low-can-subscription.hpp" -#include "application.hpp" -#include "canutil/write.h" - -low_can_socket_t::low_can_socket_t() - : index_{-1}, - event_filter_{}, - socket_{} -{} - -low_can_socket_t::low_can_socket_t(struct event_filter_t event_filter) - : event_filter_{event_filter} - {} - -low_can_socket_t::low_can_socket_t( low_can_socket_t&& s) - : index_{s.index_}, - event_filter_{s.event_filter_}, - socket_{std::move(s.socket_)} -{} - -low_can_socket_t& low_can_socket_t::operator=(const low_can_socket_t& s) -{ - socket_ = std::move(s.socket_); - return *this; -} - -low_can_socket_t::~low_can_socket_t() -{ - socket_.close(); -} - -low_can_socket_t::operator bool() const -{ - return ((can_signal_ != nullptr || ! diagnostic_message_.empty()) && ! socket_); -} - -int low_can_socket_t::get_index() const -{ - return index_; -} - -const std::shared_ptr low_can_socket_t::get_can_signal() const -{ - return can_signal_; -} - -bool low_can_socket_t::is_signal_subscription_corresponding(const std::shared_ptr can_signal, const struct event_filter_t& event_filter) const -{ - return can_signal_ == can_signal && event_filter_ == event_filter; -} - -const std::vector > low_can_socket_t::get_diagnostic_message() const -{ - return diagnostic_message_; -} - -/// @brief Retrieve a diagnostic_message subscribed from a pid -/// -/// @param[in] pid - Diagnostic messages PID to search for -/// -/// @return shared_ptr diagnostic_message_ if found and nullptr if not found -const std::shared_ptr low_can_socket_t::get_diagnostic_message(uint32_t pid) const -{ - for(const auto& diag: diagnostic_message_) - { - if(diag->get_pid() == pid) - { - return diag; - } - } - return nullptr; -} - -/// @brief Retrieve a diagnostic message search from its name -/// -/// @return shared_ptr diagnostic_message_ if found and nullptr if not found -const std::shared_ptr low_can_socket_t::get_diagnostic_message(const std::string& name) const -{ - for(const auto& diag: diagnostic_message_) - { - if(diag->get_name() == name) - { - return diag; - } - } - return nullptr; -} - -/// @brief Return the CAN signal name and empty string if not found -/// or no CAN signal subscribed -const std::string low_can_socket_t::get_name() const -{ - if (can_signal_ != nullptr) - return can_signal_->get_name(); - else if (!diagnostic_message_.empty()) - return "diagnostic_messages"; - - AFB_WARNING("No diagnostics messages nor CAN signals registered in that subscription. Name empty ! It's a bug to be reported."); - return ""; -} - -/// @brief Return name from a diagnostic message from a PID -/// -/// @param[in] pid - Diagnostic message PID -const std::string low_can_socket_t::get_name(uint32_t pid) const -{ - if (!diagnostic_message_.empty()) - return get_diagnostic_message(pid)->get_name() ; - - AFB_WARNING("No diagnostics messages nor CAN signals registered in that subscription. Name empty ! It's a bug to be reported."); - return ""; -} - -float low_can_socket_t::get_frequency() const -{ - return event_filter_.frequency; -} - -float low_can_socket_t::get_min() const -{ - return event_filter_.min; -} - -float low_can_socket_t::get_max() const -{ - return event_filter_.max; -} - -utils::socketcan_bcm_t& low_can_socket_t::get_socket() -{ - return socket_; -} - -void low_can_socket_t::set_frequency(float freq) -{ - event_filter_.frequency = freq; -} - -void low_can_socket_t::set_min(float min) -{ - event_filter_.min = min; -} - -void low_can_socket_t::set_max(float max) -{ - event_filter_.max = max; -} -/// @brief Based upon which object is a subscribed CAN signal or diagnostic message -/// it will open the socket with the required CAN bus device name. -/// -/// @return INVALID_SOCKET on failure, else positive integer -int low_can_socket_t::open_socket(const std::string& bus_name) -{ - int ret = 0; - if(! socket_) - { - if( can_signal_ != nullptr) - {ret = socket_.open(can_signal_->get_message()->get_bus_device_name());} - else if (! diagnostic_message_ .empty()) - {ret = socket_.open(application_t::instance().get_diagnostic_manager().get_bus_device_name());} - else if ( ! bus_name.empty()) - { ret = socket_.open(bus_name);} - index_ = (int)socket_.socket(); - } - return ret; -} - -/// @brief Builds a BCM message head but doesn't set can_frame. -/// -/// @returns a bcm_msg with the msg_head parts set and can_frame -/// zeroed. -struct utils::bcm_msg low_can_socket_t::make_bcm_head(uint32_t opcode, uint32_t can_id, uint32_t flags, const struct timeval& timeout, const struct timeval& frequency_thinning) const -{ - struct utils::bcm_msg bcm_msg; - ::memset(&bcm_msg, 0, sizeof(bcm_msg)); - - bcm_msg.msg_head.opcode = opcode; - bcm_msg.msg_head.can_id = can_id; - bcm_msg.msg_head.flags = flags; - bcm_msg.msg_head.ival1.tv_sec = timeout.tv_sec ; - bcm_msg.msg_head.ival1.tv_usec = timeout.tv_usec; - bcm_msg.msg_head.ival2.tv_sec = frequency_thinning.tv_sec ; - bcm_msg.msg_head.ival2.tv_usec = frequency_thinning.tv_usec; - - return bcm_msg; -} - -/// @brief Take an existing bcm_msg struct and add a can_frame. -/// Currently only 1 uniq can_frame can be added, it's not possible to build -/// a multiplexed message with several can_frame. -void low_can_socket_t::add_one_bcm_frame(struct canfd_frame& cfd, struct utils::bcm_msg& bcm_msg) const -{ - struct can_frame cf; - - if (bcm_msg.msg_head.flags & CAN_FD_FRAME) - bcm_msg.fd_frames[bcm_msg.msg_head.nframes] = cfd; - else - { - cf.can_id = cfd.can_id; - cf.can_dlc = cfd.len; - ::memcpy(&cf.data, cfd.data, cfd.len); - bcm_msg.frames[bcm_msg.msg_head.nframes] = cf; - } - bcm_msg.msg_head.nframes++; -} - -/// @brief Create a RX_SETUP receive job to be used by the BCM socket for a CAN signal -/// subscription -/// -/// @return 0 if ok else -1 -int low_can_socket_t::create_rx_filter(std::shared_ptr sig) -{ - uint32_t flags; - float val; - struct timeval freq, timeout = {0, 0}; - struct canfd_frame cfd; - can_signal_= sig; - - if (sig->get_message()->is_fd()) - { - flags = SETTIMER|RX_NO_AUTOTIMER|CAN_FD_FRAME; - cfd.len = CANFD_MAX_DLEN; - } - else - { - flags = SETTIMER|RX_NO_AUTOTIMER; - cfd.len = CAN_MAX_DLEN; - } - val = (float)(1 << can_signal_->get_bit_size()) - 1; - if(! bitfield_encode_float(val, - can_signal_->get_bit_position(), - can_signal_->get_bit_size(), - 1, - can_signal_->get_offset(), - cfd.data, - cfd.len)) - return -1; - - frequency_clock_t f = event_filter_.frequency == 0 ? can_signal_->get_frequency() : frequency_clock_t(event_filter_.frequency); - freq = f.get_timeval_from_period(); - - utils::bcm_msg bcm_msg = make_bcm_head(RX_SETUP, can_signal_->get_message()->get_id(), flags, timeout, freq); - add_one_bcm_frame(cfd, bcm_msg); - - return create_rx_filter(bcm_msg); -} - -/// @brief Create a RX_SETUP receive job to be used by the BCM socket for a -/// diagnostic message subscription. -/// -/// @return 0 if ok else -1 -int low_can_socket_t::create_rx_filter(std::shared_ptr sig) -{ - diagnostic_message_.push_back(sig); - - struct timeval freq = frequency_clock_t(event_filter_.frequency).get_timeval_from_period(); - //struct timeval timeout = frequency_clock_t(10).get_timeval_from_period(); - struct timeval timeout = {0,0}; - - utils::bcm_msg bcm_msg = make_bcm_head(RX_SETUP, OBD2_FUNCTIONAL_BROADCAST_ID, SETTIMER|RX_NO_AUTOTIMER|RX_FILTER_ID, timeout, freq); - return create_rx_filter(bcm_msg); -} - -/// @brief Create a RX_SETUP receive job used by the BCM socket directly from -/// a bcm_msg. The method should not be used directly but rather through the -/// two previous method with can_signal_t or diagnostic_message_t object. -/// -/// If the CAN arbitration ID is the OBD2 functional broadcast id the subscribed -/// to the 8 classics OBD2 functional response ID -/// -/// @return 0 if ok else -1 -int low_can_socket_t::create_rx_filter(utils::bcm_msg& bcm_msg) -{ - // Make sure that socket is opened. - if(open_socket() < 0) - {return -1;} - - // If it's not an OBD2 CAN ID then just add a simple RX_SETUP job - // else monitor all standard 8 CAN OBD2 ID response. - if(bcm_msg.msg_head.can_id != OBD2_FUNCTIONAL_BROADCAST_ID) - { - socket_ << bcm_msg; - if(! socket_) - return -1; - } - else - { - for(uint8_t i = 0; i < 8; i++) - { - bcm_msg.msg_head.can_id = OBD2_FUNCTIONAL_RESPONSE_START + i; - - socket_ << bcm_msg; - if(! socket_) - return -1; - } - } - - return 0; -} - -/// @brief Creates a TX_SEND job that is used by the BCM socket to -/// send a message -/// -/// @return 0 if ok else -1 -int low_can_socket_t::tx_send(struct canfd_frame& cfd, const std::string& bus_name) -{ - utils::bcm_msg bcm_msg = make_bcm_head(TX_SEND, cfd.can_id); - add_one_bcm_frame(cfd, bcm_msg); - - if(open_socket(bus_name) < 0) - {return -1;} - - socket_ << bcm_msg; - if(! socket_) - return -1; - - return 0; -} diff --git a/low-can-binding/binding/low-can-socket.hpp b/low-can-binding/binding/low-can-socket.hpp deleted file mode 100644 index d46f1f8c..00000000 --- a/low-can-binding/binding/low-can-socket.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2015, 2016 "IoT.bzh" - * Author "Romain Forlot" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - #pragma once - -#include -#include -#include - -#include "../can/can-signals.hpp" -#include "../diagnostic/diagnostic-message.hpp" -#include "../utils/socketcan-bcm.hpp" - -#define OBDII_MAX_SIMULTANEOUS_RESPONSES 8 - -/// @brief Filtering values. Theses values have to be tested in -/// can_bus_t::apply_filter method. -struct event_filter_t -{ - float frequency; ///< frequency - Maximum frequency which will be received and pushed to a subscribed event. - float min; ///< min - Minimum value that the signal doesn't have to go below to be pushed. - float max; ///< max - Maximum value that the signal doesn't have to go above to be pushed. - event_filter_t() : frequency{0}, min{-__FLT_MAX__}, max{__FLT_MAX__} {}; - bool operator==(const event_filter_t& ext) const { - return frequency == ext.frequency && min == ext.min && max == ext.max; - } -}; - -/// @brief The object stores socket to CAN to be used to write on it. -/// This is a simple access to a CAN bus device without any subscriptions attached -class low_can_socket_t -{ -protected: - int index_; ///< index_ - index number is the socket (int) casted - struct event_filter_t event_filter_; - - /// Signal part - std::shared_ptr can_signal_; ///< can_signal_ - the CAN signal subscribed - std::vector > diagnostic_message_; ///< diagnostic_message_ - diagnostic messages meant to receive OBD2 responses. - /// normal diagnostic request and response are not tested for now. - - utils::socketcan_bcm_t socket_; ///< socket_ - socket_ that receives CAN messages. - -public: - low_can_socket_t(); - low_can_socket_t(struct event_filter_t event_filter); - low_can_socket_t(const low_can_socket_t& s) = delete; - low_can_socket_t(low_can_socket_t&& s); - virtual ~low_can_socket_t(); - - low_can_socket_t& operator=(const low_can_socket_t& s); - explicit operator bool() const; - - int get_index() const; - const std::shared_ptr get_can_signal() const; - bool is_signal_subscription_corresponding(const std::shared_ptr, const struct event_filter_t& event_filter) const; - const std::shared_ptr get_diagnostic_message(uint32_t pid) const; - const std::vector > get_diagnostic_message() const; - const std::shared_ptr get_diagnostic_message(const std::string& name) const; - const std::string get_name() const; - const std::string get_name(uint32_t pid) const; - float get_frequency() const; - float get_min() const; - float get_max() const; - utils::socketcan_bcm_t& get_socket(); - - void set_event(afb_event_t event); - void set_frequency(float freq); - void set_min(float min); - void set_max(float max); - - struct utils::bcm_msg make_bcm_head(uint32_t opcode, uint32_t can_id = 0, uint32_t flags = 0, const struct timeval& timeout = {0,0}, const struct timeval& frequency_thinning = {0,0}) const; - void add_one_bcm_frame(struct canfd_frame& cfd, struct utils::bcm_msg& bcm_msg) const; - - int open_socket(const std::string& bus_name = ""); - - int create_rx_filter(std::shared_ptr sig); - int create_rx_filter(std::shared_ptr sig); - int create_rx_filter(utils::bcm_msg& bcm_msg); - - int tx_send(struct canfd_frame& cfd, const std::string& bus_name); -}; diff --git a/low-can-binding/binding/low-can-subscription.cpp b/low-can-binding/binding/low-can-subscription.cpp index 3f844da3..fe1ede81 100644 --- a/low-can-binding/binding/low-can-subscription.cpp +++ b/low-can-binding/binding/low-can-subscription.cpp @@ -20,12 +20,373 @@ #include "application.hpp" #include "canutil/write.h" +low_can_subscription_t::low_can_subscription_t() + : index_{-1}, + event_filter_{}, + event_{}, + socket_{} +{} + +low_can_subscription_t::low_can_subscription_t(struct event_filter_t event_filter) + : index_{-1}, + event_filter_{event_filter}, + event_{}, + socket_{} + {} + +low_can_subscription_t::low_can_subscription_t( low_can_subscription_t&& s) + : index_{s.index_}, + event_filter_{s.event_filter_}, + event_{}, + socket_{std::move(s.socket_)} +{} + +low_can_subscription_t& low_can_subscription_t::operator=(const low_can_subscription_t& s) +{ + socket_ = std::move(s.socket_); + return *this; +} + +low_can_subscription_t::~low_can_subscription_t() +{ + socket_.close(); +} + +low_can_subscription_t::operator bool() const +{ + return ((can_signal_ != nullptr || ! diagnostic_message_.empty()) && ! socket_); +} afb_event_t low_can_subscription_t::get_event() { return event_; } -void low_can_subscription_t::set_event(afb_event_t event) +/** + * @brief Set the event calling the afb_daemon_make_event function to + * create it and the checks its validity. + * + * @return int - 0 if OK, -1 if not + */ +int low_can_subscription_t::set_event() +{ + std::string event_name = get_name(); + event_ = afb_daemon_make_event(event_name.c_str()); + if (! afb_event_is_valid(event_)) + { + AFB_ERROR("Can't create an event for %s, something goes wrong.", event_name.c_str()); + return -1; + } + + return 0; +} + +/** + * @brief Subscribe to the event member of the object + * + * @param request the subscribe AFB client request which want to + * subscribe + * + * @return int - 0 if OK, -1 if not + */ +int low_can_subscription_t::subscribe(afb_req_t request) +{ + if(set_event() < 0) + return -1; + return afb_req_subscribe(request, event_); +} + +/** + * @brief Unsubscribe to the event member of the object + * + * @param request the unsubscribe AFB client request which want to + * unsubscribe + * + * @return int - 0 if OK, -1 if not + */ +int low_can_subscription_t::unsubscribe(afb_req_t request) +{ + return afb_req_unsubscribe(request, event_); +} + +int low_can_subscription_t::get_index() const +{ + return index_; +} + +const std::shared_ptr low_can_subscription_t::get_can_signal() const +{ + return can_signal_; +} + +bool low_can_subscription_t::is_signal_subscription_corresponding(const std::shared_ptr can_signal, const struct event_filter_t& event_filter) const +{ + return can_signal_ == can_signal && event_filter_ == event_filter; +} + +const std::vector > low_can_subscription_t::get_diagnostic_message() const +{ + return diagnostic_message_; +} + +/// @brief Retrieve a diagnostic_message subscribed from a pid +/// +/// @param[in] pid - Diagnostic messages PID to search for +/// +/// @return shared_ptr diagnostic_message_ if found and nullptr if not found +const std::shared_ptr low_can_subscription_t::get_diagnostic_message(uint32_t pid) const +{ + for(const auto& diag: diagnostic_message_) + { + if(diag->get_pid() == pid) + { + return diag; + } + } + return nullptr; +} + +/// @brief Retrieve a diagnostic message search from its name +/// +/// @return shared_ptr diagnostic_message_ if found and nullptr if not found +const std::shared_ptr low_can_subscription_t::get_diagnostic_message(const std::string& name) const +{ + for(const auto& diag: diagnostic_message_) + { + if(diag->get_name() == name) + { + return diag; + } + } + return nullptr; +} + +/// @brief Return the CAN signal name and empty string if not found +/// or no CAN signal subscribed +const std::string low_can_subscription_t::get_name() const +{ + if (can_signal_ != nullptr) + return can_signal_->get_name(); + else if (!diagnostic_message_.empty()) + return "diagnostic_messages"; + + AFB_WARNING("No diagnostics messages nor CAN signals registered in that subscription. Name empty ! It's a bug to be reported."); + return ""; +} + +/// @brief Return name from a diagnostic message from a PID +/// +/// @param[in] pid - Diagnostic message PID +const std::string low_can_subscription_t::get_name(uint32_t pid) const +{ + if (!diagnostic_message_.empty()) + return get_diagnostic_message(pid)->get_name() ; + + AFB_WARNING("No diagnostics messages nor CAN signals registered in that subscription. Name empty ! It's a bug to be reported."); + return ""; +} + +float low_can_subscription_t::get_frequency() const +{ + return event_filter_.frequency; +} + +float low_can_subscription_t::get_min() const +{ + return event_filter_.min; +} + +float low_can_subscription_t::get_max() const +{ + return event_filter_.max; +} + +utils::socketcan_bcm_t& low_can_subscription_t::get_socket() +{ + return socket_; +} + +void low_can_subscription_t::set_frequency(float freq) +{ + event_filter_.frequency = freq; +} + +void low_can_subscription_t::set_min(float min) +{ + event_filter_.min = min; +} + +void low_can_subscription_t::set_max(float max) +{ + event_filter_.max = max; +} +/// @brief Based upon which object is a subscribed CAN signal or diagnostic message +/// it will open the socket with the required CAN bus device name. +/// +/// @return INVALID_SOCKET on failure, else positive integer +int low_can_subscription_t::open_socket(const std::string& bus_name) +{ + int ret = 0; + if(! socket_) + { + if( can_signal_ != nullptr) + {ret = socket_.open(can_signal_->get_message()->get_bus_device_name());} + else if (! diagnostic_message_ .empty()) + {ret = socket_.open(application_t::instance().get_diagnostic_manager().get_bus_device_name());} + else if ( ! bus_name.empty()) + { ret = socket_.open(bus_name);} + index_ = (int)socket_.socket(); + } + return ret; +} + +/// @brief Builds a BCM message head but doesn't set can_frame. +/// +/// @returns a bcm_msg with the msg_head parts set and can_frame +/// zeroed. +struct utils::bcm_msg low_can_subscription_t::make_bcm_head(uint32_t opcode, uint32_t can_id, uint32_t flags, const struct timeval& timeout, const struct timeval& frequency_thinning) const +{ + struct utils::bcm_msg bcm_msg; + ::memset(&bcm_msg, 0, sizeof(bcm_msg)); + + bcm_msg.msg_head.opcode = opcode; + bcm_msg.msg_head.can_id = can_id; + bcm_msg.msg_head.flags = flags; + bcm_msg.msg_head.ival1.tv_sec = timeout.tv_sec ; + bcm_msg.msg_head.ival1.tv_usec = timeout.tv_usec; + bcm_msg.msg_head.ival2.tv_sec = frequency_thinning.tv_sec ; + bcm_msg.msg_head.ival2.tv_usec = frequency_thinning.tv_usec; + + return bcm_msg; +} + +/// @brief Take an existing bcm_msg struct and add a can_frame. +/// Currently only 1 uniq can_frame can be added, it's not possible to build +/// a multiplexed message with several can_frame. +void low_can_subscription_t::add_one_bcm_frame(struct canfd_frame& cfd, struct utils::bcm_msg& bcm_msg) const +{ + struct can_frame cf; + + if (bcm_msg.msg_head.flags & CAN_FD_FRAME) + bcm_msg.fd_frames[bcm_msg.msg_head.nframes] = cfd; + else + { + cf.can_id = cfd.can_id; + cf.can_dlc = cfd.len; + ::memcpy(&cf.data, cfd.data, cfd.len); + bcm_msg.frames[bcm_msg.msg_head.nframes] = cf; + } + bcm_msg.msg_head.nframes++; +} + +/// @brief Create a RX_SETUP receive job to be used by the BCM socket for a CAN signal +/// subscription +/// +/// @return 0 if ok else -1 +int low_can_subscription_t::create_rx_filter(std::shared_ptr sig) +{ + uint32_t flags; + float val; + struct timeval freq, timeout = {0, 0}; + struct canfd_frame cfd; + can_signal_= sig; + + if (sig->get_message()->is_fd()) + { + flags = SETTIMER|RX_NO_AUTOTIMER|CAN_FD_FRAME; + cfd.len = CANFD_MAX_DLEN; + } + else + { + flags = SETTIMER|RX_NO_AUTOTIMER; + cfd.len = CAN_MAX_DLEN; + } + val = (float)(1 << can_signal_->get_bit_size()) - 1; + if(! bitfield_encode_float(val, + can_signal_->get_bit_position(), + can_signal_->get_bit_size(), + 1, + can_signal_->get_offset(), + cfd.data, + cfd.len)) + return -1; + + frequency_clock_t f = event_filter_.frequency == 0 ? can_signal_->get_frequency() : frequency_clock_t(event_filter_.frequency); + freq = f.get_timeval_from_period(); + + utils::bcm_msg bcm_msg = make_bcm_head(RX_SETUP, can_signal_->get_message()->get_id(), flags, timeout, freq); + add_one_bcm_frame(cfd, bcm_msg); + + return create_rx_filter(bcm_msg); +} + +/// @brief Create a RX_SETUP receive job used by the BCM socket directly from +/// a bcm_msg. The method should not be used directly but rather through the +/// two previous method with can_signal_t or diagnostic_message_t object. +/// +/// If the CAN arbitration ID is the OBD2 functional broadcast id the subscribed +/// to the 8 classics OBD2 functional response ID +/// +/// @return 0 if ok else -1 +int low_can_subscription_t::create_rx_filter(utils::bcm_msg& bcm_msg) { - event_ = event; + // Make sure that socket is opened. + if(open_socket() < 0) + {return -1;} + + // If it's not an OBD2 CAN ID then just add a simple RX_SETUP job + // else monitor all standard 8 CAN OBD2 ID response. + if(bcm_msg.msg_head.can_id != OBD2_FUNCTIONAL_BROADCAST_ID) + { + socket_ << bcm_msg; + if(! socket_) + return -1; + } + else + { + for(uint8_t i = 0; i < 8; i++) + { + bcm_msg.msg_head.can_id = OBD2_FUNCTIONAL_RESPONSE_START + i; + + socket_ << bcm_msg; + if(! socket_) + return -1; + } + } + + return 0; +} + +/// @brief Create a RX_SETUP receive job to be used by the BCM socket for a +/// diagnostic message subscription. +/// +/// @return 0 if ok else -1 +int low_can_subscription_t::create_rx_filter(std::shared_ptr sig) +{ + diagnostic_message_.push_back(sig); + + struct timeval freq = frequency_clock_t(event_filter_.frequency).get_timeval_from_period(); + //struct timeval timeout = frequency_clock_t(10).get_timeval_from_period(); + struct timeval timeout = {0,0}; + + utils::bcm_msg bcm_msg = make_bcm_head(RX_SETUP, OBD2_FUNCTIONAL_BROADCAST_ID, SETTIMER|RX_NO_AUTOTIMER|RX_FILTER_ID, timeout, freq); + return create_rx_filter(bcm_msg); +} + +/// @brief Creates a TX_SEND job that is used by the BCM socket to +/// send a message +/// +/// @return 0 if ok else -1 +int low_can_subscription_t::tx_send(struct canfd_frame& cfd, const std::string& bus_name) +{ + struct utils::bcm_msg bcm_msg = make_bcm_head(TX_SEND, cfd.can_id); + add_one_bcm_frame(cfd, bcm_msg); + + if(open_socket(bus_name) < 0) + {return -1;} + + socket_ << bcm_msg; + if(! socket_) + return -1; + + return 0; } diff --git a/low-can-binding/binding/low-can-subscription.hpp b/low-can-binding/binding/low-can-subscription.hpp index 0807feef..8af0aa19 100644 --- a/low-can-binding/binding/low-can-subscription.hpp +++ b/low-can-binding/binding/low-can-subscription.hpp @@ -21,23 +21,82 @@ #include #include -#include "low-can-socket.hpp" #include "../can/can-signals.hpp" #include "../diagnostic/diagnostic-message.hpp" #include "../utils/socketcan-bcm.hpp" -/// @brief The subscription class has a context that can handle all needed values to describe a subscription -/// to the low-can binding. It can hold a CAN signal or a diagnostic message. A diagnostic message for OBD2 is -/// special because there is only 1 listener to retrieve OBD2 requests. It is required that all diagnostic message -/// subscriptions are in 1 object. -class low_can_subscription_t : public low_can_socket_t +#define OBDII_MAX_SIMULTANEOUS_RESPONSES 8 + +/// @brief Filtering values. Theses values have to be tested in +/// can_bus_t::apply_filter method. +struct event_filter_t +{ + float frequency; ///< frequency - Maximum frequency which will be received and pushed to a subscribed event. + float min; ///< min - Minimum value that the signal doesn't have to go below to be pushed. + float max; ///< max - Maximum value that the signal doesn't have to go above to be pushed. + + event_filter_t() : frequency{0}, min{-__FLT_MAX__}, max{__FLT_MAX__} {}; + bool operator==(const event_filter_t& ext) const { + return frequency == ext.frequency && min == ext.min && max == ext.max; + } +}; + +/// @brief The object stores socket to CAN to be used to write on it. +/// This is a simple access to a CAN bus device without any subscriptions attached +class low_can_subscription_t { private: + int index_; ///< index_ - index number is the socket (int) casted + struct event_filter_t event_filter_; afb_event_t event_; ///< event_ - application framework event used to push on client + /// Signal part + std::shared_ptr can_signal_; ///< can_signal_ - the CAN signal subscribed + std::vector > diagnostic_message_; ///< diagnostic_message_ - diagnostic messages meant to receive OBD2 + /// responses. Normal diagnostic request and response are not tested for now. + utils::socketcan_bcm_t socket_; ///< socket_ - socket_ that receives CAN messages. + + int set_event(); + public: - using low_can_socket_t::low_can_socket_t; + low_can_subscription_t(); + low_can_subscription_t(struct event_filter_t event_filter); + low_can_subscription_t(const low_can_subscription_t& s) = delete; + low_can_subscription_t(low_can_subscription_t&& s); + ~low_can_subscription_t(); + + low_can_subscription_t& operator=(const low_can_subscription_t& s); + explicit operator bool() const; afb_event_t get_event(); - void set_event(afb_event_t event); + int subscribe(afb_req_t request); + int unsubscribe(afb_req_t request); + + int get_index() const; + const std::shared_ptr get_can_signal() const; + bool is_signal_subscription_corresponding(const std::shared_ptr, const struct event_filter_t& event_filter) const; + const std::shared_ptr get_diagnostic_message(uint32_t pid) const; + const std::vector > get_diagnostic_message() const; + const std::shared_ptr get_diagnostic_message(const std::string& name) const; + const std::string get_name() const; + const std::string get_name(uint32_t pid) const; + float get_frequency() const; + float get_min() const; + float get_max() const; + utils::socketcan_bcm_t& get_socket(); + + void set_frequency(float freq); + void set_min(float min); + void set_max(float max); + + struct utils::bcm_msg make_bcm_head(uint32_t opcode, uint32_t can_id = 0, uint32_t flags = 0, const struct timeval& timeout = {0,0}, const struct timeval& frequency_thinning = {0,0}) const; + void add_one_bcm_frame(struct canfd_frame& cfd, struct utils::bcm_msg& bcm_msg) const; + + int open_socket(const std::string& bus_name = ""); + + int create_rx_filter(std::shared_ptr sig); + int create_rx_filter(std::shared_ptr sig); + int create_rx_filter(utils::bcm_msg& bcm_msg); + + int tx_send(struct canfd_frame& cfd, const std::string& bus_name); }; -- cgit 1.2.3-korg