CanExtractor.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2015-2020, Dataspeed Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Dataspeed Inc. nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 #include "CanExtractor.h"
36 
38 
39 CanExtractor::CanExtractor(const std::string &dbc_file, bool offline, bool expand, bool unknown) :
40  dbc_(dbc_file), offline_(offline)
41 {
42  bag_open_ = false;
43  expand_ = expand;
44  unknown_ = unknown;
45 }
46 
47 CanExtractor::CanExtractor(const std::vector<std::string> &dbc_file, bool offline, bool expand, bool unknown) :
48  dbc_(dbc_file), offline_(offline)
49 {
50  bag_open_ = false;
51  expand_ = expand;
52  unknown_ = unknown;
53 }
54 
55 
56 
57 uint64_t CanExtractor::unsignedSignalData(const std::vector<uint8_t> &buffer, const RosCanSigStruct& sig_props)
58 {
59  // 64 bytes maximum CAN FD payload.
60  if (!(buffer.size() <= 64)) {
61  ROS_WARN("unsignedSignalData(): Buffer too large: %zu bytes", buffer.size());
62  return 0;
63  }
64 
65  if (!(sig_props.start_bit >= 0 && sig_props.start_bit < (int)(buffer.size() * 8))) {
66  ROS_WARN("unsignedSignalData(): Start bit out of range: Bit %d", sig_props.start_bit);
67  return 0;
68  }
69 
70  // 64 bits maximum signal length.
71  if (!(sig_props.length >= 0 && sig_props.length <= 64)) {
72  ROS_WARN("unsignedSignalData(): Bit length out of range: %d bits", sig_props.length);
73  return 0;
74  }
75 
76  if (!(sig_props.order == INTEL || sig_props.order == MOTOROLA)) {
77  ROS_WARN("unsignedSignalData(): Invalid bit order: %d", sig_props.order);
78  return 0;
79  }
80 
81  uint16_t start_bit = (uint16_t)sig_props.start_bit;
82  uint8_t length_bits = (uint16_t)sig_props.length;
83  bool lsb_order = (sig_props.order == INTEL);
84 
85  uint64_t value = 0;
86  uint8_t bits_remaining = length_bits;
87  uint16_t current_bit = start_bit;
88  while (bits_remaining > 0) {
89  uint16_t next_bit_multiple = (((current_bit + 8) / 8) * 8);
90  uint16_t prev_bit_multiple = ((current_bit / 8) * 8);
91  uint8_t bits_to_consume = (lsb_order) ? (next_bit_multiple - current_bit) : (current_bit + 1 - prev_bit_multiple);
92  bits_to_consume = (bits_to_consume <= bits_remaining) ? bits_to_consume : bits_remaining;
93  uint8_t mask_shift = (lsb_order) ? (current_bit - prev_bit_multiple) : (current_bit + 1 - bits_to_consume - prev_bit_multiple);
94  uint8_t mask = (((1 << bits_to_consume) - 1) << mask_shift);
95  uint16_t value_shift = (lsb_order) ? (length_bits - bits_remaining) : (bits_remaining - bits_to_consume);
96  if (!((current_bit / 8) < buffer.size())) {
97  ROS_WARN("unsignedSignalData(): Calculated bit index exceeds buffer length: Bit %u: Max %zu", current_bit, ((buffer.size() * 8) - 1));
98  return 0;
99  }
100  value |= ((((uint64_t)(buffer[current_bit / 8]) & mask) >> mask_shift) << value_shift);
101  bits_remaining -= bits_to_consume;
102  if (lsb_order) {
103  current_bit += bits_to_consume;
104  } else {
105  if ((current_bit + 1 - bits_to_consume) % 8 == 0) {
106  current_bit = (((current_bit / 8) * 8) + 15);
107  } else {
108  // Only hit when (bits_remaining == 0) and loop is complete.
109  assert(bits_remaining == 0);
110  current_bit -= bits_to_consume;
111  }
112  }
113  }
114 
115  return value;
116 }
117 
118 int64_t CanExtractor::signedSignalData(const std::vector<uint8_t> &buffer, const RosCanSigStruct& sig_props)
119 {
120  uint64_t value = unsignedSignalData(buffer, sig_props);
121  // Sign extension.
122  if (sig_props.length != 64) {
123  value |= (value & (1ul << (sig_props.length - 1))) ? (((1ul << (64 - sig_props.length)) - 1) << sig_props.length) : 0;
124  }
125  return (int64_t)value;
126 }
127 
128 template<class T>
129 T CanExtractor::buildMsg(const RosCanSigStruct& info, const std::vector<uint8_t> &buffer, bool sign)
130 {
131  T msg;
132  if (sign) {
133  msg.data = (info.factor * signedSignalData(buffer, info)) + info.offset;
134  } else {
135  msg.data = (info.factor * unsignedSignalData(buffer, info)) + info.offset;
136  }
137  return msg;
138 }
139 
140 int CanExtractor::getAppropriateSize(const RosCanSigStruct& sig_props, bool output_signed)
141 {
142  if (sig_props.length >= 64) {
143  return 64;
144  }
145 
146  int64_t max_val;
147  int64_t min_val;
148  if ((sig_props.sign == SIGNED)) {
149  max_val = (((int64_t)1 << (sig_props.length - 1)) - 1);
150  min_val = -((int64_t)1 << (sig_props.length - 1));
151  } else {
152  max_val = (((int64_t)1 << sig_props.length) - 1);
153  min_val = 0;
154  }
155  max_val = max_val * (int64_t)sig_props.factor + (int64_t)sig_props.offset;
156  min_val = min_val * (int64_t)sig_props.factor + (int64_t)sig_props.offset;
157  if (max_val < min_val) {
158  std::swap(min_val, max_val);
159  }
160 
161  if (output_signed) {
162  if ((INT8_MIN <= min_val) && (max_val <= INT8_MAX)) {
163  return 8;
164  } else if ((INT16_MIN <= min_val) && (max_val <= INT16_MAX)) {
165  return 16;
166  } else if ((INT32_MIN <= min_val) && (max_val <= INT32_MAX)) {
167  return 32;
168  } else {
169  return 64;
170  }
171  } else {
172  if (max_val <= UINT8_MAX) {
173  return 8;
174  } else if (max_val <= UINT16_MAX) {
175  return 16;
176  } else if (max_val <= UINT32_MAX) {
177  return 32;
178  } else {
179  return 64;
180  }
181  }
182 }
183 
185 {
186  if (msgs_.find(can_msg.id) == msgs_.end()) {
187  for (DBCIterator::const_iterator it = dbc_.begin(); it < dbc_.end(); it++) {
188  if (it->getId() == can_msg.id) {
189  can_msg.msg_name = it->getName();
190 
191  for (Message::const_iterator m_it = it->begin(); m_it < it->end(); m_it++) {
192  RosCanSigStruct new_sig;
193  new_sig.factor = m_it->getFactor();
194  new_sig.length = m_it->getLength();
195  new_sig.maximum = m_it->getMaximum();
196  new_sig.minimum = m_it->getMinimum();
197  new_sig.offset = m_it->getOffset();
198  new_sig.order = m_it->getByteOrder();
199  new_sig.sig_name = m_it->getName();
200  new_sig.sign = m_it->getSign();
201  new_sig.start_bit = m_it->getStartbit();
202  new_sig.multiplexor = m_it->getMultiplexor();
203  new_sig.multiplexNum = m_it->getMultiplexedNumber();
204  can_msg.sigs.push_back(new_sig);
205  }
206 
207  msgs_[can_msg.id] = can_msg;
208  return true;
209  }
210  }
211 
212  if (unknown_msgs_.find(can_msg.id) == unknown_msgs_.end()) {
213  unknown_msgs_[can_msg.id] = 0;
214  if (unknown_) {
215  std::stringstream ss;
216  ss << "x" << std::hex << std::setfill('0') << std::setw(3) << can_msg.id;
217  can_msg.msg_name = ss.str();
218  msgs_[can_msg.id] = can_msg;
219  return true;
220  }
221  ROS_WARN("Received unknown CAN message with ID = 0x%X", can_msg.id);
222  }
223  } else {
224  can_msg = msgs_[can_msg.id];
225  }
226 
227  return false;
228 }
229 
231 {
232  ros::NodeHandle nh_msg(nh, info.msg_name);
233 
234  info.message_pub = nh.advertise<can_msgs::Frame>(info.msg_name, 1);
235 
236  if (expand_) {
237  ROS_DEBUG("Initializing publishers for %zu signals...", info.sigs.size());
238  for (size_t i=0; i<info.sigs.size(); i++){
239  registerCanSignalPublisher(info.sigs[i], nh_msg);
240  }
241  }
242 
243  msgs_[info.id] = info;
244 }
245 
246 bool CanExtractor::openBag(const std::string &fname, rosbag::compression::CompressionType compression)
247 {
248  if (!bag_open_) {
249  // Save the desired file name, to actually open the bag later for writing.
250  bag_.setCompression(compression);
251  bag_fname_ = fname;
252  return true;
253  }
254  return false;
255 }
256 
258 {
259  if (bag_open_) {
260  bag_.close();
261  bag_open_ = false;
262  return true;
263  }
264  return false;
265 }
266 
268 {
269  const uint32_t QUEUE_SIZE = 10;
270  if (info.length == 1) {
271  info.sig_pub = nh.advertise<std_msgs::Bool>(info.sig_name, QUEUE_SIZE);
272  } else if ((fmod(info.factor, 1.0) != 0) || (fmod(info.offset, 1.0) != 0)) {
273  info.sig_pub = nh.advertise<std_msgs::Float64>(info.sig_name, QUEUE_SIZE);
274  } else {
275  if ((info.sign == SIGNED) || (info.offset < 0) || (info.factor < 0)) {
276  switch (getAppropriateSize(info, true)) {
277  case 8: info.sig_pub = nh.advertise<std_msgs::Int8 >(info.sig_name, QUEUE_SIZE); break;
278  case 16: info.sig_pub = nh.advertise<std_msgs::Int16>(info.sig_name, QUEUE_SIZE); break;
279  case 32: info.sig_pub = nh.advertise<std_msgs::Int32>(info.sig_name, QUEUE_SIZE); break;
280  case 64: info.sig_pub = nh.advertise<std_msgs::Int64>(info.sig_name, QUEUE_SIZE); break;
281  }
282  } else {
283  switch (getAppropriateSize(info, false)) {
284  case 8: info.sig_pub = nh.advertise<std_msgs::UInt8 >(info.sig_name, QUEUE_SIZE); break;
285  case 16: info.sig_pub = nh.advertise<std_msgs::UInt16>(info.sig_name, QUEUE_SIZE); break;
286  case 32: info.sig_pub = nh.advertise<std_msgs::UInt32>(info.sig_name, QUEUE_SIZE); break;
287  case 64: info.sig_pub = nh.advertise<std_msgs::UInt64>(info.sig_name, QUEUE_SIZE); break;
288  }
289  }
290  }
291 }
292 
293 template<class T>
294 void CanExtractor::writeToBag(const std::string& frame, const ros::Time& stamp, const T& msg) {
295  // Check the bag file is open before writing.
296  if (!bag_open_) {
297  ROS_DEBUG("Opening bag file for writing...");
298  bag_open_ = true;
300  }
301  bag_.write(frame, stamp, msg);
302 }
303 
304 template<class T>
305 void CanExtractor::pubCanSig(const RosCanMsgStruct& info, const T& sig_msg, const ros::Time& stamp, size_t i) {
306  ROS_DEBUG(" Publishing value (%s): %f", info.sigs[i].sig_name.c_str(), (double)sig_msg.data);
307  if (i < info.sigs.size()) {
308  if (offline_) {
309  writeToBag(info.msg_name + "/" + info.sigs[i].sig_name, stamp, sig_msg);
310  } else {
311  info.sigs[i].sig_pub.publish(sig_msg);
312  }
313  }
314 }
315 
316 void CanExtractor::pubCanMsg(const RosCanMsgStruct& info, const can_msgs::Frame& msg, const ros::Time& stamp) {
317  if (offline_) {
318  writeToBag(info.msg_name, stamp, msg);
319  } else {
320  info.message_pub.publish(msg);
321  }
322 }
323 
324 void CanExtractor::pubCanMsg(const RosCanMsgStruct& info, const dataspeed_can_msgs::Frame& msg, const ros::Time& stamp) {
325  if (offline_) {
326  writeToBag(info.msg_name, stamp, msg);
327  } else {
328  info.message_pub.publish(msg);
329  }
330 }
331 
332 void CanExtractor::pubCanMsgSignals(const RosCanMsgStruct &info, const std::vector<uint8_t>& buffer, const ros::Time &stamp) {
333 
334  // Search for the multiplexor value, if any.
335  unsigned short multiplexorValue = -1;
336  for (size_t i = 0; i < info.sigs.size(); i++) {
337  if (info.sigs[i].multiplexor == MULTIPLEXOR) {
338  multiplexorValue = unsignedSignalData(buffer, info.sigs[i]);
339  break;
340  }
341  }
342 
343  // Publish signals on their own topics
344  for (size_t i = 0; i < info.sigs.size(); i++) {
345 
346  // Handle multiplexed signals
347  ROS_DEBUG("MSG Name: %s", info.sigs[i].sig_pub.getTopic().c_str());
348 
349  if (info.sigs[i].multiplexor == MULTIPLEXED) {
350  if (info.sigs[i].multiplexNum != multiplexorValue) {
351  ROS_DEBUG(" Skipping multiplexed value...");
352  continue; // Else, skip this iteration of the loop.
353  } // If sigs[i].multiplexNum == multiplexorValue, it should be published.
354  } // If sigs[i].multiplexor equals MULTIPLEXOR or NONE, it's fine to publish for all messages.
355 
356  // Publish various message types
357  if (info.sigs[i].length == 1) {
358  pubCanSig(info, buildMsg<std_msgs::Bool>(info.sigs[i], buffer, false), stamp, i);
359  } else if ((fmod(info.sigs[i].factor, 1.0) != 0) || fmod(info.sigs[i].offset, 1.0) != 0) {
360  pubCanSig(info, buildMsg<std_msgs::Float64>(info.sigs[i], buffer, info.sigs[i].sign == SIGNED), stamp, i);
361  } else {
362  if ((info.sigs[i].sign == SIGNED) || (info.sigs[i].offset < 0) || (info.sigs[i].factor < 0)) {
363  if (info.sigs[i].sign == SIGNED) {
364  switch (getAppropriateSize(info.sigs[i], true)) {
365  case 8: pubCanSig(info, buildMsg<std_msgs::Int8 >(info.sigs[i], buffer, true), stamp, i); break;
366  case 16: pubCanSig(info, buildMsg<std_msgs::Int16>(info.sigs[i], buffer, true), stamp, i); break;
367  case 32: pubCanSig(info, buildMsg<std_msgs::Int32>(info.sigs[i], buffer, true), stamp, i); break;
368  case 64: pubCanSig(info, buildMsg<std_msgs::Int64>(info.sigs[i], buffer, true), stamp, i); break;
369  }
370  } else {
371  switch (getAppropriateSize(info.sigs[i], true)) {
372  case 8: pubCanSig(info, buildMsg<std_msgs::Int8 >(info.sigs[i], buffer, false), stamp, i); break;
373  case 16: pubCanSig(info, buildMsg<std_msgs::Int16>(info.sigs[i], buffer, false), stamp, i); break;
374  case 32: pubCanSig(info, buildMsg<std_msgs::Int32>(info.sigs[i], buffer, false), stamp, i); break;
375  case 64: pubCanSig(info, buildMsg<std_msgs::Int64>(info.sigs[i], buffer, false), stamp, i); break;
376  }
377  }
378  } else {
379  switch (getAppropriateSize(info.sigs[i], false)) {
380  case 8: pubCanSig(info, buildMsg<std_msgs::UInt8 >(info.sigs[i], buffer, false), stamp, i); break;
381  case 16: pubCanSig(info, buildMsg<std_msgs::UInt16>(info.sigs[i], buffer, false), stamp, i); break;
382  case 32: pubCanSig(info, buildMsg<std_msgs::UInt32>(info.sigs[i], buffer, false), stamp, i); break;
383  case 64: pubCanSig(info, buildMsg<std_msgs::UInt64>(info.sigs[i], buffer, false), stamp, i); break;
384  }
385  }
386  }
387  }
388 }
389 
390 void CanExtractor::pubMessage(const can_msgs::Frame& msg, const ros::Time &stamp)
391 {
392  // Check for valid message information
393  const uint32_t id = msg.id | (msg.is_extended ? 0x80000000 : 0x00000000);
394  if (msgs_.find(id) == msgs_.end()) {
395  ROS_WARN("Skipping unknown message ID: 0x%03X", id);
396  return;
397  }
398  const RosCanMsgStruct &info = msgs_[id];
399 
400  // Re-publish CAN message on named topic
401  pubCanMsg(info, msg, stamp);
402 
403  // Publish individual expanded signals
404  if (expand_) {
405  pubCanMsgSignals(info, std::vector<uint8_t>(msg.data.begin(), msg.data.end()), stamp);
406  }
407 }
408 
409 void CanExtractor::pubMessage(const dataspeed_can_msgs::Frame& msg, const ros::Time &stamp)
410 {
411  // Check for valid message information
412  const uint32_t id = msg.id | (msg.extended ? 0x80000000 : 0x00000000);
413  if (msgs_.find(id) == msgs_.end()) {
414  ROS_WARN("Skipping unknown message ID: 0x%03X", id);
415  return;
416  }
417  const RosCanMsgStruct &info = msgs_[id];
418 
419  // Re-publish CAN message on named topic
420  pubCanMsg(info, msg, stamp);
421 
422  // Publish individual expanded signals
423  if (expand_) {
424  pubCanMsgSignals(info, msg.data, stamp);
425  }
426 }
427 
428 } // namespace dataspeed_can_tools
void pubCanMsgSignals(const RosCanMsgStruct &info, const std::vector< uint8_t > &buffer, const ros::Time &stamp)
void pubCanSig(const RosCanMsgStruct &info, const T &sig_msg, const ros::Time &stamp, size_t i)
void open(std::string const &filename, uint32_t mode=bagmode::Read)
static uint64_t unsignedSignalData(const std::vector< uint8_t > &buffer, const RosCanSigStruct &sig_props)
const_iterator begin() const
Definition: DbcIterator.hpp:66
void writeToBag(const std::string &frame, const ros::Time &stamp, const T &msg)
static void registerCanSignalPublisher(RosCanSigStruct &info, ros::NodeHandle &nh)
std::map< uint32_t, RosCanMsgStruct > msgs_
Definition: CanExtractor.h:119
#define ROS_WARN(...)
void close()
void publish(const boost::shared_ptr< M > &message) const
std::map< uint32_t, int > unknown_msgs_
Definition: CanExtractor.h:120
void setCompression(CompressionType compression)
void pubCanMsg(const RosCanMsgStruct &info, const can_msgs::Frame &msg, const ros::Time &stamp)
messages_t::const_iterator const_iterator
Definition: DbcIterator.hpp:54
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool openBag(const std::string &fname, rosbag::compression::CompressionType compression=rosbag::compression::Uncompressed)
static int getAppropriateSize(const RosCanSigStruct &sig_props, bool output_signed)
signals_t::const_iterator const_iterator
Definition: DbcMessage.hpp:65
static T buildMsg(const RosCanSigStruct &info, const std::vector< uint8_t > &buffer, bool sign)
static int64_t signedSignalData(const std::vector< uint8_t > &buffer, const RosCanSigStruct &sig_props)
void pubMessage(const can_msgs::Frame &msg, const ros::Time &stamp=ros::Time(0))
CanExtractor(const std::string &dbc_file, bool offline, bool expand=true, bool unknown=false)
bool getMessage(RosCanMsgStruct &can_msg)
std::vector< RosCanSigStruct > sigs
Definition: CanExtractor.h:79
const_iterator end() const
Definition: DbcIterator.hpp:67
void initPublishers(RosCanMsgStruct &info, ros::NodeHandle &nh)
void write(std::string const &topic, ros::MessageEvent< T > const &event)
#define ROS_DEBUG(...)


dataspeed_can_tools
Author(s): Micho Radovnikovich
autogenerated on Wed Feb 8 2023 03:16:49