PX4 Firmware
PX4 Autopilot Software http://px4.io
logger.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 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 #include <px4_platform_common/px4_config.h>
35 #include <px4_platform_common/console_buffer.h>
36 #include "logger.h"
37 #include "messages.h"
38 #include "watchdog.h"
39 
40 #include <dirent.h>
41 #include <sys/stat.h>
42 #include <errno.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <time.h>
46 
48 #include <uORB/uORBTopics.h>
51 
52 #include <drivers/drv_hrt.h>
53 #include <mathlib/math/Limits.hpp>
54 #include <px4_platform_common/getopt.h>
55 #include <px4_platform_common/log.h>
56 #include <px4_platform_common/posix.h>
57 #include <px4_platform_common/sem.h>
58 #include <px4_platform_common/shutdown.h>
59 #include <px4_platform_common/tasks.h>
60 #include <systemlib/mavlink_log.h>
61 #include <replay/definitions.hpp>
62 #include <version/version.h>
63 
64 //#define DBGPRINT //write status output every few seconds
65 
66 #if defined(DBGPRINT)
67 // needed for mallinfo
68 #if defined(__PX4_POSIX) && !defined(__PX4_DARWIN)
69 #include <malloc.h>
70 #endif /* __PX4_POSIX */
71 
72 // struct mallinfo not available on OSX?
73 #if defined(__PX4_DARWIN)
74 #undef DBGPRINT
75 #endif /* defined(__PX4_DARWIN) */
76 #endif /* defined(DBGPRINT) */
77 
78 using namespace px4::logger;
79 using namespace time_literals;
80 
81 
83  px4_sem_t semaphore;
84 
86  volatile bool watchdog_triggered = false;
87 };
88 
89 /* This is used to schedule work for the logger (periodic scan for updated topics) */
90 static void timer_callback(void *arg)
91 {
92  /* Note: we are in IRQ context here (on NuttX) */
93 
95 
96  if (watchdog_update(data->watchdog_data)) {
97  data->watchdog_triggered = true;
98  }
99 
100  /* check the value of the semaphore: if the logger cannot keep up with running it's main loop as fast
101  * as the timer_callback here increases the semaphore count, the counter would increase unbounded,
102  * leading to an overflow at some point. This case we want to avoid here, so we check the current
103  * value against a (somewhat arbitrary) threshold, and avoid calling sem_post() if it's exceeded.
104  * (it's not a problem if the threshold is a bit too large, it just means the logger will do
105  * multiple iterations at once, the next time it's scheduled). */
106  int semaphore_value;
107 
108  if (px4_sem_getvalue(&data->semaphore, &semaphore_value) == 0 && semaphore_value > 1) {
109  return;
110  }
111 
112  px4_sem_post(&data->semaphore);
113 
114 }
115 
116 
117 int logger_main(int argc, char *argv[])
118 {
119  // logger currently assumes little endian
120  int num = 1;
121 
122  if (*(char *)&num != 1) {
123  PX4_ERR("Logger only works on little endian!\n");
124  return 1;
125  }
126 
127  return Logger::main(argc, argv);
128 }
129 
130 namespace px4
131 {
132 namespace logger
133 {
134 
135 constexpr const char *Logger::LOG_ROOT[(int)LogType::Count];
136 
137 int Logger::custom_command(int argc, char *argv[])
138 {
139  if (!is_running()) {
140  print_usage("logger not running");
141  return 1;
142  }
143 
144  if (!strcmp(argv[0], "on")) {
145  get_instance()->set_arm_override(true);
146  return 0;
147  }
148 
149  if (!strcmp(argv[0], "off")) {
150  get_instance()->set_arm_override(false);
151  return 0;
152  }
153 
154  return print_usage("unknown command");
155 }
156 
157 int Logger::task_spawn(int argc, char *argv[])
158 {
159  _task_id = px4_task_spawn_cmd("logger",
160  SCHED_DEFAULT,
161  SCHED_PRIORITY_LOG_CAPTURE,
162  3700,
163  (px4_main_t)&run_trampoline,
164  (char *const *)argv);
165 
166  if (_task_id < 0) {
167  _task_id = -1;
168  return -errno;
169  }
170 
171  return 0;
172 }
173 
175 {
176  PX4_INFO("Running in mode: %s", configured_backend_mode());
177 
178  bool is_logging = false;
179 
180  if (_writer.is_started(LogType::Full, LogWriter::BackendFile)) {
181  PX4_INFO("Full File Logging Running:");
182  print_statistics(LogType::Full);
183  is_logging = true;
184  }
185 
186  if (_writer.is_started(LogType::Mission, LogWriter::BackendFile)) {
187  PX4_INFO("Mission File Logging Running:");
188  print_statistics(LogType::Mission);
189  is_logging = true;
190  }
191 
192  if (_writer.is_started(LogType::Full, LogWriter::BackendMavlink)) {
193  PX4_INFO("Mavlink Logging Running (Full log)");
194  is_logging = true;
195  }
196 
197  if (!is_logging) {
198  PX4_INFO("Not logging");
199  }
200 
201  return 0;
202 }
203 
205 {
206  if (!_writer.is_started(type, LogWriter::BackendFile)) { //currently only statistics for file logging
207  return;
208  }
209 
210  Statistics &stats = _statistics[(int)type];
211 
212  /* this is only for the file backend */
213  float kibibytes = _writer.get_total_written_file(type) / 1024.0f;
214  float mebibytes = kibibytes / 1024.0f;
215  float seconds = ((float)(hrt_absolute_time() - stats.start_time_file)) / 1000000.0f;
216 
217  PX4_INFO("Log file: %s/%s/%s", LOG_ROOT[(int)type], _file_name[(int)type].log_dir, _file_name[(int)type].log_file_name);
218 
219  if (mebibytes < 0.1f) {
220  PX4_INFO("Wrote %4.2f KiB (avg %5.2f KiB/s)", (double)kibibytes, (double)(kibibytes / seconds));
221 
222  } else {
223  PX4_INFO("Wrote %4.2f MiB (avg %5.2f KiB/s)", (double)mebibytes, (double)(kibibytes / seconds));
224  }
225 
226  PX4_INFO("Since last status: dropouts: %zu (max len: %.3f s), max used buffer: %zu / %zu B",
227  stats.write_dropouts, (double)stats.max_dropout_duration, stats.high_water, _writer.get_buffer_size_file(type));
228  stats.high_water = 0;
229  stats.write_dropouts = 0;
230  stats.max_dropout_duration = 0.f;
231 }
232 
233 Logger *Logger::instantiate(int argc, char *argv[])
234 {
235  uint32_t log_interval = 3500;
236  int log_buffer_size = 12 * 1024;
237  bool log_on_start = false;
238  bool log_until_shutdown = false;
240  bool error_flag = false;
241  bool log_name_timestamp = false;
243  const char *poll_topic = nullptr;
244 
245  int myoptind = 1;
246  int ch;
247  const char *myoptarg = nullptr;
248 
249  while ((ch = px4_getopt(argc, argv, "r:b:etfm:p:x", &myoptind, &myoptarg)) != EOF) {
250  switch (ch) {
251  case 'r': {
252  unsigned long r = strtoul(myoptarg, nullptr, 10);
253 
254  if (r <= 0) {
255  r = 1e6;
256  }
257 
258  log_interval = 1e6 / r;
259  }
260  break;
261 
262  case 'x':
263  log_mode = Logger::LogMode::rc_aux1;
264  break;
265 
266  case 'e':
267  log_on_start = true;
268  break;
269 
270  case 'b': {
271  unsigned long s = strtoul(myoptarg, nullptr, 10);
272 
273  if (s < 1) {
274  s = 1;
275  }
276 
277  log_buffer_size = 1024 * s;
278  }
279  break;
280 
281  case 't':
282  log_name_timestamp = true;
283  break;
284 
285  case 'f':
286  log_on_start = true;
287  log_until_shutdown = true;
288  break;
289 
290  case 'm':
291  if (!strcmp(myoptarg, "file")) {
292  backend = LogWriter::BackendFile;
293 
294  } else if (!strcmp(myoptarg, "mavlink")) {
295  backend = LogWriter::BackendMavlink;
296 
297  } else if (!strcmp(myoptarg, "all")) {
298  backend = LogWriter::BackendAll;
299 
300  } else {
301  PX4_ERR("unknown mode: %s", myoptarg);
302  error_flag = true;
303  }
304 
305  break;
306 
307  case 'p':
308  poll_topic = myoptarg;
309  break;
310 
311  case '?':
312  error_flag = true;
313  break;
314 
315  default:
316  PX4_WARN("unrecognized flag");
317  error_flag = true;
318  break;
319  }
320  }
321 
322  if (log_on_start) {
323  if (log_until_shutdown) {
325 
326  } else {
328  }
329  }
330 
331  if (error_flag) {
332  return nullptr;
333  }
334 
335  Logger *logger = new Logger(backend, log_buffer_size, log_interval, poll_topic, log_mode, log_name_timestamp);
336 
337 #if defined(DBGPRINT) && defined(__PX4_NUTTX)
338  struct mallinfo alloc_info = mallinfo();
339  PX4_INFO("largest free chunk: %d bytes", alloc_info.mxordblk);
340  PX4_INFO("remaining free heap: %d bytes", alloc_info.fordblks);
341 #endif /* DBGPRINT */
342 
343  if (logger == nullptr) {
344  PX4_ERR("alloc failed");
345 
346  } else {
347 #ifndef __PX4_NUTTX
348  //check for replay mode
349  const char *logfile = getenv(px4::replay::ENV_FILENAME);
350 
351  if (logfile) {
352  logger->setReplayFile(logfile);
353  }
354 
355 #endif /* __PX4_NUTTX */
356 
357  }
358 
359  return logger;
360 }
361 
362 
363 Logger::Logger(LogWriter::Backend backend, size_t buffer_size, uint32_t log_interval, const char *poll_topic_name,
364  LogMode log_mode, bool log_name_timestamp) :
365  _log_mode(log_mode),
366  _log_name_timestamp(log_name_timestamp),
367  _writer(backend, buffer_size),
368  _log_interval(log_interval)
369 {
370  _log_utc_offset = param_find("SDLOG_UTC_OFFSET");
371  _log_dirs_max = param_find("SDLOG_DIRS_MAX");
372  _sdlog_profile_handle = param_find("SDLOG_PROFILE");
373  _mission_log = param_find("SDLOG_MISSION");
374 
375  if (poll_topic_name) {
376  const orb_metadata *const *topics = orb_get_topics();
377 
378  for (size_t i = 0; i < orb_topics_count(); i++) {
379  if (strcmp(poll_topic_name, topics[i]->o_name) == 0) {
380  _polling_topic_meta = topics[i];
381  break;
382  }
383  }
384 
385  if (!_polling_topic_meta) {
386  PX4_ERR("Failed to find topic %s", poll_topic_name);
387  }
388  }
389 }
390 
392 {
393  if (_replay_file_name) {
394  free(_replay_file_name);
395  }
396 
397  if (_msg_buffer) {
398  delete[](_msg_buffer);
399  }
400 }
401 
403 {
404  if (is_running()) {
405  get_instance()->request_stop();
406  return false;
407  }
408 
409  return true;
410 }
411 
412 LoggerSubscription *Logger::add_topic(const orb_metadata *topic, uint32_t interval_ms, uint8_t instance)
413 {
414  LoggerSubscription *subscription = nullptr;
415  size_t fields_len = strlen(topic->o_fields) + strlen(topic->o_name) + 1; //1 for ':'
416 
417  if (fields_len > sizeof(ulog_message_format_s::format)) {
418  PX4_WARN("skip topic %s, format string is too large: %zu (max is %zu)", topic->o_name, fields_len,
420 
421  return nullptr;
422  }
423 
424  if (_subscriptions.push_back(LoggerSubscription{topic, interval_ms, instance})) {
425  subscription = &_subscriptions[_subscriptions.size() - 1];
426 
427  } else {
428  PX4_WARN("Too many subscriptions, failed to add: %s %d", topic->o_name, instance);
429  }
430 
431  return subscription;
432 }
433 
434 bool Logger::add_topic(const char *name, uint32_t interval_ms, uint8_t instance)
435 {
436  // if we poll on a topic, we don't use the interval and let the polled topic define the maximum interval
437  if (_polling_topic_meta) {
438  interval_ms = 0;
439  }
440 
441  const orb_metadata *const *topics = orb_get_topics();
442  LoggerSubscription *subscription = nullptr;
443 
444  for (size_t i = 0; i < orb_topics_count(); i++) {
445  if (strcmp(name, topics[i]->o_name) == 0) {
446  bool already_added = false;
447 
448  // check if already added: if so, only update the interval
449  for (size_t j = 0; j < _subscriptions.size(); ++j) {
450  if ((_subscriptions[j].get_topic() == topics[i]) && (_subscriptions[j].get_instance() == instance)) {
451 
452  PX4_DEBUG("logging topic %s(%d), interval: %i, already added, only setting interval",
453  topics[i]->o_name, instance, interval_ms);
454 
455  _subscriptions[j].set_interval_ms(interval_ms);
456 
457  subscription = &_subscriptions[j];
458  already_added = true;
459  break;
460  }
461  }
462 
463  if (!already_added) {
464  subscription = add_topic(topics[i], interval_ms, instance);
465  PX4_DEBUG("logging topic: %s(%d), interval: %i", topics[i]->o_name, instance, interval_ms);
466  break;
467  }
468  }
469  }
470 
471  return (subscription != nullptr);
472 }
473 
474 bool Logger::add_topic_multi(const char *name, uint32_t interval_ms)
475 {
476  // add all possible instances
477  for (uint8_t instance = 0; instance < ORB_MULTI_MAX_INSTANCES; instance++) {
478  add_topic(name, interval_ms, instance);
479  }
480 
481  return true;
482 }
483 
484 bool Logger::copy_if_updated(int sub_idx, void *buffer, bool try_to_subscribe)
485 {
486  LoggerSubscription &sub = _subscriptions[sub_idx];
487 
488  bool updated = false;
489 
490  if (sub.valid()) {
491  updated = sub.update(buffer);
492 
493  } else if (try_to_subscribe) {
494  if (sub.subscribe()) {
496 
497  if (sub_idx < _num_mission_subs) {
499  }
500 
501  // copy first data
502  updated = sub.copy(buffer);
503  }
504  }
505 
506  return updated;
507 }
508 
510 {
511  add_topic("actuator_controls_0", 100);
512  add_topic("actuator_controls_1", 100);
513  add_topic("airspeed", 200);
514  add_topic("airspeed_validated", 200);
515  add_topic("camera_capture");
516  add_topic("camera_trigger");
517  add_topic("camera_trigger_secondary");
518  add_topic("cpuload");
519  add_topic("ekf2_innovations", 200);
520  add_topic("ekf_gps_drift");
521  add_topic("esc_status", 250);
522  add_topic("estimator_status", 200);
523  add_topic("home_position");
524  add_topic("input_rc", 200);
525  add_topic("manual_control_setpoint", 200);
526  add_topic("mission");
527  add_topic("mission_result");
528  add_topic("optical_flow", 50);
529  add_topic("position_controller_status", 500);
530  add_topic("position_setpoint_triplet", 200);
531  add_topic("radio_status");
532  add_topic("rate_ctrl_status", 200);
533  add_topic("sensor_combined", 100);
534  add_topic("sensor_preflight", 200);
535  add_topic("system_power", 500);
536  add_topic("tecs_status", 200);
537  add_topic("trajectory_setpoint", 200);
538  add_topic("vehicle_air_data", 200);
539  add_topic("vehicle_angular_velocity", 20);
540  add_topic("vehicle_attitude", 50);
541  add_topic("vehicle_attitude_setpoint", 100);
542  add_topic("vehicle_command");
543  add_topic("vehicle_global_position", 200);
544  add_topic("vehicle_land_detected");
545  add_topic("vehicle_local_position", 100);
546  add_topic("vehicle_local_position_setpoint", 100);
547  add_topic("vehicle_magnetometer", 200);
548  add_topic("vehicle_rates_setpoint", 20);
549  add_topic("vehicle_status", 200);
550  add_topic("vehicle_status_flags");
551  add_topic("vtol_vehicle_status", 200);
552 
553  add_topic_multi("actuator_outputs", 100);
554  add_topic_multi("battery_status", 500);
555  add_topic_multi("distance_sensor", 100);
556  add_topic_multi("telemetry_status");
557  add_topic_multi("vehicle_gps_position");
558  add_topic_multi("wind_estimate", 200);
559 
560 #ifdef CONFIG_ARCH_BOARD_PX4_SITL
561 
562  add_topic("actuator_controls_virtual_fw");
563  add_topic("actuator_controls_virtual_mc");
564  add_topic("fw_virtual_attitude_setpoint");
565  add_topic("mc_virtual_attitude_setpoint");
566  add_topic("offboard_control_mode");
567  add_topic("position_controller_status");
568  add_topic("time_offset");
569  add_topic("vehicle_angular_velocity", 10);
570  add_topic("vehicle_attitude_groundtruth", 10);
571  add_topic("vehicle_global_position_groundtruth", 100);
572  add_topic("vehicle_local_position_groundtruth", 100);
573  add_topic("vehicle_roi");
574 
575  add_topic_multi("multirotor_motor_limits");
576 
577 #endif /* CONFIG_ARCH_BOARD_PX4_SITL */
578 }
579 
581 {
582  // maximum rate to analyze fast maneuvers (e.g. for racing)
583  add_topic("actuator_controls_0");
584  add_topic("actuator_outputs");
585  add_topic("manual_control_setpoint");
586  add_topic("rate_ctrl_status");
587  add_topic("sensor_combined");
588  add_topic("vehicle_angular_velocity");
589  add_topic("vehicle_attitude");
590  add_topic("vehicle_attitude_setpoint");
591  add_topic("vehicle_rates_setpoint");
592 }
593 
595 {
596  add_topic("debug_array");
597  add_topic("debug_key_value");
598  add_topic("debug_value");
599  add_topic("debug_vect");
600 }
601 
603 {
604  // for estimator replay (need to be at full rate)
605  add_topic("ekf2_timestamps");
606  add_topic("ekf_gps_position");
607 
608  // current EKF2 subscriptions
609  add_topic("airspeed");
610  add_topic("optical_flow");
611  add_topic("sensor_combined");
612  add_topic("sensor_selection");
613  add_topic("vehicle_air_data");
614  add_topic("vehicle_land_detected");
615  add_topic("vehicle_magnetometer");
616  add_topic("vehicle_status");
617  add_topic("vehicle_visual_odometry");
618  add_topic("vehicle_visual_odometry_aligned");
619  add_topic_multi("distance_sensor");
620  add_topic_multi("vehicle_gps_position");
621 }
622 
624 {
625  add_topic_multi("sensor_accel", 100);
626  add_topic_multi("sensor_baro", 100);
627  add_topic_multi("sensor_gyro", 100);
628 }
629 
631 {
632  add_topic_multi("sensor_accel", 100);
633  add_topic_multi("sensor_baro", 100);
634  add_topic_multi("sensor_gyro", 100);
635  add_topic_multi("sensor_mag", 100);
636 }
637 
639 {
640  add_topic("cellular_status", 200);
641  add_topic("collision_constraints");
642  add_topic("obstacle_distance_fused");
643  add_topic("onboard_computer_status", 200);
644  add_topic("vehicle_mocap_odometry", 30);
645  add_topic("vehicle_trajectory_waypoint", 200);
646  add_topic("vehicle_trajectory_waypoint_desired", 200);
647  add_topic("vehicle_visual_odometry", 30);
648 }
649 
651 {
652  // for system id need to log imu and controls at full rate
653  add_topic("actuator_controls_0");
654  add_topic("actuator_controls_1");
655  add_topic("sensor_combined");
656 }
657 
658 int Logger::add_topics_from_file(const char *fname)
659 {
660  int ntopics = 0;
661 
662  /* open the topic list file */
663  FILE *fp = fopen(fname, "r");
664 
665  if (fp == nullptr) {
666  return -1;
667  }
668 
669  /* call add_topic for each topic line in the file */
670  for (;;) {
671  /* get a line, bail on error/EOF */
672  char line[80];
673  line[0] = '\0';
674 
675  if (fgets(line, sizeof(line), fp) == nullptr) {
676  break;
677  }
678 
679  /* skip comment lines */
680  if ((strlen(line) < 2) || (line[0] == '#')) {
681  continue;
682  }
683 
684  // read line with format: <topic_name>[, <interval>]
685  char topic_name[80];
686  uint32_t interval_ms = 0;
687  int nfields = sscanf(line, "%s %u", topic_name, &interval_ms);
688 
689  if (nfields > 0) {
690  int name_len = strlen(topic_name);
691 
692  if (name_len > 0 && topic_name[name_len - 1] == ',') {
693  topic_name[name_len - 1] = '\0';
694  }
695 
696  /* add topic with specified interval_ms */
697  if (add_topic(topic_name, interval_ms)) {
698  ntopics++;
699 
700  } else {
701  PX4_ERR("Failed to add topic %s", topic_name);
702  }
703  }
704  }
705 
706  fclose(fp);
707  return ntopics;
708 }
709 
711 {
712  switch (_writer.backend()) {
713  case LogWriter::BackendFile: return "file";
714 
715  case LogWriter::BackendMavlink: return "mavlink";
716 
717  case LogWriter::BackendAll: return "all";
718 
719  default: return "several";
720  }
721 }
722 
724 {
725  if (type == MissionLogType::Complete) {
726  add_mission_topic("camera_capture");
727  add_mission_topic("mission_result");
728  add_mission_topic("vehicle_global_position", 1000);
729  add_mission_topic("vehicle_status", 1000);
730 
731  } else if (type == MissionLogType::Geotagging) {
732  add_mission_topic("camera_capture");
733  }
734 }
735 
737 {
738  // get the logging profile
740 
742  param_get(_sdlog_profile_handle, (int32_t *)&sdlog_profile);
743  }
744 
745  if ((int32_t)sdlog_profile == 0) {
746  PX4_WARN("No logging profile selected. Using default set");
747  sdlog_profile = SDLogProfileMask::DEFAULT;
748  }
749 
750  // load appropriate topics for profile
751  // the order matters: if several profiles add the same topic, the logging rate of the last one will be used
752  if (sdlog_profile & SDLogProfileMask::DEFAULT) {
754  }
755 
756  if (sdlog_profile & SDLogProfileMask::ESTIMATOR_REPLAY) {
758  }
759 
760  if (sdlog_profile & SDLogProfileMask::THERMAL_CALIBRATION) {
762  }
763 
764  if (sdlog_profile & SDLogProfileMask::SYSTEM_IDENTIFICATION) {
766  }
767 
768  if (sdlog_profile & SDLogProfileMask::HIGH_RATE) {
770  }
771 
772  if (sdlog_profile & SDLogProfileMask::DEBUG_TOPICS) {
774  }
775 
776  if (sdlog_profile & SDLogProfileMask::SENSOR_COMPARISON) {
778  }
779 
780  if (sdlog_profile & SDLogProfileMask::VISION_AND_AVOIDANCE) {
782  }
783 }
784 
785 
786 void Logger::add_mission_topic(const char *name, uint32_t interval_ms)
787 {
789  PX4_ERR("Max num mission topics exceeded");
790  return;
791  }
792 
793  if (add_topic(name, interval_ms)) {
797  }
798 }
799 
801 {
802  PX4_INFO("logger started (mode=%s)", configured_backend_mode());
803 
805  int mkdir_ret = mkdir(LOG_ROOT[(int)LogType::Full], S_IRWXU | S_IRWXG | S_IRWXO);
806 
807  if (mkdir_ret == 0) {
808  PX4_INFO("log root dir created: %s", LOG_ROOT[(int)LogType::Full]);
809 
810  } else if (errno != EEXIST) {
811  PX4_ERR("failed creating log root dir: %s (%i)", LOG_ROOT[(int)LogType::Full], errno);
812 
813  if ((_writer.backend() & ~LogWriter::BackendFile) == 0) {
814  return;
815  }
816  }
817 
818  int32_t max_log_dirs_to_keep = 0;
819 
820  if (_log_dirs_max != PARAM_INVALID) {
821  param_get(_log_dirs_max, &max_log_dirs_to_keep);
822  }
823 
824  if (util::check_free_space(LOG_ROOT[(int)LogType::Full], max_log_dirs_to_keep, _mavlink_log_pub,
825  _file_name[(int)LogType::Full].sess_dir_index) == 1) {
826  return;
827  }
828  }
829 
830  uORB::Subscription parameter_update_sub(ORB_ID(parameter_update));
831 
832  // mission log topics if enabled (must be added first)
833  int32_t mission_log_mode = 0;
834 
835  if (_mission_log != PARAM_INVALID) {
836  param_get(_mission_log, &mission_log_mode);
837  initialize_mission_topics((MissionLogType)mission_log_mode);
838 
839  if (_num_mission_subs > 0) {
840  int mkdir_ret = mkdir(LOG_ROOT[(int)LogType::Mission], S_IRWXU | S_IRWXG | S_IRWXO);
841 
842  if (mkdir_ret != 0 && errno != EEXIST) {
843  PX4_ERR("failed creating log root dir: %s (%i)", LOG_ROOT[(int)LogType::Mission], errno);
844  }
845  }
846  }
847 
848  int ntopics = add_topics_from_file(PX4_STORAGEDIR "/etc/logging/logger_topics.txt");
849 
850  if (ntopics > 0) {
851  PX4_INFO("logging %d topics from logger_topics.txt", ntopics);
852 
853  } else {
855  }
856 
857  //all topics added. Get required message buffer size
858  int max_msg_size = 0;
859 
860  for (const auto &subscription : _subscriptions) {
861  //use o_size, because that's what orb_copy will use
862  if (subscription.get_topic()->o_size > max_msg_size) {
863  max_msg_size = subscription.get_topic()->o_size;
864  }
865  }
866 
867  max_msg_size += sizeof(ulog_message_data_header_s);
868 
869  if (sizeof(ulog_message_logging_s) > (size_t)max_msg_size) {
870  max_msg_size = sizeof(ulog_message_logging_s);
871  }
872 
873  if (_polling_topic_meta && _polling_topic_meta->o_size > max_msg_size) {
874  max_msg_size = _polling_topic_meta->o_size;
875  }
876 
877  if (max_msg_size > _msg_buffer_len) {
878  if (_msg_buffer) {
879  delete[](_msg_buffer);
880  }
881 
882  _msg_buffer_len = max_msg_size;
883  _msg_buffer = new uint8_t[_msg_buffer_len];
884 
885  if (!_msg_buffer) {
886  PX4_ERR("failed to alloc message buffer");
887  return;
888  }
889  }
890 
891 
892  if (!_writer.init()) {
893  PX4_ERR("writer init failed");
894  return;
895  }
896 
897  /* debug stats */
898  hrt_abstime timer_start = 0;
899  uint32_t total_bytes = 0;
900 
901  px4_register_shutdown_hook(&Logger::request_stop_static);
902 
905  }
906 
907  /* init the update timer */
908  struct hrt_call timer_call {};
909  timer_callback_data_s timer_callback_data;
910  px4_sem_init(&timer_callback_data.semaphore, 0, 0);
911  /* timer_semaphore use case is a signal */
912  px4_sem_setprotocol(&timer_callback_data.semaphore, SEM_PRIO_NONE);
913 
914  int polling_topic_sub = -1;
915 
916  if (_polling_topic_meta) {
917  polling_topic_sub = orb_subscribe(_polling_topic_meta);
918 
919  if (polling_topic_sub < 0) {
920  PX4_ERR("Failed to subscribe (%i)", errno);
921  }
922 
923  } else {
924 
926 
927  const pid_t pid_self = getpid();
928  const pthread_t writer_thread = _writer.thread_id_file();
929 
930  // sched_note_start is already called from pthread_create and task_create,
931  // which means we can expect to find the tasks in system_load.tasks, as required in watchdog_initialize
932  watchdog_initialize(pid_self, writer_thread, timer_callback_data.watchdog_data);
933  }
934 
935  hrt_call_every(&timer_call, _log_interval, _log_interval, timer_callback, &timer_callback_data);
936  }
937 
938  // check for new subscription data
939  hrt_abstime next_subscribe_check = 0;
940  int next_subscribe_topic_index = -1; // this is used to distribute the checks over time
941 
942  while (!should_exit()) {
943 
944  // Start/stop logging (depending on logging mode, by default when arming/disarming)
945  const bool logging_started = start_stop_logging((MissionLogType)mission_log_mode);
946 
947  if (logging_started) {
948 #ifdef DBGPRINT
949  timer_start = hrt_absolute_time();
950  total_bytes = 0;
951 #endif /* DBGPRINT */
952  }
953 
954  /* check for logging command from MAVLink (start/stop streaming) */
956 
957  if (timer_callback_data.watchdog_triggered) {
958  timer_callback_data.watchdog_triggered = false;
960  }
961 
962 
963  const hrt_abstime loop_time = hrt_absolute_time();
964 
965  if (_writer.is_started(LogType::Full)) { // mission log only runs when full log is also started
966 
967  /* check if we need to output the process load */
968  if (_next_load_print != 0 && loop_time >= _next_load_print) {
969  _next_load_print = 0;
971 
972  if (_should_stop_file_log) {
973  _should_stop_file_log = false;
975  continue; // skip to next loop iteration
976  }
977  }
978 
979  /* Check if parameters have changed */
980  if (!_should_stop_file_log) { // do not record param changes after disarming
981  if (parameter_update_sub.updated()) {
982  // clear update
983  parameter_update_s pupdate;
984  parameter_update_sub.copy(&pupdate);
985 
987  }
988  }
989 
990  /* wait for lock on log buffer */
991  _writer.lock();
992 
993  int sub_idx = 0;
994 
995  for (LoggerSubscription &sub : _subscriptions) {
996  /* if this topic has been updated, copy the new data into the message buffer
997  * and write a message to the log
998  */
999  const bool try_to_subscribe = (sub_idx == next_subscribe_topic_index);
1000 
1001  if (copy_if_updated(sub_idx, _msg_buffer + sizeof(ulog_message_data_header_s), try_to_subscribe)) {
1002  // each message consists of a header followed by an orb data object
1003  const size_t msg_size = sizeof(ulog_message_data_header_s) + sub.get_topic()->o_size_no_padding;
1004  const uint16_t write_msg_size = static_cast<uint16_t>(msg_size - ULOG_MSG_HEADER_LEN);
1005  const uint16_t write_msg_id = sub.msg_id;
1006 
1007  //write one byte after another (necessary because of alignment)
1008  _msg_buffer[0] = (uint8_t)write_msg_size;
1009  _msg_buffer[1] = (uint8_t)(write_msg_size >> 8);
1010  _msg_buffer[2] = static_cast<uint8_t>(ULogMessageType::DATA);
1011  _msg_buffer[3] = (uint8_t)write_msg_id;
1012  _msg_buffer[4] = (uint8_t)(write_msg_id >> 8);
1013 
1014  // PX4_INFO("topic: %s, size = %zu, out_size = %zu", sub.get_topic()->o_name, sub.get_topic()->o_size, msg_size);
1015 
1016  // full log
1017  if (write_message(LogType::Full, _msg_buffer, msg_size)) {
1018 
1019 #ifdef DBGPRINT
1020  total_bytes += msg_size;
1021 #endif /* DBGPRINT */
1022  }
1023 
1024  // mission log
1025  if (sub_idx < _num_mission_subs) {
1027  if (_mission_subscriptions[sub_idx].next_write_time < (loop_time / 100000)) {
1028  unsigned delta_time = _mission_subscriptions[sub_idx].min_delta_ms;
1029 
1030  if (delta_time > 0) {
1031  _mission_subscriptions[sub_idx].next_write_time = (loop_time / 100000) + delta_time / 100;
1032  }
1033 
1035  }
1036  }
1037  }
1038  }
1039 
1040  ++sub_idx;
1041  }
1042 
1043  // check for new logging message(s)
1044  log_message_s log_message;
1045 
1046  if (_log_message_sub.update(&log_message)) {
1047  const char *message = (const char *)log_message.text;
1048  int message_len = strlen(message);
1049 
1050  if (message_len > 0) {
1051  uint16_t write_msg_size = sizeof(ulog_message_logging_s) - sizeof(ulog_message_logging_s::message)
1052  - ULOG_MSG_HEADER_LEN + message_len;
1053  _msg_buffer[0] = (uint8_t)write_msg_size;
1054  _msg_buffer[1] = (uint8_t)(write_msg_size >> 8);
1055  _msg_buffer[2] = static_cast<uint8_t>(ULogMessageType::LOGGING);
1056  _msg_buffer[3] = log_message.severity + '0';
1057  memcpy(_msg_buffer + 4, &log_message.timestamp, sizeof(ulog_message_logging_s::timestamp));
1058  strncpy((char *)(_msg_buffer + 12), message, sizeof(ulog_message_logging_s::message));
1059 
1061  }
1062  }
1063 
1064  // Add sync magic
1065  if (loop_time - _last_sync_time > 500_ms) {
1066  uint16_t write_msg_size = static_cast<uint16_t>(sizeof(ulog_message_sync_s) - ULOG_MSG_HEADER_LEN);
1067  _msg_buffer[0] = (uint8_t)write_msg_size;
1068  _msg_buffer[1] = (uint8_t)(write_msg_size >> 8);
1069  _msg_buffer[2] = static_cast<uint8_t>(ULogMessageType::SYNC);
1070 
1071  // sync byte sequence
1072  _msg_buffer[3] = 0x2F;
1073  _msg_buffer[4] = 0x73;
1074  _msg_buffer[5] = 0x13;
1075  _msg_buffer[6] = 0x20;
1076  _msg_buffer[7] = 0x25;
1077  _msg_buffer[8] = 0x0C;
1078  _msg_buffer[9] = 0xBB;
1079  _msg_buffer[10] = 0x12;
1080 
1082  _last_sync_time = loop_time;
1083  }
1084 
1085  // update buffer statistics
1086  for (int i = 0; i < (int)LogType::Count; ++i) {
1087  if (!_statistics[i].dropout_start && (_writer.get_buffer_fill_count_file((LogType)i) > _statistics[i].high_water)) {
1089  }
1090  }
1091 
1092  /* release the log buffer */
1093  _writer.unlock();
1094 
1095  /* notify the writer thread */
1096  _writer.notify();
1097 
1098  /* subscription update */
1099  if (next_subscribe_topic_index != -1) {
1100  if (++next_subscribe_topic_index >= (int)_subscriptions.size()) {
1101  next_subscribe_topic_index = -1;
1102  next_subscribe_check = loop_time + TRY_SUBSCRIBE_INTERVAL;
1103  }
1104 
1105  } else if (loop_time > next_subscribe_check) {
1106  next_subscribe_topic_index = 0;
1107  }
1108 
1109  debug_print_buffer(total_bytes, timer_start);
1110 
1111  } else { // not logging
1112 
1113  // try to subscribe to new topics, even if we don't log, so that:
1114  // - we avoid subscribing to many topics at once, when logging starts
1115  // - we'll get the data immediately once we start logging (no need to wait for the next subscribe timeout)
1116  if (next_subscribe_topic_index != -1) {
1117  if (!_subscriptions[next_subscribe_topic_index].valid()) {
1118  _subscriptions[next_subscribe_topic_index].subscribe();
1119  }
1120 
1121  if (++next_subscribe_topic_index >= (int)_subscriptions.size()) {
1122  next_subscribe_topic_index = -1;
1123  next_subscribe_check = loop_time + TRY_SUBSCRIBE_INTERVAL;
1124  }
1125 
1126  } else if (loop_time > next_subscribe_check) {
1127  next_subscribe_topic_index = 0;
1128  }
1129  }
1130 
1131  // wait for next loop iteration...
1132  if (polling_topic_sub >= 0) {
1133  px4_pollfd_struct_t fds[1];
1134  fds[0].fd = polling_topic_sub;
1135  fds[0].events = POLLIN;
1136  int pret = px4_poll(fds, 1, 1000);
1137 
1138  if (pret < 0) {
1139  PX4_ERR("poll failed (%i)", pret);
1140 
1141  } else if (pret != 0) {
1142  if (fds[0].revents & POLLIN) {
1143  // need to to an orb_copy so that the next poll will not return immediately
1144  orb_copy(_polling_topic_meta, polling_topic_sub, _msg_buffer);
1145  }
1146  }
1147 
1148  } else {
1149  /*
1150  * We wait on the semaphore, which periodically gets updated by a high-resolution timer.
1151  * The simpler alternative would be:
1152  * usleep(max(300, _log_interval - elapsed_time_since_loop_start));
1153  * And on linux this is quite accurate as well, but under NuttX it is not accurate,
1154  * because usleep() has only a granularity of CONFIG_MSEC_PER_TICK (=1ms).
1155  */
1156  while (px4_sem_wait(&timer_callback_data.semaphore) != 0) {}
1157  }
1158  }
1159 
1162 
1163  hrt_cancel(&timer_call);
1164  px4_sem_destroy(&timer_callback_data.semaphore);
1165 
1166  // stop the writer thread
1167  _writer.thread_stop();
1168 
1169  if (polling_topic_sub >= 0) {
1170  orb_unsubscribe(polling_topic_sub);
1171  }
1172 
1173  if (_mavlink_log_pub) {
1175  _mavlink_log_pub = nullptr;
1176  }
1177 
1178  px4_unregister_shutdown_hook(&Logger::request_stop_static);
1179 }
1180 
1181 void Logger::debug_print_buffer(uint32_t &total_bytes, hrt_abstime &timer_start)
1182 {
1183 #ifdef DBGPRINT
1184  double deltat = (double)(hrt_absolute_time() - timer_start) * 1e-6;
1185 
1186  if (deltat > 4.0) {
1187  struct mallinfo alloc_info = mallinfo();
1188  double throughput = total_bytes / deltat;
1189  PX4_INFO("%8.1f kB/s, %zu highWater, %d dropouts, %5.3f sec max, free heap: %d",
1190  throughput / 1.e3, _statistics[0].high_water, _statistics[0].write_dropouts,
1191  (double)_statistics[0].max_dropout_duration, alloc_info.fordblks);
1192 
1193  _statistics[0].high_water = 0;
1195  total_bytes = 0;
1196  timer_start = hrt_absolute_time();
1197  }
1198 
1199 #endif /* DBGPRINT */
1200 }
1201 
1203 {
1204  bool bret = false;
1205  bool want_start = false;
1206  bool want_stop = false;
1207 
1208  if (_log_mode == LogMode::rc_aux1) {
1209 
1210  // aux1-based logging
1211  manual_control_setpoint_s manual_sp;
1212 
1213  if (_manual_control_sp_sub.update(&manual_sp)) {
1214 
1215  bool should_start = ((manual_sp.aux1 > 0.3f) || _manually_logging_override);
1216 
1217  if (_prev_state != should_start) {
1218  _prev_state = should_start;
1219 
1220  if (should_start) {
1221  want_start = true;
1222 
1223  } else {
1224  want_stop = true;
1225  }
1226  }
1227  }
1228 
1229  } else {
1230  // arming-based logging
1231  vehicle_status_s vehicle_status;
1232 
1233  if (_vehicle_status_sub.update(&vehicle_status)) {
1234 
1235  bool armed = (vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED) || _manually_logging_override;
1236 
1237  if (_prev_state != armed && _log_mode != LogMode::boot_until_shutdown) {
1238  _prev_state = armed;
1239 
1240  if (armed) {
1241  want_start = true;
1242 
1243  } else {
1244  want_stop = true;
1245  }
1246  }
1247  }
1248  }
1249 
1250  if (want_start) {
1251  if (_should_stop_file_log) { // happens on quick stop/start toggling
1252  _should_stop_file_log = false;
1254  }
1255 
1257  bret = true;
1258 
1259  if (mission_log_type != MissionLogType::Disabled) {
1261  }
1262 
1263  } else if (want_stop) {
1264  // delayed stop: we measure the process loads and then stop
1266  _should_stop_file_log = true;
1267 
1268  if (mission_log_type != MissionLogType::Disabled) {
1270  }
1271  }
1272 
1273  return bret;
1274 }
1275 
1277 {
1279 
1280  if (_vehicle_command_sub.update(&command)) {
1281 
1282  if (command.command == vehicle_command_s::VEHICLE_CMD_LOGGING_START) {
1283 
1284  if ((int)(command.param1 + 0.5f) != 0) {
1285  ack_vehicle_command(&command, vehicle_command_s::VEHICLE_CMD_RESULT_UNSUPPORTED);
1286 
1287  } else if (can_start_mavlink_log()) {
1288  ack_vehicle_command(&command, vehicle_command_s::VEHICLE_CMD_RESULT_ACCEPTED);
1290 
1291  } else {
1292  ack_vehicle_command(&command, vehicle_command_s::VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED);
1293  }
1294 
1295  } else if (command.command == vehicle_command_s::VEHICLE_CMD_LOGGING_STOP) {
1296  stop_log_mavlink();
1297  ack_vehicle_command(&command, vehicle_command_s::VEHICLE_CMD_RESULT_ACCEPTED);
1298  }
1299  }
1300 }
1301 
1302 bool Logger::write_message(LogType type, void *ptr, size_t size)
1303 {
1304  Statistics &stats = _statistics[(int)type];
1305 
1306  if (_writer.write_message(type, ptr, size, stats.dropout_start) != -1) {
1307 
1308  if (stats.dropout_start) {
1309  float dropout_duration = (float)(hrt_elapsed_time(&stats.dropout_start) / 1000) / 1.e3f;
1310 
1311  if (dropout_duration > stats.max_dropout_duration) {
1312  stats.max_dropout_duration = dropout_duration;
1313  }
1314 
1315  stats.dropout_start = 0;
1316  }
1317 
1318  return true;
1319  }
1320 
1321  if (!stats.dropout_start) {
1322  stats.dropout_start = hrt_absolute_time();
1323  ++stats.write_dropouts;
1324  stats.high_water = 0;
1325  }
1326 
1327  return false;
1328 }
1329 
1330 int Logger::create_log_dir(LogType type, tm *tt, char *log_dir, int log_dir_len)
1331 {
1332  LogFileName &file_name = _file_name[(int)type];
1333 
1334  /* create dir on sdcard if needed */
1335  int n = snprintf(log_dir, log_dir_len, "%s/", LOG_ROOT[(int)type]);
1336 
1337  if (n >= log_dir_len) {
1338  PX4_ERR("log path too long");
1339  return -1;
1340  }
1341 
1342  if (tt) {
1343  strftime(file_name.log_dir, sizeof(LogFileName::log_dir), "%Y-%m-%d", tt);
1344  strncpy(log_dir + n, file_name.log_dir, log_dir_len - n);
1345  int mkdir_ret = mkdir(log_dir, S_IRWXU | S_IRWXG | S_IRWXO);
1346 
1347  if (mkdir_ret != OK && errno != EEXIST) {
1348  PX4_ERR("failed creating new dir: %s", log_dir);
1349  return -1;
1350  }
1351 
1352  } else {
1353  uint16_t dir_number = file_name.sess_dir_index;
1354 
1355  if (file_name.has_log_dir) {
1356  strncpy(log_dir + n, file_name.log_dir, log_dir_len - n);
1357  }
1358 
1359  /* look for the next dir that does not exist */
1360  while (!file_name.has_log_dir) {
1361  /* format log dir: e.g. /fs/microsd/log/sess001 */
1362  int n2 = snprintf(file_name.log_dir, sizeof(LogFileName::log_dir), "sess%03u", dir_number);
1363 
1364  if (n2 >= (int)sizeof(LogFileName::log_dir)) {
1365  PX4_ERR("log path too long (%i)", n);
1366  return -1;
1367  }
1368 
1369  strncpy(log_dir + n, file_name.log_dir, log_dir_len - n);
1370  int mkdir_ret = mkdir(log_dir, S_IRWXU | S_IRWXG | S_IRWXO);
1371 
1372  if (mkdir_ret == 0) {
1373  PX4_DEBUG("log dir created: %s", log_dir);
1374  file_name.has_log_dir = true;
1375 
1376  } else if (errno != EEXIST) {
1377  PX4_ERR("failed creating new dir: %s (%i)", log_dir, errno);
1378  return -1;
1379  }
1380 
1381  /* dir exists already */
1382  dir_number++;
1383  }
1384 
1385  file_name.has_log_dir = true;
1386  }
1387 
1388  return strlen(log_dir);
1389 }
1390 
1391 int Logger::get_log_file_name(LogType type, char *file_name, size_t file_name_size)
1392 {
1393  tm tt = {};
1394  bool time_ok = false;
1395 
1396  if (_log_name_timestamp) {
1397  int32_t utc_offset = 0;
1398 
1399  if (_log_utc_offset != PARAM_INVALID) {
1400  param_get(_log_utc_offset, &utc_offset);
1401  }
1402 
1403  /* use RTC time for log file naming, e.g. /fs/microsd/log/2014-01-19/19_37_52.ulg */
1404  time_ok = util::get_log_time(&tt, utc_offset * 60, false);
1405  }
1406 
1407  const char *replay_suffix = "";
1408 
1409  if (_replay_file_name) {
1410  replay_suffix = "_replayed";
1411  }
1412 
1413  char *log_file_name = _file_name[(int)type].log_file_name;
1414 
1415  if (time_ok) {
1416  int n = create_log_dir(type, &tt, file_name, file_name_size);
1417 
1418  if (n < 0) {
1419  return -1;
1420  }
1421 
1422  char log_file_name_time[16] = "";
1423  strftime(log_file_name_time, sizeof(log_file_name_time), "%H_%M_%S", &tt);
1424  snprintf(log_file_name, sizeof(LogFileName::log_file_name), "%s%s.ulg", log_file_name_time, replay_suffix);
1425  snprintf(file_name + n, file_name_size - n, "/%s", log_file_name);
1426 
1427  } else {
1428  int n = create_log_dir(type, nullptr, file_name, file_name_size);
1429 
1430  if (n < 0) {
1431  return -1;
1432  }
1433 
1434  uint16_t file_number = 1; // start with file log001
1435 
1436  /* look for the next file that does not exist */
1437  while (file_number <= MAX_NO_LOGFILE) {
1438  /* format log file path: e.g. /fs/microsd/log/sess001/log001.ulg */
1439  snprintf(log_file_name, sizeof(LogFileName::log_file_name), "log%03u%s.ulg", file_number, replay_suffix);
1440  snprintf(file_name + n, file_name_size - n, "/%s", log_file_name);
1441 
1442  if (!util::file_exist(file_name)) {
1443  break;
1444  }
1445 
1446  file_number++;
1447  }
1448 
1449  if (file_number > MAX_NO_LOGFILE) {
1450  /* we should not end up here, either we have more than MAX_NO_LOGFILE on the SD card, or another problem */
1451  return -1;
1452  }
1453  }
1454 
1455  return 0;
1456 }
1457 
1458 void Logger::setReplayFile(const char *file_name)
1459 {
1460  if (_replay_file_name) {
1461  free(_replay_file_name);
1462  }
1463 
1464  _replay_file_name = strdup(file_name);
1465 }
1466 
1468 {
1470  return;
1471  }
1472 
1473  PX4_INFO("Start file log (type: %s)", log_type_str(type));
1474 
1475  char file_name[LOG_DIR_LEN] = "";
1476 
1477  if (get_log_file_name(type, file_name, sizeof(file_name))) {
1478  PX4_ERR("failed to get log file name");
1479  return;
1480  }
1481 
1482  if (type == LogType::Full) {
1483  /* print logging path, important to find log file later */
1484  mavlink_log_info(&_mavlink_log_pub, "[logger] file: %s", file_name);
1485  }
1486 
1487  _writer.start_log_file(type, file_name);
1490  write_header(type);
1491  write_version(type);
1492  write_formats(type);
1493 
1494  if (type == LogType::Full) {
1495  write_parameters(type);
1496  write_perf_data(true);
1498  }
1499 
1503  _writer.notify();
1504 
1505  if (type == LogType::Full) {
1506  /* reset performance counters to get in-flight min and max values in post flight log */
1507  perf_reset_all();
1508 
1510  }
1511 
1512  _statistics[(int)type].start_time_file = hrt_absolute_time();
1513 
1514 }
1515 
1517 {
1519  return;
1520  }
1521 
1522  if (type == LogType::Full) {
1524  write_perf_data(false);
1526  }
1527 
1528  _writer.stop_log_file(type);
1529 }
1530 
1532 {
1533  if (!can_start_mavlink_log()) {
1534  return;
1535  }
1536 
1537  PX4_INFO("Start mavlink log");
1538 
1546  write_perf_data(true);
1551  _writer.notify();
1552 
1554 }
1555 
1557 {
1558  // don't write perf data since a client does not expect more data after a stop command
1559  PX4_INFO("Stop mavlink log");
1561 }
1562 
1565  int counter;
1567  char *buffer;
1568 };
1569 
1571 {
1572  perf_callback_data_t *callback_data = (perf_callback_data_t *)user;
1573  const int buffer_length = 220;
1574  char buffer[buffer_length];
1575  const char *perf_name;
1576 
1577  perf_print_counter_buffer(buffer, buffer_length, handle);
1578 
1579  if (callback_data->preflight) {
1580  perf_name = "perf_counter_preflight";
1581 
1582  } else {
1583  perf_name = "perf_counter_postflight";
1584  }
1585 
1586  callback_data->logger->write_info_multiple(LogType::Full, perf_name, buffer, callback_data->counter != 0);
1587  ++callback_data->counter;
1588 }
1589 
1590 void Logger::write_perf_data(bool preflight)
1591 {
1592  perf_callback_data_t callback_data = {};
1593  callback_data.logger = this;
1594  callback_data.counter = 0;
1595  callback_data.preflight = preflight;
1596 
1597  // write the perf counters
1598  perf_iterate_all(perf_iterate_callback, &callback_data);
1599 }
1600 
1601 
1603 {
1604  perf_callback_data_t *callback_data = (perf_callback_data_t *)user;
1605  const char *perf_name;
1606 
1607  if (!callback_data->buffer) {
1608  return;
1609  }
1610 
1611  switch (callback_data->logger->_print_load_reason) {
1613  perf_name = "perf_top_preflight";
1614  break;
1615 
1617  perf_name = "perf_top_postflight";
1618  break;
1619 
1621  perf_name = "perf_top_watchdog";
1622  break;
1623 
1624  default:
1625  perf_name = "perf_top";
1626  break;
1627  }
1628 
1629  callback_data->logger->write_info_multiple(LogType::Full, perf_name, callback_data->buffer,
1630  callback_data->counter != 0);
1631  ++callback_data->counter;
1632 }
1633 
1635 {
1636  perf_callback_data_t callback_data;
1637  callback_data.logger = this;
1638  callback_data.counter = 0;
1639  callback_data.buffer = nullptr;
1640  char buffer[140];
1641  hrt_abstime curr_time = hrt_absolute_time();
1642  init_print_load_s(curr_time, &_load);
1643  // this will not yet print anything
1644  print_load_buffer(curr_time, buffer, sizeof(buffer), print_load_callback, &callback_data, &_load);
1645  _next_load_print = curr_time + 1000000;
1646  _print_load_reason = reason;
1647 }
1648 
1650 {
1652  PX4_ERR("Writing watchdog data"); // this is just that we see it easily in the log
1653  }
1654 
1655  perf_callback_data_t callback_data = {};
1656  char buffer[140];
1657  callback_data.logger = this;
1658  callback_data.counter = 0;
1659  callback_data.buffer = buffer;
1660  hrt_abstime curr_time = hrt_absolute_time();
1662  // TODO: maybe we should restrict the output to a selected backend (eg. when file logging is running
1663  // and mavlink log is started, this will be added to the file as well)
1664  print_load_buffer(curr_time, buffer, sizeof(buffer), print_load_callback, &callback_data, &_load);
1666 }
1667 
1669 {
1670  const int buffer_length = 220;
1671  char buffer[buffer_length];
1672  int size = px4_console_buffer_size();
1673  int offset = -1;
1674  bool first = true;
1675 
1676  while (size > 0) {
1677  int read_size = px4_console_buffer_read(buffer, buffer_length - 1, &offset);
1678 
1679  if (read_size <= 0) { break; }
1680 
1681  buffer[math::min(read_size, size)] = '\0';
1682  write_info_multiple(LogType::Full, "boot_console_output", buffer, !first);
1683 
1684  size -= read_size;
1685  first = false;
1686  }
1687 
1688 }
1689 
1690 void Logger::write_format(LogType type, const orb_metadata &meta, WrittenFormats &written_formats,
1691  ulog_message_format_s &msg, int level)
1692 {
1693  if (level > 3) {
1694  // precaution: limit recursion level. If we land here it's either a bug or nested topic definitions. In the
1695  // latter case, increase the maximum level.
1696  PX4_ERR("max recursion level reached (%i)", level);
1697  return;
1698  }
1699 
1700  // check if we already wrote the format
1701  for (const auto &written_format : written_formats) {
1702  if (written_format == &meta) {
1703  return;
1704  }
1705  }
1706 
1707  // Write the current format (we don't need to check if we already added it to written_formats)
1708  int format_len = snprintf(msg.format, sizeof(msg.format), "%s:%s", meta.o_name, meta.o_fields);
1709  size_t msg_size = sizeof(msg) - sizeof(msg.format) + format_len;
1710  msg.msg_size = msg_size - ULOG_MSG_HEADER_LEN;
1711 
1712  write_message(type, &msg, msg_size);
1713 
1714  if (!written_formats.push_back(&meta)) {
1715  PX4_ERR("Array too small");
1716  }
1717 
1718  // Now go through the fields and check for nested type usages.
1719  // o_fields looks like this for example: "uint64_t timestamp;uint8_t[5] array;"
1720  const char *fmt = meta.o_fields;
1721 
1722  while (fmt && *fmt) {
1723  // extract the type name
1724  char type_name[64];
1725  const char *space = strchr(fmt, ' ');
1726 
1727  if (!space) {
1728  PX4_ERR("invalid format %s", fmt);
1729  break;
1730  }
1731 
1732  const char *array_start = strchr(fmt, '['); // check for an array
1733 
1734  int type_length;
1735 
1736  if (array_start && array_start < space) {
1737  type_length = array_start - fmt;
1738 
1739  } else {
1740  type_length = space - fmt;
1741  }
1742 
1743  if (type_length >= (int)sizeof(type_name)) {
1744  PX4_ERR("buf len too small");
1745  break;
1746  }
1747 
1748  memcpy(type_name, fmt, type_length);
1749  type_name[type_length] = '\0';
1750 
1751  // ignore built-in types
1752  if (strcmp(type_name, "int8_t") != 0 &&
1753  strcmp(type_name, "uint8_t") != 0 &&
1754  strcmp(type_name, "int16_t") != 0 &&
1755  strcmp(type_name, "uint16_t") != 0 &&
1756  strcmp(type_name, "int16_t") != 0 &&
1757  strcmp(type_name, "uint16_t") != 0 &&
1758  strcmp(type_name, "int32_t") != 0 &&
1759  strcmp(type_name, "uint32_t") != 0 &&
1760  strcmp(type_name, "int64_t") != 0 &&
1761  strcmp(type_name, "uint64_t") != 0 &&
1762  strcmp(type_name, "float") != 0 &&
1763  strcmp(type_name, "double") != 0 &&
1764  strcmp(type_name, "bool") != 0 &&
1765  strcmp(type_name, "char") != 0) {
1766 
1767  // find orb meta for type
1768  const orb_metadata *const *topics = orb_get_topics();
1769  const orb_metadata *found_topic = nullptr;
1770 
1771  for (size_t i = 0; i < orb_topics_count(); i++) {
1772  if (strcmp(topics[i]->o_name, type_name) == 0) {
1773  found_topic = topics[i];
1774  }
1775  }
1776 
1777  if (found_topic) {
1778 
1779  write_format(type, *found_topic, written_formats, msg, level + 1);
1780 
1781  } else {
1782  PX4_ERR("No definition for topic %s found", fmt);
1783  }
1784  }
1785 
1786  fmt = strchr(fmt, ';');
1787 
1788  if (fmt) { ++fmt; }
1789  }
1790 }
1791 
1793 {
1794  _writer.lock();
1795 
1796  // both of these are large and thus we need to be careful in terms of stack size requirements
1798  WrittenFormats written_formats;
1799 
1800  // write all subscribed formats
1801  size_t sub_count = _subscriptions.size();
1802 
1803  if (type == LogType::Mission) {
1804  sub_count = _num_mission_subs;
1805  }
1806 
1807  for (size_t i = 0; i < sub_count; ++i) {
1808  const LoggerSubscription &sub = _subscriptions[i];
1809  write_format(type, *sub.get_topic(), written_formats, msg);
1810  }
1811 
1812  _writer.unlock();
1813 }
1814 
1816 {
1817  _writer.lock();
1818 
1819  size_t sub_count = _subscriptions.size();
1820 
1821  if (type == LogType::Mission) {
1822  sub_count = _num_mission_subs;
1823  }
1824 
1825  for (size_t i = 0; i < sub_count; ++i) {
1827 
1828  if (sub.valid()) {
1829  write_add_logged_msg(type, sub);
1830  }
1831  }
1832 
1833  _writer.unlock();
1834 }
1835 
1837 {
1839 
1840  if (subscription.msg_id == MSG_ID_INVALID) {
1841  if (_next_topic_id == MSG_ID_INVALID) {
1842  // if we land here an uint8 is too small -> switch to uint16
1843  PX4_ERR("limit for _next_topic_id reached");
1844  return;
1845  }
1846 
1847  subscription.msg_id = _next_topic_id++;
1848  }
1849 
1850  msg.msg_id = subscription.msg_id;
1851  msg.multi_id = subscription.get_instance();
1852 
1853  int message_name_len = strlen(subscription.get_topic()->o_name);
1854 
1855  memcpy(msg.message_name, subscription.get_topic()->o_name, message_name_len);
1856 
1857  size_t msg_size = sizeof(msg) - sizeof(msg.message_name) + message_name_len;
1858  msg.msg_size = msg_size - ULOG_MSG_HEADER_LEN;
1859 
1860  bool prev_reliable = _writer.need_reliable_transfer();
1862  write_message(type, &msg, msg_size);
1863  _writer.set_need_reliable_transfer(prev_reliable);
1864 }
1865 
1866 void Logger::write_info(LogType type, const char *name, const char *value)
1867 {
1868  _writer.lock();
1870  uint8_t *buffer = reinterpret_cast<uint8_t *>(&msg);
1871  msg.msg_type = static_cast<uint8_t>(ULogMessageType::INFO);
1872 
1873  /* construct format key (type and name) */
1874  size_t vlen = strlen(value);
1875  msg.key_len = snprintf(msg.key, sizeof(msg.key), "char[%zu] %s", vlen, name);
1876  size_t msg_size = sizeof(msg) - sizeof(msg.key) + msg.key_len;
1877 
1878  /* copy string value directly to buffer */
1879  if (vlen < (sizeof(msg) - msg_size)) {
1880  memcpy(&buffer[msg_size], value, vlen);
1881  msg_size += vlen;
1882 
1883  msg.msg_size = msg_size - ULOG_MSG_HEADER_LEN;
1884 
1885  write_message(type, buffer, msg_size);
1886  }
1887 
1888  _writer.unlock();
1889 }
1890 
1891 void Logger::write_info_multiple(LogType type, const char *name, const char *value, bool is_continued)
1892 {
1893  _writer.lock();
1895  uint8_t *buffer = reinterpret_cast<uint8_t *>(&msg);
1896  msg.msg_type = static_cast<uint8_t>(ULogMessageType::INFO_MULTIPLE);
1897  msg.is_continued = is_continued;
1898 
1899  /* construct format key (type and name) */
1900  size_t vlen = strlen(value);
1901  msg.key_len = snprintf(msg.key, sizeof(msg.key), "char[%zu] %s", vlen, name);
1902  size_t msg_size = sizeof(msg) - sizeof(msg.key) + msg.key_len;
1903 
1904  /* copy string value directly to buffer */
1905  if (vlen < (sizeof(msg) - msg_size)) {
1906  memcpy(&buffer[msg_size], value, vlen);
1907  msg_size += vlen;
1908 
1909  msg.msg_size = msg_size - ULOG_MSG_HEADER_LEN;
1910 
1911  write_message(type, buffer, msg_size);
1912 
1913  } else {
1914  PX4_ERR("info_multiple str too long (%i), key=%s", msg.key_len, msg.key);
1915  }
1916 
1917  _writer.unlock();
1918 }
1919 
1920 void Logger::write_info(LogType type, const char *name, int32_t value)
1921 {
1922  write_info_template<int32_t>(type, name, value, "int32_t");
1923 }
1924 
1925 void Logger::write_info(LogType type, const char *name, uint32_t value)
1926 {
1927  write_info_template<uint32_t>(type, name, value, "uint32_t");
1928 }
1929 
1930 
1931 template<typename T>
1932 void Logger::write_info_template(LogType type, const char *name, T value, const char *type_str)
1933 {
1934  _writer.lock();
1936  uint8_t *buffer = reinterpret_cast<uint8_t *>(&msg);
1937  msg.msg_type = static_cast<uint8_t>(ULogMessageType::INFO);
1938 
1939  /* construct format key (type and name) */
1940  msg.key_len = snprintf(msg.key, sizeof(msg.key), "%s %s", type_str, name);
1941  size_t msg_size = sizeof(msg) - sizeof(msg.key) + msg.key_len;
1942 
1943  /* copy string value directly to buffer */
1944  memcpy(&buffer[msg_size], &value, sizeof(T));
1945  msg_size += sizeof(T);
1946 
1947  msg.msg_size = msg_size - ULOG_MSG_HEADER_LEN;
1948 
1949  write_message(type, buffer, msg_size);
1950 
1951  _writer.unlock();
1952 }
1953 
1955 {
1956  ulog_file_header_s header = {};
1957  header.magic[0] = 'U';
1958  header.magic[1] = 'L';
1959  header.magic[2] = 'o';
1960  header.magic[3] = 'g';
1961  header.magic[4] = 0x01;
1962  header.magic[5] = 0x12;
1963  header.magic[6] = 0x35;
1964  header.magic[7] = 0x01; //file version 1
1965  header.timestamp = hrt_absolute_time();
1966  _writer.lock();
1967  write_message(type, &header, sizeof(header));
1968 
1969  // write the Flags message: this MUST be written right after the ulog header
1970  ulog_message_flag_bits_s flag_bits{};
1971 
1972  flag_bits.msg_size = sizeof(flag_bits) - ULOG_MSG_HEADER_LEN;
1973  flag_bits.msg_type = static_cast<uint8_t>(ULogMessageType::FLAG_BITS);
1974 
1975  write_message(type, &flag_bits, sizeof(flag_bits));
1976 
1977  _writer.unlock();
1978 }
1979 
1981 {
1982  write_info(type, "ver_sw", px4_firmware_version_string());
1983  write_info(type, "ver_sw_release", px4_firmware_version());
1984  uint32_t vendor_version = px4_firmware_vendor_version();
1985 
1986  if (vendor_version > 0) {
1987  write_info(type, "ver_vendor_sw_release", vendor_version);
1988  }
1989 
1990  write_info(type, "ver_hw", px4_board_name());
1991  const char *board_sub_type = px4_board_sub_type();
1992 
1993  if (board_sub_type && board_sub_type[0]) {
1994  write_info(type, "ver_hw_subtype", board_sub_type);
1995  }
1996 
1997  write_info(type, "sys_name", "PX4");
1998  write_info(type, "sys_os_name", px4_os_name());
1999  const char *os_version = px4_os_version_string();
2000 
2001  const char *git_branch = px4_firmware_git_branch();
2002 
2003  if (git_branch && git_branch[0]) {
2004  write_info(type, "ver_sw_branch", git_branch);
2005  }
2006 
2007  if (os_version) {
2008  write_info(type, "sys_os_ver", os_version);
2009  }
2010 
2011  write_info(type, "sys_os_ver_release", px4_os_version());
2012  write_info(type, "sys_toolchain", px4_toolchain_name());
2013  write_info(type, "sys_toolchain_ver", px4_toolchain_version());
2014 
2015  const char *ecl_version = px4_ecl_lib_version_string();
2016 
2017  if (ecl_version && ecl_version[0]) {
2018  write_info(type, "sys_lib_ecl_ver", ecl_version);
2019  }
2020 
2021  char revision = 'U';
2022  const char *chip_name = nullptr;
2023 
2024  if (board_mcu_version(&revision, &chip_name, nullptr) >= 0) {
2025  char mcu_ver[64];
2026  snprintf(mcu_ver, sizeof(mcu_ver), "%s, rev. %c", chip_name, revision);
2027  write_info(type, "sys_mcu", mcu_ver);
2028  }
2029 
2030 #ifndef BOARD_HAS_NO_UUID
2031  /* write the UUID if enabled */
2032  param_t write_uuid_param = param_find("SDLOG_UUID");
2033 
2034  if (write_uuid_param != PARAM_INVALID) {
2035  int32_t write_uuid;
2036  param_get(write_uuid_param, &write_uuid);
2037 
2038  if (write_uuid == 1) {
2039  char px4_uuid_string[PX4_GUID_FORMAT_SIZE];
2040  board_get_px4_guid_formated(px4_uuid_string, sizeof(px4_uuid_string));
2041  write_info(type, "sys_uuid", px4_uuid_string);
2042  }
2043  }
2044 
2045 #endif /* BOARD_HAS_NO_UUID */
2046 
2047  int32_t utc_offset = 0;
2048 
2049  if (_log_utc_offset != PARAM_INVALID) {
2050  param_get(_log_utc_offset, &utc_offset);
2051  write_info(type, "time_ref_utc", utc_offset * 60);
2052  }
2053 
2054  if (_replay_file_name) {
2055  write_info(type, "replay", _replay_file_name);
2056  }
2057 
2058  if (type == LogType::Mission) {
2059  write_info(type, "log_type", "mission");
2060  }
2061 }
2062 
2064 {
2065  _writer.lock();
2067  uint8_t *buffer = reinterpret_cast<uint8_t *>(&msg);
2068 
2069  msg.msg_type = static_cast<uint8_t>(ULogMessageType::PARAMETER);
2070  int param_idx = 0;
2071  param_t param = 0;
2072 
2073  do {
2074  // skip over all parameters which are not invalid and not used
2075  do {
2076  param = param_for_index(param_idx);
2077  ++param_idx;
2078  } while (param != PARAM_INVALID && !param_used(param));
2079 
2080  // save parameters which are valid AND used
2081  if (param != PARAM_INVALID) {
2082  // get parameter type and size
2083  const char *type_str;
2084  param_type_t ptype = param_type(param);
2085  size_t value_size = 0;
2086 
2087  switch (ptype) {
2088  case PARAM_TYPE_INT32:
2089  type_str = "int32_t";
2090  value_size = sizeof(int32_t);
2091  break;
2092 
2093  case PARAM_TYPE_FLOAT:
2094  type_str = "float";
2095  value_size = sizeof(float);
2096  break;
2097 
2098  default:
2099  continue;
2100  }
2101 
2102  // format parameter key (type and name)
2103  msg.key_len = snprintf(msg.key, sizeof(msg.key), "%s %s", type_str, param_name(param));
2104  size_t msg_size = sizeof(msg) - sizeof(msg.key) + msg.key_len;
2105 
2106  // copy parameter value directly to buffer
2107  switch (ptype) {
2108  case PARAM_TYPE_INT32:
2109  param_get(param, (int32_t *)&buffer[msg_size]);
2110  break;
2111 
2112  case PARAM_TYPE_FLOAT:
2113  param_get(param, (float *)&buffer[msg_size]);
2114  break;
2115 
2116  default:
2117  continue;
2118  }
2119 
2120  msg_size += value_size;
2121 
2122  msg.msg_size = msg_size - ULOG_MSG_HEADER_LEN;
2123 
2124  write_message(type, buffer, msg_size);
2125  }
2126  } while ((param != PARAM_INVALID) && (param_idx < (int) param_count()));
2127 
2128  _writer.unlock();
2129  _writer.notify();
2130 }
2131 
2133 {
2134  _writer.lock();
2136  uint8_t *buffer = reinterpret_cast<uint8_t *>(&msg);
2137 
2138  msg.msg_type = static_cast<uint8_t>(ULogMessageType::PARAMETER);
2139  int param_idx = 0;
2140  param_t param = 0;
2141 
2142  do {
2143  // skip over all parameters which are not invalid and not used
2144  do {
2145  param = param_for_index(param_idx);
2146  ++param_idx;
2147  } while (param != PARAM_INVALID && !param_used(param));
2148 
2149  // log parameters which are valid AND used AND unsaved
2150  if ((param != PARAM_INVALID) && param_value_unsaved(param)) {
2151 
2152  // get parameter type and size
2153  const char *type_str;
2154  param_type_t ptype = param_type(param);
2155  size_t value_size = 0;
2156 
2157  switch (ptype) {
2158  case PARAM_TYPE_INT32:
2159  type_str = "int32_t";
2160  value_size = sizeof(int32_t);
2161  break;
2162 
2163  case PARAM_TYPE_FLOAT:
2164  type_str = "float";
2165  value_size = sizeof(float);
2166  break;
2167 
2168  default:
2169  continue;
2170  }
2171 
2172  // format parameter key (type and name)
2173  msg.key_len = snprintf(msg.key, sizeof(msg.key), "%s %s", type_str, param_name(param));
2174  size_t msg_size = sizeof(msg) - sizeof(msg.key) + msg.key_len;
2175 
2176  // copy parameter value directly to buffer
2177  switch (ptype) {
2178  case PARAM_TYPE_INT32:
2179  param_get(param, (int32_t *)&buffer[msg_size]);
2180  break;
2181 
2182  case PARAM_TYPE_FLOAT:
2183  param_get(param, (float *)&buffer[msg_size]);
2184  break;
2185 
2186  default:
2187  continue;
2188  }
2189 
2190  msg_size += value_size;
2191 
2192  // msg_size is now 1 (msg_type) + 2 (msg_size) + 1 (key_len) + key_len + value_size
2193  msg.msg_size = msg_size - ULOG_MSG_HEADER_LEN;
2194 
2195  write_message(type, buffer, msg_size);
2196  }
2197  } while ((param != PARAM_INVALID) && (param_idx < (int) param_count()));
2198 
2199  _writer.unlock();
2200  _writer.notify();
2201 }
2202 
2204 {
2205  vehicle_command_ack_s vehicle_command_ack = {};
2206  vehicle_command_ack.timestamp = hrt_absolute_time();
2207  vehicle_command_ack.command = cmd->command;
2208  vehicle_command_ack.result = (uint8_t)result;
2209  vehicle_command_ack.target_system = cmd->source_system;
2210  vehicle_command_ack.target_component = cmd->source_component;
2211 
2212  uORB::PublicationQueued<vehicle_command_ack_s> cmd_ack_pub{ORB_ID(vehicle_command_ack)};
2213  cmd_ack_pub.publish(vehicle_command_ack);
2214 }
2215 
2216 int Logger::print_usage(const char *reason)
2217 {
2218  if (reason) {
2219  PX4_WARN("%s\n", reason);
2220  }
2221 
2222  PRINT_MODULE_DESCRIPTION(
2223  R"DESCR_STR(
2224 ### Description
2225 System logger which logs a configurable set of uORB topics and system printf messages
2226 (`PX4_WARN` and `PX4_ERR`) to ULog files. These can be used for system and flight performance evaluation,
2227 tuning, replay and crash analysis.
2228 
2229 It supports 2 backends:
2230 - Files: write ULog files to the file system (SD card)
2231 - MAVLink: stream ULog data via MAVLink to a client (the client must support this)
2232 
2233 Both backends can be enabled and used at the same time.
2234 
2235 The file backend supports 2 types of log files: full (the normal log) and a mission
2236 log. The mission log is a reduced ulog file and can be used for example for geotagging or
2237 vehicle management. It can be enabled and configured via SDLOG_MISSION parameter.
2238 The normal log is always a superset of the mission log.
2239 
2240 ### Implementation
2241 The implementation uses two threads:
2242 - The main thread, running at a fixed rate (or polling on a topic if started with -p) and checking for
2243  data updates
2244 - The writer thread, writing data to the file
2245 
2246 In between there is a write buffer with configurable size (and another fixed-size buffer for
2247 the mission log). It should be large to avoid dropouts.
2248 
2249 ### Examples
2250 Typical usage to start logging immediately:
2251 $ logger start -e -t
2252 
2253 Or if already running:
2254 $ logger on
2255 )DESCR_STR");
2256 
2257  PRINT_MODULE_USAGE_NAME("logger", "system");
2258  PRINT_MODULE_USAGE_COMMAND("start");
2259  PRINT_MODULE_USAGE_PARAM_STRING('m', "all", "file|mavlink|all", "Backend mode", true);
2260  PRINT_MODULE_USAGE_PARAM_FLAG('x', "Enable/disable logging via Aux1 RC channel", true);
2261  PRINT_MODULE_USAGE_PARAM_FLAG('e', "Enable logging right after start until disarm (otherwise only when armed)", true);
2262  PRINT_MODULE_USAGE_PARAM_FLAG('f', "Log until shutdown (implies -e)", true);
2263  PRINT_MODULE_USAGE_PARAM_FLAG('t', "Use date/time for naming log directories and files", true);
2264  PRINT_MODULE_USAGE_PARAM_INT('r', 280, 0, 8000, "Log rate in Hz, 0 means unlimited rate", true);
2265  PRINT_MODULE_USAGE_PARAM_INT('b', 12, 4, 10000, "Log buffer size in KiB", true);
2266  PRINT_MODULE_USAGE_PARAM_STRING('p', nullptr, "<topic_name>",
2267  "Poll on a topic instead of running with fixed rate (Log rate and topic intervals are ignored if this is set)", true);
2268  PRINT_MODULE_USAGE_COMMAND_DESCR("on", "start logging now, override arming (logger must be running)");
2269  PRINT_MODULE_USAGE_COMMAND_DESCR("off", "stop logging now, override arming (logger must be running)");
2270  PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
2271 
2272  return 0;
2273 }
2274 
2275 } // namespace logger
2276 } // namespace px4
char text[127]
Definition: log_message.h:55
bool write_message(LogType type, void *ptr, size_t size)
Write exactly one ulog message to the logger and handle dropouts.
Definition: logger.cpp:1302
uint64_t timestamp
Definition: messages.h:58
#define PARAM_INVALID
Handle returned when a parameter cannot be found.
Definition: param.h:103
SDLogProfileMask
Definition: logger.h:64
orb_advert_t _mavlink_log_pub
Definition: logger.h:391
hrt_abstime dropout_start
start of current dropout (0 = no dropout)
Definition: logger.h:192
param_t _log_utc_offset
Definition: logger.h:406
void add_high_rate_topics()
Definition: logger.cpp:580
#define LOG_DIR_LEN
Definition: util.h:44
void write_parameters(LogType type)
Definition: logger.cpp:2063
int perf_print_counter_buffer(char *buffer, int length, perf_counter_t handle)
Print one performance counter to a buffer.
int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
Definition: uORB.cpp:90
LogFileName _file_name[(int) LogType::Count]
Definition: logger.h:373
bool copy(void *dst)
Copy the struct.
__EXPORT bool param_value_unsaved(param_t param)
Test whether a parameter&#39;s value has been changed but not saved.
Definition: parameters.cpp:508
const char * px4_os_name(void)
name of the operating system
Definition: version.c:297
static void perf_iterate_callback(perf_counter_t handle, void *user)
callback to write the performance counters
Definition: logger.cpp:1570
const struct orb_metadata *const * orb_get_topics() __EXPORT
uint64_t timestamp
Definition: log_message.h:53
static const char * px4_board_sub_type(void)
get the board sub type
Definition: version.h:62
Backend backend() const
Definition: log_writer.h:63
uint32_t px4_firmware_version(void)
get the PX4 Firmware version
Definition: version.c:149
uint8_t Backend
bitfield to specify a backend
Definition: log_writer.h:53
Manages starting, stopping & writing of logged data using the configured backend. ...
Definition: log_writer.h:48
const char * px4_toolchain_version(void)
Toolchain version used to compile PX4 (no particular format)
Definition: version.c:329
#define PARAM_TYPE_INT32
Parameter types.
Definition: param.h:60
bool update(void *dst)
Copy the struct if updated.
char log_dir[12]
e.g. "2018-01-01" or "sess001"
Definition: logger.h:184
__EXPORT int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
PrintLoadReason _print_load_reason
Definition: logger.h:398
volatile bool watchdog_triggered
Definition: logger.cpp:86
void write_info(LogType type, const char *name, const char *value)
Definition: logger.cpp:1866
print_load_s _load
process load data
Definition: logger.h:396
const orb_metadata * _polling_topic_meta
if non-null, poll on this topic instead of sleeping
Definition: logger.h:390
unsigned min_delta_ms
minimum time between 2 topic writes [ms]
Definition: logger.h:199
void write_header(LogType type)
write the file header with file magic and timestamp.
Definition: logger.cpp:1954
void start_log_mavlink()
Definition: logger.cpp:1531
void write_changed_parameters(LogType type)
Definition: logger.cpp:2132
uint8_t _next_topic_id
id of next subscribed ulog topic
Definition: logger.h:392
static constexpr uint8_t MSG_ID_INVALID
Definition: logger.h:86
uint16_t param_type_t
Definition: param.h:66
const bool _log_name_timestamp
Definition: logger.h:382
void initialize_load_output(PrintLoadReason reason)
initialize the output for the process load, so that ~1 second later it will be written to the log ...
Definition: logger.cpp:1634
void thread_stop()
stop all running threads and wait for them to exit
Definition: log_writer.cpp:154
LogType
Defines different log (file) types.
int main(int argc, char **argv)
Definition: main.cpp:3
char log_file_name[31]
e.g. "log001.ulg" or "12_09_00_replayed.ulg"
Definition: logger.h:186
void ack_vehicle_command(vehicle_command_s *cmd, uint32_t result)
Definition: logger.cpp:2203
bool _manually_logging_override
Definition: logger.h:376
bool copy_if_updated(int sub_idx, void *buffer, bool try_to_subscribe)
Definition: logger.cpp:484
pthread_t thread_id_file() const
Definition: log_writer.h:141
reduced mission log (e.g. for geotagging)
static constexpr Backend BackendFile
Definition: log_writer.h:54
void start_log_file(LogType type)
Definition: logger.cpp:1467
bool _should_stop_file_log
if true _next_load_print is set and file logging will be stopped after load printing (for the full lo...
Definition: logger.h:394
bool add_topic(const char *name, uint32_t interval_ms=0, uint8_t instance=0)
Add a topic to be logged.
Definition: logger.cpp:434
void stop_log_mavlink()
Definition: logger.cpp:1556
size_t high_water
maximum used write buffer
Definition: logger.h:195
static int print_usage(const char *reason=nullptr)
Definition: logger.cpp:2216
void add_sensor_comparison_topics()
Definition: logger.cpp:630
__EXPORT param_t param_for_index(unsigned index)
Look up a parameter by index.
Definition: parameters.cpp:408
void perf_reset_all(void)
Reset all of the performance counters.
int px4_poll(px4_pollfd_struct_t *fds, nfds_t nfds, int timeout)
Statistics _statistics[(int) LogType::Count]
Definition: logger.h:378
static Logger * instantiate(int argc, char *argv[])
Definition: logger.cpp:233
int write_message(LogType type, void *ptr, size_t size, uint64_t dropout_start=0)
Write a single ulog message (including header).
Definition: log_writer.cpp:161
void watchdog_initialize(const pid_t pid_logger_main, const pthread_t writer_thread, watchdog_data_t &watchdog_data)
Initialize the watchdog, fill in watchdog_data.
Definition: watchdog.cpp:134
bool need_reliable_transfer() const
Definition: log_writer.h:160
void write_console_output()
write bootup console output
Definition: logger.cpp:1668
const char * px4_firmware_git_branch(void)
get the git branch name (can be empty, for example if HEAD points to a tag)
Definition: version.c:245
MissionLogType
Definition: logger.h:75
void start_log_file(LogType type, const char *filename)
Definition: log_writer.cpp:126
Limiting / constrain helper functions.
bool watchdog_update(watchdog_data_t &watchdog_data)
Update the watchdog and trigger it if necessary.
Definition: watchdog.cpp:49
static void print_usage()
void write_perf_data(bool preflight)
write performance counters
Definition: logger.cpp:1590
const char * px4_toolchain_name(void)
Toolchain name used to compile PX4.
Definition: version.c:314
void add_thermal_calibration_topics()
Definition: logger.cpp:623
static bool is_running()
Definition: dataman.cpp:415
LidarLite * instance
Definition: ll40ls.cpp:65
void write_format(LogType type, const orb_metadata &meta, WrittenFormats &written_formats, ulog_message_format_s &msg, int level=1)
Definition: logger.cpp:1690
bool is_started(LogType type) const
whether logging is currently active or not (any of the selected backends).
Definition: log_writer.cpp:98
__EXPORT unsigned param_count(void)
Return the total number of parameters.
Definition: parameters.cpp:382
uORB::Subscription _vehicle_status_sub
Definition: logger.h:402
uint8_t is_continued
can be used for arrays: set to 1, if this message is part of the previous with the same key ...
Definition: messages.h:123
High-resolution timer with callouts and timekeeping.
void add_estimator_replay_topics()
Definition: logger.cpp:602
void write_add_logged_msg(LogType type, LoggerSubscription &subscription)
Write an ADD_LOGGED_MSG to the log for a given subscription and instance.
Definition: logger.cpp:1836
int add_topics_from_file(const char *fname)
Parse a file containing a list of uORB topics to log, calling add_topic for each. ...
Definition: logger.cpp:658
void stop_log_file(LogType type)
Definition: log_writer.cpp:133
const char * o_name
unique object name
Definition: uORB.h:51
void write_load_output()
write the process load, which was previously initialized with initialize_load_output() ...
Definition: logger.cpp:1649
uORB::Subscription _vehicle_command_sub
Definition: logger.h:401
static char msg[NUM_MSG][CONFIG_USART1_TXBUFSIZE]
Definition: px4io.c:89
static void print_load_callback(void *user)
callback for print_load_buffer() to print the process load
Definition: logger.cpp:1602
int orb_subscribe(const struct orb_metadata *meta)
Definition: uORB.cpp:75
void setReplayFile(const char *file_name)
Tell the logger that we&#39;re in replay mode.
Definition: logger.cpp:1458
char format[1500]
Definition: messages.h:71
static void timer_callback(void *arg)
Definition: logger.cpp:90
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
uint32_t px4_os_version(void)
operating system version
Definition: version.c:259
__EXPORT const char * param_name(param_t param)
Obtain the name of a parameter.
Definition: parameters.cpp:486
param_t _log_dirs_max
Definition: logger.h:407
void add_vision_and_avoidance_topics()
Definition: logger.cpp:638
void add_default_topics()
Definition: logger.cpp:509
static constexpr const char * LOG_ROOT[(int) LogType::Count]
Definition: logger.h:178
Array< LoggerSubscription, MAX_TOPICS_NUM > _subscriptions
all subscriptions for full & mission log (in front)
Definition: logger.h:384
Header common to all counters.
bool get_log_time(struct tm *tt, int utc_offset_sec, bool boot_time)
Get the time for log file name.
Definition: util.cpp:74
bool can_start_mavlink_log() const
check if mavlink logging can be started
Definition: logger.h:238
hrt_abstime _next_load_print
timestamp when to print the process load
Definition: logger.h:397
void write_version(LogType type)
Definition: logger.cpp:1980
uint8_t * data
Definition: dataman.cpp:149
first bytes of the file
Definition: messages.h:56
int orb_unsubscribe(int handle)
Definition: uORB.cpp:85
void stop_log_file(LogType type)
Definition: logger.cpp:1516
static struct actuator_armed_s armed
Definition: Commander.cpp:139
int logger_main(int argc, char *argv[])
Definition: logger.cpp:117
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
void initialize_configured_topics()
Add topic subscriptions based on the _sdlog_profile_handle parameter.
Definition: logger.cpp:736
Normal, full size log.
void initialize_mission_topics(MissionLogType type)
Add topic subscriptions based on the configured mission log type.
Definition: logger.cpp:723
static hrt_abstime hrt_elapsed_time(const hrt_abstime *then)
Compute the delta between a timestamp taken in the past and now.
Definition: drv_hrt.h:102
#define ULOG_MSG_HEADER_LEN
Definition: messages.h:61
uORB::Subscription _manual_control_sp_sub
Definition: logger.h:400
__EXPORT param_type_t param_type(param_t param)
Obtain the type of a parameter.
Definition: parameters.cpp:519
static bool request_stop_static()
request the logger thread to stop (this method does not block).
Definition: logger.cpp:402
__EXPORT void hrt_call_every(struct hrt_call *entry, hrt_abstime delay, hrt_abstime interval, hrt_callout callout, void *arg)
Call callout(arg) after delay, and then after every interval.
bool updated()
Check if there is a new update.
LogMode _log_mode
Definition: logger.h:381
static constexpr Backend BackendAll
Definition: log_writer.h:56
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
hrt_abstime start_time_file
Time when logging started, file backend (not the logger thread)
Definition: logger.h:191
void add_system_identification_topics()
Definition: logger.cpp:650
MissionSubscription _mission_subscriptions[MAX_MISSION_TOPICS_NUM]
additional data for mission subscriptions
Definition: logger.h:385
static __BEGIN_DECLS const char * px4_board_name(void)
get the board name as string (including the version if there are multiple)
Definition: version.h:54
constexpr _Tp min(_Tp a, _Tp b)
Definition: Limits.hpp:54
const char * px4_firmware_version_string(void)
Firmware version as human readable string (git tag)
Definition: version.c:338
__EXPORT param_t param_find(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:370
Tools for system version detection.
size_t get_buffer_fill_count_file(LogType type) const
Definition: log_writer.h:134
void select_write_backend(Backend sel_backend)
Select a backend, so that future calls to write_message() only write to the selected sel_backend...
Definition: log_writer.cpp:181
void perf_iterate_all(perf_callback cb, void *user)
Iterate over all performance counters using a callback.
int sess_dir_index
search starting index for &#39;sess&#39; directory name
Definition: logger.h:185
int check_free_space(const char *log_root_dir, int32_t max_log_dirs_to_keep, orb_advert_t &mavlink_log_pub, int &sess_dir_index)
Check if there is enough free space left on the SD Card.
Definition: util.cpp:114
const char * name
Definition: tests_main.c:58
static int task_spawn(int argc, char *argv[])
Definition: logger.cpp:157
uint8_t * _msg_buffer
Definition: logger.h:370
#define PARAM_TYPE_FLOAT
Definition: param.h:61
bool add_topic_multi(const char *name, uint32_t interval_ms=0)
Definition: logger.cpp:474
void write_info_multiple(LogType type, const char *name, const char *value, bool is_continued)
Definition: logger.cpp:1891
Object metadata.
Definition: uORB.h:50
uint32_t px4_firmware_vendor_version(void)
get the PX4 Firmware vendor version
Definition: version.c:240
uint32_t _log_interval
Definition: logger.h:389
const char * o_fields
semicolon separated list of fields (with type)
Definition: uORB.h:54
param_t _mission_log
Definition: logger.h:408
uORB::SubscriptionInterval _log_message_sub
Definition: logger.h:403
void add_mission_topic(const char *name, uint32_t interval_ms=0)
Add a topic to be logged for the mission log (it&#39;s also added to the full log).
Definition: logger.cpp:786
LogWriter _writer
Definition: logger.h:388
const char * configured_backend_mode() const
get the configured backend as string
Definition: logger.cpp:710
void set_need_reliable_transfer(bool need_reliable)
Indicate to the underlying backend whether future write_message() calls need a reliable transfer...
Definition: log_writer.h:153
static enum @84 backend
#define ORB_MULTI_MAX_INSTANCES
Maximum number of multi topic instances.
Definition: uORB.h:62
void write_all_add_logged_msg(LogType type)
Write an ADD_LOGGED_MSG to the log for a all current subscriptions and instances. ...
Definition: logger.cpp:1815
int orb_unadvertise(orb_advert_t handle)
Definition: uORB.cpp:65
Definition: bst.cpp:62
size_t write_dropouts
failed buffer writes due to buffer overflow
Definition: logger.h:194
void add_debug_topics()
Definition: logger.cpp:594
Callout record.
Definition: drv_hrt.h:72
void debug_print_buffer(uint32_t &total_bytes, hrt_abstime &timer_start)
Regularly print the buffer fill state (only if DBGPRINT is set)
Definition: logger.cpp:1181
char * _replay_file_name
Definition: logger.h:393
void run() override
Definition: logger.cpp:800
static constexpr hrt_abstime TRY_SUBSCRIBE_INTERVAL
Definition: logger.h:56
unsigned next_write_time
next time to write in 0.1 seconds
Definition: logger.h:200
param_t _sdlog_profile_handle
Definition: logger.h:405
hrt_abstime _last_sync_time
last time a sync msg was sent
Definition: logger.h:379
const uint16_t o_size
object size
Definition: uORB.h:52
#define OK
Definition: uavcan_main.cpp:71
watchdog_data_t watchdog_data
Definition: logger.cpp:85
px4_sem_t semaphore
Definition: logger.cpp:83
bool _prev_state
previous state depending on logging mode (arming or aux1 state)
Definition: logger.h:375
static constexpr int MAX_MISSION_TOPICS_NUM
Maximum number of mission topics.
Definition: logger.h:176
bool update(void *dst)
Update the struct.
void write_formats(LogType type)
Definition: logger.cpp:1792
int print_status() override
Definition: logger.cpp:174
void print_statistics(LogType type)
Definition: logger.cpp:204
size_t orb_topics_count() __EXPORT
bool start_stop_logging(MissionLogType mission_log_type)
check current arming state or aux channel and start/stop logging if state changed and according to co...
Definition: logger.cpp:1202
float max_dropout_duration
max duration of dropout [s]
Definition: logger.h:193
static constexpr Backend BackendMavlink
Definition: log_writer.h:55
void write_info_template(LogType type, const char *name, T value, const char *type_str)
generic common template method for write_info variants
Definition: logger.cpp:1932
static int custom_command(int argc, char *argv[])
Definition: logger.cpp:137
const char * px4_os_version_string(void)
Operating system version as human readable string (git tag)
Definition: version.c:288
bool file_exist(const char *filename)
check if a file exists
Definition: util.cpp:68
uint8_t severity
Definition: log_message.h:54
bool copy(void *dst)
Copy the struct.
int create_log_dir(LogType type, tm *tt, char *log_dir, int log_dir_len)
Create logging directory.
Definition: logger.cpp:1330
int get_log_file_name(LogType type, char *file_name, size_t file_name_size)
Get log file name with directory (create it if necessary)
Definition: logger.cpp:1391
const char * log_type_str(LogType type)
Logger(LogWriter::Backend backend, size_t buffer_size, uint32_t log_interval, const char *poll_topic_name, LogMode log_mode, bool log_name_timestamp)
Definition: logger.cpp:363
const char * px4_ecl_lib_version_string(void)
ECL lib version as human readable string (git tag)
Definition: version.c:348
uint8_t magic[8]
Definition: messages.h:57
void handle_vehicle_command_update()
Definition: logger.cpp:1276
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
uint32_t param_t
Parameter handle.
Definition: param.h:98
__EXPORT void hrt_cancel(struct hrt_call *entry)
Remove the entry from the callout list.
static constexpr unsigned MAX_NO_LOGFILE
Maximum number of log files.
Definition: logger.h:177
__EXPORT bool param_used(param_t param)
Wether a parameter is in use in the system.
Definition: parameters.cpp:826