From de8fb10e1d8e98a85dfba6fc8966266dbf5e42c6 Mon Sep 17 00:00:00 2001 From: Romain Forlot Date: Mon, 6 Mar 2017 22:28:50 +0100 Subject: Beginning of work on obd2 object and files reorganization. Change-Id: Idaa7ad05c45d734ce771506fd6e41f1a09a6ac66 Signed-off-by: Romain Forlot --- src/can-bus.cpp | 556 ---------------------------------- src/can-bus.hpp | 172 ----------- src/can-command.hpp | 66 ---- src/can-decoder.cpp | 119 -------- src/can-decoder.hpp | 161 ---------- src/can-message.cpp | 313 ------------------- src/can-message.hpp | 151 --------- src/can-signals.cpp | 101 ------ src/can-signals.hpp | 142 --------- src/can/can-bus.cpp | 556 ++++++++++++++++++++++++++++++++++ src/can/can-bus.hpp | 172 +++++++++++ src/can/can-command.hpp | 66 ++++ src/can/can-decoder.cpp | 119 ++++++++ src/can/can-decoder.hpp | 161 ++++++++++ src/can/can-message.cpp | 313 +++++++++++++++++++ src/can/can-message.hpp | 151 +++++++++ src/can/can-signals.cpp | 101 ++++++ src/can/can-signals.hpp | 142 +++++++++ src/diagnostic/diagnostic-message.cpp | 206 +++++++++++++ src/diagnostic/diagnostic-message.hpp | 94 ++++++ src/low-can-binding.cpp | 18 +- src/obd2-signals.cpp | 181 ----------- src/obd2-signals.hpp | 114 ------- src/openxc-utils.cpp | 134 -------- src/openxc-utils.hpp | 126 -------- src/signals.cpp | 76 ----- src/signals.hpp | 72 ----- src/timer.cpp | 31 -- src/timer.hpp | 45 --- src/utils/openxc-utils.cpp | 134 ++++++++ src/utils/openxc-utils.hpp | 126 ++++++++ src/utils/signals.cpp | 76 +++++ src/utils/signals.hpp | 72 +++++ src/utils/timer.cpp | 31 ++ src/utils/timer.hpp | 45 +++ 35 files changed, 2574 insertions(+), 2569 deletions(-) delete mode 100644 src/can-bus.cpp delete mode 100644 src/can-bus.hpp delete mode 100644 src/can-command.hpp delete mode 100644 src/can-decoder.cpp delete mode 100644 src/can-decoder.hpp delete mode 100644 src/can-message.cpp delete mode 100644 src/can-message.hpp delete mode 100644 src/can-signals.cpp delete mode 100644 src/can-signals.hpp create mode 100644 src/can/can-bus.cpp create mode 100644 src/can/can-bus.hpp create mode 100644 src/can/can-command.hpp create mode 100644 src/can/can-decoder.cpp create mode 100644 src/can/can-decoder.hpp create mode 100644 src/can/can-message.cpp create mode 100644 src/can/can-message.hpp create mode 100644 src/can/can-signals.cpp create mode 100644 src/can/can-signals.hpp create mode 100644 src/diagnostic/diagnostic-message.cpp create mode 100644 src/diagnostic/diagnostic-message.hpp delete mode 100644 src/obd2-signals.cpp delete mode 100644 src/obd2-signals.hpp delete mode 100644 src/openxc-utils.cpp delete mode 100644 src/openxc-utils.hpp delete mode 100644 src/signals.cpp delete mode 100644 src/signals.hpp delete mode 100644 src/timer.cpp delete mode 100644 src/timer.hpp create mode 100644 src/utils/openxc-utils.cpp create mode 100644 src/utils/openxc-utils.hpp create mode 100644 src/utils/signals.cpp create mode 100644 src/utils/signals.hpp create mode 100644 src/utils/timer.cpp create mode 100644 src/utils/timer.hpp (limited to 'src') diff --git a/src/can-bus.cpp b/src/can-bus.cpp deleted file mode 100644 index d46092bf..00000000 --- a/src/can-bus.cpp +++ /dev/null @@ -1,556 +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. - */ - -#include "can-bus.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "can-decoder.hpp" -#include "openxc-utils.hpp" - -extern "C" -{ - #include -} - -/******************************************************************************** -* -* can_bus_t method implementation -* -*********************************************************************************/ -/** -* @brief Class constructor -* -* @param struct afb_binding_interface *interface between daemon and binding -* @param int file handle to the json configuration file. -*/ -can_bus_t::can_bus_t(int conf_file) - : conf_file_{conf_file} -{ -} - -/** -* @brief thread to decoding raw CAN messages. -* -* @desc It will take from the can_message_q_ queue the next can message to process then it will search -* about signal subscribed if there is a valid afb_event for it. We only decode signal for which a -* subscription has been made. Can message will be decoded using translateSignal that will pass it to the -* corresponding decoding function if there is one assigned for that signal. If not, it will be the default -* noopDecoder function that will operate on it. -*/ -void can_bus_t::can_decode_message() -{ - can_message_t can_message; - std::vector signals; - openxc_VehicleMessage vehicle_message; - openxc_DynamicField search_key, decoded_message; - - decoder_t decoder; - - while(is_decoding_) - { - std::unique_lock can_message_lock(can_message_mutex_); - new_can_message_.wait(can_message_lock); - can_message = next_can_message(); - - /* First we have to found which CanSignal it is */ - search_key = build_DynamicField((double)can_message.get_id()); - signals.clear(); - find_can_signals(search_key, signals); - - /* Decoding the message ! Don't kill the messenger ! */ - for(auto& sig : signals) - { - std::lock_guard subscribed_signals_lock(get_subscribed_signals_mutex()); - std::map& s = get_subscribed_signals(); - - /* DEBUG message to make easier debugger STL containers... - DEBUG(binder_interface, "Operator[] key char: %s, event valid? %d", sig.generic_name, afb_event_is_valid(s[sig.generic_name])); - DEBUG(binder_interface, "Operator[] key string: %s, event valid? %d", sig.generic_name, afb_event_is_valid(s[std::string(sig.generic_name)])); - DEBUG(binder_interface, "Nb elt matched char: %d", (int)s.count(sig.generic_name)); - DEBUG(binder_interface, "Nb elt matched string: %d", (int)s.count(std::string(sig.generic_name))); */ - if( s.find(sig->generic_name) != s.end() && afb_event_is_valid(s[sig->generic_name])) - { - decoded_message = decoder.translateSignal(*sig, can_message, get_can_signals()); - - openxc_SimpleMessage s_message = build_SimpleMessage(sig->generic_name, decoded_message); - vehicle_message = build_VehicleMessage_with_SimpleMessage(openxc_DynamicField_Type::openxc_DynamicField_Type_NUM, s_message); - - std::lock_guard decoded_can_message_lock(decoded_can_message_mutex_); - push_new_vehicle_message(vehicle_message); - new_decoded_can_message_.notify_one(); - } - } - } -} - -/** -* @brief thread to push events to suscribers. It will read subscribed_signals map to look -* which are events that has to be pushed. -*/ -void can_bus_t::can_event_push() -{ - openxc_VehicleMessage v_message; - openxc_SimpleMessage s_message; - json_object* jo; - - while(is_pushing_) - { - std::unique_lock decoded_can_message_lock(decoded_can_message_mutex_); - new_decoded_can_message_.wait(decoded_can_message_lock); - v_message = next_vehicle_message(); - - s_message = get_simple_message(v_message); - { - std::lock_guard subscribed_signals_lock(get_subscribed_signals_mutex()); - std::map& s = get_subscribed_signals(); - if(s.find(std::string(s_message.name)) != s.end() && afb_event_is_valid(s[std::string(s_message.name)])) - { - jo = json_object_new_object(); - jsonify_simple(s_message, jo); - afb_event_push(s[std::string(s_message.name)], jo); - } - } - } -} - -/** - * @brief Will initialize threads that will decode - * and push subscribed events. - */ -void can_bus_t::start_threads() -{ - v_ = true; - th_decoding_ = std::thread(&can_bus_t::can_decode_message, this); - if(!th_decoding_.joinable()) - is_decoding_ = false; - - is_pushing_ = true; - th_pushing_ = std::thread(&can_bus_t::can_event_push, this); - if(!th_pushing_.joinable()) - is_pushing_ = false; -} - -/** -* @brief Will stop all threads holded by can_bus_t object -* which are decoding and pushing then will wait that's -* they'll finish their job. -*/ -void can_bus_t::stop_threads() -{ - is_decoding_ = false; - is_pushing_ = false; -} - -/** -* @brief Will initialize can_bus_dev_t objects after reading -* the configuration file passed in the constructor. -*/ -int can_bus_t::init_can_dev() -{ - std::vector devices_name; - int i; - size_t t; - - devices_name = read_conf(); - - if (! devices_name.empty()) - { - t = devices_name.size(); - i=0; - - for(const auto& device : devices_name) - { - can_devices_m_[device] = std::make_shared(device); - if (can_devices_m_[device]->open() == 0) - { - i++; - DEBUG(binder_interface, "Start reading thread"); - NOTICE(binder_interface, "%s device opened and reading", device.c_str()); - can_devices_m_[device]->start_reading(*this); - } - else - ERROR(binder_interface, "Can't open device %s", device.c_str()); - } - - NOTICE(binder_interface, "Initialized %d/%d can bus device(s)", i, t); - return 0; - } - ERROR(binder_interface, "init_can_dev: Error at CAN device initialization. No devices read from configuration file. Did you specify canbus JSON object ?"); - return 1; -} - -/** -* @brief read the conf_file_ and will parse json objects -* in it searching for canbus objects devices name. -* -* @return Vector of can bus device name string. -*/ -std::vector can_bus_t::read_conf() -{ - std::vector ret; - json_object *jo, *canbus; - int n, i; - const char* taxi; - - FILE *fd = fdopen(conf_file_, "r"); - if (fd) - { - std::string fd_conf_content; - std::fseek(fd, 0, SEEK_END); - fd_conf_content.resize(std::ftell(fd)); - std::rewind(fd); - std::fread(&fd_conf_content[0], 1, fd_conf_content.size(), fd); - std::fclose(fd); - - DEBUG(binder_interface, "Configuration file content : %s", fd_conf_content.c_str()); - jo = json_tokener_parse(fd_conf_content.c_str()); - - if (jo == NULL || !json_object_object_get_ex(jo, "canbus", &canbus)) - {/** -* @brief Telling if the pushing thread is running -* This is the boolean value on which the while loop -* take its condition. Set it to false will stop the -* according thread. -* -* @return true if pushing thread is running, false if not. -*/ - - ERROR(binder_interface, "Can't find canbus node in the configuration file. Please review it."); - ret.clear(); - } - else if (json_object_get_type(canbus) != json_type_array) - { - taxi = json_object_get_string(canbus); - DEBUG(binder_interface, "Can bus found: %s", taxi); - ret.push_back(std::string(taxi)); - } - else - { - n = json_object_array_length(canbus); - for (i = 0 ; i < n ; i++) - ret.push_back(json_object_get_string(json_object_array_get_idx(canbus, i))); - } - return ret; - } - ERROR(binder_interface, "Problem at reading the conf file"); - ret.clear(); - return ret; -} - -/** -* @brief return new_can_message_ member -* -* @return return new_can_message_ member -*/ -std::condition_variable& can_bus_t::get_new_can_message_cv() -{ - return new_can_message_cv_; -} - -/** -* @brief return can_message_mutex_ member -* -* @return return can_message_mutex_ member -*/ -std::mutex& can_bus_t::get_can_message_mutex() -{ - return can_message_mutex_; -} - -/** -* @brief Return first can_message_t on the queue -* -* @return a can_message_t -*/ -can_message_t can_bus_t::next_can_message() -{ - can_message_t can_msg; - - if(!can_message_q_.empty()) - { - can_msg = can_message_q_.front(); - can_message_q_.pop(); - DEBUG(binder_interface, "next_can_message: Here is the next can message : id %X, length %X, data %02X%02X%02X%02X%02X%02X%02X%02X", can_msg.get_id(), can_msg.get_length(), - can_msg.get_data()[0], can_msg.get_data()[1], can_msg.get_data()[2], can_msg.get_data()[3], can_msg.get_data()[4], can_msg.get_data()[5], can_msg.get_data()[6], can_msg.get_data()[7]); - return can_msg; - } - - return can_msg; -} - -/** -* @brief Push a can_message_t into the queue -* -* @param the const reference can_message_t object to push into the queue -*/ -void can_bus_t::push_new_can_message(const can_message_t& can_msg) -{ - can_message_q_.push(can_msg); -} - -/** -* @brief Return first openxc_VehicleMessage on the queue -* -* @return a openxc_VehicleMessage containing a decoded can message -*/ -openxc_VehicleMessage can_bus_t::next_vehicle_message() -{ - openxc_VehicleMessage v_msg; - - if(! vehicle_message_q_.empty()) - { - v_msg = vehicle_message_q_.front(); - vehicle_message_q_.pop(); - DEBUG(binder_interface, "next_vehicle_message: next vehicle message poped"); - return v_msg; - } - - return v_msg; -} - -/** -* @brief Push a openxc_VehicleMessage into the queue -* -* @param the const reference openxc_VehicleMessage object to push into the queue -*/ -void can_bus_t::push_new_vehicle_message(const openxc_VehicleMessage& v_msg) -{ - vehicle_message_q_.push(v_msg); -} - -/** -* @brief Return a map with the can_bus_dev_t initialized -* -* @return map can_bus_dev_m_ map -*/ -std::map> can_bus_t::get_can_devices() -{ - return can_devices_m_; -} - -/******************************************************************************** -* -* can_bus_dev_t method implementation -* -*********************************************************************************/ -/** -* @brief Class constructor -* -* @param const string representing the device name into the linux /dev tree -*/ -can_bus_dev_t::can_bus_dev_t(const std::string &dev_name) - : device_name_{dev_name}, can_socket_{-1} -{ -} - -/** -* @brief Open the can socket and returning it -* -* @return -*/ -int can_bus_dev_t::open() -{ - const int canfd_on = 1; - const int timestamp_on = 1; - struct ifreq ifr; - struct timeval timeout; - - DEBUG(binder_interface, "CAN Handler socket : %d", can_socket_); - if (can_socket_ >= 0) - return 0; - - can_socket_ = ::socket(PF_CAN, SOCK_RAW, CAN_RAW); - DEBUG(binder_interface, "CAN Handler socket correctly initialized : %d", can_socket_); - if (can_socket_ < 0) - ERROR(binder_interface, "socket could not be created. Error was : %s", ::strerror(errno)); - else - { - /* Set timeout for read */ - ::setsockopt(can_socket_, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); - /* Set timestamp for receveid frame */ - if (::setsockopt(can_socket_, SOL_SOCKET, SO_TIMESTAMP, ×tamp_on, sizeof(timestamp_on)) < 0) - WARNING(binder_interface, "setsockopt SO_TIMESTAMP error: %s", ::strerror(errno)); - DEBUG(binder_interface, "Switch CAN Handler socket to use fd mode"); - /* try to switch the socket into CAN_FD mode */ - if (::setsockopt(can_socket_, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on)) < 0) - { - NOTICE(binder_interface, "Can not switch into CAN Extended frame format."); - is_fdmode_on_ = false; - } else { - DEBUG(binder_interface, "Correctly set up CAN socket to use FD frames."); - is_fdmode_on_ = true; - } - - /* Attempts to open a socket to CAN bus */ - ::strcpy(ifr.ifr_name, device_name_.c_str()); - DEBUG(binder_interface, "ifr_name is : %s", ifr.ifr_name); - if(::ioctl(can_socket_, SIOCGIFINDEX, &ifr) < 0) - ERROR(binder_interface, "ioctl failed. Error was : %s", strerror(errno)); - else - { - txAddress_.can_family = AF_CAN; - txAddress_.can_ifindex = ifr.ifr_ifindex; - - /* And bind it to txAddress */ - DEBUG(binder_interface, "Bind the socket"); - if (::bind(can_socket_, (struct sockaddr *)&txAddress_, sizeof(txAddress_)) < 0) - ERROR(binder_interface, "Bind failed. %s", strerror(errno)); - else - return 0; - } - close(); - } - return -1; -} - -/** - * @brief Open the can socket and returning it - * - * @return - */ -int can_bus_dev_t::close() -{ - ::close(can_socket_); - can_socket_ = -1; - return can_socket_; -} - -/** -* @brief Read the can socket and retrieve canfd_frame -* -* @param const struct afb_binding_interface* interface pointer. Used to be able to log -* using application framework logger. -*/ -std::pair can_bus_dev_t::read() -{ - ssize_t nbytes; - //int maxdlen; - struct canfd_frame cfd; - - /* Test that socket is really opened */ - if (can_socket_ < 0) - { - ERROR(binder_interface, "read_can: Socket unavailable. Closing thread."); - is_running_ = false; - } - - nbytes = ::read(can_socket_, &cfd, CANFD_MTU); - - /* if we did not fit into CAN sized messages then stop_reading. */ - if (nbytes != CANFD_MTU && nbytes != CAN_MTU) - { - if (errno == ENETDOWN) - ERROR(binder_interface, "read: %s CAN device down", device_name_); - ERROR(binder_interface, "read: Incomplete CAN(FD) frame"); - ::memset(&cfd, 0, sizeof(cfd)); - } - - DEBUG(binder_interface, "read: Found id: %X, length: %X, data %02X%02X%02X%02X%02X%02X%02X%02X", cfd.can_id, cfd.len, - cfd.data[0], cfd.data[1], cfd.data[2], cfd.data[3], cfd.data[4], cfd.data[5], cfd.data[6], cfd.data[7]); - return std::pair(cfd, nbytes); -} - -/** -* @brief start reading threads and set flag is_running_ -* -* @param can_bus_t reference can_bus_t. it will be passed to the thread -* to allow using can_bus_t queue. -*/ -void can_bus_dev_t::start_reading(can_bus_t& can_bus) -{ - DEBUG(binder_interface, "Launching reading thread"); - is_running_ = true; - th_reading_ = std::thread(&can_bus_dev_t::can_reader, this, std::ref(can_bus)); - if(!th_reading_.joinable()) - is_running_ = false; -} - -/** -* @brief stop the reading thread setting flag is_running_ to false and -* and wait that the thread finish its job. -*/ -void can_bus_dev_t::stop_reading() -{ - is_running_ = false; -} - -/** -* -* @brief Thread function used to read the can socket. -* -* @param[in] can_bus_dev_t object to be used to read the can socket -* @param[in] can_bus_t object used to fill can_message_q_ queue -*/ -void can_bus_dev_t::can_reader(can_bus_t& can_bus) -{ - can_message_t can_message; - - while(is_running_) - { - can_message.convert_from_canfd_frame(read()); - - { - std::lock_guard can_message_lock(can_bus.get_can_message_mutex()); - can_bus.push_new_can_message(can_message); - } - can_bus.get_new_can_message_cv_().notify_one(); - } -} - -/** -* @brief Send a can message from a can_message_t object. -* -* @param const can_message_t& can_msg: the can message object to send -* @param const struct afb_binding_interface* interface pointer. Used to be able to log -* using application framework logger. -*/ -int can_bus_dev_t::send_can_message(can_message_t& can_msg) -{ - ssize_t nbytes; - canfd_frame f; - - f = can_msg.convert_to_canfd_frame(); - - if(can_socket_ >= 0) - { - nbytes = ::sendto(can_socket_, &f, sizeof(struct canfd_frame), 0, - (struct sockaddr*)&txAddress_, sizeof(txAddress_)); - if (nbytes == -1) - { - ERROR(binder_interface, "send_can_message: Sending CAN frame failed."); - return -1; - } - return (int)nbytes; - } - else - { - ERROR(binder_interface, "send_can_message: socket not initialized. Attempt to reopen can device socket."); - open(); - } - return 0; -} \ No newline at end of file diff --git a/src/can-bus.hpp b/src/can-bus.hpp deleted file mode 100644 index bc2bb04c..00000000 --- a/src/can-bus.hpp +++ /dev/null @@ -1,172 +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 -#include - -#include "timer.hpp" -#include "openxc.pb.h" -#include "can-signals.hpp" -#include "can-message.hpp" -#include "low-can-binding.hpp" - -// TODO actual max is 32 but dropped to 24 for memory considerations -#define MAX_ACCEPTANCE_FILTERS 24 -// TODO this takes up a ton of memory -#define MAX_DYNAMIC_MESSAGE_COUNT 12 - -#define CAN_ACTIVE_TIMEOUT_S 30 - -class can_bus_dev_t; - -/** - * @class can_bus_t - * @brief Object used to handle decoding and manage event queue to be pushed. - * - * This object is also used to initialize can_bus_dev_t object after reading - * json conf file describing the CAN devices to use. Thus, those object will read - * on the device the CAN frame and push them into the can_bus_t can_message_q_ queue. - * - * That queue will be later used to be decoded and pushed to subscribers. - */ -class can_bus_t { - private: - int conf_file_; /*!< conf_file_ - configuration file handle used to initialize can_bus_dev_t objects.*/ - - void can_decode_message(); - std::thread th_decoding_; /*!< thread that'll handle decoding a can frame */ - bool is_decoding_; /*!< boolean member controling thread while loop*/ - - void can_event_push(); - std::thread th_pushing_; /*!< thread that'll handle pushing decoded can frame to subscribers */ - bool is_pushing_; /*!< boolean member controling thread while loop*/ - - std::condition_variable new_can_message_cv_; /*!< condition_variable use to wait until there is a new CAN message to read*/ - std::mutex can_message_mutex_; /*!< mutex protecting the can_message_q_ queue.*/ - std::queue can_message_q_; /*!< queue that'll store can_message_t to decoded */ - - std::condition_variable new_decoded_can_message_; /*!< condition_variable use to wait until there is a new vehicle message to read from the queue vehicle_message_q_*/ - std::mutex decoded_can_message_mutex_; /*!< mutex protecting the vehicle_message_q_ queue.*/ - std::queue vehicle_message_q_; /*!< queue that'll store openxc_VehicleMessage to pushed */ - - std::map> can_devices_m_; /*!< Can device map containing all can_bus_dev_t objects initialized during init_can_dev function*/ - - public: - can_bus_t(int conf_file); - - int init_can_dev(); - std::vector read_conf(); - - - void start_threads(); - void stop_threads(); - - can_message_t next_can_message(); - void push_new_can_message(const can_message_t& can_msg); - std::mutex& get_can_message_mutex(); - std::condition_variable& get_new_can_message_cv_(); - - openxc_VehicleMessage next_vehicle_message(); - void push_new_vehicle_message(const openxc_VehicleMessage& v_msg); - - std::map> get_can_devices(); -}; - - -/** - * @class can_bus_dev_t - * - * @brief Object representing a can device. Handle opening, closing and reading on the - * socket. This is the low level object to be use by can_bus_t. - */ -class can_bus_dev_t { - private: - int32_t id_; /*!< int32_t id_ - an identifier used through binding that refer to that device*/ - std::string device_name_; /*!< std::string device_name_ - name of the linux device handling the can bus. Generally vcan0, can0, etc. */ - int can_socket_; /*!< socket handler for the can device */ - bool is_fdmode_on_; /*!< boolean telling if whether or not the can socket use fdmode. */ - struct sockaddr_can txAddress_; /*!< internal member using to bind to the socket */ - - std::thread th_reading_; /*!< Thread handling read the socket can device filling can_message_q_ queue of can_bus_t */ - bool is_running_; /*!< boolean telling whether or not reading is running or not */ - - void can_reader(can_bus_t& can_bus); - - public: - can_bus_dev_t(const std::string& dev_name); - - int open(); - int close(); - - - void start_reading(can_bus_t& can_bus); - - void stop_reading(); - - std::pair read(); - - int send_can_message(can_message_t& can_msg); -}; - -/** TODO: implement this function as method into can_bus class - * @fn void pre_initialize(can_bus_dev_t* bus, bool writable, can_bus_dev_t* buses, const int busCount); - * @brief Pre initialize actions made before CAN bus initialization - * - * @param[in] can_bus_dev_t bus - A CanBus struct defining the bus's metadata - * @param[in] bool writable - configure the controller in a writable mode. If false, it will be - * configured as "listen only" and will not allow writes or even CAN ACKs. - * @param[in] buses - An array of all CAN buses. - * @param[in] int busCount - The length of the buses array. - */ -void pre_initialize(can_bus_dev_t* bus, bool writable, can_bus_dev_t* buses, const int busCount); - -/** TODO: implement this function as method into can_bus class - * @fn void post_initialize(can_bus_dev_t* bus, bool writable, can_bus_dev_t* buses, const int busCount); - * @brief Post-initialize actions made after CAN bus initialization - * - * @param[in] bus - A CanBus struct defining the bus's metadata - * @param[in] writable - configure the controller in a writable mode. If false, it will be - * configured as "listen only" and will not allow writes or even CAN ACKs. - * @param[in] buses - An array of all CAN buses. - * @param[in] busCount - The length of the buses array. - */ -void post_initialize(can_bus_dev_t* bus, bool writable, can_bus_dev_t* buses, const int busCount); - -/** TODO: implement this function as method into can_bus class - * @fn bool isBusActive(can_bus_dev_t* bus); - * @brief Check if the device is connected to an active CAN bus, i.e. it's - * received a message in the recent past. - * - * @return true if a message was received on the CAN bus within - * CAN_ACTIVE_TIMEOUT_S seconds. - */ -bool isBusActive(can_bus_dev_t* bus); - -/** TODO: implement this function as method into can_bus class - * - * @fn void logBusStatistics(can_bus_dev_t* buses, const int busCount); - * @brief Log transfer statistics about all active CAN buses to the debug log. - * - * @param[in] buses - an array of active CAN buses. - * @param[in] busCount - the length of the buses array. - */ -void logBusStatistics(can_bus_dev_t* buses, const int busCount); \ No newline at end of file diff --git a/src/can-command.hpp b/src/can-command.hpp deleted file mode 100644 index 62b56c1e..00000000 --- a/src/can-command.hpp +++ /dev/null @@ -1,66 +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 "openxc.pb.h" -#include "can-signals.hpp" - -/** - * @brief The type signature for a function to handle a custom OpenXC command. - * - * @param[in] char* name - the name of the received command. - * @param[in] openxc_DynamicField* value - the value of the received command, in a DynamicField. The actual type - * may be a number, string or bool. - * @param[in] openxc_DynamicField* event - an optional event from the received command, in a DynamicField. The - * actual type may be a number, string or bool. - * @param[in] CanSignal* signals - The list of all signals. - * @param[in] int signalCount - The length of the signals array. - */ -typedef void (*CommandHandler)(const char* name, openxc_DynamicField* value, - openxc_DynamicField* event, CanSignal* signals, int signalCount); - -/* @struct CanCommand - * @brief The structure to represent a supported custom OpenXC command. - * - * @desc For completely customized CAN commands without a 1-1 mapping between an - * OpenXC message from the host and a CAN signal, you can define the name of the - * command and a custom function to handle it in the VI. An example is - * the "turn_signal_status" command in OpenXC, which has a value of "left" or - * "right". The vehicle may have separate CAN signals for the left and right - * turn signals, so you will need to implement a custom command handler to send - * the correct signals. - * - * Command handlers are also useful if you want to trigger multiple CAN messages - * or signals from a signal OpenXC message. - */ -typedef struct { - const char* generic_name; /*!< generic_name - The name of the command.*/ - CommandHandler handler; /*!< handler - An function to process the received command's data and perform some - * action.*/ -} CanCommand; - -/* Public: Return an array of all OpenXC CAN commands enabled in the active - * configuration that can write back to CAN with a custom handler. - * - * * Commands not defined here are handled using a 1-1 mapping from the signals - * list. - * */ -CanCommand* getCommands(); - -/* Public: Return the length of the array returned by getCommandCount(). */ -int getCommandCount(); \ No newline at end of file diff --git a/src/can-decoder.cpp b/src/can-decoder.cpp deleted file mode 100644 index 03471398..00000000 --- a/src/can-decoder.cpp +++ /dev/null @@ -1,119 +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. - */ - -#include "can-decoder.hpp" -#include "canutil/read.h" - -float decoder_t::parseSignalBitfield(CanSignal& signal, const can_message_t& message) -{ - return bitfield_parse_float(message.get_data(), CAN_MESSAGE_SIZE, - signal.bitPosition, signal.bitSize, signal.factor, - signal.offset); -} - -openxc_DynamicField decoder_t::noopDecoder(CanSignal& signal, - const std::vector& signals, float value, bool* send) -{ - openxc_DynamicField decoded_value; - decoded_value.has_type = true; - decoded_value.type = openxc_DynamicField_Type_NUM; - decoded_value.has_numeric_value = true; - decoded_value.numeric_value = value; - - return decoded_value; -} - -openxc_DynamicField decoder_t::booleanDecoder(CanSignal& signal, - const std::vector& signals, float value, bool* send) -{ - openxc_DynamicField decoded_value; - decoded_value.has_type = true; - decoded_value.type = openxc_DynamicField_Type_BOOL; - decoded_value.has_boolean_value = true; - decoded_value.boolean_value = value == 0.0 ? false : true; - - return decoded_value; -} - -openxc_DynamicField decoder_t::ignoreDecoder(CanSignal& signal, - const std::vector& signals, float value, bool* send) -{ - if(send) - *send = false; - - openxc_DynamicField decoded_value = {0, openxc_DynamicField_Type_BOOL, 0, "", 0, 0, 0, 0}; - - return decoded_value; -} - -openxc_DynamicField decoder_t::stateDecoder(CanSignal& signal, - const std::vector& signals, float value, bool* send) -{ - openxc_DynamicField decoded_value = {0, openxc_DynamicField_Type_BOOL, 0, "", 0, 0, 0, 0}; - decoded_value.has_type = true; - decoded_value.type = openxc_DynamicField_Type_STRING; - decoded_value.has_string_value = true; - - /* TODO: Handle SignalState - const CanSignalState* signalState = lookupSignalState(value, signal); - if(signalState != NULL) { - ::strcpy(decoded_value.string_value, signalState->name); - } else { - *send = false; - }*/ - return decoded_value; -} - -openxc_DynamicField decoder_t::translateSignal(CanSignal& signal, can_message_t& message, - const std::vector& signals) -{ - if(&signal == nullptr || &message == nullptr) - { - openxc_DynamicField ret = {0, openxc_DynamicField_Type_BOOL, 0, "", 0, 0, 0, 0}; - return ret; - } - - float value = parseSignalBitfield(signal, message); - DEBUG(binder_interface, "translateSignal: Decoded message: %f", value); - - bool send = true; - // Must call the decoders every time, regardless of if we are going to - // decide to send the signal or not. - openxc_DynamicField decoded_value = decodeSignal(signal, - value, signals, &send); - - signal.received = true; - signal.lastValue = value; - return decoded_value; -} - -openxc_DynamicField decoder_t::decodeSignal( CanSignal& signal, - float value, const std::vector& signals, bool* send) -{ - SignalDecoder decoder = signal.decoder == NULL ? - noopDecoder : signal.decoder; - openxc_DynamicField decoded_value = decoder(signal, signals, - value, send); - return decoded_value; -} - -openxc_DynamicField decoder_t::decodeSignal( CanSignal& signal, - const can_message_t& message, const std::vector& signals, bool* send) -{ - float value = parseSignalBitfield(signal, message); - return decodeSignal(signal, value, signals, send); -} diff --git a/src/can-decoder.hpp b/src/can-decoder.hpp deleted file mode 100644 index e7dfff86..00000000 --- a/src/can-decoder.hpp +++ /dev/null @@ -1,161 +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 "can-bus.hpp" - -class decoder_t -{ - private: - - public: - /* Public: Parse the signal's bitfield from the given data and return the raw - * value. - * - * signal - The signal to parse from the data. - * data - The data to parse the signal from. - * length - The length of the data array. - * - * Returns the raw value of the signal parsed as a bitfield from the given byte - * array. - */ - float parseSignalBitfield(CanSignal& signal, const can_message_t& message); - - /* Public: Find and return the corresponding string state for a CAN signal's - * raw integer value. - * - * This is an implementation of the SignalDecoder type signature, and can be - * used directly in the CanSignal.decoder field. - * - * signal - The details of the signal that contains the state mapping. - * signals - The list of all signals. - * signalCount - the length of the signals array. - * value - The numerical value that should map to a state. - * send - An output argument that will be set to false if the value should - * not be sent for any reason. - * - * Returns a DynamicField with a string value if a matching state is found in - * the signal. If an equivalent isn't found, send is sent to false and the - * return value is undefined. - */ - static openxc_DynamicField stateDecoder(CanSignal& signal, const std::vector& signals, - float value, bool* send); - - /* Public: Coerces a numerical value to a boolean. - * - * This is an implementation of the SignalDecoder type signature, and can be - * used directly in the CanSignal.decoder field. - * - * signal - The details of the signal that contains the state mapping. - * signals - The list of all signals - * signalCount - The length of the signals array - * value - The numerical value that will be converted to a boolean. - * send - An output argument that will be set to false if the value should - * not be sent for any reason. - * - * Returns a DynamicField with a boolean value of false if the raw signal value - * is 0.0, otherwise true. The 'send' argument will not be modified as this - * decoder always succeeds. - */ - static openxc_DynamicField booleanDecoder(CanSignal& signal, const std::vector& signals, - float value, bool* send); - - /* Public: Update the metadata for a signal and the newly received value. - * - * This is an implementation of the SignalDecoder type signature, and can be - * used directly in the CanSignal.decoder field. - * - * This function always flips 'send' to false. - * - * signal - The details of the signal that contains the state mapping. - * signals - The list of all signals. - * signalCount - The length of the signals array. - * value - The numerical value that will be converted to a boolean. - * send - This output argument will always be set to false, so the caller will - * know not to publish this value to the pipeline. - * - * The return value is undefined. - */ - openxc_DynamicField ignoreDecoder(CanSignal& signal, const std::vector& signals, - float value, bool* send); - - /* Public: Wrap a raw CAN signal value in a DynamicField without modification. - * - * This is an implementation of the SignalDecoder type signature, and can be - * used directly in the CanSignal.decoder field. - * - * signal - The details of the signal that contains the state mapping. - * signals - The list of all signals - * signalCount - The length of the signals array - * value - The numerical value that will be wrapped in a DynamicField. - * send - An output argument that will be set to false if the value should - * not be sent for any reason. - * - * Returns a DynamicField with the original, unmodified raw CAN signal value as - * its numeric value. The 'send' argument will not be modified as this decoder - * always succeeds. - */ - static openxc_DynamicField noopDecoder(CanSignal& signal, const std::vector& signals, - float value, bool* send); - - - /* Public: Parse a signal from a CAN message, apply any required transforations - * to get a human readable value and public the result to the pipeline. - * - * If the CanSignal has a non-NULL 'decoder' field, the raw CAN signal value - * will be passed to the decoder before publishing. - * - * signal - The details of the signal to decode and forward. - * message - The received CAN message that should contain this signal. - * signals - an array of all active signals. - * - * The decoder returns an openxc_DynamicField, which may contain a number, - * string or boolean. - */ - openxc_DynamicField translateSignal(CanSignal& signal, can_message_t& message, - const std::vector& signals); - - /* Public: Parse a signal from a CAN message and apply any required - * transforations to get a human readable value. - * - * If the CanSignal has a non-NULL 'decoder' field, the raw CAN signal value - * will be passed to the decoder before returning. - * - * signal - The details of the signal to decode and forward. - * message - The CAN message that contains the signal. - * signals - an array of all active signals. - * send - An output parameter that will be flipped to false if the value could - * not be decoded. - * - * The decoder returns an openxc_DynamicField, which may contain a number, - * string or boolean. If 'send' is false, the return value is undefined. - */ - openxc_DynamicField decodeSignal(CanSignal& signal, const can_message_t& message, - const std::vector& signals, bool* send); - - /* Public: Decode a transformed, human readable value from an raw CAN signal - * already parsed from a CAN message. - * - * This is the same as decodeSignal(const CanSignal&, CanMessage*, const CanSignal&, int, - * bool*) but you must parse the bitfield value of the signal from the CAN - * message yourself. This is useful if you need that raw value for something - * else. - */ - openxc_DynamicField decodeSignal(CanSignal& signal, float value, - const std::vector& signals, bool* send); -}; \ No newline at end of file diff --git a/src/can-message.cpp b/src/can-message.cpp deleted file mode 100644 index ad032021..00000000 --- a/src/can-message.cpp +++ /dev/null @@ -1,313 +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. - */ - -#include "can-message.hpp" - -#include - -#include "low-can-binding.hpp" - -/******************************************************************************** -* -* CanMessage method implementation -* -*********************************************************************************/ -/** -* @brief Class constructor -* -* Constructor about can_message_t class. -*/ -can_message_t::can_message_t() - : id_{0}, rtr_flag_{false}, length_{0}, flags_{0}, format_{CanMessageFormat::ERROR} -{} - -/** -* @brief Retrieve id_ member value. -* -* @return uint32_t id_ class member -*/ -uint32_t can_message_t::get_id() const -{ - return id_; -} - -/** -* @brief Retrieve RTR flag member. -* -* @return bool rtr_flags_ class member -*/ -bool can_message_t::get_rtr_flag_() const -{ - return rtr_flag_; -} - -/** -* @brief Retrieve format_ member value. -* -* @return CanMessageFormat format_ class member -*/ -int can_message_t::get_format() const -{ - if (format_ != CanMessageFormat::STANDARD || format_ != CanMessageFormat::EXTENDED) - return CanMessageFormat::ERROR; - return format_; -} - -/** -* @brief Retrieve format_ member value. -* -* @return CanMessageFormat format_ class member -*/ -uint8_t can_message_t::get_flags() const -{ - return flags_; -} - -/** -* @brief Retrieve data_ member value. -* -* @return uint8_t data_ pointer to the first element -* of class member data_ -*/ -const uint8_t* can_message_t::get_data() const -{ - return data_.data(); -} - -/** -* @brief Retrieve length_ member value. -* -* @return uint8_t length_ class member -*/ -uint8_t can_message_t::get_length() const -{ - return length_; -} - -void can_message_t::set_max_data_length(size_t nbytes) -{ - maxdlen_ = 0; - - switch(nbytes) - { - case CANFD_MTU: - DEBUG(binder_interface, "set_max_data_length: Got an CAN FD frame"); - maxdlen_ = CANFD_MAX_DLEN; - break; - case CAN_MTU: - DEBUG(binder_interface, "set_max_data_length: Got a legacy CAN frame"); - maxdlen_ = CAN_MAX_DLEN; - break; - default: - ERROR(binder_interface, "set_max_data_length: unsupported CAN frame"); - break; - } -} - -/** -* @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... -*/ -bool can_message_t::is_correct_to_send() -{ - if (id_ != 0 && length_ != 0 && format_ != CanMessageFormat::ERROR) - { - int i; - for(i=0;i maxdlen_) ? maxdlen_ : new_length; - } -} - -/** -* @brief Set data_ member value. -* -* Preferred way to initialize these members by using -* convert_from_canfd_frame method. -* -* @param uint8_t data_ array with a max size of 8 elements. -*/ -void can_message_t::set_data(const __u8* new_data) -{ - int i; - - data_.clear(); - /* maxdlen_ is now set at CAN_MAX_DLEN or CANFD_MAX_DLEN, respectively 8 and 64 bytes*/ - for(i=0;iargs) -{ - // May be it's overkill to assign member of the pair... May be it will change... - struct canfd_frame frame = args.first; - size_t nbytes = args.second; - set_max_data_length(nbytes); - set_length(frame.len); - set_id_and_format(frame.can_id); - - /* Overwrite lenght_ if RTR flags is detected. - * standard CAN frames may have RTR enabled. There are no ERR frames with RTR */ - if (frame.can_id & CAN_RTR_FLAG) - { - rtr_flag_ = true; - if(frame.len && frame.len <= CAN_MAX_DLC) - set_length(frame.len); - return; - } - - /* Flags field only present for CAN FD frames*/ - if(maxdlen_ == CANFD_MAX_DLEN) - set_flags(frame.flags); - - if ( data_.capacity() < maxdlen_) - data_.reserve(maxdlen_); - set_data(frame.data); - - DEBUG(binder_interface, "convert_from_canfd_frame: Found id: %X, format: %X, length: %X, data %02X%02X%02X%02X%02X%02X%02X%02X", id_, format_, length_, - data_[0], data_[1], data_[2], data_[3], data_[4], data_[5], data_[6], data_[7]); -} - -/** -* @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. -*/ -canfd_frame can_message_t::convert_to_canfd_frame() -{ - canfd_frame frame; - - if(is_correct_to_send()) - { - frame.can_id = get_id(); - frame.len = get_length(); - ::memcpy(frame.data, get_data(), length_); - } - else - ERROR(binder_interface, "can_message_t not correctly initialized to be sent"); - - return frame; -} - diff --git a/src/can-message.hpp b/src/can-message.hpp deleted file mode 100644 index 9f6a36da..00000000 --- a/src/can-message.hpp +++ /dev/null @@ -1,151 +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 - -#include "timer.hpp" - -#define CAN_MESSAGE_SIZE 8 - -/** - * @enum CanMessageFormat - * @brief The ID format for a CAN message. - */ -enum CanMessageFormat { - STANDARD, /*!< STANDARD - standard 11-bit CAN arbitration ID. */ - EXTENDED, /*!< EXTENDED - an extended frame, with a 29-bit arbitration ID. */ - ERROR, /*!< ERROR - ERROR code used at initialization to signify that it isn't usable'*/ -}; -typedef enum CanMessageFormat CanMessageFormat; - -/** - * @class can_message_t - * - * @brief A compact representation of a single CAN message, meant to be used in in/out - * buffers. - */ - -/************************* - * old CanMessage struct * - ************************* -struct CanMessage { - uint32_t id; - CanMessageFormat format; - uint8_t data[CAN_MESSAGE_SIZE]; - uint8_t length; -}; -typedef struct CanMessage CanMessage; -*/ -class can_message_t { - private: - uint32_t id_; /*!< uint32_t id - The ID of the message. */ - bool rtr_flag_; /*!< bool rtr_flag - Telling if the frame has RTR flag positionned. Then frame hasn't data field*/ - uint8_t length_; /*!< uint8_t length - the length of the data array (max 8). */ - uint8_t flags_; /*!< unint8_t flags of a CAN FD frame. Needed if we catch FD frames.*/ - CanMessageFormat format_; /*!< CanMessageFormat format - the format of the message's ID.*/ - std::vector data_; /*!< uint8_t data - The message's data field with a size of 8 which is the standard about CAN bus messages.*/ - - uint8_t maxdlen_; - - public: - can_message_t(); - - uint32_t get_id() const; - bool get_rtr_flag_() const; - int get_format() const; - uint8_t get_flags() const; - const uint8_t* get_data() const; - uint8_t get_length() const; - - void set_max_data_length(size_t nbytes); - void set_id_and_format(const uint32_t new_id); - void set_format(const CanMessageFormat new_format); - void set_format(const uint32_t can_id); - void set_flags(const uint8_t flags); - void set_data(const __u8* new_data); - void set_length(const uint8_t new_length); - - bool is_correct_to_send(); - - void convert_from_canfd_frame(const std::pairargs); - canfd_frame convert_to_canfd_frame(); -}; - -/** - * @struct CanMessageDefinition - * - * @brief The definition of a CAN message. This includes a lot of metadata, so - * to save memory this struct should not be used for storing incoming and - * outgoing CAN messages. - */ -struct CanMessageDefinition { - //can_bus_dev_t bus; /*!< bus - A pointer to the bus this message is on. */ - uint32_t id; /*!< id - The ID of the message.*/ - CanMessageFormat format; /*!< format - the format of the message's ID.*/ - FrequencyClock frequencyClock; /*!< 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 retriec++ if ? syntaxve.*/ - bool forceSendChanged; /*!< forceSendChanged - If true, regardless of the frequency, it will send CAN - * message if it has changed when using raw passthrough.*/ - uint8_t lastValue[CAN_MESSAGE_SIZE]; /*!< lastValue - 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.*/ -}; -typedef struct CanMessageDefinition CanMessageDefinition; - -/** - * @struct CanMessageSet - * - * @brief A parent wrapper for a particular set of CAN messages and associated - * CAN buses(e.g. a vehicle or program). - */ - typedef struct { - uint8_t index; /*! - * - * 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 "can-signals.hpp" - -#include - -#include "signals.hpp" -#include "obd2-signals.hpp" -#include "can-decoder.hpp" -#include "low-can-binding.hpp" - -std::vector> CAN_MESSAGES = { - { - {0x620, CanMessageFormat::STANDARD, {10, 0, nullptr}, false, (uint8_t)NULL}, - {0x442, CanMessageFormat::STANDARD, {10, 0, nullptr}, false, (uint8_t)NULL}, - }, -}; - -/** - * @brief Dumb SIGNALS array. It is composed by CanMessageSet - * SIGNALS[MESSAGE_SET_ID][CanSignal] - */ -std::vector> SIGNALS = { - { - {&(CAN_MESSAGES[MESSAGE_SET_ID][0]), "can.driver_door.open", 2, 4, 1.000000, 0.000000, 0.000000, 0.000000, {10, 0, nullptr}, false, true, nullptr, 0, false, decoder_t::booleanDecoder, nullptr, false, 0.0}, - {&(CAN_MESSAGES[MESSAGE_SET_ID][1]), "can.driver_door.close", 0, 4, 1.000000, 0.000000, 0.000000, 0.000000, {10, 0, nullptr}, false, true, nullptr, 0, false, decoder_t::booleanDecoder, nullptr, false, 0.0} - }, -}; - -/** - * @fn std::vector& get_can_signals() - * - * @return A reference to a vector of signals - */ -std::vector& get_can_signals() -{ - return SIGNALS[MESSAGE_SET_ID]; -} - -/** - * @fn size_t getSignalCount() - * - * @return the length of the array returned by get_can_signals(). - */ -size_t getSignalCount() -{ - return SIGNALS[MESSAGE_SET_ID].size(); -} - -/** - * @brief Retrieve can arbitration id of a given CanSignal - * - * @param[in] CanSignal& - a const reference to a CanSignal - * - * @return uint32_t - unsigned integer representing the arbitration id. - */ -uint32_t get_signal_id(const CanSignal& sig) -{ - return sig.message->id; -} - -/** - * @fn void find_can_signals(const openxc_DynamicField& key, std::vector& found_signals) - * @brief return signals name found searching through CAN_signals and OBD2 pid - * - * @param[in] const openxc_DynamicField : can contain numeric or string value in order to search against - * can signals or obd2 signals name. - * @param[out] std::vector& found_signals : provided vector to fill with ponter to signals matched. - * - */ -void find_can_signals(const openxc_DynamicField& key, std::vector& found_signals) -{ - switch(key.type) - { - case openxc_DynamicField_Type::openxc_DynamicField_Type_STRING: - lookup_signals_by_name(key.string_value, get_can_signals(), found_signals); - break; - case openxc_DynamicField_Type::openxc_DynamicField_Type_NUM: - lookup_signals_by_id(key.numeric_value, get_can_signals(), found_signals); - break; - default: - ERROR(binder_interface, "find_signals: wrong openxc_DynamicField specified. Use openxc_DynamicField_Type_NUM or openxc_DynamicField_Type_STRING type only."); - break; - } - DEBUG(binder_interface, "Found %d signal(s)", (int)found_signals.size()); -} diff --git a/src/can-signals.hpp b/src/can-signals.hpp deleted file mode 100644 index 250ff197..00000000 --- a/src/can-signals.hpp +++ /dev/null @@ -1,142 +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 -#include - -#include "obd2-signals.hpp" -#include "timer.hpp" -#include "openxc.pb.h" -#include "can-bus.hpp" -#include "can-message.hpp" - -extern "C" -{ - #include - #include -} - -#define MESSAGE_SET_ID 0 - -extern std::mutex subscribed_signals_mutex; -std::mutex& get_subscribed_signals_mutex(); - -/** - * @brief return the subscribed_signals map. - * - * return std::map - map of subscribed signals. - */ -extern std::map subscribed_signals; -std::map& get_subscribed_signals(); - -/** - * @brief The type signature for a CAN signal decoder. - * - * @desc A SignalDecoder transforms a raw floating point CAN signal into a number, - * string or boolean. - * - * @param[in] CanSignal signal - The CAN signal that we are decoding. - * @param[in] CanSignal signals - The list of all signals. - * @param[in] int signalCount - The length of the signals array. - * @param[in] float value - The CAN signal parsed from the message as a raw floating point - * value. - * @param[out] bool send - An output parameter. If the decoding failed or the CAN signal should - * not send for some other reason, this should be flipped to false. - * - * @return a decoded value in an openxc_DynamicField struct. - */ -typedef openxc_DynamicField (*SignalDecoder)(struct CanSignal& signal, - const std::vector& signals, float value, bool* send); - -/** - * @brief: The type signature for a CAN signal encoder. - * - * @desc A SignalEncoder transforms a number, string or boolean into a raw floating - * point value that fits in the CAN signal. - * - * @params[signal] - The CAN signal to encode. - * @params[value] - The dynamic field to encode. - * @params[send] - An output parameter. If the encoding failed or the CAN signal should - * not be encoded for some other reason, this will be flipped to false. - */ -typedef uint64_t (*SignalEncoder)(struct CanSignal* signal, - openxc_DynamicField* value, bool* send); - -/** - * @struct CanSignalState - * - * @brief A state encoded (SED) signal's mapping from numerical values to - * OpenXC state names. - */ -struct CanSignalState { - const int value; /*!< int value - The integer value of the state on the CAN bus.*/ - const char* name; /*!< char* name - The corresponding string name for the state in OpenXC. */ -}; -typedef struct CanSignalState CanSignalState; - -/** - * @struct CanSignal - * - * @brief A CAN signal to decode from the bus and output over USB. - */ -struct CanSignal { - struct CanMessageDefinition* message; /*!< message - The message this signal is a part of. */ - const char* generic_name; /*!< generic_name - The name of the signal to be output over USB.*/ - uint8_t bitPosition; /*!< bitPosition - The starting bit of the signal in its CAN message (assuming - * non-inverted bit numbering, i.e. the most significant bit of - * each byte is 0) */ - uint8_t bitSize; /*!< bitSize - The width of the bit field in the CAN message. */ - float factor; /*!< factor - The final value will be multiplied by this factor. Use 1 if you - * don't need a factor. */ - float offset; /*!< offset - The final value will be added to this offset. Use 0 if you - * don't need an offset. */ - float minValue; /*!< minValue - The minimum value for the processed signal.*/ - float maxValue; /*!< maxValue - The maximum value for the processed signal. */ - FrequencyClock frequencyClock; /*!< frequencyClock - A FrequencyClock struct to control the maximum frequency to - * process and send this signal. To process every value, set the - * clock's frequency to 0. */ - bool sendSame; /*!< sendSame - If true, will re-send even if the value hasn't changed.*/ - bool forceSendChanged; /*!< forceSendChanged - If true, regardless of the frequency, it will send the - * value if it has changed. */ - const CanSignalState* states; /*!< states - An array of CanSignalState describing the mapping - * between numerical and string values for valid states. */ - uint8_t stateCount; /*!< stateCount - The length of the states array. */ - bool writable; /*!< writable - True if the signal is allowed to be written from the USB host - * back to CAN. Defaults to false.*/ - SignalDecoder decoder; /*!< decoder - An optional function to decode a signal from the bus to a human - * readable value. If NULL, the default numerical decoder is used. */ - SignalEncoder encoder; /*!< encoder - An optional function to encode a signal value to be written to - * CAN into a byte array. If NULL, the default numerical encoder - * is used. */ - bool received; /*!< received - True if this signal has ever been received.*/ - float lastValue; /*!< lastValue - The last received value of the signal. If 'received' is false, - * this value is undefined. */ -}; -typedef struct CanSignal CanSignal; - -std::vector& get_can_signals(); - -size_t getSignalCount(); - -void find_can_signals(const openxc_DynamicField &key, std::vector& found_signals); - -uint32_t get_signal_id(const CanSignal& sig); \ No newline at end of file diff --git a/src/can/can-bus.cpp b/src/can/can-bus.cpp new file mode 100644 index 00000000..d46092bf --- /dev/null +++ b/src/can/can-bus.cpp @@ -0,0 +1,556 @@ +/* + * 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. + */ + +#include "can-bus.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "can-decoder.hpp" +#include "openxc-utils.hpp" + +extern "C" +{ + #include +} + +/******************************************************************************** +* +* can_bus_t method implementation +* +*********************************************************************************/ +/** +* @brief Class constructor +* +* @param struct afb_binding_interface *interface between daemon and binding +* @param int file handle to the json configuration file. +*/ +can_bus_t::can_bus_t(int conf_file) + : conf_file_{conf_file} +{ +} + +/** +* @brief thread to decoding raw CAN messages. +* +* @desc It will take from the can_message_q_ queue the next can message to process then it will search +* about signal subscribed if there is a valid afb_event for it. We only decode signal for which a +* subscription has been made. Can message will be decoded using translateSignal that will pass it to the +* corresponding decoding function if there is one assigned for that signal. If not, it will be the default +* noopDecoder function that will operate on it. +*/ +void can_bus_t::can_decode_message() +{ + can_message_t can_message; + std::vector signals; + openxc_VehicleMessage vehicle_message; + openxc_DynamicField search_key, decoded_message; + + decoder_t decoder; + + while(is_decoding_) + { + std::unique_lock can_message_lock(can_message_mutex_); + new_can_message_.wait(can_message_lock); + can_message = next_can_message(); + + /* First we have to found which CanSignal it is */ + search_key = build_DynamicField((double)can_message.get_id()); + signals.clear(); + find_can_signals(search_key, signals); + + /* Decoding the message ! Don't kill the messenger ! */ + for(auto& sig : signals) + { + std::lock_guard subscribed_signals_lock(get_subscribed_signals_mutex()); + std::map& s = get_subscribed_signals(); + + /* DEBUG message to make easier debugger STL containers... + DEBUG(binder_interface, "Operator[] key char: %s, event valid? %d", sig.generic_name, afb_event_is_valid(s[sig.generic_name])); + DEBUG(binder_interface, "Operator[] key string: %s, event valid? %d", sig.generic_name, afb_event_is_valid(s[std::string(sig.generic_name)])); + DEBUG(binder_interface, "Nb elt matched char: %d", (int)s.count(sig.generic_name)); + DEBUG(binder_interface, "Nb elt matched string: %d", (int)s.count(std::string(sig.generic_name))); */ + if( s.find(sig->generic_name) != s.end() && afb_event_is_valid(s[sig->generic_name])) + { + decoded_message = decoder.translateSignal(*sig, can_message, get_can_signals()); + + openxc_SimpleMessage s_message = build_SimpleMessage(sig->generic_name, decoded_message); + vehicle_message = build_VehicleMessage_with_SimpleMessage(openxc_DynamicField_Type::openxc_DynamicField_Type_NUM, s_message); + + std::lock_guard decoded_can_message_lock(decoded_can_message_mutex_); + push_new_vehicle_message(vehicle_message); + new_decoded_can_message_.notify_one(); + } + } + } +} + +/** +* @brief thread to push events to suscribers. It will read subscribed_signals map to look +* which are events that has to be pushed. +*/ +void can_bus_t::can_event_push() +{ + openxc_VehicleMessage v_message; + openxc_SimpleMessage s_message; + json_object* jo; + + while(is_pushing_) + { + std::unique_lock decoded_can_message_lock(decoded_can_message_mutex_); + new_decoded_can_message_.wait(decoded_can_message_lock); + v_message = next_vehicle_message(); + + s_message = get_simple_message(v_message); + { + std::lock_guard subscribed_signals_lock(get_subscribed_signals_mutex()); + std::map& s = get_subscribed_signals(); + if(s.find(std::string(s_message.name)) != s.end() && afb_event_is_valid(s[std::string(s_message.name)])) + { + jo = json_object_new_object(); + jsonify_simple(s_message, jo); + afb_event_push(s[std::string(s_message.name)], jo); + } + } + } +} + +/** + * @brief Will initialize threads that will decode + * and push subscribed events. + */ +void can_bus_t::start_threads() +{ + v_ = true; + th_decoding_ = std::thread(&can_bus_t::can_decode_message, this); + if(!th_decoding_.joinable()) + is_decoding_ = false; + + is_pushing_ = true; + th_pushing_ = std::thread(&can_bus_t::can_event_push, this); + if(!th_pushing_.joinable()) + is_pushing_ = false; +} + +/** +* @brief Will stop all threads holded by can_bus_t object +* which are decoding and pushing then will wait that's +* they'll finish their job. +*/ +void can_bus_t::stop_threads() +{ + is_decoding_ = false; + is_pushing_ = false; +} + +/** +* @brief Will initialize can_bus_dev_t objects after reading +* the configuration file passed in the constructor. +*/ +int can_bus_t::init_can_dev() +{ + std::vector devices_name; + int i; + size_t t; + + devices_name = read_conf(); + + if (! devices_name.empty()) + { + t = devices_name.size(); + i=0; + + for(const auto& device : devices_name) + { + can_devices_m_[device] = std::make_shared(device); + if (can_devices_m_[device]->open() == 0) + { + i++; + DEBUG(binder_interface, "Start reading thread"); + NOTICE(binder_interface, "%s device opened and reading", device.c_str()); + can_devices_m_[device]->start_reading(*this); + } + else + ERROR(binder_interface, "Can't open device %s", device.c_str()); + } + + NOTICE(binder_interface, "Initialized %d/%d can bus device(s)", i, t); + return 0; + } + ERROR(binder_interface, "init_can_dev: Error at CAN device initialization. No devices read from configuration file. Did you specify canbus JSON object ?"); + return 1; +} + +/** +* @brief read the conf_file_ and will parse json objects +* in it searching for canbus objects devices name. +* +* @return Vector of can bus device name string. +*/ +std::vector can_bus_t::read_conf() +{ + std::vector ret; + json_object *jo, *canbus; + int n, i; + const char* taxi; + + FILE *fd = fdopen(conf_file_, "r"); + if (fd) + { + std::string fd_conf_content; + std::fseek(fd, 0, SEEK_END); + fd_conf_content.resize(std::ftell(fd)); + std::rewind(fd); + std::fread(&fd_conf_content[0], 1, fd_conf_content.size(), fd); + std::fclose(fd); + + DEBUG(binder_interface, "Configuration file content : %s", fd_conf_content.c_str()); + jo = json_tokener_parse(fd_conf_content.c_str()); + + if (jo == NULL || !json_object_object_get_ex(jo, "canbus", &canbus)) + {/** +* @brief Telling if the pushing thread is running +* This is the boolean value on which the while loop +* take its condition. Set it to false will stop the +* according thread. +* +* @return true if pushing thread is running, false if not. +*/ + + ERROR(binder_interface, "Can't find canbus node in the configuration file. Please review it."); + ret.clear(); + } + else if (json_object_get_type(canbus) != json_type_array) + { + taxi = json_object_get_string(canbus); + DEBUG(binder_interface, "Can bus found: %s", taxi); + ret.push_back(std::string(taxi)); + } + else + { + n = json_object_array_length(canbus); + for (i = 0 ; i < n ; i++) + ret.push_back(json_object_get_string(json_object_array_get_idx(canbus, i))); + } + return ret; + } + ERROR(binder_interface, "Problem at reading the conf file"); + ret.clear(); + return ret; +} + +/** +* @brief return new_can_message_ member +* +* @return return new_can_message_ member +*/ +std::condition_variable& can_bus_t::get_new_can_message_cv() +{ + return new_can_message_cv_; +} + +/** +* @brief return can_message_mutex_ member +* +* @return return can_message_mutex_ member +*/ +std::mutex& can_bus_t::get_can_message_mutex() +{ + return can_message_mutex_; +} + +/** +* @brief Return first can_message_t on the queue +* +* @return a can_message_t +*/ +can_message_t can_bus_t::next_can_message() +{ + can_message_t can_msg; + + if(!can_message_q_.empty()) + { + can_msg = can_message_q_.front(); + can_message_q_.pop(); + DEBUG(binder_interface, "next_can_message: Here is the next can message : id %X, length %X, data %02X%02X%02X%02X%02X%02X%02X%02X", can_msg.get_id(), can_msg.get_length(), + can_msg.get_data()[0], can_msg.get_data()[1], can_msg.get_data()[2], can_msg.get_data()[3], can_msg.get_data()[4], can_msg.get_data()[5], can_msg.get_data()[6], can_msg.get_data()[7]); + return can_msg; + } + + return can_msg; +} + +/** +* @brief Push a can_message_t into the queue +* +* @param the const reference can_message_t object to push into the queue +*/ +void can_bus_t::push_new_can_message(const can_message_t& can_msg) +{ + can_message_q_.push(can_msg); +} + +/** +* @brief Return first openxc_VehicleMessage on the queue +* +* @return a openxc_VehicleMessage containing a decoded can message +*/ +openxc_VehicleMessage can_bus_t::next_vehicle_message() +{ + openxc_VehicleMessage v_msg; + + if(! vehicle_message_q_.empty()) + { + v_msg = vehicle_message_q_.front(); + vehicle_message_q_.pop(); + DEBUG(binder_interface, "next_vehicle_message: next vehicle message poped"); + return v_msg; + } + + return v_msg; +} + +/** +* @brief Push a openxc_VehicleMessage into the queue +* +* @param the const reference openxc_VehicleMessage object to push into the queue +*/ +void can_bus_t::push_new_vehicle_message(const openxc_VehicleMessage& v_msg) +{ + vehicle_message_q_.push(v_msg); +} + +/** +* @brief Return a map with the can_bus_dev_t initialized +* +* @return map can_bus_dev_m_ map +*/ +std::map> can_bus_t::get_can_devices() +{ + return can_devices_m_; +} + +/******************************************************************************** +* +* can_bus_dev_t method implementation +* +*********************************************************************************/ +/** +* @brief Class constructor +* +* @param const string representing the device name into the linux /dev tree +*/ +can_bus_dev_t::can_bus_dev_t(const std::string &dev_name) + : device_name_{dev_name}, can_socket_{-1} +{ +} + +/** +* @brief Open the can socket and returning it +* +* @return +*/ +int can_bus_dev_t::open() +{ + const int canfd_on = 1; + const int timestamp_on = 1; + struct ifreq ifr; + struct timeval timeout; + + DEBUG(binder_interface, "CAN Handler socket : %d", can_socket_); + if (can_socket_ >= 0) + return 0; + + can_socket_ = ::socket(PF_CAN, SOCK_RAW, CAN_RAW); + DEBUG(binder_interface, "CAN Handler socket correctly initialized : %d", can_socket_); + if (can_socket_ < 0) + ERROR(binder_interface, "socket could not be created. Error was : %s", ::strerror(errno)); + else + { + /* Set timeout for read */ + ::setsockopt(can_socket_, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); + /* Set timestamp for receveid frame */ + if (::setsockopt(can_socket_, SOL_SOCKET, SO_TIMESTAMP, ×tamp_on, sizeof(timestamp_on)) < 0) + WARNING(binder_interface, "setsockopt SO_TIMESTAMP error: %s", ::strerror(errno)); + DEBUG(binder_interface, "Switch CAN Handler socket to use fd mode"); + /* try to switch the socket into CAN_FD mode */ + if (::setsockopt(can_socket_, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on)) < 0) + { + NOTICE(binder_interface, "Can not switch into CAN Extended frame format."); + is_fdmode_on_ = false; + } else { + DEBUG(binder_interface, "Correctly set up CAN socket to use FD frames."); + is_fdmode_on_ = true; + } + + /* Attempts to open a socket to CAN bus */ + ::strcpy(ifr.ifr_name, device_name_.c_str()); + DEBUG(binder_interface, "ifr_name is : %s", ifr.ifr_name); + if(::ioctl(can_socket_, SIOCGIFINDEX, &ifr) < 0) + ERROR(binder_interface, "ioctl failed. Error was : %s", strerror(errno)); + else + { + txAddress_.can_family = AF_CAN; + txAddress_.can_ifindex = ifr.ifr_ifindex; + + /* And bind it to txAddress */ + DEBUG(binder_interface, "Bind the socket"); + if (::bind(can_socket_, (struct sockaddr *)&txAddress_, sizeof(txAddress_)) < 0) + ERROR(binder_interface, "Bind failed. %s", strerror(errno)); + else + return 0; + } + close(); + } + return -1; +} + +/** + * @brief Open the can socket and returning it + * + * @return + */ +int can_bus_dev_t::close() +{ + ::close(can_socket_); + can_socket_ = -1; + return can_socket_; +} + +/** +* @brief Read the can socket and retrieve canfd_frame +* +* @param const struct afb_binding_interface* interface pointer. Used to be able to log +* using application framework logger. +*/ +std::pair can_bus_dev_t::read() +{ + ssize_t nbytes; + //int maxdlen; + struct canfd_frame cfd; + + /* Test that socket is really opened */ + if (can_socket_ < 0) + { + ERROR(binder_interface, "read_can: Socket unavailable. Closing thread."); + is_running_ = false; + } + + nbytes = ::read(can_socket_, &cfd, CANFD_MTU); + + /* if we did not fit into CAN sized messages then stop_reading. */ + if (nbytes != CANFD_MTU && nbytes != CAN_MTU) + { + if (errno == ENETDOWN) + ERROR(binder_interface, "read: %s CAN device down", device_name_); + ERROR(binder_interface, "read: Incomplete CAN(FD) frame"); + ::memset(&cfd, 0, sizeof(cfd)); + } + + DEBUG(binder_interface, "read: Found id: %X, length: %X, data %02X%02X%02X%02X%02X%02X%02X%02X", cfd.can_id, cfd.len, + cfd.data[0], cfd.data[1], cfd.data[2], cfd.data[3], cfd.data[4], cfd.data[5], cfd.data[6], cfd.data[7]); + return std::pair(cfd, nbytes); +} + +/** +* @brief start reading threads and set flag is_running_ +* +* @param can_bus_t reference can_bus_t. it will be passed to the thread +* to allow using can_bus_t queue. +*/ +void can_bus_dev_t::start_reading(can_bus_t& can_bus) +{ + DEBUG(binder_interface, "Launching reading thread"); + is_running_ = true; + th_reading_ = std::thread(&can_bus_dev_t::can_reader, this, std::ref(can_bus)); + if(!th_reading_.joinable()) + is_running_ = false; +} + +/** +* @brief stop the reading thread setting flag is_running_ to false and +* and wait that the thread finish its job. +*/ +void can_bus_dev_t::stop_reading() +{ + is_running_ = false; +} + +/** +* +* @brief Thread function used to read the can socket. +* +* @param[in] can_bus_dev_t object to be used to read the can socket +* @param[in] can_bus_t object used to fill can_message_q_ queue +*/ +void can_bus_dev_t::can_reader(can_bus_t& can_bus) +{ + can_message_t can_message; + + while(is_running_) + { + can_message.convert_from_canfd_frame(read()); + + { + std::lock_guard can_message_lock(can_bus.get_can_message_mutex()); + can_bus.push_new_can_message(can_message); + } + can_bus.get_new_can_message_cv_().notify_one(); + } +} + +/** +* @brief Send a can message from a can_message_t object. +* +* @param const can_message_t& can_msg: the can message object to send +* @param const struct afb_binding_interface* interface pointer. Used to be able to log +* using application framework logger. +*/ +int can_bus_dev_t::send_can_message(can_message_t& can_msg) +{ + ssize_t nbytes; + canfd_frame f; + + f = can_msg.convert_to_canfd_frame(); + + if(can_socket_ >= 0) + { + nbytes = ::sendto(can_socket_, &f, sizeof(struct canfd_frame), 0, + (struct sockaddr*)&txAddress_, sizeof(txAddress_)); + if (nbytes == -1) + { + ERROR(binder_interface, "send_can_message: Sending CAN frame failed."); + return -1; + } + return (int)nbytes; + } + else + { + ERROR(binder_interface, "send_can_message: socket not initialized. Attempt to reopen can device socket."); + open(); + } + return 0; +} \ No newline at end of file diff --git a/src/can/can-bus.hpp b/src/can/can-bus.hpp new file mode 100644 index 00000000..bc2bb04c --- /dev/null +++ b/src/can/can-bus.hpp @@ -0,0 +1,172 @@ +/* + * 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 +#include + +#include "timer.hpp" +#include "openxc.pb.h" +#include "can-signals.hpp" +#include "can-message.hpp" +#include "low-can-binding.hpp" + +// TODO actual max is 32 but dropped to 24 for memory considerations +#define MAX_ACCEPTANCE_FILTERS 24 +// TODO this takes up a ton of memory +#define MAX_DYNAMIC_MESSAGE_COUNT 12 + +#define CAN_ACTIVE_TIMEOUT_S 30 + +class can_bus_dev_t; + +/** + * @class can_bus_t + * @brief Object used to handle decoding and manage event queue to be pushed. + * + * This object is also used to initialize can_bus_dev_t object after reading + * json conf file describing the CAN devices to use. Thus, those object will read + * on the device the CAN frame and push them into the can_bus_t can_message_q_ queue. + * + * That queue will be later used to be decoded and pushed to subscribers. + */ +class can_bus_t { + private: + int conf_file_; /*!< conf_file_ - configuration file handle used to initialize can_bus_dev_t objects.*/ + + void can_decode_message(); + std::thread th_decoding_; /*!< thread that'll handle decoding a can frame */ + bool is_decoding_; /*!< boolean member controling thread while loop*/ + + void can_event_push(); + std::thread th_pushing_; /*!< thread that'll handle pushing decoded can frame to subscribers */ + bool is_pushing_; /*!< boolean member controling thread while loop*/ + + std::condition_variable new_can_message_cv_; /*!< condition_variable use to wait until there is a new CAN message to read*/ + std::mutex can_message_mutex_; /*!< mutex protecting the can_message_q_ queue.*/ + std::queue can_message_q_; /*!< queue that'll store can_message_t to decoded */ + + std::condition_variable new_decoded_can_message_; /*!< condition_variable use to wait until there is a new vehicle message to read from the queue vehicle_message_q_*/ + std::mutex decoded_can_message_mutex_; /*!< mutex protecting the vehicle_message_q_ queue.*/ + std::queue vehicle_message_q_; /*!< queue that'll store openxc_VehicleMessage to pushed */ + + std::map> can_devices_m_; /*!< Can device map containing all can_bus_dev_t objects initialized during init_can_dev function*/ + + public: + can_bus_t(int conf_file); + + int init_can_dev(); + std::vector read_conf(); + + + void start_threads(); + void stop_threads(); + + can_message_t next_can_message(); + void push_new_can_message(const can_message_t& can_msg); + std::mutex& get_can_message_mutex(); + std::condition_variable& get_new_can_message_cv_(); + + openxc_VehicleMessage next_vehicle_message(); + void push_new_vehicle_message(const openxc_VehicleMessage& v_msg); + + std::map> get_can_devices(); +}; + + +/** + * @class can_bus_dev_t + * + * @brief Object representing a can device. Handle opening, closing and reading on the + * socket. This is the low level object to be use by can_bus_t. + */ +class can_bus_dev_t { + private: + int32_t id_; /*!< int32_t id_ - an identifier used through binding that refer to that device*/ + std::string device_name_; /*!< std::string device_name_ - name of the linux device handling the can bus. Generally vcan0, can0, etc. */ + int can_socket_; /*!< socket handler for the can device */ + bool is_fdmode_on_; /*!< boolean telling if whether or not the can socket use fdmode. */ + struct sockaddr_can txAddress_; /*!< internal member using to bind to the socket */ + + std::thread th_reading_; /*!< Thread handling read the socket can device filling can_message_q_ queue of can_bus_t */ + bool is_running_; /*!< boolean telling whether or not reading is running or not */ + + void can_reader(can_bus_t& can_bus); + + public: + can_bus_dev_t(const std::string& dev_name); + + int open(); + int close(); + + + void start_reading(can_bus_t& can_bus); + + void stop_reading(); + + std::pair read(); + + int send_can_message(can_message_t& can_msg); +}; + +/** TODO: implement this function as method into can_bus class + * @fn void pre_initialize(can_bus_dev_t* bus, bool writable, can_bus_dev_t* buses, const int busCount); + * @brief Pre initialize actions made before CAN bus initialization + * + * @param[in] can_bus_dev_t bus - A CanBus struct defining the bus's metadata + * @param[in] bool writable - configure the controller in a writable mode. If false, it will be + * configured as "listen only" and will not allow writes or even CAN ACKs. + * @param[in] buses - An array of all CAN buses. + * @param[in] int busCount - The length of the buses array. + */ +void pre_initialize(can_bus_dev_t* bus, bool writable, can_bus_dev_t* buses, const int busCount); + +/** TODO: implement this function as method into can_bus class + * @fn void post_initialize(can_bus_dev_t* bus, bool writable, can_bus_dev_t* buses, const int busCount); + * @brief Post-initialize actions made after CAN bus initialization + * + * @param[in] bus - A CanBus struct defining the bus's metadata + * @param[in] writable - configure the controller in a writable mode. If false, it will be + * configured as "listen only" and will not allow writes or even CAN ACKs. + * @param[in] buses - An array of all CAN buses. + * @param[in] busCount - The length of the buses array. + */ +void post_initialize(can_bus_dev_t* bus, bool writable, can_bus_dev_t* buses, const int busCount); + +/** TODO: implement this function as method into can_bus class + * @fn bool isBusActive(can_bus_dev_t* bus); + * @brief Check if the device is connected to an active CAN bus, i.e. it's + * received a message in the recent past. + * + * @return true if a message was received on the CAN bus within + * CAN_ACTIVE_TIMEOUT_S seconds. + */ +bool isBusActive(can_bus_dev_t* bus); + +/** TODO: implement this function as method into can_bus class + * + * @fn void logBusStatistics(can_bus_dev_t* buses, const int busCount); + * @brief Log transfer statistics about all active CAN buses to the debug log. + * + * @param[in] buses - an array of active CAN buses. + * @param[in] busCount - the length of the buses array. + */ +void logBusStatistics(can_bus_dev_t* buses, const int busCount); \ No newline at end of file diff --git a/src/can/can-command.hpp b/src/can/can-command.hpp new file mode 100644 index 00000000..62b56c1e --- /dev/null +++ b/src/can/can-command.hpp @@ -0,0 +1,66 @@ +/* + * 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 "openxc.pb.h" +#include "can-signals.hpp" + +/** + * @brief The type signature for a function to handle a custom OpenXC command. + * + * @param[in] char* name - the name of the received command. + * @param[in] openxc_DynamicField* value - the value of the received command, in a DynamicField. The actual type + * may be a number, string or bool. + * @param[in] openxc_DynamicField* event - an optional event from the received command, in a DynamicField. The + * actual type may be a number, string or bool. + * @param[in] CanSignal* signals - The list of all signals. + * @param[in] int signalCount - The length of the signals array. + */ +typedef void (*CommandHandler)(const char* name, openxc_DynamicField* value, + openxc_DynamicField* event, CanSignal* signals, int signalCount); + +/* @struct CanCommand + * @brief The structure to represent a supported custom OpenXC command. + * + * @desc For completely customized CAN commands without a 1-1 mapping between an + * OpenXC message from the host and a CAN signal, you can define the name of the + * command and a custom function to handle it in the VI. An example is + * the "turn_signal_status" command in OpenXC, which has a value of "left" or + * "right". The vehicle may have separate CAN signals for the left and right + * turn signals, so you will need to implement a custom command handler to send + * the correct signals. + * + * Command handlers are also useful if you want to trigger multiple CAN messages + * or signals from a signal OpenXC message. + */ +typedef struct { + const char* generic_name; /*!< generic_name - The name of the command.*/ + CommandHandler handler; /*!< handler - An function to process the received command's data and perform some + * action.*/ +} CanCommand; + +/* Public: Return an array of all OpenXC CAN commands enabled in the active + * configuration that can write back to CAN with a custom handler. + * + * * Commands not defined here are handled using a 1-1 mapping from the signals + * list. + * */ +CanCommand* getCommands(); + +/* Public: Return the length of the array returned by getCommandCount(). */ +int getCommandCount(); \ No newline at end of file diff --git a/src/can/can-decoder.cpp b/src/can/can-decoder.cpp new file mode 100644 index 00000000..03471398 --- /dev/null +++ b/src/can/can-decoder.cpp @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#include "can-decoder.hpp" +#include "canutil/read.h" + +float decoder_t::parseSignalBitfield(CanSignal& signal, const can_message_t& message) +{ + return bitfield_parse_float(message.get_data(), CAN_MESSAGE_SIZE, + signal.bitPosition, signal.bitSize, signal.factor, + signal.offset); +} + +openxc_DynamicField decoder_t::noopDecoder(CanSignal& signal, + const std::vector& signals, float value, bool* send) +{ + openxc_DynamicField decoded_value; + decoded_value.has_type = true; + decoded_value.type = openxc_DynamicField_Type_NUM; + decoded_value.has_numeric_value = true; + decoded_value.numeric_value = value; + + return decoded_value; +} + +openxc_DynamicField decoder_t::booleanDecoder(CanSignal& signal, + const std::vector& signals, float value, bool* send) +{ + openxc_DynamicField decoded_value; + decoded_value.has_type = true; + decoded_value.type = openxc_DynamicField_Type_BOOL; + decoded_value.has_boolean_value = true; + decoded_value.boolean_value = value == 0.0 ? false : true; + + return decoded_value; +} + +openxc_DynamicField decoder_t::ignoreDecoder(CanSignal& signal, + const std::vector& signals, float value, bool* send) +{ + if(send) + *send = false; + + openxc_DynamicField decoded_value = {0, openxc_DynamicField_Type_BOOL, 0, "", 0, 0, 0, 0}; + + return decoded_value; +} + +openxc_DynamicField decoder_t::stateDecoder(CanSignal& signal, + const std::vector& signals, float value, bool* send) +{ + openxc_DynamicField decoded_value = {0, openxc_DynamicField_Type_BOOL, 0, "", 0, 0, 0, 0}; + decoded_value.has_type = true; + decoded_value.type = openxc_DynamicField_Type_STRING; + decoded_value.has_string_value = true; + + /* TODO: Handle SignalState + const CanSignalState* signalState = lookupSignalState(value, signal); + if(signalState != NULL) { + ::strcpy(decoded_value.string_value, signalState->name); + } else { + *send = false; + }*/ + return decoded_value; +} + +openxc_DynamicField decoder_t::translateSignal(CanSignal& signal, can_message_t& message, + const std::vector& signals) +{ + if(&signal == nullptr || &message == nullptr) + { + openxc_DynamicField ret = {0, openxc_DynamicField_Type_BOOL, 0, "", 0, 0, 0, 0}; + return ret; + } + + float value = parseSignalBitfield(signal, message); + DEBUG(binder_interface, "translateSignal: Decoded message: %f", value); + + bool send = true; + // Must call the decoders every time, regardless of if we are going to + // decide to send the signal or not. + openxc_DynamicField decoded_value = decodeSignal(signal, + value, signals, &send); + + signal.received = true; + signal.lastValue = value; + return decoded_value; +} + +openxc_DynamicField decoder_t::decodeSignal( CanSignal& signal, + float value, const std::vector& signals, bool* send) +{ + SignalDecoder decoder = signal.decoder == NULL ? + noopDecoder : signal.decoder; + openxc_DynamicField decoded_value = decoder(signal, signals, + value, send); + return decoded_value; +} + +openxc_DynamicField decoder_t::decodeSignal( CanSignal& signal, + const can_message_t& message, const std::vector& signals, bool* send) +{ + float value = parseSignalBitfield(signal, message); + return decodeSignal(signal, value, signals, send); +} diff --git a/src/can/can-decoder.hpp b/src/can/can-decoder.hpp new file mode 100644 index 00000000..e7dfff86 --- /dev/null +++ b/src/can/can-decoder.hpp @@ -0,0 +1,161 @@ +/* + * 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 "can-bus.hpp" + +class decoder_t +{ + private: + + public: + /* Public: Parse the signal's bitfield from the given data and return the raw + * value. + * + * signal - The signal to parse from the data. + * data - The data to parse the signal from. + * length - The length of the data array. + * + * Returns the raw value of the signal parsed as a bitfield from the given byte + * array. + */ + float parseSignalBitfield(CanSignal& signal, const can_message_t& message); + + /* Public: Find and return the corresponding string state for a CAN signal's + * raw integer value. + * + * This is an implementation of the SignalDecoder type signature, and can be + * used directly in the CanSignal.decoder field. + * + * signal - The details of the signal that contains the state mapping. + * signals - The list of all signals. + * signalCount - the length of the signals array. + * value - The numerical value that should map to a state. + * send - An output argument that will be set to false if the value should + * not be sent for any reason. + * + * Returns a DynamicField with a string value if a matching state is found in + * the signal. If an equivalent isn't found, send is sent to false and the + * return value is undefined. + */ + static openxc_DynamicField stateDecoder(CanSignal& signal, const std::vector& signals, + float value, bool* send); + + /* Public: Coerces a numerical value to a boolean. + * + * This is an implementation of the SignalDecoder type signature, and can be + * used directly in the CanSignal.decoder field. + * + * signal - The details of the signal that contains the state mapping. + * signals - The list of all signals + * signalCount - The length of the signals array + * value - The numerical value that will be converted to a boolean. + * send - An output argument that will be set to false if the value should + * not be sent for any reason. + * + * Returns a DynamicField with a boolean value of false if the raw signal value + * is 0.0, otherwise true. The 'send' argument will not be modified as this + * decoder always succeeds. + */ + static openxc_DynamicField booleanDecoder(CanSignal& signal, const std::vector& signals, + float value, bool* send); + + /* Public: Update the metadata for a signal and the newly received value. + * + * This is an implementation of the SignalDecoder type signature, and can be + * used directly in the CanSignal.decoder field. + * + * This function always flips 'send' to false. + * + * signal - The details of the signal that contains the state mapping. + * signals - The list of all signals. + * signalCount - The length of the signals array. + * value - The numerical value that will be converted to a boolean. + * send - This output argument will always be set to false, so the caller will + * know not to publish this value to the pipeline. + * + * The return value is undefined. + */ + openxc_DynamicField ignoreDecoder(CanSignal& signal, const std::vector& signals, + float value, bool* send); + + /* Public: Wrap a raw CAN signal value in a DynamicField without modification. + * + * This is an implementation of the SignalDecoder type signature, and can be + * used directly in the CanSignal.decoder field. + * + * signal - The details of the signal that contains the state mapping. + * signals - The list of all signals + * signalCount - The length of the signals array + * value - The numerical value that will be wrapped in a DynamicField. + * send - An output argument that will be set to false if the value should + * not be sent for any reason. + * + * Returns a DynamicField with the original, unmodified raw CAN signal value as + * its numeric value. The 'send' argument will not be modified as this decoder + * always succeeds. + */ + static openxc_DynamicField noopDecoder(CanSignal& signal, const std::vector& signals, + float value, bool* send); + + + /* Public: Parse a signal from a CAN message, apply any required transforations + * to get a human readable value and public the result to the pipeline. + * + * If the CanSignal has a non-NULL 'decoder' field, the raw CAN signal value + * will be passed to the decoder before publishing. + * + * signal - The details of the signal to decode and forward. + * message - The received CAN message that should contain this signal. + * signals - an array of all active signals. + * + * The decoder returns an openxc_DynamicField, which may contain a number, + * string or boolean. + */ + openxc_DynamicField translateSignal(CanSignal& signal, can_message_t& message, + const std::vector& signals); + + /* Public: Parse a signal from a CAN message and apply any required + * transforations to get a human readable value. + * + * If the CanSignal has a non-NULL 'decoder' field, the raw CAN signal value + * will be passed to the decoder before returning. + * + * signal - The details of the signal to decode and forward. + * message - The CAN message that contains the signal. + * signals - an array of all active signals. + * send - An output parameter that will be flipped to false if the value could + * not be decoded. + * + * The decoder returns an openxc_DynamicField, which may contain a number, + * string or boolean. If 'send' is false, the return value is undefined. + */ + openxc_DynamicField decodeSignal(CanSignal& signal, const can_message_t& message, + const std::vector& signals, bool* send); + + /* Public: Decode a transformed, human readable value from an raw CAN signal + * already parsed from a CAN message. + * + * This is the same as decodeSignal(const CanSignal&, CanMessage*, const CanSignal&, int, + * bool*) but you must parse the bitfield value of the signal from the CAN + * message yourself. This is useful if you need that raw value for something + * else. + */ + openxc_DynamicField decodeSignal(CanSignal& signal, float value, + const std::vector& signals, bool* send); +}; \ No newline at end of file diff --git a/src/can/can-message.cpp b/src/can/can-message.cpp new file mode 100644 index 00000000..ad032021 --- /dev/null +++ b/src/can/can-message.cpp @@ -0,0 +1,313 @@ +/* + * 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. + */ + +#include "can-message.hpp" + +#include + +#include "low-can-binding.hpp" + +/******************************************************************************** +* +* CanMessage method implementation +* +*********************************************************************************/ +/** +* @brief Class constructor +* +* Constructor about can_message_t class. +*/ +can_message_t::can_message_t() + : id_{0}, rtr_flag_{false}, length_{0}, flags_{0}, format_{CanMessageFormat::ERROR} +{} + +/** +* @brief Retrieve id_ member value. +* +* @return uint32_t id_ class member +*/ +uint32_t can_message_t::get_id() const +{ + return id_; +} + +/** +* @brief Retrieve RTR flag member. +* +* @return bool rtr_flags_ class member +*/ +bool can_message_t::get_rtr_flag_() const +{ + return rtr_flag_; +} + +/** +* @brief Retrieve format_ member value. +* +* @return CanMessageFormat format_ class member +*/ +int can_message_t::get_format() const +{ + if (format_ != CanMessageFormat::STANDARD || format_ != CanMessageFormat::EXTENDED) + return CanMessageFormat::ERROR; + return format_; +} + +/** +* @brief Retrieve format_ member value. +* +* @return CanMessageFormat format_ class member +*/ +uint8_t can_message_t::get_flags() const +{ + return flags_; +} + +/** +* @brief Retrieve data_ member value. +* +* @return uint8_t data_ pointer to the first element +* of class member data_ +*/ +const uint8_t* can_message_t::get_data() const +{ + return data_.data(); +} + +/** +* @brief Retrieve length_ member value. +* +* @return uint8_t length_ class member +*/ +uint8_t can_message_t::get_length() const +{ + return length_; +} + +void can_message_t::set_max_data_length(size_t nbytes) +{ + maxdlen_ = 0; + + switch(nbytes) + { + case CANFD_MTU: + DEBUG(binder_interface, "set_max_data_length: Got an CAN FD frame"); + maxdlen_ = CANFD_MAX_DLEN; + break; + case CAN_MTU: + DEBUG(binder_interface, "set_max_data_length: Got a legacy CAN frame"); + maxdlen_ = CAN_MAX_DLEN; + break; + default: + ERROR(binder_interface, "set_max_data_length: unsupported CAN frame"); + break; + } +} + +/** +* @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... +*/ +bool can_message_t::is_correct_to_send() +{ + if (id_ != 0 && length_ != 0 && format_ != CanMessageFormat::ERROR) + { + int i; + for(i=0;i maxdlen_) ? maxdlen_ : new_length; + } +} + +/** +* @brief Set data_ member value. +* +* Preferred way to initialize these members by using +* convert_from_canfd_frame method. +* +* @param uint8_t data_ array with a max size of 8 elements. +*/ +void can_message_t::set_data(const __u8* new_data) +{ + int i; + + data_.clear(); + /* maxdlen_ is now set at CAN_MAX_DLEN or CANFD_MAX_DLEN, respectively 8 and 64 bytes*/ + for(i=0;iargs) +{ + // May be it's overkill to assign member of the pair... May be it will change... + struct canfd_frame frame = args.first; + size_t nbytes = args.second; + set_max_data_length(nbytes); + set_length(frame.len); + set_id_and_format(frame.can_id); + + /* Overwrite lenght_ if RTR flags is detected. + * standard CAN frames may have RTR enabled. There are no ERR frames with RTR */ + if (frame.can_id & CAN_RTR_FLAG) + { + rtr_flag_ = true; + if(frame.len && frame.len <= CAN_MAX_DLC) + set_length(frame.len); + return; + } + + /* Flags field only present for CAN FD frames*/ + if(maxdlen_ == CANFD_MAX_DLEN) + set_flags(frame.flags); + + if ( data_.capacity() < maxdlen_) + data_.reserve(maxdlen_); + set_data(frame.data); + + DEBUG(binder_interface, "convert_from_canfd_frame: Found id: %X, format: %X, length: %X, data %02X%02X%02X%02X%02X%02X%02X%02X", id_, format_, length_, + data_[0], data_[1], data_[2], data_[3], data_[4], data_[5], data_[6], data_[7]); +} + +/** +* @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. +*/ +canfd_frame can_message_t::convert_to_canfd_frame() +{ + canfd_frame frame; + + if(is_correct_to_send()) + { + frame.can_id = get_id(); + frame.len = get_length(); + ::memcpy(frame.data, get_data(), length_); + } + else + ERROR(binder_interface, "can_message_t not correctly initialized to be sent"); + + return frame; +} + diff --git a/src/can/can-message.hpp b/src/can/can-message.hpp new file mode 100644 index 00000000..9f6a36da --- /dev/null +++ b/src/can/can-message.hpp @@ -0,0 +1,151 @@ +/* + * 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 + +#include "timer.hpp" + +#define CAN_MESSAGE_SIZE 8 + +/** + * @enum CanMessageFormat + * @brief The ID format for a CAN message. + */ +enum CanMessageFormat { + STANDARD, /*!< STANDARD - standard 11-bit CAN arbitration ID. */ + EXTENDED, /*!< EXTENDED - an extended frame, with a 29-bit arbitration ID. */ + ERROR, /*!< ERROR - ERROR code used at initialization to signify that it isn't usable'*/ +}; +typedef enum CanMessageFormat CanMessageFormat; + +/** + * @class can_message_t + * + * @brief A compact representation of a single CAN message, meant to be used in in/out + * buffers. + */ + +/************************* + * old CanMessage struct * + ************************* +struct CanMessage { + uint32_t id; + CanMessageFormat format; + uint8_t data[CAN_MESSAGE_SIZE]; + uint8_t length; +}; +typedef struct CanMessage CanMessage; +*/ +class can_message_t { + private: + uint32_t id_; /*!< uint32_t id - The ID of the message. */ + bool rtr_flag_; /*!< bool rtr_flag - Telling if the frame has RTR flag positionned. Then frame hasn't data field*/ + uint8_t length_; /*!< uint8_t length - the length of the data array (max 8). */ + uint8_t flags_; /*!< unint8_t flags of a CAN FD frame. Needed if we catch FD frames.*/ + CanMessageFormat format_; /*!< CanMessageFormat format - the format of the message's ID.*/ + std::vector data_; /*!< uint8_t data - The message's data field with a size of 8 which is the standard about CAN bus messages.*/ + + uint8_t maxdlen_; + + public: + can_message_t(); + + uint32_t get_id() const; + bool get_rtr_flag_() const; + int get_format() const; + uint8_t get_flags() const; + const uint8_t* get_data() const; + uint8_t get_length() const; + + void set_max_data_length(size_t nbytes); + void set_id_and_format(const uint32_t new_id); + void set_format(const CanMessageFormat new_format); + void set_format(const uint32_t can_id); + void set_flags(const uint8_t flags); + void set_data(const __u8* new_data); + void set_length(const uint8_t new_length); + + bool is_correct_to_send(); + + void convert_from_canfd_frame(const std::pairargs); + canfd_frame convert_to_canfd_frame(); +}; + +/** + * @struct CanMessageDefinition + * + * @brief The definition of a CAN message. This includes a lot of metadata, so + * to save memory this struct should not be used for storing incoming and + * outgoing CAN messages. + */ +struct CanMessageDefinition { + //can_bus_dev_t bus; /*!< bus - A pointer to the bus this message is on. */ + uint32_t id; /*!< id - The ID of the message.*/ + CanMessageFormat format; /*!< format - the format of the message's ID.*/ + FrequencyClock frequencyClock; /*!< 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 retriec++ if ? syntaxve.*/ + bool forceSendChanged; /*!< forceSendChanged - If true, regardless of the frequency, it will send CAN + * message if it has changed when using raw passthrough.*/ + uint8_t lastValue[CAN_MESSAGE_SIZE]; /*!< lastValue - 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.*/ +}; +typedef struct CanMessageDefinition CanMessageDefinition; + +/** + * @struct CanMessageSet + * + * @brief A parent wrapper for a particular set of CAN messages and associated + * CAN buses(e.g. a vehicle or program). + */ + typedef struct { + uint8_t index; /*! + * + * 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 "can-signals.hpp" + +#include + +#include "signals.hpp" +#include "obd2-signals.hpp" +#include "can-decoder.hpp" +#include "low-can-binding.hpp" + +std::vector> CAN_MESSAGES = { + { + {0x620, CanMessageFormat::STANDARD, {10, 0, nullptr}, false, (uint8_t)NULL}, + {0x442, CanMessageFormat::STANDARD, {10, 0, nullptr}, false, (uint8_t)NULL}, + }, +}; + +/** + * @brief Dumb SIGNALS array. It is composed by CanMessageSet + * SIGNALS[MESSAGE_SET_ID][CanSignal] + */ +std::vector> SIGNALS = { + { + {&(CAN_MESSAGES[MESSAGE_SET_ID][0]), "can.driver_door.open", 2, 4, 1.000000, 0.000000, 0.000000, 0.000000, {10, 0, nullptr}, false, true, nullptr, 0, false, decoder_t::booleanDecoder, nullptr, false, 0.0}, + {&(CAN_MESSAGES[MESSAGE_SET_ID][1]), "can.driver_door.close", 0, 4, 1.000000, 0.000000, 0.000000, 0.000000, {10, 0, nullptr}, false, true, nullptr, 0, false, decoder_t::booleanDecoder, nullptr, false, 0.0} + }, +}; + +/** + * @fn std::vector& get_can_signals() + * + * @return A reference to a vector of signals + */ +std::vector& get_can_signals() +{ + return SIGNALS[MESSAGE_SET_ID]; +} + +/** + * @fn size_t getSignalCount() + * + * @return the length of the array returned by get_can_signals(). + */ +size_t getSignalCount() +{ + return SIGNALS[MESSAGE_SET_ID].size(); +} + +/** + * @brief Retrieve can arbitration id of a given CanSignal + * + * @param[in] CanSignal& - a const reference to a CanSignal + * + * @return uint32_t - unsigned integer representing the arbitration id. + */ +uint32_t get_signal_id(const CanSignal& sig) +{ + return sig.message->id; +} + +/** + * @fn void find_can_signals(const openxc_DynamicField& key, std::vector& found_signals) + * @brief return signals name found searching through CAN_signals and OBD2 pid + * + * @param[in] const openxc_DynamicField : can contain numeric or string value in order to search against + * can signals or obd2 signals name. + * @param[out] std::vector& found_signals : provided vector to fill with ponter to signals matched. + * + */ +void find_can_signals(const openxc_DynamicField& key, std::vector& found_signals) +{ + switch(key.type) + { + case openxc_DynamicField_Type::openxc_DynamicField_Type_STRING: + lookup_signals_by_name(key.string_value, get_can_signals(), found_signals); + break; + case openxc_DynamicField_Type::openxc_DynamicField_Type_NUM: + lookup_signals_by_id(key.numeric_value, get_can_signals(), found_signals); + break; + default: + ERROR(binder_interface, "find_signals: wrong openxc_DynamicField specified. Use openxc_DynamicField_Type_NUM or openxc_DynamicField_Type_STRING type only."); + break; + } + DEBUG(binder_interface, "Found %d signal(s)", (int)found_signals.size()); +} diff --git a/src/can/can-signals.hpp b/src/can/can-signals.hpp new file mode 100644 index 00000000..250ff197 --- /dev/null +++ b/src/can/can-signals.hpp @@ -0,0 +1,142 @@ +/* + * 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 +#include + +#include "obd2-signals.hpp" +#include "timer.hpp" +#include "openxc.pb.h" +#include "can-bus.hpp" +#include "can-message.hpp" + +extern "C" +{ + #include + #include +} + +#define MESSAGE_SET_ID 0 + +extern std::mutex subscribed_signals_mutex; +std::mutex& get_subscribed_signals_mutex(); + +/** + * @brief return the subscribed_signals map. + * + * return std::map - map of subscribed signals. + */ +extern std::map subscribed_signals; +std::map& get_subscribed_signals(); + +/** + * @brief The type signature for a CAN signal decoder. + * + * @desc A SignalDecoder transforms a raw floating point CAN signal into a number, + * string or boolean. + * + * @param[in] CanSignal signal - The CAN signal that we are decoding. + * @param[in] CanSignal signals - The list of all signals. + * @param[in] int signalCount - The length of the signals array. + * @param[in] float value - The CAN signal parsed from the message as a raw floating point + * value. + * @param[out] bool send - An output parameter. If the decoding failed or the CAN signal should + * not send for some other reason, this should be flipped to false. + * + * @return a decoded value in an openxc_DynamicField struct. + */ +typedef openxc_DynamicField (*SignalDecoder)(struct CanSignal& signal, + const std::vector& signals, float value, bool* send); + +/** + * @brief: The type signature for a CAN signal encoder. + * + * @desc A SignalEncoder transforms a number, string or boolean into a raw floating + * point value that fits in the CAN signal. + * + * @params[signal] - The CAN signal to encode. + * @params[value] - The dynamic field to encode. + * @params[send] - An output parameter. If the encoding failed or the CAN signal should + * not be encoded for some other reason, this will be flipped to false. + */ +typedef uint64_t (*SignalEncoder)(struct CanSignal* signal, + openxc_DynamicField* value, bool* send); + +/** + * @struct CanSignalState + * + * @brief A state encoded (SED) signal's mapping from numerical values to + * OpenXC state names. + */ +struct CanSignalState { + const int value; /*!< int value - The integer value of the state on the CAN bus.*/ + const char* name; /*!< char* name - The corresponding string name for the state in OpenXC. */ +}; +typedef struct CanSignalState CanSignalState; + +/** + * @struct CanSignal + * + * @brief A CAN signal to decode from the bus and output over USB. + */ +struct CanSignal { + struct CanMessageDefinition* message; /*!< message - The message this signal is a part of. */ + const char* generic_name; /*!< generic_name - The name of the signal to be output over USB.*/ + uint8_t bitPosition; /*!< bitPosition - The starting bit of the signal in its CAN message (assuming + * non-inverted bit numbering, i.e. the most significant bit of + * each byte is 0) */ + uint8_t bitSize; /*!< bitSize - The width of the bit field in the CAN message. */ + float factor; /*!< factor - The final value will be multiplied by this factor. Use 1 if you + * don't need a factor. */ + float offset; /*!< offset - The final value will be added to this offset. Use 0 if you + * don't need an offset. */ + float minValue; /*!< minValue - The minimum value for the processed signal.*/ + float maxValue; /*!< maxValue - The maximum value for the processed signal. */ + FrequencyClock frequencyClock; /*!< frequencyClock - A FrequencyClock struct to control the maximum frequency to + * process and send this signal. To process every value, set the + * clock's frequency to 0. */ + bool sendSame; /*!< sendSame - If true, will re-send even if the value hasn't changed.*/ + bool forceSendChanged; /*!< forceSendChanged - If true, regardless of the frequency, it will send the + * value if it has changed. */ + const CanSignalState* states; /*!< states - An array of CanSignalState describing the mapping + * between numerical and string values for valid states. */ + uint8_t stateCount; /*!< stateCount - The length of the states array. */ + bool writable; /*!< writable - True if the signal is allowed to be written from the USB host + * back to CAN. Defaults to false.*/ + SignalDecoder decoder; /*!< decoder - An optional function to decode a signal from the bus to a human + * readable value. If NULL, the default numerical decoder is used. */ + SignalEncoder encoder; /*!< encoder - An optional function to encode a signal value to be written to + * CAN into a byte array. If NULL, the default numerical encoder + * is used. */ + bool received; /*!< received - True if this signal has ever been received.*/ + float lastValue; /*!< lastValue - The last received value of the signal. If 'received' is false, + * this value is undefined. */ +}; +typedef struct CanSignal CanSignal; + +std::vector& get_can_signals(); + +size_t getSignalCount(); + +void find_can_signals(const openxc_DynamicField &key, std::vector& found_signals); + +uint32_t get_signal_id(const CanSignal& sig); \ No newline at end of file diff --git a/src/diagnostic/diagnostic-message.cpp b/src/diagnostic/diagnostic-message.cpp new file mode 100644 index 00000000..bfca85bc --- /dev/null +++ b/src/diagnostic/diagnostic-message.cpp @@ -0,0 +1,206 @@ +/* + * 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. + */ + +#include "obd2-signals.hpp" + +#include "signals.hpp" + +const char *UNIT_NAMES[10] = { + "POURCENT", + "DEGREES_CELSIUS", + "KPA", + "RPM", + "GRAMS_SEC", + "SECONDS", + "KM", + "KM_H", + "PA", + "NM" +}; + +/* +* Pre-defined OBD-II PIDs to query for if supported by the vehicle. +*/ + std::vector OBD2_PIDS = { + { 0x04, "obd2.engine.load", 0, 100, POURCENT, 5, false}, + { 0x05, "obd2.engine.coolant.temperature", -40, 215, DEGREES_CELSIUS, 1, false}, + { 0x0a, "obd2.fuel.pressure", 0, 765, KPA, 1, false}, + { 0x0b, "obd2.intake.manifold.pressure", 0, 255, KPA, 1, false}, + { 0x0c, "obd2.engine.speed", 0, 16383, RPM, 5, false}, + { 0x0d, "obd2.vehicle.speed", 0, 255, KM_H, 5, false}, + { 0x0f, "obd2.intake.air.temperature", -40, 215, DEGREES_CELSIUS, 1, false}, + { 0x10, "obd2.mass.airflow", 0, 655, GRAMS_SEC, 5, false}, + { 0x11, "obd2.throttle.position", 0, 100, POURCENT, 5, false}, + { 0x1f, "obd2.running.time", 0, 65535, SECONDS, 1, false}, + { 0x2d, "obd2.EGR.error", -100, 99, POURCENT, 0, false}, + { 0x2f, "obd2.fuel.level", 0, 100, POURCENT, 1, false}, + { 0x33, "obd2.barometric.pressure", 0, 255, KPA, 1, false}, + { 0x4c, "obd2.commanded.throttle.position", 0, 100, POURCENT, 1, false}, + { 0x52, "obd2.ethanol.fuel.percentage", 0, 100, POURCENT, 1, false}, + { 0x5a, "obd2.accelerator.pedal.position", 0, 100, POURCENT, 5, false}, + { 0x5b, "obd2.hybrid.battery-pack.remaining.life", 0, 100, POURCENT, 5, false}, + { 0x5c, "obd2.engine.oil.temperature",-40, 210, DEGREES_CELSIUS, 1, false}, + { 0x63, "obd2.engine.torque", 0, 65535, NM, 1, false} +}; + +obd2_signals_t::obd2_signals_t(uint8_t pid, const char* generic_name, const int min, const int max, enum UNIT unit, int frequency, bool supported) + : pid_{pid}, generic_name_{generic_name}, min_{min}, max_{max}, unit_{unit}, frequency_{frequency}, supported_{supported} +{ +} + +void obd2_signals_t::init_diagnostic_shims(can_bus_dev_t& can_bus_dev) +{ + DiagnosticShims shims_ = diagnostic_init_shims(shims_logger, can_bus_dev.send_can_message, NULL); + + int n_pids_, i_; + + n_pids_ = size(Obd2Pid); + for(i_=0; i_<=n_pids_; i_++) + { + } +} + +/** + * @fn std::vector find_signals(const openxc_DynamicField &key) + * @brief return signals name found searching through CAN_signals and OBD2 pid + * + * @param[in] const openxc_DynamicField : can contain numeric or string value in order to search against + * can signals or obd2 signals name. + * + * @return std::vector Vector of signals name found. + */ +void obd2_signals_t::find_obd2_signals(const openxc_DynamicField &key, std::vector& found_signals) +{ + switch(key.type) + { + case openxc_DynamicField_Type::openxc_DynamicField_Type_STRING: + lookup_signals_by_name(key.string_value, get_obd2_signals(), found_signals); + break; + case openxc_DynamicField_Type::openxc_DynamicField_Type_NUM: + lookup_signals_by_id(key.numeric_value, get_obd2_signals(), found_signals); + break; + default: + ERROR(binder_interface, "find_signals: wrong openxc_DynamicField specified. Use openxc_DynamicField_Type_NUM or openxc_DynamicField_Type_STRING type only."); + break; + } + DEBUG(binder_interface, "Found %d signal(s)", (int)found_signals.size()); +} + +std::vector& get_obd2_signals() +{ + return OBD2_PIDS; +} + +uint32_t get_signal_id(const Obd2Pid& sig) +{ + return (uint32_t)sig.pid; +} + +void shims_logger(const char* m, const struct afb_binding_interface *interface) +{ + DEBUG(interface, "%s", m); +} + +void shims_timer() +{ +} + +bool obd2_signals_t::is_obd2_response(can_message_t can_message) +{ + if(can_message.get_id() >= 0x7E8 && can_message.get_id() <= 0x7EF) + { + openxc_VehicleMessage message = {0}; + message.has_type = true; + message.type = openxc_VehicleMessage_Type_DIAGNOSTIC; + message.has_diagnostic_response = true; + message.diagnostic_response = {0}; + message.diagnostic_response.has_bus = true; + message.diagnostic_response.bus = bus->address; + message.diagnostic_response.has_message_id = true; + //7DF should respond with a random message id between 7e8 and 7ef + //7E0 through 7E7 should respond with a id that is 8 higher (7E0->7E8) + if(can_message.get_id() == 0x7DF) + { + message.diagnostic_response.message_id = rand()%(0x7EF-0x7E8 + 1) + 0x7E8; + } + else if(commandRequest->message_id >= 0x7E0 && commandRequest->message_id <= 0x7E7) + { + message.diagnostic_response.message_id = commandRequest->message_id + 8; + } + message.diagnostic_response.has_mode = true; + message.diagnostic_response.mode = commandRequest->mode; + if(commandRequest->has_pid) + { + message.diagnostic_response.has_pid = true; + message.diagnostic_response.pid = commandRequest->pid; + } + message.diagnostic_response.has_value = true; + message.diagnostic_response.value = rand() % 100; + pipeline::publish(&message, &getConfiguration()->pipeline); + } + else //If it's outside the range, the command_request will return false + { + debug("Sent message ID is outside the valid range for emulator (7DF to 7E7)"); + status=false; +}; + +void obd2_signals_t::add_request(int pid) +{ + DiagnosticRequest request = { + arbitration_id: OBD2_FUNCTIONAL_BROADCAST_ID, + mode: 0x1, has_true, pid}; +} + +/** +* @brief Check if a request is an OBD-II PID request. +* +* @return true if the request is a mode 1 request and it has a 1 byte PID. +*/ +bool obd2_signals_t::is_obd2_request(DiagnosticRequest* request) +{ + return request->mode == 0x1 && request->has_pid && request->pid < 0xff; +} + +/** +* @brief Check if requested signal name is an obd2 pid +* +* @return true if name began with obd2 else false. +*/ +bool obd2_signals_t::is_obd2_signal(const char *name) +{ + if(fnmatch("obd2.*", name, FNM_CASEFOLD) == 0) + return true; + return false; +} + +/** +* @brief Decode the payload of an OBD-II PID. +* +* This function matches the type signature for a DiagnosticResponseDecoder, so +* it can be used as the decoder for a DiagnosticRequest. It returns the decoded +* value of the PID, using the standard formulas (see +* http://en.wikipedia.org/wiki/OBD-II_PIDs#Mode_01). +* +* @param[in] DiagnosticResponse response - the received DiagnosticResponse (the data is in response.payload, +* a byte array). This is most often used when the byte order is +* signiticant, i.e. with many OBD-II PID formulas. +* @param[in] float parsed_payload - the entire payload of the response parsed as an int. +*/ +float obd2_signals_t::decode_obd2_response(const DiagnosticResponse* response, float parsedPayload) +{ + return diagnostic_decode_obd2_pid(response); +} \ No newline at end of file diff --git a/src/diagnostic/diagnostic-message.hpp b/src/diagnostic/diagnostic-message.hpp new file mode 100644 index 00000000..a8f21e09 --- /dev/null +++ b/src/diagnostic/diagnostic-message.hpp @@ -0,0 +1,94 @@ +/* + * 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 "uds/uds.h" +#include "can-bus.hpp" +#include "can-message.hpp" + +#include "low-can-binding.hpp" + +enum UNIT { + POURCENT, + DEGREES_CELSIUS, + KPA, + RPM, + GRAMS_SEC, + SECONDS, + KM, + KM_H, + PA, + NM, + INVALID +}; + +/** + * @brief A representation of an OBD-II PID. + * + * pid - The 1 byte PID. + * name - A human readable name to use for this PID when published. + * min - minimum value for this pid + * max - maximum value for this pid + * unit - unit used + * frequency - The frequency to request this PID if supported by the vehicle + * when automatic, recurring OBD-II requests are enabled. + * supported - is it supported by the vehicle. Initialized after scan + */ +typedef struct _Obd2Pid { + uint8_t pid; + const char* generic_name; + const int min; + const int max; + enum UNIT unit; + int frequency; + bool supported; +} Obd2Pid; + +/** + * @brief - Object to handle obd2 session with pre-scan of supported pid + * then request them regularly + */ +class obd2_signals_t { + private: + uint8_t pid_; + const char* generic_name_; + const int min_; + const int max_; + enum UNIT unit_; + int frequency_; + bool supported_; + + public: + obd2_signals_t(uint8_t pid, const char* generic_name, const int min_, const int max_, enum UNIT unit, int frequency, bool supported); + + void init_diagnostic_shims(can_bus_dev_t& can_bus_dev) + void add_request(int pid); + std::vector& get_obd2_signals(); + + uint32_t get_signal_id(const Obd2Pid& sig); + void find_obd2_signals(const openxc_DynamicField &key, std::vector& found_signals); + + + bool is_obd2_response(can_message_t can_message); + bool is_obd2_request(DiagnosticRequest *request); + bool is_obd2_signal(const char *name); + + float decode_obd2_response(const DiagnosticResponse* response, float parsedPayload); +}; \ No newline at end of file diff --git a/src/low-can-binding.cpp b/src/low-can-binding.cpp index b9fb27df..043261fa 100644 --- a/src/low-can-binding.cpp +++ b/src/low-can-binding.cpp @@ -119,33 +119,33 @@ static int subscribe_unsubscribe_signal(struct afb_req request, bool subscribe, * * @return Number of correctly subscribed signal */ -static int subscribe_unsubscribe_signals(struct afb_req request, bool subscribe, std::vector& signals) +static int subscribe_unsubscribe_signals(struct afb_req request, bool subscribe, const std::vector& signals) { int rets = 0; - for(auto& signal_i : signals) + for(auto& sig : signals) { - int ret = subscribe_unsubscribe_signal(request, subscribe, signal_i); + int ret = subscribe_unsubscribe_signal(request, subscribe, sig); if(ret <= 0) return ret; rets++; - DEBUG(binder_interface, "Signal: %s subscribed", signal_i.c_str()); + DEBUG(binder_interface, "Signal: %s subscribed", sig.c_str()); } return rets; } static int subscribe_unsubscribe_name(struct afb_req request, bool subscribe, const char *name) { - std::vector sig; + std::vector signals; int ret = 0; openxc_DynamicField search_key = build_DynamicField(std::string(name)); - sig = find_signals(search_key); - if (sig.empty()) + signals = find_signals(search_key); + if (signalssig.empty()) ret = 0; - ret = subscribe_unsubscribe_signals(request, subscribe, sig); - NOTICE(binder_interface, "Subscribed correctly to %d/%d signal(s).", ret, (int)sig.size()); + ret = subscribe_unsubscribe_signals(request, subscribe, signals); + NOTICE(binder_interface, "Subscribed correctly to %d/%d signal(s).", ret, (int)signals.size()); return ret; } diff --git a/src/obd2-signals.cpp b/src/obd2-signals.cpp deleted file mode 100644 index 1a562d06..00000000 --- a/src/obd2-signals.cpp +++ /dev/null @@ -1,181 +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. - */ - -#include "obd2-signals.hpp" - -#include "signals.hpp" - -const char *UNIT_NAMES[10] = { - "POURCENT", - "DEGREES_CELSIUS", - "KPA", - "RPM", - "GRAMS_SEC", - "SECONDS", - "KM", - "KM_H", - "PA", - "NM" -}; - -/* -* Pre-defined OBD-II PIDs to query for if supported by the vehicle. -*/ - std::vector OBD2_PIDS = { - { 0x04, "obd2.engine.load", 0, 100, POURCENT, 5, false}, - { 0x05, "obd2.engine.coolant.temperature", -40, 215, DEGREES_CELSIUS, 1, false}, - { 0x0a, "obd2.fuel.pressure", 0, 765, KPA, 1, false}, - { 0x0b, "obd2.intake.manifold.pressure", 0, 255, KPA, 1, false}, - { 0x0c, "obd2.engine.speed", 0, 16383, RPM, 5, false}, - { 0x0d, "obd2.vehicle.speed", 0, 255, KM_H, 5, false}, - { 0x0f, "obd2.intake.air.temperature", -40, 215, DEGREES_CELSIUS, 1, false}, - { 0x10, "obd2.mass.airflow", 0, 655, GRAMS_SEC, 5, false}, - { 0x11, "obd2.throttle.position", 0, 100, POURCENT, 5, false}, - { 0x1f, "obd2.running.time", 0, 65535, SECONDS, 1, false}, - { 0x2d, "obd2.EGR.error", -100, 99, POURCENT, 0, false}, - { 0x2f, "obd2.fuel.level", 0, 100, POURCENT, 1, false}, - { 0x33, "obd2.barometric.pressure", 0, 255, KPA, 1, false}, - { 0x4c, "obd2.commanded.throttle.position", 0, 100, POURCENT, 1, false}, - { 0x52, "obd2.ethanol.fuel.percentage", 0, 100, POURCENT, 1, false}, - { 0x5a, "obd2.accelerator.pedal.position", 0, 100, POURCENT, 5, false}, - { 0x5b, "obd2.hybrid.battery-pack.remaining.life", 0, 100, POURCENT, 5, false}, - { 0x5c, "obd2.engine.oil.temperature",-40, 210, DEGREES_CELSIUS, 1, false}, - { 0x63, "obd2.engine.torque", 0, 65535, NM, 1, false} -}; - -/** - * @fn std::vector find_signals(const openxc_DynamicField &key) - * @brief return signals name found searching through CAN_signals and OBD2 pid - * - * @param[in] const openxc_DynamicField : can contain numeric or string value in order to search against - * can signals or obd2 signals name. - * - * @return std::vector Vector of signals name found. - */ -void find_signals(const openxc_DynamicField &key, std::vector& found_signals) -{ - switch(key.type) - { - case openxc_DynamicField_Type::openxc_DynamicField_Type_STRING: - lookup_signals_by_name(key.string_value, get_obd2_signals(), found_signals); - break; - case openxc_DynamicField_Type::openxc_DynamicField_Type_NUM: - lookup_signals_by_id(key.numeric_value, get_obd2_signals(), found_signals); - break; - default: - ERROR(binder_interface, "find_signals: wrong openxc_DynamicField specified. Use openxc_DynamicField_Type_NUM or openxc_DynamicField_Type_STRING type only."); - break; - } - DEBUG(binder_interface, "Found %d signal(s)", (int)found_signals.size()); -} - -std::vector& get_obd2_signals() -{ - return OBD2_PIDS; -} - -uint32_t get_signal_id(const Obd2Pid& sig) -{ - return (uint32_t)sig.pid; -} - -void shims_logger(const char* m, const struct afb_binding_interface *interface) -{ - DEBUG(interface, "%s", m); -} - -void shims_timer() -{ -} - -bool is_obd2_response(can_message_t can_message) -{ - if(can_message.get_id() >= 0x7E8 && can_message.get_id() <= 0x7EF) - { - openxc_VehicleMessage message = {0}; - message.has_type = true; - message.type = openxc_VehicleMessage_Type_DIAGNOSTIC; - message.has_diagnostic_response = true; - message.diagnostic_response = {0}; - message.diagnostic_response.has_bus = true; - message.diagnostic_response.bus = bus->address; - message.diagnostic_response.has_message_id = true; - //7DF should respond with a random message id between 7e8 and 7ef - //7E0 through 7E7 should respond with a id that is 8 higher (7E0->7E8) - if(commandRequest->message_id == 0x7DF) - { - message.diagnostic_response.message_id = rand()%(0x7EF-0x7E8 + 1) + 0x7E8; - } - else if(commandRequest->message_id >= 0x7E0 && commandRequest->message_id <= 0x7E7) - { - message.diagnostic_response.message_id = commandRequest->message_id + 8; - } - message.diagnostic_response.has_mode = true; - message.diagnostic_response.mode = commandRequest->mode; - if(commandRequest->has_pid) - { - message.diagnostic_response.has_pid = true; - message.diagnostic_response.pid = commandRequest->pid; - } - message.diagnostic_response.has_value = true; - message.diagnostic_response.value = rand() % 100; - pipeline::publish(&message, &getConfiguration()->pipeline); - } - else //If it's outside the range, the command_request will return false - { - debug("Sent message ID is outside the valid range for emulator (7DF to 7E7)"); - status=false; -}; - -/* - * Will scan for supported Obd2 pids - * -obd2_handler_t::obd2_handler_t() -{ - DiagnosticShims shims_ = diagnostic_init_shims(shims_logger, can_bus.send_can_message, NULL); - - int n_pids_, i_; - - n_pids_ = size(Obd2Pid); - for(i_=0; i_<=n_pids_; i_++) - { - } -} - -void obd2_handler_t::add_request(int pid) -{ - DiagnosticRequest request = { - arbitration_id: OBD2_FUNCTIONAL_BROADCAST_ID, - mode: 0x1, has_true, pid}; -} - -bool obd2_handler_t::is_obd2_request(DiagnosticRequest* request) -{ - return request->mode == 0x1 && request->has_pid && request->pid < 0xff; -} - -bool obd2_handler_t::is_obd2_signal(const char *name) -{ - if(fnmatch("obd2.*", name, FNM_CASEFOLD) == 0) - return true; - return false; -} - -bool obd2_handler_t::decode_obd2_response(DiagnosticResponse* responce) -{ - return diagnostic_decode_obd2_pid(response); -}*/ \ No newline at end of file diff --git a/src/obd2-signals.hpp b/src/obd2-signals.hpp deleted file mode 100644 index c9fa4f61..00000000 --- a/src/obd2-signals.hpp +++ /dev/null @@ -1,114 +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 "uds/uds.h" -#include "can-bus.hpp" -#include "can-message.hpp" - -#include "low-can-binding.hpp" - -enum UNIT { - POURCENT, - DEGREES_CELSIUS, - KPA, - RPM, - GRAMS_SEC, - SECONDS, - KM, - KM_H, - PA, - NM, - INVALID -}; - -/** - * @brief A representation of an OBD-II PID. - * - * pid - The 1 byte PID. - * name - A human readable name to use for this PID when published. - * min - minimum value for this pid - * max - maximum value for this pid - * unit - unit used - * frequency - The frequency to request this PID if supported by the vehicle - * when automatic, recurring OBD-II requests are enabled. - * supported - is it supported by the vehicle. Initialized after scan - */ -typedef struct _Obd2Pid { - uint8_t pid; - const char* generic_name; - const int min; - const int max; - enum UNIT unit; - int frequency; - bool supported; -} Obd2Pid; - -std::vector& get_obd2_signals(); -uint32_t get_signal_id(const Obd2Pid& sig); -void find_obd2_signals(const openxc_DynamicField &key, std::vector& found_signals); - -bool is_obd2_response(can_message_t can_message); - -/** - * @brief - Object to handle obd2 session with pre-scan of supported pid - * then request them regularly - */ -class obd2_handler_t { - private: - - public: - obd2_handler_t(); - /** - * @brief: - * - * Returns - */ - void find_obd2_pid(const char *name, std::vector *pids); - - /** - * @brief Check if a request is an OBD-II PID request. - * - * @return true if the request is a mode 1 request and it has a 1 byte PID. - */ - bool is_obd2_request(DiagnosticRequest *request); - - /** - * @brief Check if requested signal name is an obd2 pid - * - * @return true if name began with obd2 else false. - */ - bool is_obd2_signal(const char *name); - - /** - * @brief Decode the payload of an OBD-II PID. - * - * This function matches the type signature for a DiagnosticResponseDecoder, so - * it can be used as the decoder for a DiagnosticRequest. It returns the decoded - * value of the PID, using the standard formulas (see - * http://en.wikipedia.org/wiki/OBD-II_PIDs#Mode_01). - * - * @param[in] DiagnosticResponse response - the received DiagnosticResponse (the data is in response.payload, - * a byte array). This is most often used when the byte order is - * signiticant, i.e. with many OBD-II PID formulas. - * @param[in] float parsed_payload - the entire payload of the response parsed as an int. - */ - float handle_obd2_pid(const DiagnosticResponse* response, float parsedPayload); -}; \ No newline at end of file diff --git a/src/openxc-utils.cpp b/src/openxc-utils.cpp deleted file mode 100644 index 40745a06..00000000 --- a/src/openxc-utils.cpp +++ /dev/null @@ -1,134 +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 "openxc-utils.hpp" - -openxc_VehicleMessage build_VehicleMessage_with_SimpleMessage(openxc_DynamicField_Type type, const openxc_SimpleMessage& message) -{ - struct timeb t_msec; - long long int timestamp_msec; - - openxc_VehicleMessage v; - - if(!::ftime(&t_msec)) - { - timestamp_msec = ((long long int) t_msec.time) * 1000ll + - (long long int) t_msec.millitm; - - v.has_type = true; - v.type = openxc_VehicleMessage_Type::openxc_VehicleMessage_Type_SIMPLE; - v.has_simple_message = true; - v.simple_message = message; - v.has_timestamp = true; - v.timestamp = timestamp_msec; - - return v; - } - - v.has_type = true, - v.type = openxc_VehicleMessage_Type::openxc_VehicleMessage_Type_SIMPLE; - v.has_simple_message = true; - v.simple_message = message; - - return v; -} - -openxc_SimpleMessage build_SimpleMessage(const std::string& name, const openxc_DynamicField& value) -{ - openxc_SimpleMessage s; - - s.has_name = true; - ::strncpy(s.name, name.c_str(), 100); - s.has_value = true; - s.value = value; - - return s; -} - -openxc_DynamicField build_DynamicField(const std::string& value) -{ - openxc_DynamicField d; - d.has_type = true; - d.type = openxc_DynamicField_Type_STRING; - - d.has_string_value = true; - d.has_numeric_value = false; - d.has_boolean_value = false; - ::strncpy(d.string_value, value.c_str(), 100); - - return d; -} - -openxc_DynamicField build_DynamicField(double value) -{ - openxc_DynamicField d; - d.has_type = true; - d.type = openxc_DynamicField_Type_NUM; - - d.has_string_value = false; - d.has_numeric_value = true; - d.has_boolean_value = false; - d.numeric_value = value; - - return d; -} - -openxc_DynamicField build_DynamicField(bool value) -{ - openxc_DynamicField d; - d.has_type = true; - d.type = openxc_DynamicField_Type_BOOL; - - d.has_string_value = false; - d.has_numeric_value = false; - d.has_boolean_value = true; - d.boolean_value = value; - - return d; -} - -openxc_SimpleMessage get_simple_message(const openxc_VehicleMessage& v_msg) -{ - if (v_msg.has_simple_message) - return v_msg.simple_message; - - openxc_SimpleMessage s_msg = { false, "", false, build_DynamicField(false), false, build_DynamicField(false)}; - return s_msg; -} - -void jsonify_DynamicField(const openxc_DynamicField& field, json_object* value) -{ - if(field.has_numeric_value) - json_object_object_add(value, "value", json_object_new_double(field.numeric_value)); - else if(field.has_boolean_value) - json_object_object_add(value, "value", json_object_new_boolean(field.boolean_value)); - else if(field.has_string_value) - json_object_object_add(value, "value", json_object_new_string(field.string_value)); -} - -bool jsonify_simple(const openxc_SimpleMessage& s_msg, json_object* json) -{ - if(s_msg.has_name) - { - json_object_object_add(json, "name", json_object_new_string(s_msg.name)); - jsonify_DynamicField(s_msg.value, json); - return true; - } - json_object_object_add(json, "error", json_object_new_string("openxc_SimpleMessage doesn't have name'")); - return false; -} \ No newline at end of file diff --git a/src/openxc-utils.hpp b/src/openxc-utils.hpp deleted file mode 100644 index b7fa7884..00000000 --- a/src/openxc-utils.hpp +++ /dev/null @@ -1,126 +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. - */ - -#pragma once - -#include -#include -#include - -#include "openxc.pb.h" - -/** - * @fn openxc_VehicleMessage build_VehicleMessage_with_SimpleMessage(openxc_DynamicField_Type type, const openxc_SimpleMessage& message); - * - * @brief Build a specific VehicleMessage containing a SimpleMessage. - * - * @param[in] openxc_DynamicField_Type type - The type of message to build - * @param[in] openxc_SimpleMessage& message - simple message to include into openxc_VehicleMessage - * - * @return openxc_VehicleMessage - a vehicle message including simple message that will be convert into - * a JSON object before being pushed to the subscribers - */ -openxc_VehicleMessage build_VehicleMessage_with_SimpleMessage(openxc_DynamicField_Type type, const openxc_SimpleMessage& message); - -/** - * @fn openxc_SimpleMessage build_SimpleMessage(const std::string& name, const openxc_DynamicField& value); - * - * @brief Build an openxc_SimpleMessage associating a name to an openxc_DynamicField - * - * @param[in] std::string name - const string reference name to assign to the created SimpleMessage - * this will set has_name member to true and assign name to the name member. Maximum size for name is - * set to 100 char. - * @param[in] openxc_DynamicField value - const reference with DynamicField to assign to SimpleMessage - * value. - * - * @return an openxc_SimpleMessage struct initialized with name and value provided. - */ -openxc_SimpleMessage build_SimpleMessage(const std::string& name, const openxc_DynamicField& value); - -/** - * @fn openxc_DynamicField build_DynamicField(const std::string& value); - * - * @brief Build an openxc_DynamicField with a string value - * - * @param std::string value - const string reference value to assign to builded - * openxc_DynamicField. - * - * @return openxc_DynamicField initialized with a string value. - */ -openxc_DynamicField build_DynamicField(const std::string& value); - -/** - * @fn openxc_DynamicField build_DynamicField(double value); - * - * @brief Build an openxc_DynamicField with a double value - * - * @param double value - double value to assign to builded openxc_DynamicField. - * - * @return openxc_DynamicField initialized with a double value. - */ -openxc_DynamicField build_DynamicField(double value); - -/** - * @fn openxc_DynamicField build_DynamicField(bool value); - * - * @brief Build an openxc_DynamicField with a boolean value - * - * @param bool value - boolean value to assign to builded openxc_DynamicField. - * - * @return openxc_DynamicField initialized with a boolean value. - */ -openxc_DynamicField build_DynamicField(bool value); - -/** - * @fn void jsonify_DynamicField(const openxc_DynamicField& field, json_object* value); - * - * @brief Make a JSON object from a DynamicField - * - * @param[in] openxc_DynamicField field - openxc_DynamicField struct to convert into - * a json object. - * @param[out] json_object pointer with the DynamicField converted - */ -void jsonify_DynamicField(const openxc_DynamicField& field, json_object* value); - -/** - * @fn openxc_SimpleMessage get_simple_message(const openxc_VehicleMessage& v_msg); - * - * @brief Extract the simple message value from an openxc_VehicleMessage - * and return it. If there isn't SimpleMessage in the VehicleMessage then - * returned value will be a SimpleMessage with all field set at false. - * DynamicField from SimpleMessage will be boolean DynamicField set to false too. - * - * @param[in] openxc_VehicleMessage - const reference to openxc_VehicleMessage - * - * @return openxc_SimpleMessage - SimpleMessage from the provided VehicleMessage. - */ -openxc_SimpleMessage get_simple_message(const openxc_VehicleMessage& v_msg); - -/** - * @fn json_object* jsonify_simple(const openxc_SimpleMessage& s_msg); - * - * @brief Make a JSON object from a SimpleMessage - * - * @param[in] openxc_SimpleMessage s_msg - const reference to an openxc_SimpleMessage - * struct to convert into a json object. - * @param[out] json_object pointer with the DynamicField converted - * - * @return boolean value - true if SimpleMessage has been transformed into json object - * and false if not. In such case, a json object is returned { "error": "error msg"} - */ -bool jsonify_simple(const openxc_SimpleMessage& s_msg, json_object* json); \ No newline at end of file diff --git a/src/signals.cpp b/src/signals.cpp deleted file mode 100644 index 6e2f396a..00000000 --- a/src/signals.cpp +++ /dev/null @@ -1,76 +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. - */ - -#include "signals.hpp" - -/** - * @brief Can signal event map making access to afb_event - * externaly to an openxc existing structure. - * - * @desc Event map is making relation between CanSignal generic name - * and the afb_event struct used by application framework to pushed - * to the subscriber. - */ -std::map subscribed_signals; - -/** -* @brief Mutex allowing safe manipulation on subscribed_signals map. -* @desc To ensure that the map object isn't modified when we read it, you -* have to set this mutex before use subscribed_signals map object. -*/ -std::mutex subscribed_signals_mutex; - -std::mutex& get_subscribed_signals_mutex() -{ - return subscribed_signals_mutex; -} - -std::map& get_subscribed_signals() -{ - return subscribed_signals; -} - -/** - * @fn std::vector find_signals(const openxc_DynamicField &key) - * @brief return signals name found searching through CAN_signals and OBD2 pid - * - * @param[in] const openxc_DynamicField : can contain numeric or string value in order to search against - * can signals or obd2 signals name. - * - * @return std::vector Vector of signals name found. - */ -std::vector find_signals(const openxc_DynamicField &key) -{ - std::vector found_signals_name; - - switch(key.type) - { - case openxc_DynamicField_Type::openxc_DynamicField_Type_STRING: - lookup_signals_by_name(key.string_value, get_can_signals(), found_signals_name); - lookup_signals_by_name(key.string_value, get_obd2_signals(), found_signals_name); - break; - case openxc_DynamicField_Type::openxc_DynamicField_Type_NUM: - lookup_signals_by_id(key.numeric_value, get_can_signals(), found_signals_name); - lookup_signals_by_id(key.numeric_value, get_obd2_signals(), found_signals_name); - break; - default: - ERROR(binder_interface, "find_signals: wrong openxc_DynamicField specified. Use openxc_DynamicField_Type_NUM or openxc_DynamicField_Type_STRING type only."); - break; - } - DEBUG(binder_interface, "Found %d signal(s)", (int)found_signals_name.size()); - return found_signals_name; -} diff --git a/src/signals.hpp b/src/signals.hpp deleted file mode 100644 index 81ed4248..00000000 --- a/src/signals.hpp +++ /dev/null @@ -1,72 +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 "openxc.pb.h" -#include "can-signals.hpp" -#include "obd2-signals.hpp" - -template -void lookup_signals_by_name(const std::string& key, std::vector& signals, std::vector& found_signals) -{ - for(T& s : signals) - { - if(::fnmatch(key.c_str(), s.generic_name, FNM_CASEFOLD) == 0) - found_signals.push_back(&s); - } -} - -template -void lookup_signals_by_name(const std::string& key, std::vector& signals, std::vector& found_signals_name) -{ - for(const T& s : signals) - { - if(::fnmatch(key.c_str(), s.generic_name, FNM_CASEFOLD) == 0) - found_signals_name.push_back(s.generic_name); - } -} - -template -void lookup_signals_by_id(const double key, std::vector& signals, std::vector& found_signals) -{ - for(T& s : signals) - { - if(get_signal_id(s) == key) - { - found_signals.push_back(&s); - } - } -} - -template -void lookup_signals_by_id(const double key, std::vector& signals, std::vector& found_signals_name) -{ - for(T& s : signals) - { - if(get_signal_id(s) == key) - { - found_signals_name.push_back(s.generic_name); - } - } -} - -std::vector find_signals(const openxc_DynamicField &key); diff --git a/src/timer.cpp b/src/timer.cpp deleted file mode 100644 index 01fd7636..00000000 --- a/src/timer.cpp +++ /dev/null @@ -1,31 +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. - */ - -#include "timer.hpp" - -long long int systemTimeMs() -{ - struct timeb t_msec; - long long int timestamp_msec; - - if(!::ftime(&t_msec)) - { - timestamp_msec = (t_msec.time) * 1000ll + - t_msec.millitm; - } - return timestamp_msec; -} \ No newline at end of file diff --git a/src/timer.hpp b/src/timer.hpp deleted file mode 100644 index 7d0e4669..00000000 --- a/src/timer.hpp +++ /dev/null @@ -1,45 +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 - -/* - * @brief return epoch in milliseconds - * - * @return long long int epoch in milliseconds - */ -typedef long long int (*TimeFunction)(); - -/** - * @struct FrequencyClock - * @brief A frequency counting clock. - * - * @var FrequencyClock::frequency - * the clock frequency in Hz. - * @var FrequencyClock::last_time - * the last time (in milliseconds since startup) that the clock - * ticked. - * @var FrequencyClock::time_function - * a function returning current time - */ -typedef struct { - float frequency; - unsigned long lastTick; - TimeFunction timeFunction; -} FrequencyClock; \ No newline at end of file diff --git a/src/utils/openxc-utils.cpp b/src/utils/openxc-utils.cpp new file mode 100644 index 00000000..40745a06 --- /dev/null +++ b/src/utils/openxc-utils.cpp @@ -0,0 +1,134 @@ +/* + * 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 "openxc-utils.hpp" + +openxc_VehicleMessage build_VehicleMessage_with_SimpleMessage(openxc_DynamicField_Type type, const openxc_SimpleMessage& message) +{ + struct timeb t_msec; + long long int timestamp_msec; + + openxc_VehicleMessage v; + + if(!::ftime(&t_msec)) + { + timestamp_msec = ((long long int) t_msec.time) * 1000ll + + (long long int) t_msec.millitm; + + v.has_type = true; + v.type = openxc_VehicleMessage_Type::openxc_VehicleMessage_Type_SIMPLE; + v.has_simple_message = true; + v.simple_message = message; + v.has_timestamp = true; + v.timestamp = timestamp_msec; + + return v; + } + + v.has_type = true, + v.type = openxc_VehicleMessage_Type::openxc_VehicleMessage_Type_SIMPLE; + v.has_simple_message = true; + v.simple_message = message; + + return v; +} + +openxc_SimpleMessage build_SimpleMessage(const std::string& name, const openxc_DynamicField& value) +{ + openxc_SimpleMessage s; + + s.has_name = true; + ::strncpy(s.name, name.c_str(), 100); + s.has_value = true; + s.value = value; + + return s; +} + +openxc_DynamicField build_DynamicField(const std::string& value) +{ + openxc_DynamicField d; + d.has_type = true; + d.type = openxc_DynamicField_Type_STRING; + + d.has_string_value = true; + d.has_numeric_value = false; + d.has_boolean_value = false; + ::strncpy(d.string_value, value.c_str(), 100); + + return d; +} + +openxc_DynamicField build_DynamicField(double value) +{ + openxc_DynamicField d; + d.has_type = true; + d.type = openxc_DynamicField_Type_NUM; + + d.has_string_value = false; + d.has_numeric_value = true; + d.has_boolean_value = false; + d.numeric_value = value; + + return d; +} + +openxc_DynamicField build_DynamicField(bool value) +{ + openxc_DynamicField d; + d.has_type = true; + d.type = openxc_DynamicField_Type_BOOL; + + d.has_string_value = false; + d.has_numeric_value = false; + d.has_boolean_value = true; + d.boolean_value = value; + + return d; +} + +openxc_SimpleMessage get_simple_message(const openxc_VehicleMessage& v_msg) +{ + if (v_msg.has_simple_message) + return v_msg.simple_message; + + openxc_SimpleMessage s_msg = { false, "", false, build_DynamicField(false), false, build_DynamicField(false)}; + return s_msg; +} + +void jsonify_DynamicField(const openxc_DynamicField& field, json_object* value) +{ + if(field.has_numeric_value) + json_object_object_add(value, "value", json_object_new_double(field.numeric_value)); + else if(field.has_boolean_value) + json_object_object_add(value, "value", json_object_new_boolean(field.boolean_value)); + else if(field.has_string_value) + json_object_object_add(value, "value", json_object_new_string(field.string_value)); +} + +bool jsonify_simple(const openxc_SimpleMessage& s_msg, json_object* json) +{ + if(s_msg.has_name) + { + json_object_object_add(json, "name", json_object_new_string(s_msg.name)); + jsonify_DynamicField(s_msg.value, json); + return true; + } + json_object_object_add(json, "error", json_object_new_string("openxc_SimpleMessage doesn't have name'")); + return false; +} \ No newline at end of file diff --git a/src/utils/openxc-utils.hpp b/src/utils/openxc-utils.hpp new file mode 100644 index 00000000..b7fa7884 --- /dev/null +++ b/src/utils/openxc-utils.hpp @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include + +#include "openxc.pb.h" + +/** + * @fn openxc_VehicleMessage build_VehicleMessage_with_SimpleMessage(openxc_DynamicField_Type type, const openxc_SimpleMessage& message); + * + * @brief Build a specific VehicleMessage containing a SimpleMessage. + * + * @param[in] openxc_DynamicField_Type type - The type of message to build + * @param[in] openxc_SimpleMessage& message - simple message to include into openxc_VehicleMessage + * + * @return openxc_VehicleMessage - a vehicle message including simple message that will be convert into + * a JSON object before being pushed to the subscribers + */ +openxc_VehicleMessage build_VehicleMessage_with_SimpleMessage(openxc_DynamicField_Type type, const openxc_SimpleMessage& message); + +/** + * @fn openxc_SimpleMessage build_SimpleMessage(const std::string& name, const openxc_DynamicField& value); + * + * @brief Build an openxc_SimpleMessage associating a name to an openxc_DynamicField + * + * @param[in] std::string name - const string reference name to assign to the created SimpleMessage + * this will set has_name member to true and assign name to the name member. Maximum size for name is + * set to 100 char. + * @param[in] openxc_DynamicField value - const reference with DynamicField to assign to SimpleMessage + * value. + * + * @return an openxc_SimpleMessage struct initialized with name and value provided. + */ +openxc_SimpleMessage build_SimpleMessage(const std::string& name, const openxc_DynamicField& value); + +/** + * @fn openxc_DynamicField build_DynamicField(const std::string& value); + * + * @brief Build an openxc_DynamicField with a string value + * + * @param std::string value - const string reference value to assign to builded + * openxc_DynamicField. + * + * @return openxc_DynamicField initialized with a string value. + */ +openxc_DynamicField build_DynamicField(const std::string& value); + +/** + * @fn openxc_DynamicField build_DynamicField(double value); + * + * @brief Build an openxc_DynamicField with a double value + * + * @param double value - double value to assign to builded openxc_DynamicField. + * + * @return openxc_DynamicField initialized with a double value. + */ +openxc_DynamicField build_DynamicField(double value); + +/** + * @fn openxc_DynamicField build_DynamicField(bool value); + * + * @brief Build an openxc_DynamicField with a boolean value + * + * @param bool value - boolean value to assign to builded openxc_DynamicField. + * + * @return openxc_DynamicField initialized with a boolean value. + */ +openxc_DynamicField build_DynamicField(bool value); + +/** + * @fn void jsonify_DynamicField(const openxc_DynamicField& field, json_object* value); + * + * @brief Make a JSON object from a DynamicField + * + * @param[in] openxc_DynamicField field - openxc_DynamicField struct to convert into + * a json object. + * @param[out] json_object pointer with the DynamicField converted + */ +void jsonify_DynamicField(const openxc_DynamicField& field, json_object* value); + +/** + * @fn openxc_SimpleMessage get_simple_message(const openxc_VehicleMessage& v_msg); + * + * @brief Extract the simple message value from an openxc_VehicleMessage + * and return it. If there isn't SimpleMessage in the VehicleMessage then + * returned value will be a SimpleMessage with all field set at false. + * DynamicField from SimpleMessage will be boolean DynamicField set to false too. + * + * @param[in] openxc_VehicleMessage - const reference to openxc_VehicleMessage + * + * @return openxc_SimpleMessage - SimpleMessage from the provided VehicleMessage. + */ +openxc_SimpleMessage get_simple_message(const openxc_VehicleMessage& v_msg); + +/** + * @fn json_object* jsonify_simple(const openxc_SimpleMessage& s_msg); + * + * @brief Make a JSON object from a SimpleMessage + * + * @param[in] openxc_SimpleMessage s_msg - const reference to an openxc_SimpleMessage + * struct to convert into a json object. + * @param[out] json_object pointer with the DynamicField converted + * + * @return boolean value - true if SimpleMessage has been transformed into json object + * and false if not. In such case, a json object is returned { "error": "error msg"} + */ +bool jsonify_simple(const openxc_SimpleMessage& s_msg, json_object* json); \ No newline at end of file diff --git a/src/utils/signals.cpp b/src/utils/signals.cpp new file mode 100644 index 00000000..6e2f396a --- /dev/null +++ b/src/utils/signals.cpp @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#include "signals.hpp" + +/** + * @brief Can signal event map making access to afb_event + * externaly to an openxc existing structure. + * + * @desc Event map is making relation between CanSignal generic name + * and the afb_event struct used by application framework to pushed + * to the subscriber. + */ +std::map subscribed_signals; + +/** +* @brief Mutex allowing safe manipulation on subscribed_signals map. +* @desc To ensure that the map object isn't modified when we read it, you +* have to set this mutex before use subscribed_signals map object. +*/ +std::mutex subscribed_signals_mutex; + +std::mutex& get_subscribed_signals_mutex() +{ + return subscribed_signals_mutex; +} + +std::map& get_subscribed_signals() +{ + return subscribed_signals; +} + +/** + * @fn std::vector find_signals(const openxc_DynamicField &key) + * @brief return signals name found searching through CAN_signals and OBD2 pid + * + * @param[in] const openxc_DynamicField : can contain numeric or string value in order to search against + * can signals or obd2 signals name. + * + * @return std::vector Vector of signals name found. + */ +std::vector find_signals(const openxc_DynamicField &key) +{ + std::vector found_signals_name; + + switch(key.type) + { + case openxc_DynamicField_Type::openxc_DynamicField_Type_STRING: + lookup_signals_by_name(key.string_value, get_can_signals(), found_signals_name); + lookup_signals_by_name(key.string_value, get_obd2_signals(), found_signals_name); + break; + case openxc_DynamicField_Type::openxc_DynamicField_Type_NUM: + lookup_signals_by_id(key.numeric_value, get_can_signals(), found_signals_name); + lookup_signals_by_id(key.numeric_value, get_obd2_signals(), found_signals_name); + break; + default: + ERROR(binder_interface, "find_signals: wrong openxc_DynamicField specified. Use openxc_DynamicField_Type_NUM or openxc_DynamicField_Type_STRING type only."); + break; + } + DEBUG(binder_interface, "Found %d signal(s)", (int)found_signals_name.size()); + return found_signals_name; +} diff --git a/src/utils/signals.hpp b/src/utils/signals.hpp new file mode 100644 index 00000000..81ed4248 --- /dev/null +++ b/src/utils/signals.hpp @@ -0,0 +1,72 @@ +/* + * 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 "openxc.pb.h" +#include "can-signals.hpp" +#include "obd2-signals.hpp" + +template +void lookup_signals_by_name(const std::string& key, std::vector& signals, std::vector& found_signals) +{ + for(T& s : signals) + { + if(::fnmatch(key.c_str(), s.generic_name, FNM_CASEFOLD) == 0) + found_signals.push_back(&s); + } +} + +template +void lookup_signals_by_name(const std::string& key, std::vector& signals, std::vector& found_signals_name) +{ + for(const T& s : signals) + { + if(::fnmatch(key.c_str(), s.generic_name, FNM_CASEFOLD) == 0) + found_signals_name.push_back(s.generic_name); + } +} + +template +void lookup_signals_by_id(const double key, std::vector& signals, std::vector& found_signals) +{ + for(T& s : signals) + { + if(get_signal_id(s) == key) + { + found_signals.push_back(&s); + } + } +} + +template +void lookup_signals_by_id(const double key, std::vector& signals, std::vector& found_signals_name) +{ + for(T& s : signals) + { + if(get_signal_id(s) == key) + { + found_signals_name.push_back(s.generic_name); + } + } +} + +std::vector find_signals(const openxc_DynamicField &key); diff --git a/src/utils/timer.cpp b/src/utils/timer.cpp new file mode 100644 index 00000000..01fd7636 --- /dev/null +++ b/src/utils/timer.cpp @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#include "timer.hpp" + +long long int systemTimeMs() +{ + struct timeb t_msec; + long long int timestamp_msec; + + if(!::ftime(&t_msec)) + { + timestamp_msec = (t_msec.time) * 1000ll + + t_msec.millitm; + } + return timestamp_msec; +} \ No newline at end of file diff --git a/src/utils/timer.hpp b/src/utils/timer.hpp new file mode 100644 index 00000000..7d0e4669 --- /dev/null +++ b/src/utils/timer.hpp @@ -0,0 +1,45 @@ +/* + * 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 + +/* + * @brief return epoch in milliseconds + * + * @return long long int epoch in milliseconds + */ +typedef long long int (*TimeFunction)(); + +/** + * @struct FrequencyClock + * @brief A frequency counting clock. + * + * @var FrequencyClock::frequency + * the clock frequency in Hz. + * @var FrequencyClock::last_time + * the last time (in milliseconds since startup) that the clock + * ticked. + * @var FrequencyClock::time_function + * a function returning current time + */ +typedef struct { + float frequency; + unsigned long lastTick; + TimeFunction timeFunction; +} FrequencyClock; \ No newline at end of file -- cgit 1.2.3-korg