blob: 330da4f2dee297853d5ac2c6e8462e9bf1434a8a [file] [log] [blame]
#ifndef DOCUMENTATION_AOS_EXAMPLES_CLOCK_OFFSET_READER_H_
#define DOCUMENTATION_AOS_EXAMPLES_CLOCK_OFFSET_READER_H_
#include "aos/events/event_loop.h"
#include "aos/network/message_bridge_server_generated.h"
#include "documentation/aos/examples/sensor_data_generated.h"
namespace examples {
// This class is a sample that is shown in the markdown documentation.
// If it needs to get updated, the sample should get updated as well.
// TODO(james): Get a convenient way to directly include portions of files in
// markdown so that we don't just manually copy-and-paste the code between
// spots.
class SensorAgeReader {
public:
SensorAgeReader(aos::EventLoop *event_loop)
: event_loop_(event_loop),
clock_offset_fetcher_(
event_loop->MakeFetcher<aos::message_bridge::ServerStatistics>(
"/aos")) {
event_loop_->MakeWatcher(
"/input", [this](const SensorData &msg) { HandleSensorData(msg); });
}
void HandleSensorData(const SensorData &msg) {
std::chrono::nanoseconds monotonic_offset{0};
clock_offset_fetcher_.Fetch();
if (clock_offset_fetcher_.get() != nullptr) {
for (const auto connection : *clock_offset_fetcher_->connections()) {
if (connection->has_node() && connection->node()->has_name() &&
connection->node()->name()->string_view() == "sensor") {
if (connection->has_monotonic_offset()) {
monotonic_offset =
std::chrono::nanoseconds(connection->monotonic_offset());
} else {
// If we don't have a monotonic offset, that means we aren't
// connected, in which case we should just exit early.
// The ServerStatistics message will always populate statuses for
// every node, so we don't have to worry about missing the "sensor"
// node (although it can be good practice to check that the node you
// are looking for actually exists, to protect against programming
// errors).
LOG(WARNING) << "Message bridge disconnected.";
return;
}
break;
}
}
} else {
LOG(WARNING) << "No message bridge status available.";
return;
}
const aos::monotonic_clock::time_point now = event_loop_->monotonic_now();
// The monotonic_remote_time will be the time that the message was sent on
// the source node; by offsetting it by the monotonic_offset, we should get
// a reasonable estimate of when it was sent. This does not account for any
// delays between the sensor reading and when it actually got sent.
const aos::monotonic_clock::time_point send_time(
event_loop_->context().monotonic_remote_time - monotonic_offset);
// Many sensors may include some sort of hardware timestamp indicating when
// the measurement was taken, which is likely before the sent time. This can
// be populated as a data field inside of the message, and if it is using
// the same monotonic clock as AOS is then we can do the same offset
// computation, but get a timestamp for when the data was actually captured.
const aos::monotonic_clock::time_point capture_time(
std::chrono::nanoseconds(msg.hardware_capture_time_ns()) -
monotonic_offset);
LOG(INFO) << "The sensor data was sent "
<< aos::time::DurationInSeconds(now - send_time)
<< " seconds ago.";
LOG(INFO) << "The sensor data was read off of the hardware "
<< aos::time::DurationInSeconds(now - capture_time)
<< " seconds ago.";
}
aos::EventLoop *event_loop_;
aos::Fetcher<aos::message_bridge::ServerStatistics> clock_offset_fetcher_;
};
} // namespace examples
#endif // DOCUMENTATION_AOS_EXAMPLES_CLOCK_OFFSET_READER_H_