Program Listing for File serialization.hpp

Return to documentation for file (/tmp/ws/src/ublox/ublox_serialization/include/ublox_serialization/serialization.hpp)

//==============================================================================
// Copyright (c) 2012, Johannes Meyer, TU Darmstadt
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the Flight Systems and Automatic Control group,
//       TU Darmstadt, nor the names of its contributors may be used to
//       endorse or promote products derived from this software without
//       specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//==============================================================================

#ifndef UBLOX_SERIALIZATION_SERIALIZATION_HPP
#define UBLOX_SERIALIZATION_SERIALIZATION_HPP

#include <algorithm>
#include <array>
#include <cstdint>
#include <cstring>
#include <string>
#include <utility>
#include <vector>

#include "checksum.hpp"



namespace ublox {

static const uint8_t DEFAULT_SYNC_A = 0xB5;
static const uint8_t DEFAULT_SYNC_B = 0x62;
static const uint8_t kHeaderLength = 6;
static const uint8_t kChecksumLength = 2;

template<typename T, typename Enabled = void>
struct UbloxSerializer
{
  template<typename Stream>
  inline static void write(Stream& stream, const T & t);

  template<typename Stream>
  inline static void read(Stream& stream, T & t);

  inline static uint32_t serializedLength(const T & t);
};

template <typename T>
struct UbloxSerializer<T, typename std::enable_if<std::is_same<T, uint8_t>::value ||
                                                  std::is_same<T, uint16_t>::value ||
                                                  std::is_same<T, uint32_t>::value ||
                                                  std::is_same<T, uint64_t>::value ||
                                                  std::is_same<T, int8_t>::value ||
                                                  std::is_same<T, int16_t>::value ||
                                                  std::is_same<T, int32_t>::value ||
                                                  std::is_same<T, int64_t>::value ||
                                                  std::is_same<T, float>::value ||
                                                  std::is_same<T, double>::value>::type>
{
  template<typename Stream>
  inline static void write(Stream& stream, const T v) {
    *reinterpret_cast<T*>(stream.advance(sizeof(v))) = v;
  }

  template<typename Stream>
  inline static void read(Stream& stream, T& v) {
    v = *reinterpret_cast<T*>(stream.advance(sizeof(v)));
  }

  inline static uint32_t serializedLength(const T& v) {
    (void)v;
    return sizeof(T);
  }
};

template<typename T, typename Stream>
inline void serialize(Stream& stream, const T& t) {
  UbloxSerializer<T>::write(stream, t);
}

template<typename T, typename Stream>
inline void deserialize(Stream& stream, T& t) {
  UbloxSerializer<T>::read(stream, t);
}

template<typename T>
inline uint32_t serializationLength(const T& t) {
  return UbloxSerializer<T>::serializedLength(t);
}

template<typename T, size_t N, class Enabled = void>
struct StdArrayUbloxSerializer
{};

template<typename T, size_t N>
struct StdArrayUbloxSerializer<T, N, typename std::enable_if<std::is_same<T, uint8_t>::value ||
                                                             std::is_same<T, uint16_t>::value ||
                                                             std::is_same<T, uint32_t>::value ||
                                                             std::is_same<T, uint64_t>::value ||
                                                             std::is_same<T, int8_t>::value ||
                                                             std::is_same<T, int16_t>::value ||
                                                             std::is_same<T, int32_t>::value ||
                                                             std::is_same<T, int64_t>::value ||
                                                             std::is_same<T, float>::value ||
                                                             std::is_same<T, double>::value>::type>
{
  template<typename Stream>
  inline static void write(Stream& stream, const std::array<T, N>& v) {
    const uint32_t data_len = N * sizeof(T);
    std::memcpy(stream.advance(data_len), &v.front(), data_len);
  }

  template<typename Stream>
  inline static void read(Stream& stream, std::array<T, N>& v) {
    const uint32_t data_len = N * sizeof(T);
    std::memcpy(&v.front(), stream.advance(data_len), data_len);
  }

  inline static uint32_t serializedLength(const std::array<T, N>& v) {
    (void)v;
    return N * sizeof(T);
  }
};

template<typename T, size_t N, typename Stream>
inline void serialize(Stream& stream, const std::array<T, N>& t)
{
  StdArrayUbloxSerializer<T, N>::write(stream, t);
}

template<typename T, size_t N, typename Stream>
inline void deserialize(Stream& stream, std::array<T, N>& t) {
  StdArrayUbloxSerializer<T, N>::read(stream, t);
}

template<typename T, size_t N>
inline uint32_t serializationLength(const std::array<T, N>& t)
{
  return StdArrayUbloxSerializer<T, N>::serializedLength(t);
}

struct UbloxStream
{
  /*
   * \brief Returns a pointer to the current position of the stream
   */
  inline uint8_t* getData() {
    return data_;
  }
  uint8_t* advance(uint32_t len) {
    uint8_t* old_data = data_;
    data_ += len;
    if (data_ > end_) {
      // Throwing directly here causes a significant speed hit due to the extra code generated
      // for the throw statement
      //throwStreamOverrun();
    }
    return old_data;
  }

  inline uint32_t getLength() { return static_cast<uint32_t>(end_ - data_); }

protected:
  UbloxStream(uint8_t* _data, uint32_t _count)
  : data_(_data)
  , end_(_data + _count)
  {}

private:
  uint8_t* data_;
  uint8_t* end_;
};

struct UbloxIStream : public UbloxStream
{
  UbloxIStream(uint8_t* data, uint32_t count)
  : UbloxStream(data, count)
  {}

  template<typename T>
  void next(T& t) {
    deserialize(*this, t);
  }
};

struct UbloxOStream : public UbloxStream
{
  UbloxOStream(uint8_t* data, uint32_t count)
  : UbloxStream(data, count)
  {}

  template<typename T>
  void next(const T& t) {
    serialize(*this, t);
  }
};

template <typename T>
class Message {
 public:
  static bool canDecode(uint8_t class_id, uint8_t message_id) {
    return std::find(keys_.begin(), keys_.end(),
                     std::make_pair(class_id, message_id)) != keys_.end();
  }

  static void addKey(uint8_t class_id, uint8_t message_id) {
    keys_.emplace_back(std::make_pair(class_id, message_id));
  }

  struct StaticKeyInitializer
  {
    StaticKeyInitializer(uint8_t class_id, uint8_t message_id) {
      Message<T>::addKey(class_id, message_id);
    }
  };

 private:
  static std::vector<std::pair<uint8_t,uint8_t> > keys_;
};

struct Options {
  Options() : sync_a(DEFAULT_SYNC_A), sync_b(DEFAULT_SYNC_B),
              header_length(kHeaderLength), checksum_length(kChecksumLength) {}
  uint8_t sync_a;
  uint8_t sync_b;
  uint8_t header_length;
  uint8_t checksum_length;

  uint32_t wrapper_length() {
    return header_length + checksum_length;
  }
};

class Reader {
 public:
  Reader(const uint8_t *data, uint32_t count,
         const Options &options = Options()) :
      data_(data), count_(count), found_(false), options_(options)
  {
    extra_data_.reserve(1024);
  }

  using iterator = const uint8_t *;

  iterator search()
  {
    if (found_) {
      next();
    }

    // Search for a message header
    for( ; count_ > 0; --count_, ++data_) {
      if (data_[0] == options_.sync_a &&
          (count_ == 1 || data_[1] == options_.sync_b)) {
        break;
      } else {
        extra_data_.push_back(data_[0]);
      }
    }

    return data_;
  }

  bool found()
  {
    if (found_) {
      return true;
    }
    // Verify message is long enough to have sync chars, id, length & checksum
    if (count_ < options_.wrapper_length()) {
      return false;
    }
    // Verify the header bits
    if (data_[0] != options_.sync_a || data_[1] != options_.sync_b) {
      return false;
    }
    // Verify that the buffer length is long enough based on the received
    // message length
    if (count_ < length() + options_.wrapper_length()) {
      return false;
    }

    found_ = true;
    return true;
  }

  iterator next() {
    if (found()) {
      uint32_t size = length() + options_.wrapper_length();
      data_ += size; count_ -= size;
    }
    found_ = false;
    return data_;
  }

  iterator pos() {
    return data_;
  }

  iterator end() {
    return data_ + count_;
  }

  uint8_t classId() { return data_[2]; }
  uint8_t messageId() { return data_[3]; }

  uint32_t length() { return (data_[5] << 8) + data_[4]; }
  const uint8_t *data() { return data_ + options_.header_length; }

  uint16_t checksum() {
    return *reinterpret_cast<const uint16_t *>(data_ + options_.header_length +
                                               length());
  }

  template <typename T>
  bool read(T &message,
            bool search = false) {
    if (search) {
      this->search();
    }
    if (!found()) {
      return false;
    }
    if (!Message<T>::canDecode(classId(), messageId())) {
      return false;
    }

    uint16_t chk{0};
    if (calculateChecksum(data_ + 2, length() + 4, chk) != this->checksum()) {
      // checksum error
      // Note that it is possible (and even likely) that we get here without
      // having the entire packet available.  This happens when there are both
      // NMEA and UBlox messages configured on the serial wire, and the packet is
      // laid out like:
      //
      // <NMEA_message><UBlox_message_missing_two_bytes>
      //
      // Therefore, we do not print errors in this case and instead just don't
      // do any additional work.
      return false;
    }

    UbloxSerializer<T>::read(data_ + options_.header_length, length(), message);
    return true;
  }

  template <typename T>
  bool hasType() {
    if (!found()) {
      return false;
    }
    return Message<T>::canDecode(classId(), messageId());
  }

  bool isMessage(uint8_t class_id, uint8_t message_id) {
    if (!found()) {
      return false;
    }
    return (classId() == class_id && messageId() == message_id);
  }

  const std::string &getExtraData() const {
    return extra_data_;
  }

private:
  const uint8_t *data_;
  std::string extra_data_;
  uint32_t count_;
  bool found_;
  Options options_;
};

class Writer {
 public:
  using iterator = uint8_t *;

  Writer(uint8_t *data, uint32_t size, const Options &options = Options()) :
      data_(data), size_(size), options_(options) {}

  template <typename T> bool write(const T& message,
                                   uint8_t class_id = T::CLASS_ID,
                                   uint8_t message_id = T::MESSAGE_ID) {
    // Check for buffer overflow
    uint32_t length = UbloxSerializer<T>::serializedLength(message);
    if (size_ < length + options_.wrapper_length()) {
      // ROS_ERROR("u-blox write buffer overflow. Message %u / %u not written",
      //           class_id, message_id);
      return false;
    }
    // Encode the message and add it to the buffer
    UbloxSerializer<T>::write(data_ + options_.header_length,
                         size_ - options_.header_length, message);
    return write(nullptr, length, class_id, message_id);
  }

  bool write(const uint8_t* message, uint32_t length, uint8_t class_id,
             uint8_t message_id) {
    if (size_ < length + options_.wrapper_length()) {
      // ROS_ERROR("u-blox write buffer overflow. Message %u / %u not written",
      //           class_id, message_id);
      return false;
    }
    iterator start = data_;

    // write header
    *data_++ = options_.sync_a;
    *data_++ = options_.sync_b;
    *data_++ = class_id;
    *data_++ = message_id;
    *data_++ = length & 0xFF;
    *data_++ = (length >> 8) & 0xFF;
    size_ -= options_.header_length;

    // write message
    if (message) {
      std::copy(message, message + length, data_);
    }
    data_ += length;
    size_ -= length;

    // write checksum
    uint8_t ck_a, ck_b;
    calculateChecksum(start + 2, length + 4, ck_a, ck_b);
    *data_++ = ck_a;
    *data_++ = ck_b;
    size_ -= options_.checksum_length;

    return true;
  }

  iterator end() {
    return data_;
  }

 private:
  iterator data_;

  uint32_t size_;
  Options options_;
};

}  // namespace ublox

// Use to declare u-blox messages and message serializers
#define DECLARE_UBLOX_MESSAGE(class_id, message_id, package, message) \
  template class ublox::UbloxSerializer<package::msg::message>; \
  template class ublox::Message<package::msg::message>; \
  namespace package { namespace { \
    static const ublox::Message<package::msg::message>::StaticKeyInitializer static_key_initializer_##message(class_id, message_id); \
  } } \

// Use for messages which have the same structure but different IDs, e.g. INF
// Call DECLARE_UBLOX_MESSAGE for the first message and DECLARE_UBLOX_MESSAGE_ID
// for following declarations
#define DECLARE_UBLOX_MESSAGE_ID(class_id, message_id, package, message, name) \
  namespace package { namespace { \
    static const ublox::Message<package::msg::message>::StaticKeyInitializer static_key_initializer_##name(class_id, message_id); \
  } } \

#endif  // UBLOX_SERIALIZATION_SERIALIZATION_HPP