summaryrefslogtreecommitdiffstats
path: root/tools/battor_agent/battor_agent.h
blob: 524b5a8991e9c2ec29094fe098b77aec0c1cfe0f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef TOOLS_BATTOR_AGENT_BATTOR_AGENT_H_
#define TOOLS_BATTOR_AGENT_BATTOR_AGENT_H_

#include <map>

#include "base/cancelable_callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "tools/battor_agent/battor_connection.h"
#include "tools/battor_agent/battor_error.h"

namespace battor {

// A BattOrAgent is a class used to asynchronously communicate with a BattOr for
// the purpose of collecting power samples. A BattOr is an external USB device
// that's capable of recording accurate, high-frequency (2000Hz) power samples.
//
// The serial connection is automatically opened when the first command
// (e.g. StartTracing(), StopTracing(), etc.) is issued, and automatically
// closed when either StopTracing() or the destructor is called. For Telemetry,
// this means that the connection must be reinitialized for every command that's
// issued because a new BattOrAgent is constructed. For Chromium, we use the
// same BattOrAgent for multiple commands and thus avoid having to reinitialize
// the serial connection.
//
// This class is NOT thread safe. Any interactions with this class that involve
// IO (i.e. any interactions that require a callback) must be done from the
// same IO thread, which must also have a running MessageLoop.
class BattOrAgent : public BattOrConnection::Listener,
                    public base::SupportsWeakPtr<BattOrAgent> {
 public:
  // The listener interface that must be implemented in order to interact with
  // the BattOrAgent.
  class Listener {
   public:
    virtual void OnStartTracingComplete(BattOrError error) = 0;
    virtual void OnStopTracingComplete(const std::string& trace,
                                       BattOrError error) = 0;
    virtual void OnRecordClockSyncMarkerComplete(BattOrError error) = 0;
  };

  BattOrAgent(
      const std::string& path,
      Listener* listener,
      scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner,
      scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner);
  virtual ~BattOrAgent();

  void StartTracing();
  void StopTracing();
  void RecordClockSyncMarker(const std::string& marker);

  // Returns whether the BattOr is able to record clock sync markers in its own
  // trace log.
  static bool SupportsExplicitClockSync() { return true; }

  // BattOrConnection::Listener implementation.
  void OnConnectionOpened(bool success) override;
  void OnBytesSent(bool success) override;
  void OnMessageRead(bool success,
                     BattOrMessageType type,
                     scoped_ptr<std::vector<char>> bytes) override;

 protected:
  // The connection that knows how to communicate with the BattOr in terms of
  // protocol primitives. This is protected so that it can be replaced with a
  // fake in testing.
  scoped_ptr<BattOrConnection> connection_;

  // Timeout for when an action isn't completed within the allotted time. This
  // is virtual and protected so that timeouts can be disabled in testing. The
  // testing task runner that runs delayed tasks immediately deals poorly with
  // timeouts posted as future tasks.
  virtual void OnActionTimeout();

 private:
  enum class Command {
    INVALID,
    START_TRACING,
    STOP_TRACING,
    RECORD_CLOCK_SYNC_MARKER,
  };

  enum class Action {
    INVALID,

    // Actions required to connect to a BattOr.
    REQUEST_CONNECTION,

    // Actions required for starting tracing.
    SEND_RESET,
    SEND_INIT,
    READ_INIT_ACK,
    SEND_SET_GAIN,
    READ_SET_GAIN_ACK,
    SEND_START_TRACING,
    READ_START_TRACING_ACK,

    // Actions required for stopping tracing.
    SEND_EEPROM_REQUEST,
    READ_EEPROM,
    SEND_SAMPLES_REQUEST,
    READ_CALIBRATION_FRAME,
    READ_DATA_FRAME,

    // Actions required for recording a clock sync marker.
    SEND_CURRENT_SAMPLE_REQUEST,
    READ_CURRENT_SAMPLE,
  };

  // Performs an action.
  void PerformAction(Action action);
  // Performs an action after a delay.
  void PerformDelayedAction(Action action, base::TimeDelta delay);



  // Requests a connection to the BattOr.
  void BeginConnect();

  // Sends a control message over the connection.
  void SendControlMessage(BattOrControlMessageType type,
                          uint16_t param1,
                          uint16_t param2);

  // Completes the command with the specified error.
  void CompleteCommand(BattOrError error);

  // Returns a formatted version of samples_ with timestamps and real units.
  std::string SamplesToString();

  // The listener that handles the commands' results. It must outlive the agent.
  Listener* listener_;

  // The last action executed by the agent. This should only be updated in
  // PerformAction().
  Action last_action_;

  // The tracing command currently being executed by the agent.
  Command command_;

  // A map from the sample number (including samples from the calibration frame)
  // to the ID of the clock sync marker that is associated with that sample
  // number. If we ever have to store a large number of these, consider using an
  // unordered map.
  std::map<uint32_t, std::string> clock_sync_markers_;

  // The clock sync marker being recorded (if we're currently recording one).
  std::string pending_clock_sync_marker_;

  // The time at which the last clock sync marker was recorded.
  base::TimeTicks last_clock_sync_time_;

  // Checker to make sure that this is only ever called on the IO thread.
  base::ThreadChecker thread_checker_;

  // The BattOr's EEPROM (which is required for calibration).
  scoped_ptr<BattOrEEPROM> battor_eeprom_;

  // The first frame (required for calibration).
  std::vector<RawBattOrSample> calibration_frame_;

  // The actual data samples recorded.
  std::vector<RawBattOrSample> samples_;

  // The expected sequence number of the next frame. We use this to ensure that
  // we receive frames in order.
  uint32_t next_sequence_number_;

  // The number of times that we've attempted to read the last message.
  uint8_t num_read_attempts_;

  // The timeout that's run when an action times out.
  base::CancelableClosure timeout_callback_;

  DISALLOW_COPY_AND_ASSIGN(BattOrAgent);
};

}  // namespace battor

#endif  // TOOLS_BATTOR_AGENT_BATTOR_AGENT_H_