diff options
Diffstat (limited to 'low-can-binding/can')
-rw-r--r-- | low-can-binding/can/can-bus.cpp | 337 | ||||
-rw-r--r-- | low-can-binding/can/can-bus.hpp | 94 | ||||
-rw-r--r-- | low-can-binding/can/can-command.hpp | 55 | ||||
-rw-r--r-- | low-can-binding/can/can-decoder.cpp | 244 | ||||
-rw-r--r-- | low-can-binding/can/can-decoder.hpp | 49 | ||||
-rw-r--r-- | low-can-binding/can/can-message-definition.cpp | 100 | ||||
-rw-r--r-- | low-can-binding/can/can-message-definition.hpp | 71 | ||||
-rw-r--r-- | low-can-binding/can/can-message-set.cpp | 59 | ||||
-rw-r--r-- | low-can-binding/can/can-message-set.hpp | 50 | ||||
-rw-r--r-- | low-can-binding/can/can-message.cpp | 389 | ||||
-rw-r--r-- | low-can-binding/can/can-message.hpp | 80 | ||||
-rw-r--r-- | low-can-binding/can/can-signals.cpp | 203 | ||||
-rw-r--r-- | low-can-binding/can/can-signals.hpp | 151 |
13 files changed, 1882 insertions, 0 deletions
diff --git a/low-can-binding/can/can-bus.cpp b/low-can-binding/can/can-bus.cpp new file mode 100644 index 0000000..6167335 --- /dev/null +++ b/low-can-binding/can/can-bus.cpp @@ -0,0 +1,337 @@ +/* + * 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 <net/if.h> +#include <sys/socket.h> +#include <json-c/json.h> +#include <linux/can/raw.h> +#include <map> +#include <cerrno> +#include <vector> +#include <string> +#include <algorithm> + +#include "can-bus.hpp" + +#include "can-signals.hpp" +#include "can-decoder.hpp" +#include "../binding/application.hpp" +#include "../utils/signals.hpp" +#include "../utils/openxc-utils.hpp" + +/// @brief Class constructor +/// +/// @param[in] conf_file - handle to the json configuration file. +can_bus_t::can_bus_t(utils::config_parser_t conf_file) + : conf_file_{conf_file} +{} + +/// @brief Take a decoded message to determine if its value comply with the wanted +/// filtering values. +/// +/// @param[in] vehicle_message - A decoded message to analyze +/// @param[in] can_subscription - the subscription which will be notified depending +/// on its filtering values. Filtering values are stored in the event_filtermember. +/// +/// @return True if the value is compliant with event filter values, false if not... +bool can_bus_t::apply_filter(const openxc_VehicleMessage& vehicle_message, std::shared_ptr<low_can_subscription_t> can_subscription) +{ + bool send = false; + if(is_valid(vehicle_message)) + { + float min = std::isnan(can_subscription->get_min()) ? -INFINITY : can_subscription->get_min(); + float max = std::isnan(can_subscription->get_max()) ? INFINITY : can_subscription->get_max(); + double value = get_numerical_from_DynamicField(vehicle_message); + send = (value < min && value > max) ? false : true; + } + return send; +} + +/// @brief Will make the decoding operation on a classic CAN message. It will not +/// handle CAN commands nor diagnostic messages that have their own method to get +/// this happens. +/// +/// It will add to the vehicle_message queue the decoded message and tell the event push +/// thread to process it. +/// +/// @param[in] can_message - a single CAN message from the CAN socket read, to be decode. +/// +/// @return How many signals has been decoded. +void can_bus_t::process_can_signals(const can_message_t& can_message, std::map<int, std::shared_ptr<low_can_subscription_t> >& s) +{ + int subscription_id = can_message.get_sub_id(); + openxc_DynamicField decoded_message; + openxc_VehicleMessage vehicle_message; + + // First we have to found which can_signal_t it is + std::shared_ptr<low_can_subscription_t> sig = s[subscription_id]; + + if( s.find(subscription_id) != s.end() && afb_event_is_valid(s[subscription_id]->get_event())) + { + bool send = true; + decoded_message = decoder_t::translateSignal(*sig->get_can_signal(), can_message, application_t::instance().get_all_can_signals(), &send); + openxc_SimpleMessage s_message = build_SimpleMessage(sig->get_name(), decoded_message); + vehicle_message = build_VehicleMessage(s_message, can_message.get_timestamp()); + + if(send && apply_filter(vehicle_message, sig)) + { + std::lock_guard<std::mutex> decoded_can_message_lock(decoded_can_message_mutex_); + push_new_vehicle_message(subscription_id, vehicle_message); + DEBUG("%s CAN signals processed.", sig->get_name().c_str()); + } + } +} + +/// @brief Will make the decoding operation on a diagnostic CAN message.Then it find the subscribed signal +/// corresponding and will add the vehicle_message to the queue of event to pushed before notifying +/// the event push thread to process it. +/// +/// @param[in] manager - the diagnostic manager object that handle diagnostic communication +/// @param[in] can_message - a single CAN message from the CAN socket read, to be decode. +/// +/// @return How many signals has been decoded. +void can_bus_t::process_diagnostic_signals(diagnostic_manager_t& manager, const can_message_t& can_message, std::map<int, std::shared_ptr<low_can_subscription_t> >& s) +{ + int subscription_id = can_message.get_sub_id(); + + openxc_VehicleMessage vehicle_message = manager.find_and_decode_adr(can_message); + if( (vehicle_message.has_simple_message && vehicle_message.simple_message.has_name) && + s.find(subscription_id) != s.end() && afb_event_is_valid(s[subscription_id]->get_event())) + { + if (apply_filter(vehicle_message, s[subscription_id])) + { + std::lock_guard<std::mutex> decoded_can_message_lock(decoded_can_message_mutex_); + push_new_vehicle_message(subscription_id, vehicle_message); + DEBUG("%s CAN signals processed.", s[subscription_id]->get_name().c_str()); + } + } +} + +/// @brief thread to decoding raw CAN messages. +/// +/// Depending on the nature of message, if arbitration ID matches ID for a diagnostic response +/// then decoding a diagnostic message else use classic CAN signals decoding functions. +/// +/// It will take from the can_message_q_ queue the next can message to process then it 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. +/// +/// TODO: make diagnostic messages parsing optionnal. +void can_bus_t::can_decode_message() +{ + utils::signals_manager_t& sm = utils::signals_manager_t::instance(); + + while(is_decoding_) + { + std::unique_lock<std::mutex> can_message_lock(can_message_mutex_); + new_can_message_cv_.wait(can_message_lock); + while(!can_message_q_.empty()) + { + const can_message_t can_message = next_can_message(); + can_message_lock.unlock(); + + { + std::lock_guard<std::mutex> subscribed_signals_lock(sm.get_subscribed_signals_mutex()); + std::map<int, std::shared_ptr<low_can_subscription_t> >& s = sm.get_subscribed_signals(); + if(application_t::instance().get_diagnostic_manager().is_diagnostic_response(can_message)) + {process_diagnostic_signals(application_t::instance().get_diagnostic_manager(), can_message, s);} + else + {process_can_signals(can_message, s);} + } + can_message_lock.lock(); + } + new_decoded_can_message_.notify_one(); + can_message_lock.unlock(); + } +} + +/// @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_SimpleMessage s_message; + json_object* jo; + utils::signals_manager_t& sm = utils::signals_manager_t::instance(); + + 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); + while(!vehicle_message_q_.empty()) + { + std::pair<int, openxc_VehicleMessage> v_message = next_vehicle_message(); + decoded_can_message_lock.unlock(); + { + std::lock_guard<std::mutex> subscribed_signals_lock(sm.get_subscribed_signals_mutex()); + std::map<int, std::shared_ptr<low_can_subscription_t> >& s = sm.get_subscribed_signals(); + s_message = get_simple_message(v_message.second); + if(s.find(v_message.first) != s.end() && afb_event_is_valid(s[v_message.first]->get_event())) + { + jo = json_object_new_object(); + jsonify_simple(s_message, jo); + if(afb_event_push(s[v_message.first]->get_event(), jo) == 0) + { + if(v_message.second.has_diagnostic_response) + {on_no_clients(s[v_message.first], v_message.second.diagnostic_response.pid, s);} + else + {on_no_clients(s[v_message.first], s);} + } + } + } + decoded_can_message_lock.lock(); + } + decoded_can_message_lock.unlock(); + } +} + +/// @brief Will initialize threads that will decode +/// and push subscribed events. +void can_bus_t::start_threads() +{ + is_decoding_ = true; + th_decoding_ = std::thread(&can_bus_t::can_decode_message, this); + th_decoding_.detach(); + + is_pushing_ = true; + th_pushing_ = std::thread(&can_bus_t::can_event_push, this); + th_pushing_.detach(); +} + +/// @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 return new_can_message_cv_ member +/// +/// @return return new_can_message_cv_ 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 +const 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("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[in] can_msg - 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 +std::pair<int, openxc_VehicleMessage> can_bus_t::next_vehicle_message() +{ + std::pair<int, openxc_VehicleMessage> v_msg; + + if(! vehicle_message_q_.empty()) + { + v_msg = vehicle_message_q_.front(); + vehicle_message_q_.pop(); + DEBUG("next vehicle message poped"); + return v_msg; + } + + return v_msg; +} + +/// @brief Push a openxc_VehicleMessage into the queue +/// +/// @param[in] v_msg - const reference openxc_VehicleMessage object to push into the queue +void can_bus_t::push_new_vehicle_message(int subscription_id, const openxc_VehicleMessage& v_msg) +{ + vehicle_message_q_.push(std::make_pair(subscription_id, v_msg)); +} + +/// @brief Fills the CAN device map member with value from device +/// mapping configuration file read at initialization. +void can_bus_t::set_can_devices() +{ + can_devices_ = conf_file_.get_devices_name(); + + if(can_devices_.empty()) + { + ERROR("No mapping found in config file: '%s'. Check it that it have a CANbus-mapping section.", + conf_file_.filepath().c_str()); + } +} + + +/// @brief Return the CAN device index from the map +/// map are sorted so index depend upon alphabetical sorting. +int can_bus_t::get_can_device_index(const std::string& bus_name) const +{ + int i = 0; + for(const auto& d: can_devices_) + { + if(d.first == bus_name) + break; + i++; + } + return i; +} + +/// @brief Return CAN device name from a logical CAN device name gotten from +/// the signals.json description file which comes from a CAN databases file in +/// general. +const std::string can_bus_t::get_can_device_name(const std::string& id_name) const +{ + std::string ret; + for(const auto& d: can_devices_) + { + if(d.first == id_name) + { + ret = d.second; + break; + } + } + return ret; +} diff --git a/low-can-binding/can/can-bus.hpp b/low-can-binding/can/can-bus.hpp new file mode 100644 index 0000000..a76d3e8 --- /dev/null +++ b/low-can-binding/can/can-bus.hpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * Author "Loïc Collignon" <loic.collignon@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 <utility> +#include <mutex> +#include <queue> +#include <thread> +#include <linux/can.h> +#include <condition_variable> + +#include "openxc.pb.h" +#include "can-message.hpp" +#include "../utils/config-parser.hpp" +#include "../binding/low-can-hat.hpp" +#include "../binding/low-can-subscription.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 diagnostic_manager_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: + utils::config_parser_t conf_file_; ///< configuration file handle used to initialize can_bus_dev_t objects. + + bool apply_filter(const openxc_VehicleMessage& vehicle_message, std::shared_ptr<low_can_subscription_t> can_subscription); + void process_can_signals(const can_message_t& can_message, std::map<int, std::shared_ptr<low_can_subscription_t> >& s); + void process_diagnostic_signals(diagnostic_manager_t& manager, const can_message_t& can_message, std::map<int, std::shared_ptr<low_can_subscription_t> >& s); + + void can_decode_message(); + std::thread th_decoding_; ///< thread that'll handle decoding a can frame + bool is_decoding_ = false; ///< 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_ = false; ///< 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 <std::pair<int, openxc_VehicleMessage> > vehicle_message_q_; ///< queue that'll store openxc_VehicleMessage to pushed + + std::vector<std::pair<std::string, std::string> > can_devices_; ///< can_devices_ - holds a mapping between logical CAN devices names and linux CAN devices names. +public: + explicit can_bus_t(utils::config_parser_t conf_file); + can_bus_t(can_bus_t&&); + + void set_can_devices(); + int get_can_device_index(const std::string& bus_name) const; + const std::string get_can_device_name(const std::string& id_name) const; + + void start_threads(); + void stop_threads(); + + const 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(); + + std::pair<int, openxc_VehicleMessage> next_vehicle_message(); + void push_new_vehicle_message(int subscription_id, const openxc_VehicleMessage& v_msg); +}; diff --git a/low-can-binding/can/can-command.hpp b/low-can-binding/can/can-command.hpp new file mode 100644 index 0000000..4cf20cb --- /dev/null +++ b/low-can-binding/can/can-command.hpp @@ -0,0 +1,55 @@ +/* + * 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] name - the name of the received command. +/// @param[in] value - the value of the received command, in a DynamicField. The actual type +/// may be a number, string or bool. +/// @param[in] event - an optional event from the received command, in a DynamicField. The +/// actual type may be a number, string or bool. +/// @param[in] signals - The list of all signals. +/// @param[in] signalCount - The length of the signals array. +/// +typedef void (*CommandHandler)(const char* name, openxc_DynamicField* value, + openxc_DynamicField* event, can_signal_t* signals, int signalCount); + +/// @struct CanCommand +/// @brief The structure to represent a supported custom OpenXC command. +/// +/// 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; diff --git a/low-can-binding/can/can-decoder.cpp b/low-can-binding/can/can-decoder.cpp new file mode 100644 index 0000000..61bfe75 --- /dev/null +++ b/low-can-binding/can/can-decoder.cpp @@ -0,0 +1,244 @@ +/* + * 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" +#include "../utils/openxc-utils.hpp" +#include "can-message-definition.hpp" +#include "../binding/low-can-hat.hpp" + +/// @brief Parse the signal's bitfield from the given data and return the raw +/// value. +/// +/// @param[in] signal - The signal to parse from the data. +/// @param[in] message - can_message_t to parse +/// +/// @return Returns the raw value of the signal parsed as a bitfield from the given byte +/// array. +/// +float decoder_t::parseSignalBitfield(can_signal_t& signal, const can_message_t& message) +{ + return bitfield_parse_float(message.get_data(), CAN_MESSAGE_SIZE, + signal.get_bit_position(), signal.get_bit_size(), signal.get_factor(), + signal.get_offset()); +} + +/// @brief 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 can_signal_t.decoder field. +/// +/// @param[in] signal - The details of the signal that contains the state mapping. +/// @param[in] signals - The list of all signals +/// @param[in] value - The numerical value that will be wrapped in a DynamicField. +/// @param[out] send - An output argument that will be set to false if the value should +/// not be sent for any reason. +/// +/// @return 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. +/// +openxc_DynamicField decoder_t::noopDecoder(can_signal_t& signal, + const std::vector<std::shared_ptr<can_signal_t> >& signals, float value, bool* send) +{ + openxc_DynamicField decoded_value = build_DynamicField(value); + + return decoded_value; +} +/// @brief Coerces a numerical value to a boolean. +/// +/// This is an implementation of the SignalDecoder type signature, and can be +/// used directly in the can_signal_t.decoder field. +/// +/// @param[in] signal - The details of the signal that contains the state mapping. +/// @param[in] signals - The list of all signals +/// @param[in] value - The numerical value that will be converted to a boolean. +/// @param[out] send - An output argument that will be set to false if the value should +/// not be sent for any reason. +/// +/// @return 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. +/// +openxc_DynamicField decoder_t::booleanDecoder(can_signal_t& signal, + const std::vector<std::shared_ptr<can_signal_t> >& signals, float value, bool* send) +{ + openxc_DynamicField decoded_value = build_DynamicField(value == 0.0 ? false : true); + + return decoded_value; +} +/// @brief 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 can_signal_t.decoder field. +/// +/// This function always flips 'send' to false. +/// +/// @param[in] signal - The details of the signal that contains the state mapping. +/// @param[in] signals - The list of all signals. +/// @param[in] value - The numerical value that will be converted to a boolean. +/// @param[out] send - This output argument will always be set to false, so the caller will +/// know not to publish this value to the pipeline. +/// +/// @return Return value is undefined. +/// +openxc_DynamicField decoder_t::ignoreDecoder(can_signal_t& signal, + const std::vector<std::shared_ptr<can_signal_t> >& signals, float value, bool* send) +{ + if(send) + *send = false; + + openxc_DynamicField decoded_value; + + return decoded_value; +} + +/// @brief 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 can_signal_t.decoder field. +/// +/// @param[in] signal - The details of the signal that contains the state mapping. +/// @param[in] signals - The list of all signals. +/// @param[in] value - The numerical value that should map to a state. +/// @param[out] send - An output argument that will be set to false if the value should +/// not be sent for any reason. +/// +/// @return 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. +/// +openxc_DynamicField decoder_t::stateDecoder(can_signal_t& signal, + const std::vector<std::shared_ptr<can_signal_t> >& signals, float value, bool* send) +{ + const std::string signal_state = signal.get_states((uint8_t)value); + openxc_DynamicField decoded_value = build_DynamicField(signal_state); + if(signal_state.size() <= 0) + { + *send = false; + ERROR("No state found with index: %d", (int)value); + } + return decoded_value; +} + + +/// @brief 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 can_signal_t has a non-NULL 'decoder' field, the raw CAN signal value +/// will be passed to the decoder before publishing. +/// +/// @param[in] signal - The details of the signal to decode and forward. +/// @param[in] message - The received CAN message that should contain this signal. +/// @param[in] signals - an array of all active signals. +/// @param[out] 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. +/// +openxc_DynamicField decoder_t::translateSignal(can_signal_t& signal, const can_message_t& message, + const std::vector<std::shared_ptr<can_signal_t> >& signals, bool* send) +{ + float value = decoder_t::parseSignalBitfield(signal, message); + DEBUG("Decoded message from parseSignalBitfield: %f", value); + + // Must call the decoders every time, regardless of if we are going to + // decide to send the signal or not. + openxc_DynamicField decoded_value = decoder_t::decodeSignal(signal, + value, signals, send); + + signal.set_received(true); + + // Don't send if they is no changes + if ((signal.get_last_value() == value && !signal.get_send_same()) || !*send ) + { + *send = false; + } + signal.set_last_value(value); + signal.set_timestamp(message.get_timestamp()); + signal.get_message()->set_last_value(message); + return decoded_value; +} + +/// @brief Parse a signal from a CAN message and apply any required +/// transforations to get a human readable value. +/// +/// If the can_signal_t has a non-NULL 'decoder' field, the raw CAN signal value +/// will be passed to the decoder before returning. +/// +/// @param[in] signal - The details of the signal to decode and forward. +/// @param[in] value - The numerical value that will be converted to a boolean. +/// @param[in] signals - an array of all active signals. +/// @param[out] send - An output parameter that will be flipped to false if the value could +/// not be decoded. +/// +/// @return 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 decoder_t::decodeSignal( can_signal_t& signal, + float value, const std::vector<std::shared_ptr<can_signal_t> >& signals, bool* send) +{ + SignalDecoder decoder = signal.get_decoder() == nullptr ? + noopDecoder : signal.get_decoder(); + openxc_DynamicField decoded_value = decoder(signal, signals, + value, send); + return decoded_value; +} + +/// @brief Decode a transformed, human readable value from an raw CAN signal +/// already parsed from a CAN message. +/// +/// This is the same as decodeSignal 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. +/// +/// @param[in] signal - The details of the signal to decode and forward. +/// @param[in] message - Raw CAN message to decode +/// @param[in] signals - an array of all active signals. +/// @param[out] send - An output parameter that will be flipped to false if the value could +/// not be decoded. +/// +openxc_DynamicField decoder_t::decodeSignal( can_signal_t& signal, + const can_message_t& message, const std::vector<std::shared_ptr<can_signal_t> >& signals, bool* send) +{ + float value = parseSignalBitfield(signal, message); + return decodeSignal(signal, value, signals, send); +} + + +/// +/// @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] 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] parsed_payload - the entire payload of the response parsed as an int. +/// +/// @return Float decoded value. +/// +float decoder_t::decode_obd2_response(const DiagnosticResponse* response, float parsed_payload) +{ + return diagnostic_decode_obd2_pid(response); +}
\ No newline at end of file diff --git a/low-can-binding/can/can-decoder.hpp b/low-can-binding/can/can-decoder.hpp new file mode 100644 index 0000000..7eaa4a7 --- /dev/null +++ b/low-can-binding/can/can-decoder.hpp @@ -0,0 +1,49 @@ +/* + * 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-signals.hpp" +#include "can-message.hpp" +#include "openxc.pb.h" + +class decoder_t +{ +public: + static float parseSignalBitfield(can_signal_t& signal, const can_message_t& message); + + static openxc_DynamicField stateDecoder(can_signal_t& signal, const std::vector<std::shared_ptr<can_signal_t> >& signals, + float value, bool* send); + static openxc_DynamicField booleanDecoder(can_signal_t& signal, const std::vector<std::shared_ptr<can_signal_t> >& signals, + float value, bool* send); + static openxc_DynamicField ignoreDecoder(can_signal_t& signal, const std::vector<std::shared_ptr<can_signal_t> >& signals, + float value, bool* send); + static openxc_DynamicField noopDecoder(can_signal_t& signal, const std::vector<std::shared_ptr<can_signal_t> >& signals, + float value, bool* send); + + static openxc_DynamicField translateSignal(can_signal_t& signal, const can_message_t& message, + const std::vector<std::shared_ptr<can_signal_t> >& signals, bool* send); + + static openxc_DynamicField decodeSignal(can_signal_t& signal, const can_message_t& message, + const std::vector<std::shared_ptr<can_signal_t> >& signals, bool* send); + + static openxc_DynamicField decodeSignal(can_signal_t& signal, float value, + const std::vector<std::shared_ptr<can_signal_t> >& signals, bool* send); + + static float decode_obd2_response(const DiagnosticResponse* response, float parsed_payload); + +};
\ No newline at end of file diff --git a/low-can-binding/can/can-message-definition.cpp b/low-can-binding/can/can-message-definition.cpp new file mode 100644 index 0000000..28d45fe --- /dev/null +++ b/low-can-binding/can/can-message-definition.cpp @@ -0,0 +1,100 @@ +/* + * 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-definition.hpp" + +#include "../binding/application.hpp" + +can_message_definition_t::can_message_definition_t(const std::string bus) + : parent_{nullptr}, bus_{bus}, last_value_{CAN_MESSAGE_SIZE} +{} + +can_message_definition_t::can_message_definition_t( + const std::string bus, + uint32_t id, + frequency_clock_t frequency_clock, + bool force_send_changed) + : parent_{nullptr}, + bus_{bus}, + id_{id}, + frequency_clock_{frequency_clock}, + force_send_changed_{force_send_changed}, + last_value_{CAN_MESSAGE_SIZE} +{} + +can_message_definition_t::can_message_definition_t( + const std::string bus, + uint32_t id, + can_message_format_t format, + frequency_clock_t frequency_clock, + bool force_send_changed) + : parent_{nullptr}, + bus_{bus}, + id_{id}, + format_{format}, + frequency_clock_{frequency_clock}, + force_send_changed_{force_send_changed}, + last_value_{CAN_MESSAGE_SIZE} +{} + +can_message_definition_t::can_message_definition_t( + const std::string bus, + uint32_t id, + can_message_format_t format, + frequency_clock_t frequency_clock, + bool force_send_changed, + const std::vector<std::shared_ptr<can_signal_t> >& can_signals) + : parent_{nullptr}, + bus_{bus}, + id_{id}, + format_{format}, + frequency_clock_{frequency_clock}, + force_send_changed_{force_send_changed}, + last_value_{CAN_MESSAGE_SIZE}, + can_signals_{can_signals} +{} + +const std::string can_message_definition_t::get_bus_name() const +{ + return bus_; +} + +const std::string can_message_definition_t::get_bus_device_name() const +{ + return application_t::instance().get_can_bus_manager() + .get_can_device_name(bus_); +} + +uint32_t can_message_definition_t::get_id() const +{ + return id_; +} + +std::vector<std::shared_ptr<can_signal_t> >& can_message_definition_t::get_can_signals() +{ + return can_signals_; +} + +void can_message_definition_t::set_parent(can_message_set_t* parent) +{ + parent_= parent; +} + +void can_message_definition_t::set_last_value(const can_message_t& cm) +{ + last_value_= cm.get_data_vector(); +}
\ No newline at end of file diff --git a/low-can-binding/can/can-message-definition.hpp b/low-can-binding/can/can-message-definition.hpp new file mode 100644 index 0000000..6d0f17d --- /dev/null +++ b/low-can-binding/can/can-message-definition.hpp @@ -0,0 +1,71 @@ +/* + * 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. + */ + +/** + * @class can_message_definition_t + * + * @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. + */ + +#pragma once + +#include <vector> +#include <memory> + +#include "can-signals.hpp" +#include "can-message.hpp" +#include "can-message-set.hpp" +#include "../utils/timer.hpp" + +class can_message_set_t; + +/// @brief The definition of a CAN message. This includes a lot of metadata, so +/// to save memory this class gets the can_signal_t object related to a CAN message. +class can_message_definition_t +{ +private: + can_message_set_t* parent_; ///< parent_ - Pointer to the CAN message set holding this CAN message definition */ + std::string bus_; ///< bus_ - Address of CAN bus device. */ + uint32_t id_; ///< id_ - The ID of the message.*/ + can_message_format_t format_; ///< format_ - the format of the message's ID.*/ + frequency_clock_t frequency_clock_; ///< clock_ - an optional frequency clock to control the output of this + /// message, if sent raw, or simply to mark the max frequency for custom + /// handlers to retrieve.*/ + bool force_send_changed_; ///< force_send_changed_ - If true, regardless of the frequency, it will send CAN + /// message if it has changed when using raw passthrough.*/ + std::vector<uint8_t> last_value_; ///< last_value_ - The last received value of the message. Defaults to undefined. + /// This is required for the forceSendChanged functionality, as the stack + /// needs to compare an incoming CAN message with the previous frame.*/ + std::vector<std::shared_ptr<can_signal_t> > can_signals_; ///< can_signals_ - Vector holding can_signal_t object which share the same arbitration ID */ + +public: + //can_message_definition_t(const can_message_definition_t& b); + can_message_definition_t(const std::string bus); + can_message_definition_t(const std::string bus, uint32_t id, frequency_clock_t frequency_clock, bool force_send_changed); + can_message_definition_t(const std::string bus, uint32_t id, can_message_format_t format, frequency_clock_t frequency_clock, bool force_send_changed); + can_message_definition_t(const std::string bus, uint32_t id, can_message_format_t format, frequency_clock_t frequency_clock, bool force_send_changed, const std::vector<std::shared_ptr<can_signal_t> >& can_signals); + + const std::string get_bus_name() const; + const std::string get_bus_device_name() const; + uint32_t get_id() const; + std::vector<std::shared_ptr<can_signal_t> >& get_can_signals(); + + void set_parent(can_message_set_t* parent); + void set_last_value(const can_message_t& cm); +}; diff --git a/low-can-binding/can/can-message-set.cpp b/low-can-binding/can/can-message-set.cpp new file mode 100644 index 0000000..c31dec6 --- /dev/null +++ b/low-can-binding/can/can-message-set.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * Author "Loïc Collignon" <loic.collignon@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-set.hpp" + +#include "../can/can-message-definition.hpp" + +can_message_set_t::can_message_set_t( + uint8_t index, + const std::string& name, + const std::vector<std::shared_ptr<can_message_definition_t> >& can_messages_definition, + const std::vector<std::shared_ptr<diagnostic_message_t> >& diagnostic_messages) + : index_{index} + , name_{name} + , can_messages_definition_{can_messages_definition} + , diagnostic_messages_{diagnostic_messages} +{} + +/// @brief Return vector holding all message definition handled by this message set. +std::vector<std::shared_ptr<can_message_definition_t> >& can_message_set_t::get_can_message_definition() +{ + return can_messages_definition_; +} + +std::vector<std::shared_ptr<can_signal_t> > can_message_set_t::get_all_can_signals() const +{ + std::vector<std::shared_ptr<can_signal_t> > can_signals; + for(const auto& cmd: can_messages_definition_) + { + std::vector<std::shared_ptr<can_signal_t> >& cmd_signals = cmd->get_can_signals(); + can_signals.insert( can_signals.end(), + cmd_signals.begin(), + cmd_signals.end() + ); + } + + return can_signals; +} + +/// @brief Return vector holding all diagnostic messages definitions handled by this message set. +std::vector<std::shared_ptr<diagnostic_message_t> >& can_message_set_t::get_diagnostic_messages() +{ + return diagnostic_messages_; +}
\ No newline at end of file diff --git a/low-can-binding/can/can-message-set.hpp b/low-can-binding/can/can-message-set.hpp new file mode 100644 index 0000000..2a7cb53 --- /dev/null +++ b/low-can-binding/can/can-message-set.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * Author "Loïc Collignon" <loic.collignon@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 <cstdint> +#include <string> +#include <vector> +#include <memory> + +class can_signal_t; +class can_message_definition_t; +class diagnostic_message_t; + +/// @brief A parent wrapper for a particular set of CAN messages and diagnostic messages +/// (e.g. a vehicle or program). +class can_message_set_t +{ +private: + uint8_t index_; /// < A numerical ID for the message set, ideally the index in an array for fast lookup + const std::string name_; /// < The name of the message set. + std::vector<std::shared_ptr<can_message_definition_t> > can_messages_definition_; ///< Vector holding all message definition handled by this message set. + std::vector<std::shared_ptr<diagnostic_message_t> > diagnostic_messages_; ///< Vector holding all diagnostics messages from JSON signals description file. First vector map to message set + +public: + can_message_set_t( + uint8_t index, + const std::string& name, + const std::vector<std::shared_ptr<can_message_definition_t> >& can_messages_definition, + const std::vector<std::shared_ptr<diagnostic_message_t> >& diagnostic_messages); + + std::vector<std::shared_ptr<can_message_definition_t> >& get_can_message_definition(); + std::vector<std::shared_ptr<can_signal_t> > get_all_can_signals() const; + std::vector<std::shared_ptr<diagnostic_message_t> >& get_diagnostic_messages(); +}; diff --git a/low-can-binding/can/can-message.cpp b/low-can-binding/can/can-message.cpp new file mode 100644 index 0000000..057bc71 --- /dev/null +++ b/low-can-binding/can/can-message.cpp @@ -0,0 +1,389 @@ +/* + * 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 "../binding/low-can-hat.hpp" + +/// +/// @brief Class constructor +/// +/// Constructor about can_message_t class. +/// +can_message_t::can_message_t() + : maxdlen_{0}, + id_{0}, + length_{0}, + format_{can_message_format_t::INVALID}, + rtr_flag_{false}, + flags_{0}, + timestamp_{0}, + sub_id_{-1} +{} + +can_message_t::can_message_t(uint8_t maxdlen, + uint32_t id, + uint8_t length, + can_message_format_t format, + bool rtr_flag, + uint8_t flags, + std::vector<uint8_t>& data, + uint64_t timestamp) + : maxdlen_{maxdlen}, + id_{id}, + length_{length}, + format_{format}, + rtr_flag_{rtr_flag}, + flags_{flags}, + data_{data}, + timestamp_{timestamp}, + sub_id_{-1} +{} + +/// +/// @brief Retrieve id_ member value. +/// +/// @return id_ class member +/// +uint32_t can_message_t::get_id() const +{ + return id_; +} + +int can_message_t::get_sub_id() const +{ + return sub_id_; +} + +/// +/// @brief Retrieve RTR flag member. +/// +/// @return rtr_flags_ class member +/// +bool can_message_t::get_rtr_flag_() const +{ + return rtr_flag_; +} + +/// +/// @brief Retrieve format_ member value. +/// +/// @return format_ class member. Default to INVALID. +/// +can_message_format_t can_message_t::get_format() const +{ + if (format_ != can_message_format_t::STANDARD || format_ != can_message_format_t::EXTENDED) + return can_message_format_t::INVALID; + return format_; +} + +/// +/// @brief Retrieve flags_ member value. +/// +/// @return flags_ class member +/// +uint8_t can_message_t::get_flags() const +{ + return flags_; +} + +/// +/// @brief Retrieve data_ member value. +/// +/// @return pointer to the first element +/// of class member data_ +/// +const uint8_t* can_message_t::get_data() const +{ + return data_.data(); +} + +/// +/// @brief Retrieve data_ member whole vector +/// +/// @return the vector as is +/// +const std::vector<uint8_t> can_message_t::get_data_vector() const +{ + return data_; +} + +/// +/// @brief Retrieve length_ member value. +/// +/// @return length_ class member +/// +uint8_t can_message_t::get_length() const +{ + return length_; +} + +void can_message_t::set_sub_id(int sub_id) +{ + sub_id_ = sub_id; +} + +uint64_t can_message_t::get_timestamp() const +{ + return timestamp_; +} + +void can_message_t::set_timestamp(uint64_t timestamp) +{ + timestamp_ = timestamp; +} + +/// @brief Control whether the object is correctly initialized +/// to be sent over the CAN bus +/// +/// @return True if object correctly initialized and false if not. +bool can_message_t::is_correct_to_send() +{ + if (id_ != 0 && length_ != 0 && format_ != can_message_format_t::INVALID) + { + int i; + for(i=0;i<CAN_MESSAGE_SIZE;i++) + if(data_[i] != 0) + return true; + } + return false; +} + +/// @brief Set format_ member value. +/// +/// Preferred way to initialize these members by using +/// convert_from_canfd_frame method. +/// +/// @param[in] new_format - class member +void can_message_t::set_format(const can_message_format_t new_format) +{ + if(new_format == can_message_format_t::STANDARD || new_format == can_message_format_t::EXTENDED || new_format == can_message_format_t::INVALID) + format_ = new_format; + else + ERROR("Can set format, wrong format chosen"); +} + +/// @brief Take a canfd_frame struct to initialize class members +/// +/// This is the preferred way to initialize class members. +/// +/// @param[in] frame - canfd_frame to convert coming from a read of CAN socket +/// @param[in] nbytes - bytes read from socket read operation. +/// +/// @return A can_message_t object fully initialized with canfd_frame values. +can_message_t can_message_t::convert_from_frame(const struct canfd_frame& frame, size_t nbytes, uint64_t timestamp) +{ + uint8_t maxdlen, length, flags = (uint8_t)NULL; + uint32_t id; + can_message_format_t format; + bool rtr_flag; + std::vector<uint8_t> data; + + switch(nbytes) + { + case CANFD_MTU: + DEBUG("Got an CAN FD frame"); + maxdlen = CANFD_MAX_DLEN; + break; + case CAN_MTU: + DEBUG("Got a legacy CAN frame"); + maxdlen = CAN_MAX_DLEN; + break; + default: + ERROR("unsupported CAN frame"); + break; + } + + if (frame.can_id & CAN_ERR_FLAG) + { + format = can_message_format_t::INVALID; + id = frame.can_id & (CAN_ERR_MASK|CAN_ERR_FLAG); + } + else if (frame.can_id & CAN_EFF_FLAG) + { + format = can_message_format_t::EXTENDED; + id = frame.can_id & CAN_EFF_MASK; + } + else + { + format = can_message_format_t::STANDARD; + id = frame.can_id & CAN_SFF_MASK; + } + + /* Overwrite length_ 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) + { + if(rtr_flag) + length = frame.len& 0xF; + else + { + length = (frame.len > maxdlen) ? maxdlen : frame.len; + } + } + } + else + { + length = (frame.len > maxdlen) ? maxdlen : frame.len; + + /* Flags field only present for CAN FD frames*/ + if(maxdlen == CANFD_MAX_DLEN) + flags = frame.flags & 0xF; + + if (data.capacity() < maxdlen) + data.reserve(maxdlen); + 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(frame.data[i]); + }; + + DEBUG("Found id: %X, format: %X, length: %X, data %02X%02X%02X%02X%02X%02X%02X%02X", + id, (uint8_t)format, length, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); + } + + return can_message_t(maxdlen, id, length, format, rtr_flag, flags, data, timestamp); +} + +/// @brief Take a can_frame struct to initialize class members +/// +/// This is the preferred way to initialize class members. +/// +/// @param[in] frame - can_frame to convert coming from a read of CAN socket +/// @param[in] nbytes - bytes read from socket read operation. +/// +/// @return A can_message_t object fully initialized with can_frame values. +can_message_t can_message_t::convert_from_frame(const struct can_frame& frame, size_t nbytes, uint64_t timestamp) +{ + uint8_t maxdlen, length, flags = (uint8_t)NULL; + uint32_t id; + can_message_format_t format; + bool rtr_flag; + std::vector<uint8_t> data; + + if(nbytes <= CAN_MTU) + { + DEBUG("Got a legacy CAN frame"); + maxdlen = CAN_MAX_DLEN; + } + else + { + ERROR("unsupported CAN frame"); + } + + if (frame.can_id & CAN_ERR_FLAG) + { + format = can_message_format_t::INVALID; + id = frame.can_id & (CAN_ERR_MASK|CAN_ERR_FLAG); + } + else if (frame.can_id & CAN_EFF_FLAG) + { + format = can_message_format_t::EXTENDED; + id = frame.can_id & CAN_EFF_MASK; + } + else + { + format = can_message_format_t::STANDARD; + id = frame.can_id & CAN_SFF_MASK; + } + + /* Overwrite length_ 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.can_dlc && frame.can_dlc <= CAN_MAX_DLC) + { + if(rtr_flag) + length = frame.can_dlc& 0xF; + else + { + length = (frame.can_dlc > maxdlen) ? maxdlen : frame.can_dlc; + } + } + } + else + { + length = (frame.can_dlc > maxdlen) ? maxdlen : frame.can_dlc; + + if (data.capacity() < maxdlen) + data.reserve(maxdlen); + 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(frame.data[i]); + }; + +// DEBUG("Found id: %X, format: %X, length: %X, data %02X%02X%02X%02X%02X%02X%02X%02X", +// id, (uint8_t)format, length, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); + } + + return can_message_t(maxdlen, id, length, format, rtr_flag, flags, data, timestamp); +} + +/// @brief Take all initialized class's members and build an +/// canfd_frame struct that can be use to send a CAN message over +/// the bus. +/// +/// @return canfd_frame struct built from class members. +struct canfd_frame can_message_t::convert_to_canfd_frame() +{ + canfd_frame frame; + + if(is_correct_to_send()) + { + frame.can_id = get_id(); + frame.len = get_length(); + ::memcpy(frame.data, get_data(), length_); + } + else + ERROR("can_message_t not correctly initialized to be sent"); + + return frame; +} + +/// @brief Take all initialized class's members and build an +/// can_frame struct that can be use to send a CAN message over +/// the bus. +/// +/// @return can_frame struct built from class members. +struct can_frame can_message_t::convert_to_can_frame() +{ + can_frame frame; + + if(is_correct_to_send()) + { + frame.can_id = get_id(); + frame.can_dlc = get_length(); + ::memcpy(frame.data, get_data(), length_); + } + else + ERROR("can_message_t not correctly initialized to be sent"); + + return frame; +} diff --git a/low-can-binding/can/can-message.hpp b/low-can-binding/can/can-message.hpp new file mode 100644 index 0000000..b206ebd --- /dev/null +++ b/low-can-binding/can/can-message.hpp @@ -0,0 +1,80 @@ +/* + * 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 "../utils/timer.hpp" + +#define CAN_MESSAGE_SIZE 8 + +/** + * @enum can_message_format_t + * @brief The ID format for a CAN message. + */ +enum class can_message_format_t { + STANDARD, ///< STANDARD - standard 11-bit CAN arbitration ID. */ + EXTENDED, ///< EXTENDED - an extended frame, with a 29-bit arbitration ID. */ + INVALID, ///< INVALID - INVALID code used at initialization to signify that it isn't usable'*/ +}; + +/// @class can_message_t +/// +/// @brief A compact representation of a single CAN message, meant to be used in in/out +/// buffers. It is a wrapper of a can_frame struct with some sugar around it for binding purposes. +class can_message_t { +private: + uint8_t maxdlen_; ///< maxdlen_ - Max data length deduce from number of bytes read from the socket.*/ + uint32_t id_; ///< id_ - The ID of the message. */ + uint8_t length_; ///< length_ - the length of the data array (max 8). */ + can_message_format_t format_; ///< format_ - the format of the message's ID.*/ + bool rtr_flag_; ///< rtr_flag_ - Telling if the frame has RTR flag positionned. Then frame hasn't data field*/ + uint8_t flags_; ///< flags_ - flags of a CAN FD frame. Needed if we catch FD frames.*/ + std::vector<uint8_t> data_; ///< data_ - The message's data field with a size of 8 which is the standard about CAN bus messages.*/ + uint64_t timestamp_; ///< timestamp_ - timestamp of the received message*/ + int sub_id_; ///< sub_id_ - Subscription index. */ + +public: + can_message_t(); + can_message_t(uint8_t maxdlen, uint32_t id, uint8_t length, can_message_format_t format, bool rtr_flag_, uint8_t flags, std::vector<uint8_t>& data, uint64_t timestamp); + + uint32_t get_id() const; + int get_sub_id() const; + bool get_rtr_flag_() const; + can_message_format_t get_format() const; + uint8_t get_flags() const; + const uint8_t* get_data() const; + const std::vector<uint8_t> get_data_vector() const; + uint8_t get_length() const; + uint64_t get_timestamp() const; + + void set_sub_id(int sub_id); + void set_timestamp(uint64_t timestamp); + void set_format(const can_message_format_t new_format); + + bool is_correct_to_send(); + + static can_message_t convert_from_frame(const canfd_frame& frame, size_t nbytes, uint64_t timestamp); + static can_message_t convert_from_frame(const can_frame& frame, size_t nbytes, uint64_t timestamp); + + struct canfd_frame convert_to_canfd_frame(); + struct can_frame convert_to_can_frame(); +}; diff --git a/low-can-binding/can/can-signals.cpp b/low-can-binding/can/can-signals.cpp new file mode 100644 index 0000000..fe74115 --- /dev/null +++ b/low-can-binding/can/can-signals.cpp @@ -0,0 +1,203 @@ +/* + * 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 <fnmatch.h> + +#include "can-signals.hpp" + +#include "../binding/application.hpp" +#include "../utils/signals.hpp" +#include "can-decoder.hpp" +#include "can-message.hpp" +#include "can-bus.hpp" +#include "../diagnostic/diagnostic-message.hpp" +#include "canutil/write.h" + +std::string can_signal_t::prefix_ = "messages"; + +can_signal_t::can_signal_t( + std::string generic_name, + uint8_t bit_position, + uint8_t bit_size, + float factor, + float offset, + float min_value, + float max_value, + frequency_clock_t frequency, + bool send_same, + bool force_send_changed, + std::map<uint8_t, std::string> states, + bool writable, + SignalDecoder decoder, + SignalEncoder encoder, + bool received) + : parent_{nullptr}, + generic_name_{ generic_name } + , bit_position_{ bit_position } + , bit_size_{ bit_size } + , factor_{ factor } + , offset_{ offset } + , min_value_{min_value} + , max_value_{max_value} + , frequency_{frequency} + , send_same_{send_same} + , force_send_changed_{force_send_changed} + , states_{states} + , writable_{writable} + , decoder_{decoder} + , encoder_{encoder} + , received_{received} + , last_value_{.0f} +{} + +can_message_definition_t* can_signal_t::get_message() const +{ + return parent_; +} + +const std::string can_signal_t::get_generic_name() const +{ + return generic_name_; +} + +const std::string can_signal_t::get_name() const +{ + return prefix_ + "." + generic_name_; +} + +const std::string can_signal_t::get_prefix() const +{ + return prefix_; +} + +uint8_t can_signal_t::get_bit_position() const +{ + return bit_position_; +} + +uint8_t can_signal_t::get_bit_size() const +{ + return bit_size_; +} + +float can_signal_t::get_factor() const +{ + return factor_; +} + +float can_signal_t::get_offset() const +{ + return offset_; +} + +float can_signal_t::get_min_value() const +{ + return min_value_; +} + +float can_signal_t::get_max_value() const +{ + return max_value_; +} + +frequency_clock_t& can_signal_t::get_frequency() +{ + return frequency_; +} + +bool can_signal_t::get_send_same() const +{ + return send_same_; +} + +bool can_signal_t::get_force_send_changed() const +{ + return force_send_changed_; +} + +const std::map<uint8_t, std::string>& can_signal_t::get_states() const +{ + return states_; +} + +const std::string can_signal_t::get_states(uint8_t value) +{ + if (value < states_.size()) + return states_[value]; + return std::string(); +} + +size_t can_signal_t::get_state_count() const +{ + return states_.size(); +} + +bool can_signal_t::get_writable() const +{ + return writable_; +} + +SignalDecoder& can_signal_t::get_decoder() +{ + return decoder_; +} + +SignalEncoder& can_signal_t::get_encoder() +{ + return encoder_; +} + +bool can_signal_t::get_received() const +{ + return received_; +} + +float can_signal_t::get_last_value() const +{ + return last_value_; +} + +std::pair<float, uint64_t> can_signal_t::get_last_value_with_timestamp() const +{ + return std::make_pair(last_value_, frequency_.get_last_tick()); +} + +void can_signal_t::set_parent(can_message_definition_t* parent) +{ + parent_ = parent; +} + +void can_signal_t::set_prefix(std::string val) +{ + prefix_ = val; +} + +void can_signal_t::set_received(bool r) +{ + received_ = r; +} + +void can_signal_t::set_last_value(float val) +{ + last_value_ = val; +} + +void can_signal_t::set_timestamp(uint64_t timestamp) +{ + frequency_.tick(timestamp); +} + diff --git a/low-can-binding/can/can-signals.hpp b/low-can-binding/can/can-signals.hpp new file mode 100644 index 0000000..a56fc8c --- /dev/null +++ b/low-can-binding/can/can-signals.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 <map> +#include <mutex> +#include <vector> +#include <string> +#include <memory> + +#include "openxc.pb.h" +#include "can-message-definition.hpp" +#include "../utils/timer.hpp" +#include "../utils/socketcan-bcm.hpp" +#include "can-message.hpp" +#include "../diagnostic/diagnostic-message.hpp" + +#define MESSAGE_SET_ID 0 + +class can_signal_t; +/// +/// @brief The type signature for a CAN signal decoder. +/// +/// A SignalDecoder transforms a raw floating point CAN signal into a number, +/// string or boolean. +/// +/// @param[in] signal - The CAN signal that we are decoding. +/// @param[in] signals - The list of all signals. +/// @param[in] signalCount - The length of the signals array. +/// @param[in] value - The CAN signal parsed from the message as a raw floating point +/// value. +/// @param[out] 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)(can_signal_t& signal, + const std::vector<std::shared_ptr<can_signal_t> >& signals, float value, bool* send); + +/// +/// @brief: The type signature for a CAN signal encoder. +/// +/// A SignalEncoder transforms a number, string or boolean into a raw floating +/// point value that fits in the CAN signal. +/// +/// @param[in] signal - The CAN signal to encode. +/// @param[in] value - The dynamic field to encode. +/// @param[out] 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)(can_signal_t* signal, + openxc_DynamicField* value, bool* send); + +class can_signal_t +{ +private: + can_message_definition_t* parent_; /*!< parent_ - pointer to the parent message definition holding this signal*/ + std::string generic_name_; /*!< generic_name_ - The name of the signal to be output.*/ + static std::string prefix_; /*!< prefix_ - generic_name_ will be prefixed with it. It has to reflect the used protocol. + * which make easier to sort message when the come in.*/ + uint8_t bit_position_; /*!< 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 bit_size_; /*!< bit_size_ - 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 min_value_; /*!< min_value_ - The minimum value for the processed signal.*/ + float max_value_; /*!< max_value_ - The maximum value for the processed signal. */ + frequency_clock_t frequency_; /*!< frequency_ - A frequency_clock_t struct to control the maximum frequency to + * process and send this signal. To process every value, set the + * clock's frequency to 0. */ + bool send_same_; /*!< send_same_ - If true, will re-send even if the value hasn't changed.*/ + bool force_send_changed_; /*!< force_send_changed_ - If true, regardless of the frequency, it will send the + * value if it has changed. */ + std::map<uint8_t, std::string> states_; /*!< states_ - A map of CAN signal state describing the mapping + * between numerical and string values for valid states. */ + 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 last_value_; /*!< lastValue_ - The last received value of the signal. If 'received' is false, + * this value is undefined. */ + +public: + can_signal_t( + std::string generic_name, + uint8_t bit_position, + uint8_t bit_size, + float factor, + float offset, + float min_value, + float max_value, + frequency_clock_t frequency, + bool send_same, + bool force_send_changed, + std::map<uint8_t, std::string> states, + bool writable, + SignalDecoder decoder, + SignalEncoder encoder, + bool received); + + can_message_definition_t* get_message() const; + const std::string get_generic_name() const; + const std::string get_name() const; + const std::string get_prefix() const; + uint8_t get_bit_position() const; + uint8_t get_bit_size() const; + float get_factor() const; + float get_offset() const; + float get_min_value() const; + float get_max_value() const; + frequency_clock_t& get_frequency(); + bool get_send_same() const; + bool get_force_send_changed() const; + const std::map<uint8_t, std::string>& get_states() const; + const std::string get_states(uint8_t value); + size_t get_state_count() const; + bool get_writable() const; + SignalDecoder& get_decoder(); + SignalEncoder& get_encoder(); + bool get_received() const; + float get_last_value() const; + std::pair<float, uint64_t> get_last_value_with_timestamp() const; + + void set_parent(can_message_definition_t* parent); + void set_prefix(std::string val); + void set_received(bool r); + void set_last_value(float val); + void set_timestamp(uint64_t timestamp); +}; |