PX4 Firmware
PX4 Autopilot Software http://px4.io
mavlink_log_handler.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2014-2016 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /// @file mavlink_log_handler.h
35 /// @author px4dev, Gus Grubba <mavlink@grubba.com>
36 
37 #include "mavlink_log_handler.h"
38 #include "mavlink_main.h"
39 #include <sys/stat.h>
40 #include <time.h>
41 
42 #define MOUNTPOINT PX4_STORAGEDIR
43 
44 static const char *kLogRoot = MOUNTPOINT "/log";
45 static const char *kLogData = MOUNTPOINT "/logdata.txt";
46 static const char *kTmpData = MOUNTPOINT "/$log$.txt";
47 
48 #ifdef __PX4_NUTTX
49 #define PX4LOG_REGULAR_FILE DTYPE_FILE
50 #define PX4LOG_DIRECTORY DTYPE_DIRECTORY
51 #else
52 #define PX4LOG_REGULAR_FILE DT_REG
53 #define PX4LOG_DIRECTORY DT_DIR
54 #endif
55 
56 //#define MAVLINK_LOG_HANDLER_VERBOSE
57 
58 #ifdef MAVLINK_LOG_HANDLER_VERBOSE
59 #define PX4LOG_WARN(fmt, ...) warnx(fmt, ##__VA_ARGS__)
60 #else
61 #define PX4LOG_WARN(fmt, ...)
62 #endif
63 
64 //-------------------------------------------------------------------
65 static bool
66 stat_file(const char *file, time_t *date = nullptr, uint32_t *size = nullptr)
67 {
68  struct stat st;
69 
70  if (stat(file, &st) == 0) {
71  if (date) { *date = st.st_mtime; }
72 
73  if (size) { *size = st.st_size; }
74 
75  return true;
76  }
77 
78  return false;
79 }
80 
81 //-------------------------------------------------------------------
83  : _pLogHandlerHelper(nullptr),
84  _mavlink(mavlink)
85 {
86 
87 }
88 
89 //-------------------------------------------------------------------
90 void
91 MavlinkLogHandler::handle_message(const mavlink_message_t *msg)
92 {
93  switch (msg->msgid) {
94  case MAVLINK_MSG_ID_LOG_REQUEST_LIST:
95  _log_request_list(msg);
96  break;
97 
98  case MAVLINK_MSG_ID_LOG_REQUEST_DATA:
99  _log_request_data(msg);
100  break;
101 
102  case MAVLINK_MSG_ID_LOG_ERASE:
103  _log_request_erase(msg);
104  break;
105 
106  case MAVLINK_MSG_ID_LOG_REQUEST_END:
107  _log_request_end(msg);
108  break;
109  }
110 }
111 
112 //-------------------------------------------------------------------
113 unsigned
115 {
116  //-- Sending Log Entries
118  return MAVLINK_MSG_ID_LOG_ENTRY_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
119  //-- Sending Log Data (contents of one of the log files)
120 
122  return MAVLINK_MSG_ID_LOG_DATA_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
123  //-- Idle
124 
125  } else {
126  return 0;
127  }
128 }
129 
130 //-------------------------------------------------------------------
131 void
133 {
134  //-- An arbitrary count of max bytes in one go (one of the two below but never both)
135 #define MAX_BYTES_SEND 256 * 1024
136  size_t count = 0;
137 
138  //-- Log Entries
140  && _mavlink->get_free_tx_buf() > get_size() && count < MAX_BYTES_SEND) {
141  count += _log_send_listing();
142  }
143 
144  //-- Log Data
146  && _mavlink->get_free_tx_buf() > get_size() && count < MAX_BYTES_SEND) {
147  count += _log_send_data();
148  }
149 }
150 
151 //-------------------------------------------------------------------
152 void
153 MavlinkLogHandler::_log_request_list(const mavlink_message_t *msg)
154 {
155  mavlink_log_request_list_t request;
156  mavlink_msg_log_request_list_decode(msg, &request);
157 
158  //-- Check for re-requests (data loss) or new request
159  if (_pLogHandlerHelper) {
161 
162  //-- Is this a new request?
163  if ((request.end - request.start) > _pLogHandlerHelper->log_count) {
164  delete _pLogHandlerHelper;
165  _pLogHandlerHelper = nullptr;
166  }
167  }
168 
169  if (!_pLogHandlerHelper) {
170  //-- Prepare new request
172  }
173 
175  //-- Define (and clamp) range
176  _pLogHandlerHelper->next_entry = request.start < _pLogHandlerHelper->log_count ? request.start :
178  _pLogHandlerHelper->last_entry = request.end < _pLogHandlerHelper->log_count ? request.end :
180  }
181 
182  PX4LOG_WARN("\nMavlinkLogHandler::_log_request_list: start: %u last: %u count: %u\n",
186  //-- Enable streaming
188 }
189 
190 //-------------------------------------------------------------------
191 void
192 MavlinkLogHandler::_log_request_data(const mavlink_message_t *msg)
193 {
194  //-- If we haven't listed, we can't do much
195  if (!_pLogHandlerHelper) {
196  PX4LOG_WARN("MavlinkLogHandler::_log_request_data Log request with no list requested.\n");
197  return;
198  }
199 
200  mavlink_log_request_data_t request;
201  mavlink_msg_log_request_data_decode(msg, &request);
202 
203  //-- Does the requested log exist?
204  if (request.id >= _pLogHandlerHelper->log_count) {
205  PX4LOG_WARN("MavlinkLogHandler::_log_request_data Requested log %u but we only have %u.\n", request.id,
207  return;
208  }
209 
210  //-- If we were sending log entries, stop it
212 
213  if (_pLogHandlerHelper->current_log_index != request.id) {
214  //-- Init send log dataset
217  uint32_t time_utc = 0;
218 
220  time_utc,
222  PX4LOG_WARN("LogListHelper::get_entry failed.\n");
223  return;
224  }
225 
227  }
228 
230 
233 
234  } else {
236  }
237 
238  if (_pLogHandlerHelper->current_log_data_remaining > request.count) {
240  }
241 
242  //-- Enable streaming
244 }
245 
246 //-------------------------------------------------------------------
247 void
248 MavlinkLogHandler::_log_request_erase(const mavlink_message_t * /*msg*/)
249 {
250  /*
251  mavlink_log_erase_t request;
252  mavlink_msg_log_erase_decode(msg, &request);
253  */
254  if (_pLogHandlerHelper) {
255  delete _pLogHandlerHelper;
256  _pLogHandlerHelper = nullptr;
257  }
258 
259  //-- Delete all logs
261 }
262 
263 //-------------------------------------------------------------------
264 void
265 MavlinkLogHandler::_log_request_end(const mavlink_message_t * /*msg*/)
266 {
267  PX4LOG_WARN("MavlinkLogHandler::_log_request_end\n");
268 
269  if (_pLogHandlerHelper) {
270  delete _pLogHandlerHelper;
271  _pLogHandlerHelper = nullptr;
272  }
273 }
274 
275 //-------------------------------------------------------------------
276 size_t
278 {
279  mavlink_log_entry_t response;
280  uint32_t size, date;
282  response.size = size;
283  response.time_utc = date;
284  response.id = _pLogHandlerHelper->next_entry;
285  response.num_logs = _pLogHandlerHelper->log_count;
286  response.last_log_num = _pLogHandlerHelper->last_entry;
287  mavlink_msg_log_entry_send_struct(_mavlink->get_channel(), &response);
288 
289  //-- If we're done listing, flag it.
292 
293  } else {
295  }
296 
297  PX4LOG_WARN("MavlinkLogHandler::_log_send_listing id: %u count: %u last: %u size: %u date: %u status: %d\n",
298  response.id,
299  response.num_logs,
300  response.last_log_num,
301  response.size,
302  response.time_utc,
304  return sizeof(response);
305 }
306 
307 //-------------------------------------------------------------------
308 size_t
310 {
311  mavlink_log_data_t response;
312  memset(&response, 0, sizeof(response));
314 
315  if (len > sizeof(response.data)) {
316  len = sizeof(response.data);
317  }
318 
319  size_t read_size = _pLogHandlerHelper->get_log_data(len, response.data);
321  response.id = _pLogHandlerHelper->current_log_index;
322  response.count = read_size;
323  mavlink_msg_log_data_send_struct(_mavlink->get_channel(), &response);
326 
327  if (read_size < sizeof(response.data) || _pLogHandlerHelper->current_log_data_remaining == 0) {
329  }
330 
331  return sizeof(response);
332 }
333 
334 //-------------------------------------------------------------------
336  : next_entry(0)
337  , last_entry(0)
338  , log_count(0)
339  , current_status(LOG_HANDLER_IDLE)
340  , current_log_index(UINT16_MAX)
341  , current_log_size(0)
342  , current_log_data_offset(0)
343  , current_log_data_remaining(0)
344  , current_log_filep(nullptr)
345 {
346  _init();
347 }
348 
349 //-------------------------------------------------------------------
351 {
352  // Remove log data files (if any)
353  unlink(kLogData);
354  unlink(kTmpData);
355 }
356 
357 //-------------------------------------------------------------------
358 bool
359 LogListHelper::get_entry(int idx, uint32_t &size, uint32_t &date, char *filename, int filename_len)
360 {
361  //-- Find log file in log list file created during init()
362  size = 0;
363  date = 0;
364  bool result = false;
365  //-- Open list of log files
366  FILE *f = ::fopen(kLogData, "r");
367 
368  if (f) {
369  //--- Find requested entry
370  char line[160];
371  int count = 0;
372 
373  while (fgets(line, sizeof(line), f)) {
374  //-- Found our "index"
375  if (count++ == idx) {
376  char file[160];
377 
378  if (sscanf(line, "%u %u %s", &date, &size, file) == 3) {
379  if (filename && filename_len > 0) {
380  strncpy(filename, file, filename_len);
381  filename[filename_len - 1] = 0; // ensure null-termination
382  }
383 
384  result = true;
385  break;
386  }
387  }
388  }
389 
390  fclose(f);
391  }
392 
393  return result;
394 }
395 
396 //-------------------------------------------------------------------
397 bool
399 {
400  if (current_log_filep) {
401  ::fclose(current_log_filep);
402  current_log_filep = nullptr;
403  }
404 
405  current_log_filep = ::fopen(current_log_filename, "rb");
406 
407  if (!current_log_filep) {
408  PX4LOG_WARN("MavlinkLogHandler::open_for_transmit Could not open %s\n", current_log_filename);
409  return false;
410  }
411 
412  return true;
413 }
414 
415 //-------------------------------------------------------------------
416 size_t
417 LogListHelper::get_log_data(uint8_t len, uint8_t *buffer)
418 {
419  if (!current_log_filename[0]) {
420  return 0;
421  }
422 
423  if (!current_log_filep) {
424  PX4LOG_WARN("MavlinkLogHandler::get_log_data file not open %s\n", current_log_filename);
425  return 0;
426  }
427 
428  long int offset = current_log_data_offset - ftell(current_log_filep);
429 
430  if (offset && fseek(current_log_filep, offset, SEEK_CUR)) {
431  fclose(current_log_filep);
432  current_log_filep = nullptr;
433  PX4LOG_WARN("MavlinkLogHandler::get_log_data Seek error in %s\n", current_log_filename);
434  return 0;
435  }
436 
437  size_t result = fread(buffer, 1, len, current_log_filep);
438  return result;
439 }
440 
441 //-------------------------------------------------------------------
442 void
444 {
445  /*
446 
447  When this helper is created, it scans the log directory
448  and collects all log files found into one file for easy,
449  subsequent access.
450  */
451 
452  current_log_filename[0] = 0;
453  // Remove old log data file (if any)
454  unlink(kLogData);
455  // Open log directory
456  DIR *dp = opendir(kLogRoot);
457 
458  if (dp == nullptr) {
459  // No log directory. Nothing to do.
460  return;
461  }
462 
463  // Create work file
464  FILE *f = ::fopen(kTmpData, "w");
465 
466  if (!f) {
467  PX4LOG_WARN("MavlinkLogHandler::init Error creating %s\n", kTmpData);
468  closedir(dp);
469  return;
470  }
471 
472  // Scan directory and collect log files
473  struct dirent *result = nullptr;
474 
475  while ((result = readdir(dp))) {
476  if (result->d_type == PX4LOG_DIRECTORY) {
477  time_t tt = 0;
478  char log_path[128];
479  int ret = snprintf(log_path, sizeof(log_path), "%s/%s", kLogRoot, result->d_name);
480  bool path_is_ok = (ret > 0) && (ret < (int)sizeof(log_path));
481 
482  if (path_is_ok) {
483  if (_get_session_date(log_path, result->d_name, tt)) {
484  _scan_logs(f, log_path, tt);
485  }
486  }
487  }
488  }
489 
490  closedir(dp);
491  fclose(f);
492 
493  // Rename temp file to data file
494  if (rename(kTmpData, kLogData)) {
495  PX4LOG_WARN("MavlinkLogHandler::init Error renaming %s\n", kTmpData);
496  log_count = 0;
497  }
498 }
499 
500 //-------------------------------------------------------------------
501 bool
502 LogListHelper::_get_session_date(const char *path, const char *dir, time_t &date)
503 {
504  if (strlen(dir) > 4) {
505  // Always try to get file time first
506  if (stat_file(path, &date)) {
507  // Try to prevent taking date if it's around 1970 (use the logic below instead)
508  if (date > 60 * 60 * 24) {
509  return true;
510  }
511  }
512 
513  // Convert "sess000" to 00:00 Jan 1 1970 (day per session)
514  if (strncmp(dir, "sess", 4) == 0) {
515  unsigned u;
516 
517  if (sscanf(&dir[4], "%u", &u) == 1) {
518  date = u * 60 * 60 * 24;
519  return true;
520  }
521  }
522  }
523 
524  return false;
525 }
526 
527 //-------------------------------------------------------------------
528 void
529 LogListHelper::_scan_logs(FILE *f, const char *dir, time_t &date)
530 {
531  DIR *dp = opendir(dir);
532 
533  if (dp) {
534  struct dirent *result = nullptr;
535 
536  while ((result = readdir(dp))) {
537  if (result->d_type == PX4LOG_REGULAR_FILE) {
538  time_t ldate = date;
539  uint32_t size = 0;
540  char log_file_path[128];
541  int ret = snprintf(log_file_path, sizeof(log_file_path), "%s/%s", dir, result->d_name);
542  bool path_is_ok = (ret > 0) && (ret < (int)sizeof(log_file_path));
543 
544  if (path_is_ok) {
545  if (_get_log_time_size(log_file_path, result->d_name, ldate, size)) {
546  //-- Write result->out to list file
547  fprintf(f, "%u %u %s\n", (unsigned)ldate, (unsigned)size, log_file_path);
548  log_count++;
549  }
550  }
551  }
552  }
553 
554  closedir(dp);
555  }
556 }
557 
558 //-------------------------------------------------------------------
559 bool
560 LogListHelper::_get_log_time_size(const char *path, const char *file, time_t &date, uint32_t &size)
561 {
562  if (file && file[0]) {
563  if (strstr(file, ".px4log") || strstr(file, ".ulg")) {
564  // Always try to get file time first
565  if (stat_file(path, &date, &size)) {
566  // Try to prevent taking date if it's around 1970 (use the logic below instead)
567  if (date > 60 * 60 * 24) {
568  return true;
569  }
570  }
571 
572  // Convert "log000" to 00:00 (minute per flight in session)
573  if (strncmp(file, "log", 3) == 0) {
574  unsigned u;
575 
576  if (sscanf(&file[3], "%u", &u) == 1) {
577  date += (u * 60);
578 
579  if (stat_file(path, nullptr, &size)) {
580  return true;
581  }
582  }
583  }
584  }
585  }
586 
587  return false;
588 }
589 
590 //-------------------------------------------------------------------
591 void
593 {
594  //-- Open log directory
595  DIR *dp = opendir(dir);
596 
597  if (dp == nullptr) {
598  return;
599  }
600 
601  struct dirent *result = nullptr;
602 
603  while ((result = readdir(dp))) {
604  // no more entries?
605  if (result == nullptr) {
606  break;
607  }
608 
609  if (result->d_type == PX4LOG_DIRECTORY && result->d_name[0] != '.') {
610  char log_path[128];
611  int ret = snprintf(log_path, sizeof(log_path), "%s/%s", dir, result->d_name);
612  bool path_is_ok = (ret > 0) && (ret < (int)sizeof(log_path));
613 
614  if (path_is_ok) {
615  LogListHelper::delete_all(log_path);
616 
617  if (rmdir(log_path)) {
618  PX4LOG_WARN("MavlinkLogHandler::delete_all Error removing %s\n", log_path);
619  }
620  }
621  }
622 
623  if (result->d_type == PX4LOG_REGULAR_FILE) {
624  char log_path[128];
625  int ret = snprintf(log_path, sizeof(log_path), "%s/%s", dir, result->d_name);
626  bool path_is_ok = (ret > 0) && (ret < (int)sizeof(log_path));
627 
628  if (path_is_ok) {
629  if (unlink(log_path)) {
630  PX4LOG_WARN("MavlinkLogHandler::delete_all Error deleting %s\n", log_path);
631  }
632  }
633  }
634  }
635 
636  closedir(dp);
637 }
static void delete_all(const char *dir)
uint16_t current_log_index
uint32_t current_log_data_remaining
char current_log_filename[128]
size_t get_log_data(uint8_t len, uint8_t *buffer)
bool _get_log_time_size(const char *path, const char *file, time_t &date, uint32_t &size)
static char msg[NUM_MSG][CONFIG_USART1_TXBUFSIZE]
Definition: px4io.c:89
bool get_entry(int idx, uint32_t &size, uint32_t &date, char *filename=0, int filename_len=0)
bool _get_session_date(const char *path, const char *dir, time_t &date)
void _scan_logs(FILE *f, const char *dir, time_t &date)
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
uint32_t current_log_data_offset
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
uint32_t current_log_size
struct @83::@85::@87 file