Program Listing for File channel.hpp

Return to documentation for file (/tmp/ws/src/data_tamer/data_tamer_cpp/include/data_tamer/channel.hpp)

#pragma once

#include "data_tamer/values.hpp"
#include "data_tamer/data_sink.hpp"
#include "data_tamer/details/mutex.hpp"
#include "data_tamer/details/locked_reference.hpp"

#include <chrono>
#include <memory>

namespace DataTamer
{

// Utility
inline std::chrono::nanoseconds NsecSinceEpoch()
{
  auto since_epoch = std::chrono::system_clock::now().time_since_epoch();
  return std::chrono::duration_cast<std::chrono::nanoseconds>(since_epoch);
}

class DataSinkBase;
class LogChannel;
class ChannelsRegistry;

template <typename T>
class LoggedValue
{
protected:
  LoggedValue(const std::shared_ptr<LogChannel>& channel, const std::string& name,
              T initial_value);

  friend LogChannel;

public:
  LoggedValue() {}

  ~LoggedValue();

  LoggedValue(LoggedValue const& other) = delete;
  LoggedValue& operator=(LoggedValue const& other) = delete;

  LoggedValue(LoggedValue&& other) = default;
  LoggedValue& operator=(LoggedValue&& other) = default;

  void set(const T& value, bool auto_enable = true);

  [[nodiscard]] T get();

  [[nodiscard]] LockedRef<T, Mutex> getLockedReference();

  void setEnabled(bool enabled);

  [[nodiscard]] bool isEnabled() const { return enabled_; }

private:
  std::weak_ptr<LogChannel> channel_;
  T value_ = {};
  RegistrationID id_;
  bool enabled_ = true;
};

//---------------------------------------------------------

class LogChannel : public std::enable_shared_from_this<LogChannel>
{
protected:
  // We make this private because the object must be wrapped
  // inside a std::shared_ptr.
  // This allows us to use std::weak_ptr in LoggedValue
  LogChannel(std::string name);

public:
  static std::shared_ptr<LogChannel> create(std::string name);

  ~LogChannel();

  LogChannel(const LogChannel&) = delete;
  LogChannel& operator=(const LogChannel&) = delete;

  LogChannel(LogChannel&&) = delete;
  LogChannel& operator=(LogChannel&&) = delete;

  template <typename T>
  RegistrationID registerValue(const std::string& name, const T* value);

  template <template <class, class> class Container, class T, class... TArgs>
  RegistrationID registerValue(const std::string& name,
                               const Container<T, TArgs...>* value);

  template <typename T, size_t N>
  RegistrationID registerValue(const std::string& name, const std::array<T, N>* value);

  template <typename T>
  RegistrationID registerCustomValue(const std::string& name, const T* value,
                                     CustomSerializer::Ptr type_info);

  template <typename T = double>
  [[nodiscard]] std::shared_ptr<LoggedValue<T>> createLoggedValue(std::string const& name,
                                                                  T initial_value = T{});

  [[nodiscard]] const std::string& channelName() const;

  void setEnabled(const RegistrationID& id, bool enable);

  void unregister(const RegistrationID& id);

  void addDataSink(std::shared_ptr<DataSinkBase> sink);

  bool takeSnapshot(std::chrono::nanoseconds timestamp = NsecSinceEpoch());

  [[nodiscard]] const ActiveMask& getActiveFlags();

  [[nodiscard]] Schema getSchema() const;

  Mutex& writeMutex();

private:
  struct Pimpl;
  std::unique_ptr<Pimpl> _p;

  TypesRegistry _type_registry;

  template <typename T>
  void updateTypeRegistry();

  template <typename T>
  void updateTypeRegistryImpl(FieldsVector& fields, const char* name);

  void addCustomType(const std::string& custom_type_name, const FieldsVector& fields);

  [[nodiscard]] RegistrationID registerValueImpl(const std::string& name,
                                                 ValuePtr&& value_ptr,
                                                 CustomSerializer::Ptr type_info);
};

//----------------------------------------------------------------------
//----------------------------------------------------------------------
//----------------------------------------------------------------------
template <typename T>
void LogChannel::updateTypeRegistryImpl(FieldsVector& fields, const char* field_name)
{
  using SerializeMe::container_info;
  TypeField field;
  field.field_name = field_name;
  field.type = GetBasicType<T>();

  if constexpr (GetBasicType<T>() == BasicType::OTHER)
  {
    field.type_name = TypeDefinition<T>().typeName();

    if constexpr (container_info<T>::is_container)
    {
      field.is_vector = true;
      field.array_size = container_info<T>::size;
      using Type = typename container_info<T>::value_type;
      updateTypeRegistry<Type>();
    }
    else
    {
      updateTypeRegistry<T>();
    }
  }
  fields.push_back(field);
}

template <typename T>
inline void LogChannel::updateTypeRegistry()
{
  FieldsVector fields;

  if constexpr (!IsNumericType<T>())
  {
    const auto& type_name = DataTamer::TypeDefinition<T>().typeName();
    auto added_serializer = _type_registry.addType<T>(type_name, true);
    if (added_serializer)
    {
      if constexpr (has_typedef_with_object<T>())
      {
        auto func = [this, &fields](const char* field_name, const auto* member) {
          using MemberType =
              typename std::remove_cv_t<std::remove_reference_t<decltype(*member)>>;
          updateTypeRegistryImpl<MemberType>(fields, field_name);
        };
        TypeDefinition<T>().typeDef({}, func);
      }
      else
      {
        auto func = [this, &fields](const char* field_name, const auto& member) {
          using MemberType = decltype(getPointerType(member));
          this->updateTypeRegistryImpl<MemberType>(fields, field_name);
        };
        TypeDefinition<T>().typeDef(func);
      }
      addCustomType(type_name, fields);
    }
  }
}

template <typename T>
inline RegistrationID LogChannel::registerValue(const std::string& name,
                                                const T* value_ptr)
{
  if constexpr (IsNumericType<T>())
  {
    return registerValueImpl(name, ValuePtr(value_ptr), {});
  }
  else
  {
    updateTypeRegistry<T>();
    auto def = _type_registry.getSerializer<T>();
    return registerValueImpl(name, ValuePtr(value_ptr, def), def);
  }
}

template <typename T>
inline RegistrationID LogChannel::registerCustomValue(const std::string& name,
                                                      const T* value_ptr,
                                                      CustomSerializer::Ptr serializer)
{
  static_assert(!IsNumericType<T>(), "This method should be used only for custom types");

  return registerValueImpl(name, ValuePtr(value_ptr, serializer), serializer);
}

template <template <class, class> class Container, class T, class... TArgs>
inline RegistrationID LogChannel::registerValue(const std::string& prefix,
                                                const Container<T, TArgs...>* vect)
{
  if constexpr (IsNumericType<T>())
  {
    return registerValueImpl(prefix, ValuePtr(vect), {});
  }
  else
  {
    updateTypeRegistry<T>();
    auto def = _type_registry.getSerializer<T>();
    return registerValueImpl(prefix, ValuePtr(vect), def);
  }
}

template <typename T, size_t N>
inline RegistrationID LogChannel::registerValue(const std::string& prefix,
                                                const std::array<T, N>* vect)
{
  if constexpr (IsNumericType<T>())
  {
    return registerValueImpl(prefix, ValuePtr(vect), {});
  }
  else
  {
    updateTypeRegistry<T>();
    auto def = _type_registry.getSerializer<T>();
    return registerValueImpl(prefix, ValuePtr(vect, def), def);
  }
}

template <typename T>
inline std::shared_ptr<LoggedValue<T>>
LogChannel::createLoggedValue(std::string const& name, T initial_value)
{
  auto val = new LoggedValue<T>(shared_from_this(), name, initial_value);
  return std::shared_ptr<LoggedValue<T>>(val);
}

template <typename T>
inline LoggedValue<T>::LoggedValue(const std::shared_ptr<LogChannel>& channel,
                                   const std::string& name, T initial_value) :
  channel_(channel), value_(initial_value), id_(channel->registerValue(name, &value_))
{}

template <typename T>
inline LoggedValue<T>::~LoggedValue()
{
  if (auto channel = channel_.lock())
  {
    channel->unregister(id_);
  }
}

template <typename T>
inline void LoggedValue<T>::setEnabled(bool enabled)
{
  if (auto channel = channel_.lock())
  {
    channel->setEnabled(id_, enabled);
  }
  enabled_ = enabled;
}

template <typename T>
inline void LoggedValue<T>::set(const T& val, bool auto_enable)
{
  if (auto channel = channel_.lock())
  {
    value_ = val;
    if (!enabled_ && auto_enable)
    {
      channel->setEnabled(id_, true);
      enabled_ = true;
    }
  }
  else
  {
    value_ = val;
    enabled_ |= auto_enable;
  }
}

template <typename T>
inline T LoggedValue<T>::get()
{
  if (auto channel = channel_.lock())
  {
    std::lock_guard const lock(channel->writeMutex());
    return value_;
  }
  return value_;
}

template <typename T>
inline LockedRef<T, Mutex> LoggedValue<T>::getLockedReference()
{
  Mutex* mutex_ptr = nullptr;
  if (auto chan = channel_.lock())
  {
    mutex_ptr = &chan->writeMutex();
  }
  return LockedRef<T, Mutex>(&value_, mutex_ptr);
}

}   // namespace DataTamer