PX4 Firmware
PX4 Autopilot Software http://px4.io
uORBDeviceMaster.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-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 "uORBDeviceMaster.hpp"
35 #include "uORBDeviceNode.hpp"
36 #include "uORBManager.hpp"
37 #include "uORBUtils.hpp"
38 
39 #ifdef ORB_COMMUNICATOR
40 #include "uORBCommunicator.hpp"
41 #endif /* ORB_COMMUNICATOR */
42 
43 #include <px4_platform_common/sem.hpp>
44 #include <systemlib/px4_macros.h>
45 
47 {
48  px4_sem_init(&_lock, 0, 1);
50 }
51 
53 {
54  px4_sem_destroy(&_lock);
55 }
56 
57 int
58 uORB::DeviceMaster::advertise(const struct orb_metadata *meta, bool is_advertiser, int *instance, int priority)
59 {
60  int ret = PX4_ERROR;
61 
62  char nodepath[orb_maxpath];
63 
64  /* construct a path to the node - this also checks the node name */
65  ret = uORB::Utils::node_mkpath(nodepath, meta, instance);
66 
67  if (ret != PX4_OK) {
68  return ret;
69  }
70 
71  ret = PX4_ERROR;
72 
73  /* try for topic groups */
74  const unsigned max_group_tries = (instance != nullptr) ? ORB_MULTI_MAX_INSTANCES : 1;
75  unsigned group_tries = 0;
76 
77  if (instance) {
78  /* for an advertiser, this will be 0, but a for subscriber that requests a certain instance,
79  * we do not want to start with 0, but with the instance the subscriber actually requests.
80  */
81  group_tries = *instance;
82 
83  if (group_tries >= max_group_tries) {
84  return -ENOMEM;
85  }
86  }
87 
88  SmartLock smart_lock(_lock);
89 
90  do {
91  /* if path is modifyable change try index */
92  if (instance != nullptr) {
93  /* replace the number at the end of the string */
94  nodepath[strlen(nodepath) - 1] = '0' + group_tries;
95  *instance = group_tries;
96  }
97 
98  /* driver wants a permanent copy of the path, so make one here */
99  const char *devpath = strdup(nodepath);
100 
101  if (devpath == nullptr) {
102  return -ENOMEM;
103  }
104 
105  /* construct the new node, passing the ownership of path to it */
106  uORB::DeviceNode *node = new uORB::DeviceNode(meta, group_tries, devpath, priority);
107 
108  /* if we didn't get a device, that's bad, free the path too */
109  if (node == nullptr) {
110  free((void *)devpath);
111  return -ENOMEM;
112  }
113 
114  /* initialise the node - this may fail if e.g. a node with this name already exists */
115  ret = node->init();
116 
117  /* if init failed, discard the node and its name */
118  if (ret != PX4_OK) {
119  delete node;
120 
121  if (ret == -EEXIST) {
122  /* if the node exists already, get the existing one and check if it's advertised. */
123  uORB::DeviceNode *existing_node = getDeviceNodeLocked(meta, group_tries);
124 
125  /*
126  * We can claim an existing node in these cases:
127  * - The node is not advertised (yet). It means there is already one or more subscribers or it was
128  * unadvertised.
129  * - We are a single-instance advertiser requesting the first instance.
130  * (Usually we don't end up here, but we might in case of a race condition between 2
131  * advertisers).
132  * - We are a subscriber requesting a certain instance.
133  * (Also we usually don't end up in that case, but we might in case of a race condtion
134  * between an advertiser and subscriber).
135  */
136  bool is_single_instance_advertiser = is_advertiser && !instance;
137 
138  if (existing_node != nullptr &&
139  (!existing_node->is_advertised() || is_single_instance_advertiser || !is_advertiser)) {
140  if (is_advertiser) {
141  existing_node->set_priority(priority);
142  /* Set as advertised to avoid race conditions (otherwise 2 multi-instance advertisers
143  * could get the same instance).
144  */
145  existing_node->mark_as_advertised();
146  }
147 
148  ret = PX4_OK;
149 
150  } else {
151  /* otherwise: already advertised, keep looking */
152  }
153  }
154 
155  } else {
156  if (is_advertiser) {
157  node->mark_as_advertised();
158  }
159 
160  // add to the node map.
161  _node_list.add(node);
162  }
163 
164  group_tries++;
165 
166  } while (ret != PX4_OK && (group_tries < max_group_tries));
167 
168  if (ret != PX4_OK && group_tries >= max_group_tries) {
169  ret = -ENOMEM;
170  }
171 
172  return ret;
173 }
174 
176 {
177  hrt_abstime current_time = hrt_absolute_time();
178  PX4_INFO("Statistics, since last output (%i ms):", (int)((current_time - _last_statistics_output) / 1000));
179  _last_statistics_output = current_time;
180 
181  PX4_INFO("TOPIC, NR LOST MSGS");
182  bool had_print = false;
183 
184  /* Add all nodes to a list while locked, and then print them in unlocked state, to avoid potential
185  * dead-locks (where printing blocks) */
186  lock();
187  DeviceNodeStatisticsData *first_node = nullptr;
188  DeviceNodeStatisticsData *cur_node = nullptr;
189  size_t max_topic_name_length = 0;
190  int num_topics = 0;
191  int ret = addNewDeviceNodes(&first_node, num_topics, max_topic_name_length, nullptr, 0);
192  unlock();
193 
194  if (ret != 0) {
195  PX4_ERR("addNewDeviceNodes failed (%i)", ret);
196  }
197 
198  cur_node = first_node;
199 
200  while (cur_node) {
201  if (cur_node->node->print_statistics(reset)) {
202  had_print = true;
203  }
204 
205  DeviceNodeStatisticsData *prev = cur_node;
206  cur_node = cur_node->next;
207  delete prev;
208  }
209 
210  if (!had_print) {
211  PX4_INFO("No lost messages");
212  }
213 }
214 
216  size_t &max_topic_name_length, char **topic_filter, int num_filters)
217 {
218  DeviceNodeStatisticsData *cur_node = nullptr;
219  num_topics = 0;
220  DeviceNodeStatisticsData *last_node = *first_node;
221 
222  if (last_node) {
223  while (last_node->next) {
224  last_node = last_node->next;
225  }
226  }
227 
228  for (const auto &node : _node_list) {
229 
230  ++num_topics;
231 
232  //check if already added
233  cur_node = *first_node;
234 
235  while (cur_node && cur_node->node != node) {
236  cur_node = cur_node->next;
237  }
238 
239  if (cur_node) {
240  continue;
241  }
242 
243  if (num_filters > 0 && topic_filter) {
244  bool matched = false;
245 
246  for (int i = 0; i < num_filters; ++i) {
247  if (strstr(node->get_meta()->o_name, topic_filter[i])) {
248  matched = true;
249  }
250  }
251 
252  if (!matched) {
253  continue;
254  }
255  }
256 
257  if (last_node) {
258  last_node->next = new DeviceNodeStatisticsData();
259  last_node = last_node->next;
260 
261  } else {
262  *first_node = last_node = new DeviceNodeStatisticsData();
263  }
264 
265  if (!last_node) {
266  return -ENOMEM;
267  }
268 
269  last_node->node = node;
270 
271  size_t name_length = strlen(last_node->node->get_meta()->o_name);
272 
273  if (name_length > max_topic_name_length) {
274  max_topic_name_length = name_length;
275  }
276 
277  last_node->last_lost_msg_count = last_node->node->lost_message_count();
278  last_node->last_pub_msg_count = last_node->node->published_message_count();
279  }
280 
281  return 0;
282 }
283 
284 #define CLEAR_LINE "\033[K"
285 
286 void uORB::DeviceMaster::showTop(char **topic_filter, int num_filters)
287 {
288  bool print_active_only = true;
289  bool only_once = false; // if true, run only once, then exit
290 
291  if (topic_filter && num_filters > 0) {
292  bool show_all = false;
293 
294  for (int i = 0; i < num_filters; ++i) {
295  if (!strcmp("-a", topic_filter[i])) {
296  show_all = true;
297 
298  } else if (!strcmp("-1", topic_filter[i])) {
299  only_once = true;
300  }
301  }
302 
303  print_active_only = only_once ? (num_filters == 1) : false; // print non-active if -a or some filter given
304 
305  if (show_all || print_active_only) {
306  num_filters = 0;
307  }
308  }
309 
310  PX4_INFO_RAW("\033[2J\n"); //clear screen
311 
312  lock();
313 
314  if (_node_list.empty()) {
315  unlock();
316  PX4_INFO("no active topics");
317  return;
318  }
319 
320  DeviceNodeStatisticsData *first_node = nullptr;
321  DeviceNodeStatisticsData *cur_node = nullptr;
322  size_t max_topic_name_length = 0;
323  int num_topics = 0;
324  int ret = addNewDeviceNodes(&first_node, num_topics, max_topic_name_length, topic_filter, num_filters);
325 
326  /* a DeviceNode is never deleted, so it's save to unlock here and still access the DeviceNodes */
327  unlock();
328 
329  if (ret != 0) {
330  PX4_ERR("addNewDeviceNodes failed (%i)", ret);
331  }
332 
333 #ifdef __PX4_QURT //QuRT has no poll()
334  only_once = true;
335 #else
336  const int stdin_fileno = 0;
337 
338  struct pollfd fds;
339  fds.fd = stdin_fileno;
340  fds.events = POLLIN;
341 #endif
342  bool quit = false;
343 
344  hrt_abstime start_time = hrt_absolute_time();
345 
346  while (!quit) {
347 
348 #ifndef __PX4_QURT
349 
350  /* Sleep 200 ms waiting for user input five times ~ 1s */
351  for (int k = 0; k < 5; k++) {
352  char c;
353 
354  ret = ::poll(&fds, 1, 0); //just want to check if there is new data available
355 
356  if (ret > 0) {
357 
358  ret = ::read(stdin_fileno, &c, 1);
359 
360  if (ret) {
361  quit = true;
362  break;
363  }
364  }
365 
366  px4_usleep(200000);
367  }
368 
369 #endif
370 
371  if (!quit) {
372 
373  //update the stats
374  hrt_abstime current_time = hrt_absolute_time();
375  float dt = (current_time - start_time) / 1.e6f;
376  cur_node = first_node;
377 
378  while (cur_node) {
379  uint32_t num_lost = cur_node->node->lost_message_count();
380  unsigned int num_msgs = cur_node->node->published_message_count();
381  cur_node->pub_msg_delta = (num_msgs - cur_node->last_pub_msg_count) / dt;
382  cur_node->lost_msg_delta = (num_lost - cur_node->last_lost_msg_count) / dt;
383  cur_node->last_lost_msg_count = num_lost;
384  cur_node->last_pub_msg_count = num_msgs;
385  cur_node = cur_node->next;
386  }
387 
388  start_time = current_time;
389 
390 
391  PX4_INFO_RAW("\033[H"); // move cursor home and clear screen
392  PX4_INFO_RAW(CLEAR_LINE "update: 1s, num topics: %i\n", num_topics);
393  PX4_INFO_RAW(CLEAR_LINE "%-*s INST #SUB #MSG #LOST #QSIZE\n", (int)max_topic_name_length - 2, "TOPIC NAME");
394  cur_node = first_node;
395 
396  while (cur_node) {
397 
398  if (!print_active_only || cur_node->pub_msg_delta > 0) {
399  PX4_INFO_RAW(CLEAR_LINE "%-*s %2i %4i %4i %5i %i\n", (int)max_topic_name_length,
400  cur_node->node->get_meta()->o_name, (int)cur_node->node->get_instance(),
401  (int)cur_node->node->subscriber_count(), cur_node->pub_msg_delta,
402  (int)cur_node->lost_msg_delta, cur_node->node->get_queue_size());
403  }
404 
405  cur_node = cur_node->next;
406  }
407 
408  lock();
409  ret = addNewDeviceNodes(&first_node, num_topics, max_topic_name_length, topic_filter, num_filters);
410  unlock();
411 
412  if (ret != 0) {
413  PX4_ERR("addNewDeviceNodes failed (%i)", ret);
414  }
415 
416  }
417 
418  if (only_once) {
419  quit = true;
420  }
421  }
422 
423  //cleanup
424  cur_node = first_node;
425 
426  while (cur_node) {
427  DeviceNodeStatisticsData *next_node = cur_node->next;
428  delete cur_node;
429  cur_node = next_node;
430  }
431 }
432 
433 #undef CLEAR_LINE
434 
436 {
437  lock();
438 
439  for (uORB::DeviceNode *node : _node_list) {
440  if (strcmp(node->get_devname(), nodepath) == 0) {
441  unlock();
442  return node;
443  }
444  }
445 
446  unlock();
447 
448  return nullptr;
449 }
450 
452 {
453  if (meta == nullptr) {
454  return nullptr;
455  }
456 
457  lock();
458  uORB::DeviceNode *node = getDeviceNodeLocked(meta, instance);
459  unlock();
460 
461  //We can safely return the node that can be used by any thread, because
462  //a DeviceNode never gets deleted.
463  return node;
464 }
465 
467 {
468  for (uORB::DeviceNode *node : _node_list) {
469  if ((strcmp(node->get_name(), meta->o_name) == 0) && (node->get_instance() == instance)) {
470  return node;
471  }
472  }
473 
474  return nullptr;
475 }
#define CLEAR_LINE
uint8_t get_queue_size() const
List< uORB::DeviceNode * > _node_list
void set_priority(uint8_t priority)
uint32_t lost_message_count() const
virtual int init()
Definition: CDev.cpp:119
int addNewDeviceNodes(DeviceNodeStatisticsData **first_node, int &num_topics, size_t &max_topic_name_length, char **topic_filter, int num_filters)
A set of useful macros for enhanced runtime and compile time error detection and warning suppression...
int8_t subscriber_count() const
int reset(enum LPS22HB_BUS busid)
Reset the driver.
LidarLite * instance
Definition: ll40ls.cpp:65
uORB::DeviceNode * getDeviceNodeLocked(const struct orb_metadata *meta, const uint8_t instance)
Find a node give its name.
const char * o_name
unique object name
Definition: uORB.h:51
static void read(bootloader_app_shared_t *pshared)
unsigned published_message_count() const
Per-object device instance.
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
bool is_advertised() const
Return true if this topic has been advertised.
uint8_t get_instance() const
hrt_abstime _last_statistics_output
const orb_metadata * get_meta() const
void showTop(char **topic_filter, int num_filters)
Continuously print statistics, like the unix top command for processes.
static constexpr unsigned orb_maxpath
Definition: uORBCommon.hpp:45
Object metadata.
Definition: uORB.h:50
float dt
Definition: px4io.c:73
px4_sem_t _lock
lock to protect access to all class members (also for derived classes)
#define ORB_MULTI_MAX_INSTANCES
Maximum number of multi topic instances.
Definition: uORB.h:62
bool print_statistics(bool reset)
Print statistics (nr of lost messages)
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
uORB::DeviceNode * getDeviceNode(const char *node_name)
Public interface for getDeviceNodeLocked().
void printStatistics(bool reset)
Print statistics for each existing topic.
static int node_mkpath(char *buf, const struct orb_metadata *meta, int *instance=nullptr)
Definition: uORBUtils.cpp:38
int advertise(const struct orb_metadata *meta, bool is_advertiser, int *instance, int priority)