API
telemeter.hpp
Go to the documentation of this file.
1 /** \file telemeter.hpp
2  * \author Jared R. Males
3  * \brief Configuration and control of a telemetry logger
4  *
5  * \ingroup app_files
6  *
7  */
8 
9 #ifndef app_telemeter_hpp
10 #define app_telemeter_hpp
11 
12 namespace MagAOX
13 {
14 namespace app
15 {
16 namespace dev
17 {
18 
19 /// A device which saves telemetry.
20 /**
21  * The `derivedT` class must be a MagAOXApp, and must implement the following interface:
22  *
23  * 1) `int checkRecordTimes()`
24  * This function MUST be implemented as follows:
25  \code
26  int checkRecordTimes()
27  {
28  return dev::telemeter<derivedT>::checkRecordTimes( telem_type1(), telem_type2(), ..., telem_typeN());
29  }
30  \endcode
31  * where there is one constructor-call argument for each telemetry log type recorded by this device. The resultant objects are not used, rather the types
32  * are used for variadic template resolution.
33  *
34  * 2) Next, there must be one overload of the function
35  \code
36  int recordTelem( telem_type1 * )
37  {
38  return m_tel<telem_type1>( { message entered here } );
39  }
40  \endcode
41  * for each of the telemetry types. You MUST NOT use the pointer argument, it is for type resolution only -- you should fill in the telemetry log
42  * message using internal values. Note that
43  * calls to this function should result in a telemetry log entry every time -- it is called when the minimum interval has elapsed since the last entry.
44  *
45  * Additionally, calls to this classes setupConfig, `loadConfig`, `appStartup`, `appLogic`, and `appShutdown` should be placed
46  * in the corresponding function of `derivedT`, with error checking.
47  *
48  * \ingroup appdev
49  */
50 template<class derivedT>
51 struct telemeter
52 {
53  ///The log manager type.
55 
57 
58  double m_maxInterval {10.0}; ///< The maximum interval, in seconds, between telemetry records. Default is 10.0 seconds.
59 
61 
62  /// Make a telemetry recording
63  /** Wrapper for logManager::log, which updates telT::lastRecord.
64  *
65  * \tparam logT the log entry type
66  * \tparam retval the value returned by this method.
67  *
68  */
69  template<typename telT>
70  int telem( const typename telT::messageT & msg /**< [in] the data to log */);
71 
72  /// Make a telemetry recording, for an empty record
73  /** Wrapper for logManager::log, which updates telT::lastRecord.
74  *
75  * \tparam logT the log entry type
76  * \tparam retval the value returned by this method.
77  *
78  */
79  template<typename telT>
80  int telem();
81 
82  ///Setup an application configurator for the device section
83  /**
84  * \returns 0 on success.
85  * \returns -1 on error (nothing implemented yet)
86  */
87  int setupConfig( appConfigurator & config /**< [in] an application configuration to setup */);
88 
89  ///Load the device section from an application configurator
90  /**
91  *
92  * \returns 0 on success
93  * \returns -1 on error (nothing implemented yet)
94  */
95  int loadConfig( appConfigurator & config /**< [in] an application configuration from which to load values */);
96 
97  /// Starts the telemetry log thread.
98  /**
99  * This should be called from `derivedT::appStartup`
100  *
101  * \returns 0 on success
102  * \returns -1 on error
103  */
104  int appStartup();
105 
106  /// Perform `telemeter` application logic
107  /** This calls `derivedT::checkRecordTimes()`, and should be called from `derivedT::appLogic`, but only
108  * when the FSM is in states where telemetry logging makes sense.
109  *
110  * \returns 0 on success
111  * \returns -1 on error
112  */
113  int appLogic();
114 
115  /// Perform `telemeter` application shutdown
116  /** This currently does nothing.
117  *
118  * \returns 0 on success
119  * \returns -1 on error
120  */
121  int appShutdown();
122 
123  /// Check the time of the last record for each telemetry type and make an entry if needed
124  /** This must be called from `derivedT::checkRecordTimes()`, with one template parameter
125  * for ach telemetry log type being recorded.
126  *
127  * \returns 0 on succcess
128  * \returns -1 on error
129  */
130  template<class telT, class... telTs>
131  int checkRecordTimes( const telT & tel, ///< [in] [unused] object of the telemetry type to record
132  telTs... tels ///< [in] [unused] objects of the additional telemetry types to record
133  );
134 
135  /// Worker function to actually perform the record time checking logic
136  /** Recursively calls itself until the variadic template list is exhausted.
137  *
138  * \returns 0 on succcess
139  * \returns -1 on error
140  */
141  template<class telT, class... telTs>
142  int checkRecordTimes( timespec & ts, ///<[in] [unused] the timestamp that records are compared to
143  const telT & tel, ///< [in] [unused] objects of the telemetry type to record
144  telTs... tels ///< [in] [unused] objects of the additional telemetry types to record
145  );
146 
147  /// Empty function called at the end of the template list
148  /**
149  * \returns 0 on succcess
150  * \returns -1 on error
151  */
152  int checkRecordTimes(timespec & ts /**<[in] [unused] the timestamp that records are compared to */);
153 
154 private:
155 
156  /// Access the derived class.
157  derivedT & derived()
158  {
159  return *static_cast<derivedT *>(this);
160  }
161 };
162 
163 template<class derivedT>
165 {
166 }
167 
168 template<class derivedT>
169 template<typename telT>
170 int telemeter<derivedT>::telem( const typename telT::messageT & msg)
171 {
172 
173  m_tel.template log<telT>(msg, logPrio::LOG_TELEM);
174 
175  //Set timestamp
176  clock_gettime(CLOCK_REALTIME, &telT::lastRecord);
177 
178  return 0;
179 }
180 
181 template<class derivedT>
182 template<typename telT>
184 {
185 
186  m_tel.template log<telT>(logPrio::LOG_TELEM);
187 
188  //Set timestamp
189  clock_gettime(CLOCK_REALTIME, &telT::lastRecord);
190 
191  return 0;
192 }
193 
194 template<class derivedT>
195 int telemeter<derivedT>::setupConfig( mx::app::appConfigurator & config )
196 {
197  m_tel.m_configSection = "telemeter";
198 
199 
200  m_tel.setupConfig(config);
201 
202  config.add("telemeter.maxInterval", "", "telemeter.maxInterval", argType::Required, "telemeter", "maxInterval", false, "double", "The maximum interval, in seconds, between telemetry records. Default is 10.0 seconds.");
203 
204  return 0;
205 }
206 
207 template<class derivedT>
208 int telemeter<derivedT>::loadConfig( mx::app::appConfigurator & config )
209 {
210  m_tel.m_logLevel = logPrio::LOG_TELEM;
211 
212  m_tel.logPath(std::string(derived().MagAOXPath) + "/" + MAGAOX_telRelPath);
213 
214  m_tel.logExt("bintel");
215 
216  m_tel.logName(derived().m_configName);
217 
218  m_tel.loadConfig(config);
219 
220  config(m_maxInterval, "telemeter.maxInterval");
221 
222  return 0;
223 }
224 
225 template<class derivedT>
227 {
228  //----------------------------------------//
229  // Begin the telemetry system
230  //----------------------------------------//
231 
232  m_tel.logThreadStart();
233 
234  //Give up to 2 secs to make sure log thread has time to get started and try to open a file.
235  int w = 0;
236  while(m_tel.logThreadRunning() == false && w < 20)
237  {
238  //Sleep for 100 msec
239  std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>(100000000));
240  ++w;
241  }
242 
243  if(m_tel.logThreadRunning() == false)
244  {
245  derivedT::template log<software_critical>({__FILE__, __LINE__, "telemetry thread not running. exiting."});
246  return -1;
247  }
248 
249  return 0;
250 }
251 
252 template<class derivedT>
254 {
255  return derived().checkRecordTimes();
256 }
257 
258 template<class derivedT>
260 {
261  return 0;
262 }
263 
264 template<class derivedT>
265 template<class telT, class... telTs>
266 int telemeter<derivedT>::checkRecordTimes(const telT & tel, telTs... tels)
267 {
268  timespec ts;
269 
270  clock_gettime(CLOCK_REALTIME, &ts);
271  return checkRecordTimes(ts, tel, tels...);
272 
273 }
274 
275 template<class derivedT>
276 template<class telT, class... telTs>
277 int telemeter<derivedT>::checkRecordTimes( timespec & ts, const telT & tel, telTs... tels)
278 {
279  //Check if it's been more than maxInterval seconds since the last record. This is corrected for the pause of the main loop.
280  if( ( (double) ts.tv_sec - ((double) ts.tv_nsec)/1e9)- ((double) telT::lastRecord.tv_sec - ((double) telT::lastRecord.tv_nsec)/1e9) > m_maxInterval - ((double)derived().m_loopPause) / 1e9)
281  {
282  derived().recordTelem( &tel );
283  }
284 
285  return checkRecordTimes(ts, tels...);
286 
287 }
288 
289 template<class derivedT>
291 {
292  static_cast<void>(ts); //be unused
293 
294  return 0;
295 }
296 
297 } //namespace dev
298 } //namespace tty
299 } //namespace MagAOX
300 
301 #endif //tty_telemeter_hpp
#define MAGAOX_telRelPath
The relative path to the telemetry directory.
Definition: paths.hpp:57
std::stringstream msg
Definition: dm.hpp:24
constexpr static logPrioT LOG_TELEM
A telemetry recording.
Definition: logPriority.hpp:58
A device which saves telemetry.
Definition: telemeter.hpp:52
double m_maxInterval
The maximum interval, in seconds, between telemetry records. Default is 10.0 seconds.
Definition: telemeter.hpp:58
int telem()
Make a telemetry recording, for an empty record.
Definition: telemeter.hpp:183
int appShutdown()
Perform telemeter application shutdown.
Definition: telemeter.hpp:259
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
Definition: telemeter.hpp:208
int telem(const typename telT::messageT &msg)
Make a telemetry recording.
Definition: telemeter.hpp:170
int appLogic()
Perform telemeter application logic.
Definition: telemeter.hpp:253
logger::logManager< derivedT, logFileRaw > logManagerT
The log manager type.
Definition: telemeter.hpp:54
int checkRecordTimes(timespec &ts)
Empty function called at the end of the template list.
Definition: telemeter.hpp:290
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
Definition: telemeter.hpp:195
int appStartup()
Starts the telemetry log thread.
Definition: telemeter.hpp:226
int checkRecordTimes(timespec &ts, const telT &tel, telTs... tels)
Worker function to actually perform the record time checking logic.
Definition: telemeter.hpp:277
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Definition: telemeter.hpp:266
derivedT & derived()
Access the derived class.
Definition: telemeter.hpp:157