blob: d1eb0c6757f2b858db24bc4bfd363318ecbaef74 [file] [log] [blame]
#ifndef AOS_EVENTS_LOGGING_CONFIG_REMAPPER_H_
#define AOS_EVENTS_LOGGING_CONFIG_REMAPPER_H_
#include <map>
#include <string_view>
#include <tuple>
#include <vector>
#include "flatbuffers/flatbuffers.h"
#include "aos/events/event_loop.h"
#include "aos/events/logging/logger_generated.h"
#include "aos/events/logging/replay_channels.h"
namespace aos {
// This class is used for remapping and renaming channels with the passed in
// configuration to the constructor. Typically, the templated versions of
// RemapOriginalChannel and RenameOriginalChannel are the main functions to use
// for type safety. After remapping and renaming, the remapped configuration can
// be accessed through remapped_configuration(), and the original configuration
// that is not mutated can be accessed through original_configuration.
//
// This class assumes no ownership over any pointers provided to it.
//
// Timestamp channels are automatically remapped on construction
//
// Note: This class does not need logfiles to function unlike LogReader. This
// logic originally lived in LogReader and was refactored out into this class.
// The same API for remapping and renaming still exists in LogReader which now
// just passes along the args to this class.
class ConfigRemapper {
public:
ConfigRemapper(const Configuration *config,
const Configuration *replay_config = nullptr,
const logger::ReplayChannels *replay_channels = nullptr);
~ConfigRemapper();
// Map of channel indices to new name. The channel index will be an index into
// original_configuration(), and the string key will be the name of the
// channel to send on instead of the orignal channel name.
struct RemappedChannel {
std::string remapped_name;
std::string new_type;
};
// Enum to use for indicating how RemapOriginalChannel behaves when there is
// already a channel with the remapped name (e.g., as may happen when
// replaying a logfile that was itself generated from replay).
enum class RemapConflict {
// LOG(FATAL) on conflicts in remappings.
kDisallow,
// If we run into a conflict, attempt to remap the channel we would be
// overriding (and continue to do so if remapping *that* channel also
// generates a conflict).
// This will mean that if we repeatedly replay a log, we will end up
// stacking more and more /original's on the start of the oldest version
// of the channels.
kCascade
};
// Remaps a channel from the original configuration passed to the constructor
// to the given one. This operates on raw channel names, without any node or
// application specific mappings.
void RemapOriginalChannel(
std::string_view name, std::string_view type,
std::string_view add_prefix = "/original", std::string_view new_type = "",
RemapConflict conflict_handling = RemapConflict::kCascade);
template <typename T>
void RemapOriginalChannel(
std::string_view name, std::string_view add_prefix = "/original",
std::string_view new_type = "",
RemapConflict conflict_handling = RemapConflict::kCascade) {
RemapOriginalChannel(name, T::GetFullyQualifiedName(), add_prefix, new_type,
conflict_handling);
}
// Remaps the provided channel, though this respects node mappings, and
// preserves them too. This makes it so if /aos -> /pi1/aos on one node,
// /original/aos -> /original/pi1/aos on the same node after renaming, just
// like you would hope. If new_type is not empty, the new channel will use
// the provided type instead. This allows for renaming messages.
//
// TODO(austin): If you have 2 nodes remapping something to the same channel,
// this doesn't handle that. No use cases exist yet for that, so it isn't
// being done yet.
void RemapOriginalChannel(
std::string_view name, std::string_view type, const Node *node,
std::string_view add_prefix = "/original", std::string_view new_type = "",
RemapConflict conflict_handling = RemapConflict::kCascade);
template <typename T>
void RemapOriginalChannel(
std::string_view name, const Node *node,
std::string_view add_prefix = "/original", std::string_view new_type = "",
RemapConflict conflict_handling = RemapConflict::kCascade) {
RemapOriginalChannel(name, T::GetFullyQualifiedName(), node, add_prefix,
new_type, conflict_handling);
}
// Similar to RemapOriginalChannel(), but lets you specify a name for the new
// channel without constraints. By default, this will not add any maps for the
// new channel. Use add_maps to specify any maps you'd like added.
void RenameOriginalChannel(std::string_view name, std::string_view type,
std::string_view new_name,
const std::vector<MapT> &add_maps = {});
template <typename T>
void RenameOriginalChannel(std::string_view name, std::string_view new_name,
const std::vector<MapT> &add_maps = {}) {
RenameOriginalChannel(name, T::GetFullyQualifiedName(), new_name, add_maps);
}
// The following overloads are more suitable for multi-node configurations,
// and let you rename a channel on a specific node.
void RenameOriginalChannel(std::string_view name, std::string_view type,
const Node *node, std::string_view new_name,
const std::vector<MapT> &add_maps = {});
template <typename T>
void RenameOriginalChannel(std::string_view name, const Node *node,
std::string_view new_name,
const std::vector<MapT> &add_maps = {}) {
RenameOriginalChannel(name, T::GetFullyQualifiedName(), node, new_name,
add_maps);
}
template <typename T>
bool HasChannel(std::string_view name, const Node *node = nullptr) {
return HasChannel(name, T::GetFullyQualifiedName(), node);
}
bool HasChannel(std::string_view name, std::string_view type,
const Node *node) {
return configuration::GetChannel(original_configuration(), name, type, "",
node, true) != nullptr;
}
// Returns true if the channel exists on the node and was in the original
// config
template <typename T>
bool HasOriginalChannel(std::string_view name, const Node *node = nullptr) {
const Channel *channel =
configuration::GetChannel(original_configuration(), name,
T::GetFullyQualifiedName(), "", node, true);
if (channel == nullptr) return false;
return channel->logger() != LoggerConfig::NOT_LOGGED;
}
template <typename T>
void MaybeRemapOriginalChannel(std::string_view name,
const Node *node = nullptr) {
if (HasChannel<T>(name, node)) {
RemapOriginalChannel<T>(name, node);
}
}
template <typename T>
void MaybeRenameOriginalChannel(std::string_view name, const Node *node,
std::string_view new_name,
const std::vector<MapT> &add_maps = {}) {
if (HasChannel<T>(name, node)) {
RenameOriginalChannel<T>(name, node, new_name, add_maps);
}
}
const Channel *RemapChannel(const EventLoop *event_loop, const Node *node,
const Channel *channel);
// Returns a list of all the original channels from remapping.
std::vector<const Channel *> RemappedChannels() const;
void set_configuration(const Configuration *configuration);
// Returns the configuration that was originally passed to the constructor.
// This class does not own this pointer.
const Configuration *original_configuration() const;
// Returns the configuration that contains the remapping and renamings done on
// the original configuration. The pointer is invalidated whenever
// RemapOriginalChannel is called.
const Configuration *remapped_configuration() const;
private:
// Handle constructing a configuration with all the additional remapped
// channels from calls to RemapOriginalChannel.
void MakeRemappedConfig();
std::map<size_t, RemappedChannel> remapped_channels_;
std::vector<MapT> maps_;
std::unique_ptr<FlatbufferDetachedBuffer<Configuration>>
remapped_configuration_buffer_;
const Configuration *remapped_configuration_ = nullptr;
const Configuration *original_configuration_ = nullptr;
const Configuration *replay_configuration_ = nullptr;
const logger::ReplayChannels *replay_channels_ = nullptr;
}; // class ConfigRemapper
} // namespace aos
#endif // AOS_EVENTS_LOGGING_CONFIG_REMAPPER_H_