API
 
Loading...
Searching...
No Matches
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
12namespace MagAOX
13{
14namespace app
15{
16namespace dev
17{
18
19/// A device base class which saves telemetry.
20/**
21 * CRTP class `derivedT` has the following requirements:
22 * - Must be a MagAOXApp
23 * - Must include the following friend declaration:
24 * \code
25 * friend class dev::telemeter<DERIVEDNAME>; //replace DERIVEDNAME with derivedT class name
26 * \endcode
27 * - Must include the following typedef:
28 * \code
29 * typedef dev::telemeter<DERIVEDNAME> telemeterT; //replace DERIVEDNAME with derivedT class name
30 * \endcode
31 * - Must implement the following interface:
32 * \code
33 * int checkRecordTimes()
34 * {
35 * // Must call this variadic template function with each relevant telemetry type exactly like this
36 * return telemeterT::checkRecordTimes( telem_type1(), telem_type2(), ..., telem_typeN());
37 * }
38 * \endcode
39 * where there is one constructor-call argument for each telemetry log type recorded by this device. The resultant
40 * objects are not used, rather the types are just used for variadic template resolution.
41 *
42 * - Must provide one overload of the following function for each telemetry type:
43 * \code
44 * int recordTelem( const telem_type1 * )
45 * {
46 * //DO NOT USE telem_type1
47 * return m_tel<telem_type1>( { message entered here } );
48 * }
49 * \endcode
50 * You MUST NOT use the pointer argument, it is for type resolution only -- you
51 * should fill in the telemetry log message using internal values. Note that calls to this function should result
52 * in a telemetry log entry every time -- it is called when the minimum interval has elapsed since the last entry.
53 *
54 * - Must call this class's setupConfig(), loadConfig(), appStartup(), appLogic(), and appShutdown()
55 * in the corresponding function of `derivedT`, with error checking.
56 * For convenience the following macros are defined to provide error checking:
57 * \code
58 * TELEMETER_SETUP_CONFIG( cfig )
59 * TELEMETER_LOAD_CONFIG( cfig )
60 * TELEMETER_APP_STARTUP
61 * TELEMETER_APP_LOGIC
62 * TELEMETER_APP_SHUTDOWN
63 * \endcode
64 *
65 * \ingroup appdev
66 */
67template <class derivedT>
69{
71
72 /// The log manager type.
74
76
77 double m_maxInterval{10.0}; ///< The maximum interval, in seconds, between telemetry records. Default is 10.0 seconds.
78
80
81 /// Make a telemetry recording
82 /** Wrapper for logManager::log, which updates telT::lastRecord.
83 *
84 * \tparam logT the log entry type
85 * \tparam retval the value returned by this method.
86 *
87 */
88 template <typename telT>
89 int telem(const typename telT::messageT &msg /**< [in] the data to log */);
90
91 /// Make a telemetry recording, for an empty record
92 /** Wrapper for logManager::log, which updates telT::lastRecord.
93 *
94 * \tparam logT the log entry type
95 * \tparam retval the value returned by this method.
96 *
97 */
98 template <typename telT>
99 int telem();
100
101 /// Setup an application configurator for the device section
102 /**
103 * \returns 0 on success.
104 * \returns -1 on error (nothing implemented yet)
105 */
106 int setupConfig(appConfigurator &config /**< [in] an application configuration to setup */);
107
108 /// Load the device section from an application configurator
109 /**
110 *
111 * \returns 0 on success
112 * \returns -1 on error (nothing implemented yet)
113 */
114 int loadConfig(appConfigurator &config /**< [in] an application configuration from which to load values */);
115
116 /// Starts the telemetry log thread.
117 /**
118 * This should be called from `derivedT::appStartup`
119 *
120 * \returns 0 on success
121 * \returns -1 on error
122 */
124
125 /// Perform `telemeter` application logic
126 /** This calls `derivedT::checkRecordTimes()`, and should be called from `derivedT::appLogic`, but only
127 * when the FSM is in states where telemetry logging makes sense.
128 *
129 * \returns 0 on success
130 * \returns -1 on error
131 */
132 int appLogic();
133
134 /// Perform `telemeter` application shutdown
135 /** This currently does nothing.
136 *
137 * \returns 0 on success
138 * \returns -1 on error
139 */
141
142 /// Check the time of the last record for each telemetry type and make an entry if needed
143 /** This must be called from `derivedT::checkRecordTimes()`, with one template parameter
144 * for ach telemetry log type being recorded.
145 *
146 * \returns 0 on succcess
147 * \returns -1 on error
148 */
149 template <class telT, class... telTs>
150 int checkRecordTimes(const telT &tel, ///< [in] [unused] object of the telemetry type to record
151 telTs... tels ///< [in] [unused] objects of the additional telemetry types to record
152 );
153
154 /// Worker function to actually perform the record time checking logic
155 /** Recursively calls itself until the variadic template list is exhausted.
156 *
157 * \returns 0 on succcess
158 * \returns -1 on error
159 */
160 template <class telT, class... telTs>
161 int checkRecordTimes(timespec &ts, ///<[in] [unused] the timestamp that records are compared to
162 const telT &tel, ///< [in] [unused] objects of the telemetry type to record
163 telTs... tels ///< [in] [unused] objects of the additional telemetry types to record
164 );
165
166 /// Empty function called at the end of the template list
167 /**
168 * \returns 0 on succcess
169 * \returns -1 on error
170 */
171 int checkRecordTimes(timespec &ts /**<[in] [unused] the timestamp that records are compared to */);
172
173private:
174 /// Access the derived class.
175 derivedT &derived()
176 {
177 return *static_cast<derivedT *>(this);
178 }
179};
180
181template <class derivedT>
185
186template <class derivedT>
187template <typename telT>
188int telemeter<derivedT>::telem(const typename telT::messageT &msg)
189{
190
191 m_tel.template log<telT>(msg, logPrio::LOG_TELEM);
192
193 // Set timestamp
194 clock_gettime(CLOCK_REALTIME, &telT::lastRecord);
195
196 return 0;
197}
198
199template <class derivedT>
200template <typename telT>
202{
203
204 m_tel.template log<telT>(logPrio::LOG_TELEM);
205
206 // Set timestamp
207 clock_gettime(CLOCK_REALTIME, &telT::lastRecord);
208
209 return 0;
210}
211
212template <class derivedT>
213int telemeter<derivedT>::setupConfig(mx::app::appConfigurator &config)
214{
215 m_tel.m_configSection = "telemeter";
216
217 m_tel.setupConfig(config);
218
219 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.");
220
221 return 0;
222}
223
224template <class derivedT>
225int telemeter<derivedT>::loadConfig(mx::app::appConfigurator &config)
226{
227 m_tel.m_logLevel = logPrio::LOG_TELEM;
228
229 // Setup default log path
230 std::string tmpstr = mx::sys::getEnv( MAGAOX_env_telem );
231 if( tmpstr == "" )
232 {
233 tmpstr = MAGAOX_telRelPath;
234 }
235 m_tel.logPath(std::string(derived().basePath()) + "/" + tmpstr);
236
237 m_tel.logExt("bintel");
238
239 m_tel.logName(derived().m_configName);
240
241 m_tel.loadConfig(config);
242
243 config(m_maxInterval, "telemeter.maxInterval");
244
245 return 0;
246}
247
248template <class derivedT>
250{
251 //----------------------------------------//
252 // Begin the telemetry system
253 //----------------------------------------//
254
255 m_tel.logThreadStart();
256
257 // Give up to 2 secs to make sure log thread has time to get started and try to open a file.
258 int w = 0;
259 while (m_tel.logThreadRunning() == false && w < 20)
260 {
261 // Sleep for 100 msec
262 std::this_thread::sleep_for(std::chrono::duration<unsigned long, std::nano>(100000000));
263 ++w;
264 }
265
266 if (m_tel.logThreadRunning() == false)
267 {
268 derivedT::template log<software_critical>({__FILE__, __LINE__, "telemetry thread not running. exiting."});
269 return -1;
270 }
271
272 return 0;
273}
274
275template <class derivedT>
277{
278 return derived().checkRecordTimes();
279}
280
281template <class derivedT>
283{
284 return 0;
285}
286
287template <class derivedT>
288template <class telT, class... telTs>
289int telemeter<derivedT>::checkRecordTimes(const telT &tel, telTs... tels)
290{
291 timespec ts;
292
293 clock_gettime(CLOCK_REALTIME, &ts);
294 return checkRecordTimes(ts, tel, tels...);
295}
296
297template <class derivedT>
298template <class telT, class... telTs>
299int telemeter<derivedT>::checkRecordTimes(timespec &ts, const telT &tel, telTs... tels)
300{
301 // Check if it's been more than maxInterval seconds since the last record. This is corrected for the pause of the main loop.
302 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)
303 {
304 derived().recordTelem(&tel);
305 }
306
307 return checkRecordTimes(ts, tels...);
308}
309
310template <class derivedT>
312{
313 static_cast<void>(ts); // be unused
314
315 return 0;
316}
317
318/// Call telemeter::setupConfig with error checking
319/**
320 * \param cfig the application configurator
321 */
322#define TELEMETER_SETUP_CONFIG( cfig ) \
323 if (telemeterT::setupConfig( cfig) < 0) \
324 { \
325 log<software_error>({__FILE__, __LINE__, "Error from telemeterT::setupConfig"}); \
326 m_shutdown = true; \
327 }
328
329/// Call telemeter::loadConfig with error checking
330/** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
331 * \param cfig the application configurator
332 */
333#define TELEMETER_LOAD_CONFIG( cfig ) \
334 if (telemeterT::loadConfig(cfig) < 0) \
335 { \
336 return log<software_error, -1>({__FILE__, __LINE__, "Error from telemeterT::loadConfig"}); \
337 }
338
339/// Call telemeter::appStartup with error checking
340#define TELEMETER_APP_STARTUP \
341 if (telemeterT::appStartup() < 0) \
342 { \
343 return log<software_error, -1>({__FILE__, __LINE__}); \
344 }
345
346/// Call telemeter::appLogic with error checking
347#define TELEMETER_APP_LOGIC \
348 if (telemeterT::appLogic() < 0) \
349 { \
350 return log<software_error, -1>({__FILE__, __LINE__}); \
351 }
352
353/// Call telemeter::appShutdown with error checking
354#define TELEMETER_APP_SHUTDOWN \
355 if (telemeterT::appShutdown() < 0) \
356 { \
357 log<software_error>({__FILE__, __LINE__, "error from telemeterT::appShutdown"}); \
358 }
359
360} // namespace dev
361} // namespace tty
362} // namespace MagAOX
363
364#endif // tty_telemeter_hpp
#define XWC_DEFAULT_VERBOSITY
Definition defaults.hpp:53
#define MAGAOX_telRelPath
The relative path to the telemetry directory.
Definition paths.hpp:57
#define MAGAOX_env_telem
Environment variable setting the relative telem path.
std::stringstream msg
Definition dm.hpp:28
A device base class which saves telemetry.
Definition telemeter.hpp:69
XWC_DEFAULT_VERBOSITY verboseT
Definition telemeter.hpp:70
double m_maxInterval
The maximum interval, in seconds, between telemetry records. Default is 10.0 seconds.
Definition telemeter.hpp:77
int telem()
Make a telemetry recording, for an empty record.
int appShutdown()
Perform telemeter application shutdown.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int telem(const typename telT::messageT &msg)
Make a telemetry recording.
int appLogic()
Perform telemeter application logic.
derivedT & derived()
Access the derived class.
logger::logManager< derivedT, logFileRaw< verboseT > > logManagerT
The log manager type.
Definition telemeter.hpp:73
int checkRecordTimes(timespec &ts)
Empty function called at the end of the template list.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
int appStartup()
Starts the telemetry log thread.
int checkRecordTimes(timespec &ts, const telT &tel, telTs... tels)
Worker function to actually perform the record time checking logic.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
The standard MagAOX log manager, used for both process logs and telemetry streams.