diff options
Diffstat (limited to 'src/can')
-rw-r--r-- | src/can/can-bus.cpp | 556 | ||||
-rw-r--r-- | src/can/can-bus.hpp | 172 | ||||
-rw-r--r-- | src/can/can-command.hpp | 66 | ||||
-rw-r--r-- | src/can/can-decoder.cpp | 119 | ||||
-rw-r--r-- | src/can/can-decoder.hpp | 161 | ||||
-rw-r--r-- | src/can/can-message.cpp | 313 | ||||
-rw-r--r-- | src/can/can-message.hpp | 151 | ||||
-rw-r--r-- | src/can/can-signals.cpp | 101 | ||||
-rw-r--r-- | src/can/can-signals.hpp | 142 |
9 files changed, 1781 insertions, 0 deletions
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" <romain.forlot@iot.bzh> + * + * 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 <map> +#include <cerrno> +#include <vector> +#include <string> +#include <fcntl.h> +#include <unistd.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <json-c/json.h> +#include <linux/can/raw.h> + +#include "can-decoder.hpp" +#include "openxc-utils.hpp" + +extern "C" +{ + #include <afb/afb-binding.h> +} + +/******************************************************************************** +* +* 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 <CanSignal*> signals; + openxc_VehicleMessage vehicle_message; + openxc_DynamicField search_key, decoded_message; + + decoder_t decoder; + + while(is_decoding_) + { + std::unique_lock<std::mutex> 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<std::mutex> subscribed_signals_lock(get_subscribed_signals_mutex()); + std::map<std::string, struct afb_event>& 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<std::mutex> 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<std::mutex> 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<std::mutex> subscribed_signals_lock(get_subscribed_signals_mutex()); + std::map<std::string, struct afb_event>& 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<std::string> 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<can_bus_dev_t>(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<std::string> can_bus_t::read_conf() +{ + std::vector<std::string> 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<std::string, std::shared_ptr<can_bus_dev_t>> 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<struct canfd_frame&, size_t> 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<struct canfd_frame&, size_t>(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<std::mutex> 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" <romain.forlot@iot.bzh> + * + * 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 <mutex> +#include <queue> +#include <thread> +#include <linux/can.h> +#include <condition_variable> + +#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_t> 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 <openxc_VehicleMessage> vehicle_message_q_; /*!< queue that'll store openxc_VehicleMessage to pushed */ + + std::map<std::string, std::shared_ptr<can_bus_dev_t>> 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<std::string> 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<std::string, std::shared_ptr<can_bus_dev_t>> 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<struct canfd_frame&, size_t> 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" <romain.forlot@iot.bzh> + * + * 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" <romain.forlot@iot.bzh> + * + * 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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" <romain.forlot@iot.bzh> + * + * 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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<CanSignal>& 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" <romain.forlot@iot.bzh> + * + * 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 <cstring> + +#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<CAN_MESSAGE_SIZE;i++) + if(data_[i] != 0) + return true; + } + return false; +} + +/** +* @brief Set id_ member value. +* +* Preferred way to initialize these members by using +* convert_from_canfd_frame method. +* +* @param uint32_t id_ class member +*/ +void can_message_t::set_id_and_format(const uint32_t new_id) +{ + set_format(new_id); + switch(format_) + { + case CanMessageFormat::STANDARD: + id_ = new_id & CAN_SFF_MASK; + break; + case CanMessageFormat::EXTENDED: + id_ = new_id & CAN_EFF_MASK; + break; + case CanMessageFormat::ERROR: + id_ = new_id & (CAN_ERR_MASK|CAN_ERR_FLAG); + break; + default: + ERROR(binder_interface, "ERROR: Can set id, not a compatible format or format not set prior to set id."); + break; + } +} + +/** +* @brief Set format_ member value. +* +* Preferred way to initialize these members by using +* convert_from_canfd_frame method. +* +* @param CanMessageFormat format_ class member +*/ +void can_message_t::set_format(const CanMessageFormat new_format) +{ + if(new_format == CanMessageFormat::STANDARD || new_format == CanMessageFormat::EXTENDED || new_format == CanMessageFormat::ERROR) + format_ = new_format; + else + ERROR(binder_interface, "ERROR: Can set format, wrong format chosen"); +} + +/** +* @brief Set format_ member value. Deducing from the can_id +* of a canfd_frame. +* +* Preferred way to initialize these members by using +* convert_from_canfd_frame method. +* +* @param uint32_t can_id integer from a canfd_frame +*/ +void can_message_t::set_format(const uint32_t can_id) +{ + if (can_id & CAN_ERR_FLAG) + format_ = CanMessageFormat::ERROR; + else if (can_id & CAN_EFF_FLAG) + format_ = CanMessageFormat::EXTENDED; + else + format_ = CanMessageFormat::STANDARD; +} + +/** +* @brief Set format_ member value. +* +* Preferred way to initialize these members by using +* convert_from_canfd_frame method. +* +* @param CanMessageFormat format_ class member +*/ +void can_message_t::set_flags(const uint8_t flags) +{ + flags_ = flags & 0xF; +} + +/** +* @brief Set length_ member value. +* +* Preferred way to initialize these members by using +* convert_from_canfd_frame method. +* +* @param uint8_t length_ array with a max size of 8 elements. +*/ +void can_message_t::set_length(const uint8_t new_length) +{ + if(rtr_flag_) + length_ = new_length & 0xF; + else + { + length_ = (new_length > 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;i<maxdlen_;i++) + { + data_.push_back(new_data[i]); + } +} + +/** +* @brief Take a canfd_frame struct to initialize class members +* +* This is the preferred way to initialize class members. +* +* @param canfd_frame struct read from can bus device. +*/ +void can_message_t::convert_from_canfd_frame(const std::pair<struct canfd_frame&, size_t>args) +{ + // 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" <romain.forlot@iot.bzh> + * + * 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 <vector> +#include <string> +#include <cstdint> +#include <linux/can.h> + +#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<uint8_t> 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::pair<struct canfd_frame&, size_t>args); + 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; /*!<index - A numerical ID for the message set, ideally the index in an array + * for fast lookup*/ + const std::string name; /*!< name - The name of the message set.*/ + uint8_t busCount; /*!< busCount - The number of CAN buses defined for this message set.*/ + unsigned short messageCount; /*!< messageCount - The number of CAN messages (across all buses) defined for + * this message set.*/ + unsigned short signalCount; /*!< signalCount - The number of CAN signals (across all messages) defined for + * this message set.*/ + unsigned short commandCount; /*!< commandCount - The number of CanCommmands defined for this message set.*/ +} CanMessageSet; + +/** Public: Return the currently active CAN configuration. */ +CanMessageSet* getActiveMessageSet(); + +/** Public: Retrive a list of all possible CAN configurations. + * + * Returns a pointer to an array of all configurations. + */ +CanMessageSet* getMessageSets(); + +/** Public: Return the length of the array returned by getMessageSets() */ +int getMessageSetCount(); + +/* Public: Return an array of all CAN messages to be processed in the active + * configuration. + */ +CanMessageDefinition* getMessages(); + +/* Public: Return the length of the array returned by getMessages(). */ +int getMessageCount();
\ No newline at end of file diff --git a/src/can/can-signals.cpp b/src/can/can-signals.cpp new file mode 100644 index 00000000..07689a01 --- /dev/null +++ b/src/can/can-signals.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * 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 <fnmatch.h> + +#include "signals.hpp" +#include "obd2-signals.hpp" +#include "can-decoder.hpp" +#include "low-can-binding.hpp" + +std::vector<std::vector<CanMessageDefinition>> 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<std::vector<CanSignal>> 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<CanSignal>& get_can_signals() + * + * @return A reference to a vector of signals + */ +std::vector<CanSignal>& 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<CanSignal*>& 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<CanSignal*>& found_signals : provided vector to fill with ponter to signals matched. + * + */ +void find_can_signals(const openxc_DynamicField& key, std::vector<CanSignal*>& 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" <romain.forlot@iot.bzh> + * + * 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 <map> +#include <mutex> +#include <queue> +#include <vector> +#include <string> + +#include "obd2-signals.hpp" +#include "timer.hpp" +#include "openxc.pb.h" +#include "can-bus.hpp" +#include "can-message.hpp" + +extern "C" +{ + #include <afb/afb-binding.h> + #include <afb/afb-event-itf.h> +} + +#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<std::string, struct afb_event> - map of subscribed signals. + */ +extern std::map<std::string, struct afb_event> subscribed_signals; +std::map<std::string, struct afb_event>& 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<CanSignal>& 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<CanSignal>& get_can_signals(); + +size_t getSignalCount(); + +void find_can_signals(const openxc_DynamicField &key, std::vector<CanSignal*>& found_signals); + +uint32_t get_signal_id(const CanSignal& sig);
\ No newline at end of file |