blob: 330da4f2dee297853d5ac2c6e8462e9bf1434a8a [file] [log] [blame]
James Kuszmaul60bb8682023-08-07 07:39:34 -07001#ifndef DOCUMENTATION_AOS_EXAMPLES_CLOCK_OFFSET_READER_H_
2#define DOCUMENTATION_AOS_EXAMPLES_CLOCK_OFFSET_READER_H_
3#include "aos/events/event_loop.h"
4#include "aos/network/message_bridge_server_generated.h"
5#include "documentation/aos/examples/sensor_data_generated.h"
6
7namespace examples {
8
9// This class is a sample that is shown in the markdown documentation.
10// If it needs to get updated, the sample should get updated as well.
11// TODO(james): Get a convenient way to directly include portions of files in
12// markdown so that we don't just manually copy-and-paste the code between
13// spots.
14class SensorAgeReader {
15 public:
16 SensorAgeReader(aos::EventLoop *event_loop)
17 : event_loop_(event_loop),
18 clock_offset_fetcher_(
19 event_loop->MakeFetcher<aos::message_bridge::ServerStatistics>(
20 "/aos")) {
21 event_loop_->MakeWatcher(
22 "/input", [this](const SensorData &msg) { HandleSensorData(msg); });
23 }
24
25 void HandleSensorData(const SensorData &msg) {
26 std::chrono::nanoseconds monotonic_offset{0};
27 clock_offset_fetcher_.Fetch();
28 if (clock_offset_fetcher_.get() != nullptr) {
29 for (const auto connection : *clock_offset_fetcher_->connections()) {
30 if (connection->has_node() && connection->node()->has_name() &&
31 connection->node()->name()->string_view() == "sensor") {
32 if (connection->has_monotonic_offset()) {
33 monotonic_offset =
34 std::chrono::nanoseconds(connection->monotonic_offset());
35 } else {
36 // If we don't have a monotonic offset, that means we aren't
37 // connected, in which case we should just exit early.
38 // The ServerStatistics message will always populate statuses for
39 // every node, so we don't have to worry about missing the "sensor"
40 // node (although it can be good practice to check that the node you
41 // are looking for actually exists, to protect against programming
42 // errors).
43 LOG(WARNING) << "Message bridge disconnected.";
44 return;
45 }
46 break;
47 }
48 }
49 } else {
50 LOG(WARNING) << "No message bridge status available.";
51 return;
52 }
53 const aos::monotonic_clock::time_point now = event_loop_->monotonic_now();
54 // The monotonic_remote_time will be the time that the message was sent on
55 // the source node; by offsetting it by the monotonic_offset, we should get
56 // a reasonable estimate of when it was sent. This does not account for any
57 // delays between the sensor reading and when it actually got sent.
58 const aos::monotonic_clock::time_point send_time(
59 event_loop_->context().monotonic_remote_time - monotonic_offset);
60 // Many sensors may include some sort of hardware timestamp indicating when
61 // the measurement was taken, which is likely before the sent time. This can
62 // be populated as a data field inside of the message, and if it is using
63 // the same monotonic clock as AOS is then we can do the same offset
64 // computation, but get a timestamp for when the data was actually captured.
65 const aos::monotonic_clock::time_point capture_time(
66 std::chrono::nanoseconds(msg.hardware_capture_time_ns()) -
67 monotonic_offset);
68 LOG(INFO) << "The sensor data was sent "
69 << aos::time::DurationInSeconds(now - send_time)
70 << " seconds ago.";
71 LOG(INFO) << "The sensor data was read off of the hardware "
72 << aos::time::DurationInSeconds(now - capture_time)
73 << " seconds ago.";
74 }
75
76 aos::EventLoop *event_loop_;
77 aos::Fetcher<aos::message_bridge::ServerStatistics> clock_offset_fetcher_;
78};
79} // namespace examples
80#endif // DOCUMENTATION_AOS_EXAMPLES_CLOCK_OFFSET_READER_H_