CanExtractor.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2015-2018, Dataspeed Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of Dataspeed Inc. nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 #include "CanExtractor.h"
00036 
00037 namespace dataspeed_can_tools {
00038 
00039 CanExtractor::CanExtractor(const std::string &dbc_file, bool offline, bool expand, bool unknown) :
00040     dbc_(dbc_file), offline_(offline)
00041 {
00042   bag_open_ = false;
00043   expand_ = expand;
00044   unknown_ = unknown;
00045 }
00046 
00047 CanExtractor::CanExtractor(const std::vector<std::string> &dbc_file, bool offline, bool expand, bool unknown) :
00048     dbc_(dbc_file), offline_(offline)
00049 {
00050   bag_open_ = false;
00051   expand_ = expand;
00052   unknown_ = unknown;
00053 }
00054 
00055 uint64_t CanExtractor::unsignedSignalData(uint64_t raw_data, const RosCanSigStruct& sig_props)
00056 {
00057   // C++ facts! Bitshifting a value by greater than or equal to the number of bits in the left expression's type
00058   // has an undefined result! ((uint32_t) 1) << 32) may have unexpected results on different compilers
00059   // (we had an issue where the value was equal to 1 rather than all zeros).
00060   uint64_t mask;
00061   if (sig_props.length >= 64) {
00062     mask = ((uint64_t) -1); // 0xFFFFFFFFFFFFFFFF
00063   } else {
00064     // Make sure the 1 is an unsigned long!
00065     mask = (((uint64_t)1) << sig_props.length) - 1;
00066   }
00067    
00068   if (sig_props.order == INTEL) {
00069     // Little Endian 
00070     return (raw_data >> sig_props.start_bit) & mask;
00071   } else {
00072     // Big Endian
00073     int intel_start_bit = 56 - 8 * (sig_props.start_bit / 8) + (sig_props.start_bit % 8) - (sig_props.length - 1);
00074     return (__builtin_bswap64(raw_data) >> intel_start_bit) & mask;
00075   }
00076 }
00077 
00078 int64_t CanExtractor::signedSignalData(uint64_t raw_data, const RosCanSigStruct& sig_props)
00079 {
00080   uint64_t mask;
00081   if (sig_props.length >= 64) {
00082     mask = ((uint64_t) -1); // 0xFFFFFFFFFFFFFFFF
00083   } else {
00084     // Make sure the 1 is an unsigned long!
00085     mask = (((uint64_t)1) << sig_props.length) - 1;
00086   }
00087 
00088   int64_t val = unsignedSignalData(raw_data, sig_props);
00089 
00090   if (val & (1 << (sig_props.length - 1))) {
00091     val |= ~mask;
00092   }
00093 
00094   return val;
00095 }
00096 
00097 template<class T>
00098 T CanExtractor::buildMsg(const RosCanSigStruct& info, const uint64_t& data, bool sign)
00099 {
00100   T msg;
00101   if (sign) {
00102     msg.data = (info.factor *   signedSignalData(data, info)) + info.offset;
00103   } else {
00104     msg.data = (info.factor * unsignedSignalData(data, info)) + info.offset;
00105   }
00106   return msg;
00107 }
00108 
00109 int CanExtractor::getAppropriateSize(const RosCanSigStruct& sig_props, bool output_signed)
00110 {
00111   if (sig_props.length >= 64) {
00112     return 64;
00113   }
00114 
00115   int64_t max_val;
00116   int64_t min_val;
00117   if ((sig_props.sign == SIGNED)) {
00118     max_val = (((int64_t)1 << (sig_props.length - 1)) - 1);
00119     min_val = -((int64_t)1 << (sig_props.length - 1));
00120   } else {
00121     max_val = (((int64_t)1 << sig_props.length) - 1);
00122     min_val = 0;
00123   }
00124   max_val = max_val * (int64_t)sig_props.factor + (int64_t)sig_props.offset;
00125   min_val = min_val * (int64_t)sig_props.factor + (int64_t)sig_props.offset;
00126   if (max_val < min_val) {
00127     std::swap(min_val, max_val);
00128   }
00129 
00130   if (output_signed) {
00131     if ((INT8_MIN <= min_val) && (max_val <= INT8_MAX)) {
00132       return 8;
00133     } else if ((INT16_MIN <= min_val) && (max_val <= INT16_MAX)) {
00134       return 16;
00135     } else if ((INT32_MIN <= min_val) && (max_val <= INT32_MAX)) {
00136       return 32;
00137     } else {
00138       return 64;
00139     }
00140   } else {
00141     if (max_val <= UINT8_MAX) {
00142       return 8;
00143     } else if (max_val <= UINT16_MAX) {
00144       return 16;
00145     } else if (max_val <= UINT32_MAX) {
00146       return 32;
00147     } else {
00148       return 64;
00149     }
00150   }
00151 }
00152 
00153 bool CanExtractor::getMessage(RosCanMsgStruct& can_msg)
00154 {
00155   if (msgs_.find(can_msg.id) == msgs_.end()) {
00156     for (DBCIterator::const_iterator it = dbc_.begin(); it < dbc_.end(); it++) {
00157       if (it->getId() == can_msg.id) {
00158         can_msg.msg_name = it->getName();
00159 
00160         for (Message::const_iterator m_it = it->begin(); m_it < it->end(); m_it++) {
00161           RosCanSigStruct new_sig;
00162           new_sig.factor = m_it->getFactor();
00163           new_sig.length = m_it->getLength();
00164           new_sig.maximum = m_it->getMaximum();
00165           new_sig.minimum = m_it->getMinimum();
00166           new_sig.offset = m_it->getOffset();
00167           new_sig.order = m_it->getByteOrder();
00168           new_sig.sig_name = m_it->getName();
00169           new_sig.sign = m_it->getSign();
00170           new_sig.start_bit = m_it->getStartbit();
00171           new_sig.multiplexor = m_it->getMultiplexor();
00172           new_sig.multiplexNum = m_it->getMultiplexedNumber();
00173           can_msg.sigs.push_back(new_sig);
00174         }
00175 
00176         msgs_[can_msg.id] = can_msg;
00177         return true;
00178       }
00179     }
00180 
00181     if (unknown_msgs_.find(can_msg.id) == unknown_msgs_.end()) {
00182       unknown_msgs_[can_msg.id] = 0;
00183       if (unknown_) {
00184         std::stringstream ss;
00185         ss << "x" << std::hex << std::setfill('0') << std::setw(3) << can_msg.id;
00186         can_msg.msg_name = ss.str();
00187         msgs_[can_msg.id] = can_msg;
00188         return true;
00189       }
00190       ROS_WARN("Received unknown CAN message with ID = 0x%X", can_msg.id);
00191     }
00192   } else {
00193     can_msg = msgs_[can_msg.id];
00194   }
00195 
00196   return false;
00197 }
00198 
00199 void CanExtractor::initPublishers(RosCanMsgStruct& info, ros::NodeHandle& nh)
00200 {
00201   ros::NodeHandle nh_msg(nh, info.msg_name);
00202 
00203   info.message_pub = nh.advertise<can_msgs::Frame>(info.msg_name, 1);
00204 
00205   if (expand_) {
00206     ROS_DEBUG("Initializing publishers for %lu signals...",info.sigs.size());
00207     for (size_t i=0; i<info.sigs.size(); i++){
00208       registerCanSignalPublisher(info.sigs[i], nh_msg);
00209     }
00210   }
00211 
00212   msgs_[info.id] = info;
00213 }
00214 
00215 bool CanExtractor::openBag(const std::string &fname, rosbag::compression::CompressionType compression)
00216 {
00217   if (!bag_open_) {
00218     // Save the desired file name, to actually open the bag later for writing.
00219     bag_.setCompression(compression);
00220     bag_fname_ = fname;
00221     return true;
00222   }
00223   return false;
00224 }
00225 
00226 bool CanExtractor::closeBag()
00227 {
00228   if (bag_open_) {
00229     bag_.close();
00230     bag_open_ = false;
00231     return true;
00232   }
00233   return false;
00234 }
00235 
00236 void CanExtractor::registerCanSignalPublisher(RosCanSigStruct& info, ros::NodeHandle& nh)
00237 {
00238   const uint32_t QUEUE_SIZE = 10;
00239   if (info.length == 1) {
00240     info.sig_pub = nh.advertise<std_msgs::Bool>(info.sig_name, QUEUE_SIZE);
00241   } else if ((fmod(info.factor, 1.0) != 0) || (fmod(info.offset, 1.0) != 0)) {
00242     info.sig_pub = nh.advertise<std_msgs::Float64>(info.sig_name, QUEUE_SIZE);
00243   } else {
00244     if ((info.sign == SIGNED) || (info.offset < 0) || (info.factor < 0)) {
00245       switch (getAppropriateSize(info, true)) {
00246         case  8: info.sig_pub = nh.advertise<std_msgs::Int8 >(info.sig_name, QUEUE_SIZE); break;
00247         case 16: info.sig_pub = nh.advertise<std_msgs::Int16>(info.sig_name, QUEUE_SIZE); break;
00248         case 32: info.sig_pub = nh.advertise<std_msgs::Int32>(info.sig_name, QUEUE_SIZE); break;
00249         case 64: info.sig_pub = nh.advertise<std_msgs::Int64>(info.sig_name, QUEUE_SIZE); break;
00250       }
00251     } else {
00252       switch (getAppropriateSize(info, false)) {
00253         case  8: info.sig_pub = nh.advertise<std_msgs::UInt8 >(info.sig_name, QUEUE_SIZE); break;
00254         case 16: info.sig_pub = nh.advertise<std_msgs::UInt16>(info.sig_name, QUEUE_SIZE); break;
00255         case 32: info.sig_pub = nh.advertise<std_msgs::UInt32>(info.sig_name, QUEUE_SIZE); break;
00256         case 64: info.sig_pub = nh.advertise<std_msgs::UInt64>(info.sig_name, QUEUE_SIZE); break;
00257       }
00258     }
00259   }
00260 }
00261 
00262 template<class T>
00263 void CanExtractor::writeToBag(const std::string& frame, const ros::Time& stamp, const T& msg) {
00264   // Check the bag file is open before writing.
00265   if (!bag_open_) {
00266     ROS_DEBUG("Opening bag file for writing...");
00267     bag_open_ = true;
00268     bag_.open(bag_fname_, rosbag::bagmode::Write);
00269   }
00270   bag_.write(frame, stamp, msg);
00271 }
00272 
00273 template<class T>
00274 void CanExtractor::pubCanSig(const RosCanMsgStruct& info, const T& sig_msg, const ros::Time& stamp, size_t i) {
00275   ROS_DEBUG("  Publishing value (%s): %f", info.sigs[i].sig_name.c_str(), (double)sig_msg.data);
00276   if (i < info.sigs.size()) {
00277     if (offline_) {
00278       writeToBag(info.msg_name + "/" + info.sigs[i].sig_name, stamp, sig_msg);
00279     } else {
00280       info.sigs[i].sig_pub.publish(sig_msg);
00281     }
00282   }
00283 }
00284 
00285 void CanExtractor::pubCanMsg(const RosCanMsgStruct& info, const can_msgs::Frame& msg, const ros::Time& stamp) {
00286   if (offline_) {
00287     writeToBag(info.msg_name, stamp, msg);
00288   } else {
00289     info.message_pub.publish(msg);
00290   }
00291 }
00292 
00293 void CanExtractor::pubMessage(const can_msgs::Frame& msg, const ros::Time &stamp)
00294 {
00295   // Check for valid message information
00296   const uint32_t id = msg.id | (msg.is_extended ? 0x80000000 : 0x00000000);
00297   if (msgs_.find(id) == msgs_.end()) {
00298     ROS_WARN("Bad message ID...");
00299     return;
00300   }
00301   const RosCanMsgStruct &info = msgs_[id];
00302   const uint64_t data = *((uint64_t*)&msg.data[0]);
00303 
00304   // Re-publish CAN message on named topic
00305   pubCanMsg(info, msg, stamp);
00306 
00307   // Only continue if individual signals are to be expanded and published
00308   if (!expand_) return;
00309 
00310   // Search for the multiplexor value, if any.
00311   unsigned short multiplexorValue = -1;
00312   for (size_t i = 0; i < info.sigs.size(); i++) {
00313     if (info.sigs[i].multiplexor == MULTIPLEXOR) {
00314       multiplexorValue = unsignedSignalData(data, info.sigs[i]);
00315       break;
00316     }
00317   }
00318 
00319   // Publish signals on their own topics
00320   for (size_t i = 0; i < info.sigs.size(); i++) {
00321 
00322     // Handle multiplexed signals
00323     ROS_DEBUG("MSG Name: %s", info.sigs[i].sig_pub.getTopic().c_str());
00324 
00325     if (info.sigs[i].multiplexor == MULTIPLEXED) {
00326       if (info.sigs[i].multiplexNum != multiplexorValue) {
00327         ROS_DEBUG("    Skipping multiplexed value...");
00328         continue; // Else, skip this iteration of the loop.
00329       } // If sigs[i].multiplexNum == multiplexorValue, it should be published.
00330     } // If sigs[i].multiplexor equals MULTIPLEXOR or NONE, it's fine to publish for all messages.
00331 
00332     // Publish various message types
00333     if (info.sigs[i].length == 1) {
00334       pubCanSig(info, buildMsg<std_msgs::Bool>(info.sigs[i], data, false), stamp, i);
00335     } else if ((fmod(info.sigs[i].factor, 1.0) != 0) || fmod(info.sigs[i].offset, 1.0) != 0) {
00336       pubCanSig(info, buildMsg<std_msgs::Float64>(info.sigs[i], data, info.sigs[i].sign == SIGNED), stamp, i);
00337     } else {
00338       if ((info.sigs[i].sign == SIGNED) || (info.sigs[i].offset < 0) || (info.sigs[i].factor < 0)) {
00339         if (info.sigs[i].sign == SIGNED) {
00340           switch (getAppropriateSize(info.sigs[i], true)) {
00341             case  8: pubCanSig(info, buildMsg<std_msgs::Int8 >(info.sigs[i], data, true), stamp, i); break;
00342             case 16: pubCanSig(info, buildMsg<std_msgs::Int16>(info.sigs[i], data, true), stamp, i); break;
00343             case 32: pubCanSig(info, buildMsg<std_msgs::Int32>(info.sigs[i], data, true), stamp, i); break;
00344             case 64: pubCanSig(info, buildMsg<std_msgs::Int64>(info.sigs[i], data, true), stamp, i); break;
00345           }
00346         } else {
00347           switch (getAppropriateSize(info.sigs[i], true)) {
00348             case  8: pubCanSig(info, buildMsg<std_msgs::Int8 >(info.sigs[i], data, false), stamp, i); break;
00349             case 16: pubCanSig(info, buildMsg<std_msgs::Int16>(info.sigs[i], data, false), stamp, i); break;
00350             case 32: pubCanSig(info, buildMsg<std_msgs::Int32>(info.sigs[i], data, false), stamp, i); break;
00351             case 64: pubCanSig(info, buildMsg<std_msgs::Int64>(info.sigs[i], data, false), stamp, i); break;
00352           }
00353         }
00354       } else {
00355         switch (getAppropriateSize(info.sigs[i], false)) {
00356           case  8: pubCanSig(info, buildMsg<std_msgs::UInt8 >(info.sigs[i], data, false), stamp, i); break;
00357           case 16: pubCanSig(info, buildMsg<std_msgs::UInt16>(info.sigs[i], data, false), stamp, i); break;
00358           case 32: pubCanSig(info, buildMsg<std_msgs::UInt32>(info.sigs[i], data, false), stamp, i); break;
00359           case 64: pubCanSig(info, buildMsg<std_msgs::UInt64>(info.sigs[i], data, false), stamp, i); break;
00360         }
00361       }
00362     }
00363   }
00364 }
00365 
00366 } // namespace dataspeed_can_tools
00367 


dataspeed_can_tools
Author(s): Micho Radovnikovich
autogenerated on Thu Jun 6 2019 21:16:40