#include "application.hpp"
#include "canutil/write.h"
-///******************************************************************************
-///
-/// low_can_subscription_t object
-///
-///*******************************************************************************/
-
low_can_subscription_t::low_can_subscription_t()
: index_{-1},
event_filter_{event_filter_t()},
socket_{std::move(s.socket_)}
{}
- low_can_subscription_t& low_can_subscription_t::operator=(const low_can_subscription_t& s)
+low_can_subscription_t& low_can_subscription_t::operator=(const low_can_subscription_t& s)
{
socket_ = std::move(s.socket_);
return *this;
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<diagnostic_message_t> low_can_subscription_t::get_diagnostic_message(uint32_t pid) const
{
for(const auto& diag: diagnostic_message_)
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<diagnostic_message_t> low_can_subscription_t::get_diagnostic_message(const std::string& name) const
{
for(const auto& diag: diagnostic_message_)
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 "";
}
+/// @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())
event_filter_.max = max;
}
+/// @brief Based upon which object is subscribed CAN signal or diagnostic message
+/// this 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()
{
int ret = 0;
return ret;
}
+/// @brief Build a BCM message head but don't set can_frame.
+///
+/// @return a simple_bcm_msg with the msg_head parts set and can_frame
+/// zeroed.
struct utils::simple_bcm_msg low_can_subscription_t::make_bcm_head(uint32_t can_id, uint32_t flags, const struct timeval& timeout, const struct timeval& frequency_thinning) const
{
struct utils::simple_bcm_msg bcm_msg;
return bcm_msg;
}
+/// @brief Take an existing simple_bcm_msg struct and add a can_frame to it.
+/// Only possible for now to add 1 uniq can_frame, it isn't possible to build
+/// a multiplexed message with several can_frame.
void low_can_subscription_t::add_bcm_frame(const struct can_frame& cfd, struct utils::simple_bcm_msg& bcm_msg) const
{
for(int i=0; i < CAN_MAX_DLEN; i++)
}
}
-/// @brief Create a RX_SETUP receive job used by the BCM socket.
-///
-/// @return 0 if ok else -1
-int low_can_subscription_t::create_rx_filter()
-{
- int ret = -1;
- if ( can_signal_ != nullptr)
- {ret = create_rx_filter(can_signal_);}
- else if (! diagnostic_message_ .empty())
- {ret = create_rx_filter(diagnostic_message_.front());}
-
- return ret;
-}
-
-/// @brief Create a RX_SETUP receive job used by the BCM socket.
+/// @brief Create a RX_SETUP receive job 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<can_signal_t> sig)
return create_rx_filter(bcm_msg);
}
-/// @brief Create a RX_SETUP receive job used by the BCM socket.
+/// @brief Create a RX_SETUP receive job 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<diagnostic_message_t> sig)
return create_rx_filter(bcm_msg);
}
-/// @brief Create a RX_SETUP receive job used by the BCM socket.
+/// @brief Create a RX_SETUP receive job used by the BCM socket directly from
+/// a simple_bcm_msg. You will not use this method directly but rather use 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::simple_bcm_msg& bcm_msg)
#include "../diagnostic/diagnostic-message.hpp"
#include "../utils/socketcan-bcm.hpp"
+/// @brief Filtering values. Theses values has to be tested into
+/// can_bus_t::apply_filter method.
struct event_filter_t
{
- float frequency;
- float min;
- float max;
+ float frequency; ///< frequency - Maximum frequency which will be received and pushed a subscribed event.
+ float min; ///< min - Minimum value that the signal don't have to go below to be pushed.
+ float max; ///< max - Maximum value that the signal don't have to go above to be pushed.
event_filter_t() : frequency{NAN}, min{NAN}, max{NAN} {}
};
+/// @brief A subscription object used has a context that handle all needed values to describe a subscription
+/// to the low-can binding. It can holds a CAN signal or diagnostic message. Diagnostic message for OBD2 is a kind
+/// of special because there is only 1 listener to retrieve OBD2 requests. So it's needed that all diagnostic messages
+/// subscriptions is to be in 1 object.
class low_can_subscription_t
{
private:
- int index_;
- struct afb_event event_;
+ int index_; ///< index_ - index number is the socket (int) casted
+ struct afb_event event_; ///< event_ - application framework event used to push on client
/// Signal part
- std::shared_ptr<can_signal_t> can_signal_;
- std::vector<std::shared_ptr<diagnostic_message_t> > diagnostic_message_;
+ std::shared_ptr<can_signal_t> can_signal_; ///< can_signal_ - the CAN signal subscribed
+ std::vector<std::shared_ptr<diagnostic_message_t> > diagnostic_message_; ///< diagnostic_message_ - diagnostic messages meant to received OBD2 responses.
+ /// normal diagnostic request and response not tested for now.
/// Filtering part
- struct event_filter_t event_filter_;
+ struct event_filter_t event_filter_; ///< event_filter_ - filtering values applied to a subscription
- utils::socketcan_bcm_t socket_;
+ utils::socketcan_bcm_t socket_; ///< socket_ - socket_ that receives CAN messages.
public:
low_can_subscription_t();
low_can_subscription_t(struct event_filter_t event_filter);
struct utils::simple_bcm_msg make_bcm_head(uint32_t can_id, uint32_t flags, const struct timeval& timeout, const struct timeval& frequency_thinning) const;
void add_bcm_frame(const struct can_frame& cfd, struct utils::simple_bcm_msg& bcm_msg) const;
int open_socket();
- int create_rx_filter();
int create_rx_filter(std::shared_ptr<can_signal_t> sig);
int create_rx_filter(std::shared_ptr<diagnostic_message_t> sig);
int create_rx_filter(utils::simple_bcm_msg& bcm_msg);
: conf_file_{conf_file}
{}
+/// @brief Take a decoded message to determine if its value comply with the wanted
+/// filtering values.
+///
+/// @param[in] vehicle_message - A decoded message to analyze
+/// @param[in] can_subscription - the subscription which will be notified depending
+/// on its filtering values. Filtering values are stored in the event_filtermember.
+///
+/// @return True if the value is compliant with event filter values, false if not...
bool can_bus_t::apply_filter(const openxc_VehicleMessage& vehicle_message, std::shared_ptr<low_can_subscription_t> can_subscription)
{
bool send = false;
vehicle_message_q_.push(std::make_pair(subscription_id, v_msg));
}
-/// @brief Return the shared pointer on the can_bus_dev_t initialized
-/// with device_name "bus"
-///
-/// @param[in] bus - CAN bus device name to retrieve.
-///
-/// @return A shared pointer on an object can_bus_dev_t
+/// @brief Fills the CAN device map member with value from device
+/// mapping configuration file read at initialization.
void can_bus_t::set_can_devices()
{
can_devices_ = conf_file_.get_devices_name();
}
}
+
+/// @brief Return the CAN device index from the map
+/// map are sorted so index depend upon alphabetical sorting.
int can_bus_t::get_can_device_index(const std::string& bus_name) const
{
int i = 0;
return i;
}
+/// @brief Return CAN device name from a logical CAN device name gotten from
+/// the signals.json description file which comes from a CAN databases file in
+/// general.
const std::string can_bus_t::get_can_device_name(const std::string& id_name) const
{
std::string ret;
std::mutex decoded_can_message_mutex_; ///< mutex protecting the vehicle_message_q_ queue.
std::queue <std::pair<int, openxc_VehicleMessage> > vehicle_message_q_; ///< queue that'll store openxc_VehicleMessage to pushed
- std::vector<std::pair<std::string, std::string> > can_devices_;
+ std::vector<std::pair<std::string, std::string> > can_devices_; ///< can_devices_ - holds a mapping between logical CAN devices names and linux CAN devices names.
public:
explicit can_bus_t(utils::config_parser_t conf_file);
can_bus_t(can_bus_t&&);
class can_message_set_t;
+/// @brief The definition of a CAN message. This includes a lot of metadata, so
+/// to save memory this class gets the can_signal_t object related to a CAN message.
class can_message_definition_t
{
private:
- can_message_set_t* parent_; /*!< parent_ - Pointer to the CAN message set holding this CAN message definition */
- std::string bus_; /*!< bus_ - Address of CAN bus device. */
- uint32_t id_; /*!< id_ - The ID of the message.*/
- can_message_format_t format_; /*!< format_ - the format of the message's ID.*/
- frequency_clock_t frequency_clock_; /*!< clock_ - an optional frequency clock to control the output of this
- * message, if sent raw, or simply to mark the max frequency for custom
- * handlers to retrieve.*/
- bool force_send_changed_; /*!< force_send_changed_ - If true, regardless of the frequency, it will send CAN
- * message if it has changed when using raw passthrough.*/
- std::vector<uint8_t> last_value_; /*!< last_value_ - The last received value of the message. Defaults to undefined.
- * This is required for the forceSendChanged functionality, as the stack
- * needs to compare an incoming CAN message with the previous frame.*/
- std::vector<std::shared_ptr<can_signal_t> > can_signals_; /*!< can_signals_ - Vector holding can_signal_t object which share the same arbitration ID */
+ can_message_set_t* parent_; ///< parent_ - Pointer to the CAN message set holding this CAN message definition */
+ std::string bus_; ///< bus_ - Address of CAN bus device. */
+ uint32_t id_; ///< id_ - The ID of the message.*/
+ can_message_format_t format_; ///< format_ - the format of the message's ID.*/
+ frequency_clock_t frequency_clock_; ///< clock_ - an optional frequency clock to control the output of this
+ /// message, if sent raw, or simply to mark the max frequency for custom
+ /// handlers to retrieve.*/
+ bool force_send_changed_; ///< force_send_changed_ - If true, regardless of the frequency, it will send CAN
+ /// message if it has changed when using raw passthrough.*/
+ std::vector<uint8_t> last_value_; ///< last_value_ - The last received value of the message. Defaults to undefined.
+ /// This is required for the forceSendChanged functionality, as the stack
+ /// needs to compare an incoming CAN message with the previous frame.*/
+ std::vector<std::shared_ptr<can_signal_t> > can_signals_; ///< can_signals_ - Vector holding can_signal_t object which share the same arbitration ID */
public:
//can_message_definition_t(const can_message_definition_t& b);
return can_signals;
}
+/// @brief Return vector holding all diagnostic messages definitions handled by this message set.
std::vector<std::shared_ptr<diagnostic_message_t> >& can_message_set_t::get_diagnostic_messages()
{
return diagnostic_messages_;
class can_message_definition_t;
class diagnostic_message_t;
-/// @brief A parent wrapper for a particular set of CAN messages and associated
-/// CAN buses(e.g. a vehicle or program).
+/// @brief A parent wrapper for a particular set of CAN messages and diagnostic messages
+/// (e.g. a vehicle or program).
class can_message_set_t
{
private:
///
/// @brief Retrieve format_ member value.
///
-/// @return format_ class member
+/// @return format_ class member. Default to INVALID.
///
can_message_format_t can_message_t::get_format() const
{
timestamp_ = timestamp;
}
-///
/// @brief Control whether the object is correctly initialized
/// to be sent over the CAN bus
///
-/// @return true if object correctly initialized and false if not.
-///
+/// @return True if object correctly initialized and false if not.
bool can_message_t::is_correct_to_send()
{
if (id_ != 0 && length_ != 0 && format_ != can_message_format_t::INVALID)
return false;
}
-///
/// @brief Set format_ member value.
///
/// Preferred way to initialize these members by using
/// convert_from_canfd_frame method.
///
/// @param[in] new_format - class member
-///
void can_message_t::set_format(const can_message_format_t new_format)
{
if(new_format == can_message_format_t::STANDARD || new_format == can_message_format_t::EXTENDED || new_format == can_message_format_t::INVALID)
/// @param[in] nbytes - bytes read from socket read operation.
///
/// @return A can_message_t object fully initialized with canfd_frame values.
-///
can_message_t can_message_t::convert_from_frame(const struct canfd_frame& frame, size_t nbytes, uint64_t timestamp)
{
uint8_t maxdlen, length, flags = (uint8_t)NULL;
return can_message_t(maxdlen, id, length, format, rtr_flag, flags, data, timestamp);
}
+/// @brief Take a can_frame struct to initialize class members
+///
+/// This is the preferred way to initialize class members.
+///
+/// @param[in] frame - can_frame to convert coming from a read of CAN socket
+/// @param[in] nbytes - bytes read from socket read operation.
+///
+/// @return A can_message_t object fully initialized with can_frame values.
can_message_t can_message_t::convert_from_frame(const struct can_frame& frame, size_t nbytes, uint64_t timestamp)
{
uint8_t maxdlen, length, flags = (uint8_t)NULL;
return can_message_t(maxdlen, id, length, format, rtr_flag, flags, data, timestamp);
}
-///
/// @brief Take all initialized class's members and build an
/// canfd_frame struct that can be use to send a CAN message over
/// the bus.
///
/// @return canfd_frame struct built from class members.
-///
struct canfd_frame can_message_t::convert_to_canfd_frame()
{
canfd_frame frame;
return frame;
}
+/// @brief Take all initialized class's members and build an
+/// can_frame struct that can be use to send a CAN message over
+/// the bus.
+///
+/// @return can_frame struct built from class members.
struct can_frame can_message_t::convert_to_can_frame()
{
can_frame frame;
* @brief The ID format for a CAN message.
*/
enum class can_message_format_t {
- STANDARD, /*!< STANDARD - standard 11-bit CAN arbitration ID. */
- EXTENDED, /*!< EXTENDED - an extended frame, with a 29-bit arbitration ID. */
- INVALID, /*!< INVALID - INVALID code used at initialization to signify that it isn't usable'*/
+ STANDARD, ///< STANDARD - standard 11-bit CAN arbitration ID. */
+ EXTENDED, ///< EXTENDED - an extended frame, with a 29-bit arbitration ID. */
+ INVALID, ///< INVALID - INVALID code used at initialization to signify that it isn't usable'*/
};
-/**
- * @class can_message_t
- *
- * @brief A compact representation of a single CAN message, meant to be used in in/out
- * buffers.
- */
+/// @class can_message_t
+///
+/// @brief A compact representation of a single CAN message, meant to be used in in/out
+/// buffers. It is a wrapper of a can_frame struct with some sugar around it for binding purposes.
class can_message_t {
private:
- uint8_t maxdlen_; /*!< maxdlen_ - Max data length deduce from number of bytes read from the socket.*/
- uint32_t id_; /*!< id_ - The ID of the message. */
- uint8_t length_; /*!< length_ - the length of the data array (max 8). */
- can_message_format_t format_; /*!< format_ - the format of the message's ID.*/
- bool rtr_flag_; /*!< rtr_flag_ - Telling if the frame has RTR flag positionned. Then frame hasn't data field*/
- uint8_t flags_; /*!< flags_ - flags of a CAN FD frame. Needed if we catch FD frames.*/
- std::vector<uint8_t> data_; /*!< data_ - The message's data field with a size of 8 which is the standard about CAN bus messages.*/
- uint64_t timestamp_; /*!< timestamp_ - timestamp of the received message*/
- int sub_id_; /*!< sub_id_ - Subscription index. */
+ uint8_t maxdlen_; ///< maxdlen_ - Max data length deduce from number of bytes read from the socket.*/
+ uint32_t id_; ///< id_ - The ID of the message. */
+ uint8_t length_; ///< length_ - the length of the data array (max 8). */
+ can_message_format_t format_; ///< format_ - the format of the message's ID.*/
+ bool rtr_flag_; ///< rtr_flag_ - Telling if the frame has RTR flag positionned. Then frame hasn't data field*/
+ uint8_t flags_; ///< flags_ - flags of a CAN FD frame. Needed if we catch FD frames.*/
+ std::vector<uint8_t> data_; ///< data_ - The message's data field with a size of 8 which is the standard about CAN bus messages.*/
+ uint64_t timestamp_; ///< timestamp_ - timestamp of the received message*/
+ int sub_id_; ///< sub_id_ - Subscription index. */
public:
can_message_t();
cleanup_active_requests(true);
}
-/// @brief send function use by diagnostic library. Only one bus used for now
-/// so diagnostic request is sent using the default diagnostic bus not matter of
-/// which is specified in the diagnostic message definition.
+/// @brief send function use by diagnostic library. It will open a BCM CAN socket TX_SETUP type.
+/// That socket will send cyclic messages configured from a diagnostic request.
///
/// @param[in] arbitration_id - CAN arbitration ID to use when send message. OBD2 broadcast ID
/// is 0x7DF by example.
return shims_;
}
-bool diagnostic_manager_t::is_active_requests_running()
-{
- if(non_recurring_requests_.empty() && recurring_requests_.empty())
- {
- return true;
- }
- return false;
-}
-
/// @brief Search for a specific active diagnostic request in the provided requests list
/// and erase it from the vector. This is useful at unsubscription to clean up the list otherwize
/// all received CAN messages will be passed to DiagnosticRequestHandle of all active diagnostic request
requests_list.erase(i);
}
-// @brief TODO: implement cancel_request if needed... Don't know.
+/// @brief Free memory allocated on active_diagnostic_request_t object and close the socket.
void diagnostic_manager_t::cancel_request(active_diagnostic_request_t* entry)
{
entry->get_socket().close();
return nullptr;
}
-/// @brief Add and send a new one-time diagnostic request.
+/// @brief Add and send a new one-time diagnostic request. DON'T USED AT THIS TIME
///
/// A one-time (aka non-recurring) request can existing in parallel with a
/// recurring request for the same PID or mode, that's not a problem.
return entry;
}
+/// @brief Validate frequency asked don't get higher than the maximum of a classical
+/// CAN bus OBD2 request.
+///
+/// @param[in] frequencyHz - frequency asked for sending diagnostic requests.
+///
+/// @return True if frequency is below the Maximum false if not.
bool diagnostic_manager_t::validate_optional_request_attributes(float frequencyHz)
{
if(frequencyHz > MAX_RECURRING_DIAGNOSTIC_FREQUENCY_HZ) {
///
/// At most one recurring request can be active for the same arbitration ID, mode
/// and (if set) PID on the same bus at one time. If you try and call
-/// addRecurringRequest with the same key, it will return an error.
-///
-/// TODO: This also adds any neccessary CAN acceptance filters so we can receive the
-/// response. If the request is to the functional broadcast ID (0x7df) filters
-/// are added for all functional addresses (0x7e8 to 0x7f0).
-///
-/// Example:
-///
-/// // Creating a functional broadcast, mode 1 request for PID 2.
-/// DiagnosticRequest request = {
-/// arbitration_id: 0x7df,
-/// mode: 1,
-/// has_pid: true,
-/// pid: 2
-/// };
-///
-/// // Add a recurring request, to be sent at 1Hz, and published with the
-/// // name "my_pid_request"
-/// addRecurringRequest(&getConfiguration()->diagnosticsManager,
-/// canBus,
-/// &request,
-/// "my_pid_request",
-/// false,
-/// NULL,
-/// NULL,
-/// 1);
+/// add_recurring_request with the same key, it will return an error.
///
/// @param[in] request - The parameters for the request.
/// @param[in] name - An optional human readable name this response, to be used when
const std::string get_bus_device_name() const;
active_diagnostic_request_t* get_last_recurring_requests() const;
DiagnosticShims& get_shims();
- bool is_active_requests_running();
void find_and_erase(active_diagnostic_request_t* entry, std::vector<active_diagnostic_request_t*>& requests_list);
void cancel_request(active_diagnostic_request_t* entry);
class can_message_set_t;
-///
/// @brief - A representation of an OBD-II PID.
-///
class diagnostic_message_t
{
private:
std::vector<std::shared_ptr<diagnostic_message_t> > diagnostic_messages;
};
+ /// @brief Signal manager singleton hold subscription object with attached afb_event and its mutex
+ /// to read and write it safely.
+ /// It can be used to browse CAN signals and Diagnostic messages vectors and find a particular signal to
+ /// subscribe to.
class signals_manager_t
{
private:
std::mutex subscribed_signals_mutex_;
- std::map<int, std::shared_ptr<low_can_subscription_t> > subscribed_signals_;
+ std::map<int, std::shared_ptr<low_can_subscription_t> > subscribed_signals_; ///< Map containing all subscribed signals, key is the socket int value.
signals_manager_t(); ///< Private constructor to make singleton class.
return socket_;
}
+ /// Read the socket to retrieve the associated CAN message. All the hard work is do into
+ /// convert_from_frame method and if there isn't CAN message retrieve, only BCM head struct,
+ /// then CAN message will be zeroed and must be handled later.
socketcan_bcm_t& operator>>(socketcan_bcm_t& s, can_message_t& cm)
{
struct utils::simple_bcm_msg msg;
canfd_bcm_msg() { msg_head.flags |= CAN_FD_FRAME; }
};
+ /// @brief derivated socketcan class specialized for BCM CAN socket.
class socketcan_bcm_t : public socketcan_t
{
public:
return frequency_ == 0 ? 0 : 1 / frequency_;
}
+/// @brief Return a timeval struct based on the frequency_ member. used to
+/// specified CAN BCM timers.
const struct timeval frequency_clock_t::get_timeval_from_period() const
{
struct timeval freq = {0, 0};