Package node_manager_fkie :: Module echo_dialog
[frames] | no frames]

Source Code for Module node_manager_fkie.echo_dialog

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Fraunhofer nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33  import os 
 34  import itertools 
 35  import math 
 36  import sys 
 37  import threading 
 38  import time 
 39  from datetime import datetime 
 40  from python_qt_binding import loadUi 
 41  from python_qt_binding.QtCore import Qt, QUrl, QTimer, Signal 
 42  from python_qt_binding.QtGui import QIcon, QTextDocument 
 43  try: 
 44      from python_qt_binding.QtGui import QApplication, QDialog 
 45  except: 
 46      from python_qt_binding.QtWidgets import QApplication, QDialog 
 47   
 48  from roslib import message 
 49  from genpy.rostime import Time, TVal 
 50  import rospy 
 51   
 52  import gui_resources 
 53  import node_manager_fkie as nm 
 54  from node_manager_fkie.common import utf8 
 55   
 56   
57 -def isstring(s):
58 """Small helper version to check an object is a string in a way that works 59 for both Python 2 and 3 60 """ 61 try: 62 return isinstance(s, basestring) 63 except NameError: 64 return isinstance(s, str)
65 66
67 -def _convert_getattr(val, f, t):
68 """ 69 Convert atttribute types on the fly, if necessary. This is mainly 70 to convert uint8[] fields back to an array type. 71 """ 72 attr = getattr(val, f) 73 if isstring(attr) and 'uint8[' in t: 74 return [ord(x) for x in attr] 75 else: 76 return attr
77 78
79 -class EchoDialog(QDialog):
80 81 MESSAGE_CHARS_LIMIT = 1000 82 MESSAGE_LINE_LIMIT = 80 83 MESSAGE_HZ_LIMIT = 10 84 MAX_DISPLAY_MSGS = 25 85 STATISTIC_QUEUE_LEN = 1000 86 SHOW_BYTES = True 87 SHOW_JITTER = True 88 SHOW_STD_DEV = False 89 SHOW_WINDOW_SIZE = False 90 91 ''' 92 This dialog shows the output of a topic. 93 ''' 94 95 finished_signal = Signal(str) 96 ''' 97 finished_signal has as parameter the name of the topic and is emitted, if this 98 dialog was closed. 99 ''' 100 101 msg_signal = Signal(object, bool) 102 ''' 103 msg_signal is a signal, which is emitted, if a new message was received. 104 ''' 105 106 text_hz_signal = Signal(str) 107 text_signal = Signal(str) 108 ''' 109 text_signal is a signal, which is emitted, if a new text to display was received. 110 ''' 111 112 text_error_signal = Signal(str) 113 ''' 114 text_error_signal is a signal, which is emitted, if a new error text to display was received. 115 ''' 116 117 request_pw = Signal(object) 118
119 - def __init__(self, topic, msg_type, show_only_rate=False, masteruri=None, use_ssh=False, parent=None):
120 ''' 121 Creates an input dialog. 122 @param topic: the name of the topic 123 @type topic: C{str} 124 @param msg_type: the type of the topic 125 @type msg_type: C{str} 126 @raise Exception: if no topic class was found for the given type 127 ''' 128 QDialog.__init__(self, parent=parent) 129 self._masteruri = masteruri 130 masteruri_str = '' if masteruri is None else '[%s]' % masteruri 131 echo_dialog_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'EchoDialog.ui') 132 loadUi(echo_dialog_file, self) 133 self.setObjectName(' - '.join(['EchoDialog', topic, masteruri_str])) 134 self.setAttribute(Qt.WA_DeleteOnClose, True) 135 self.setWindowFlags(Qt.Window) 136 self.setWindowTitle('%s %s %s' % ('Echo --- ' if not show_only_rate else 'Hz --- ', topic, masteruri_str)) 137 self.resize(900, 512) 138 139 self.topic = topic 140 self.show_only_rate = show_only_rate 141 self.lock = threading.RLock() 142 self.last_printed_count = 0 143 self.msg_t0 = -1. 144 self.msg_tn = 0 145 self.times = [] 146 self.bytes = [] 147 148 self.message_count = 0 149 self._state_message = '' 150 self._state_size_message = '' 151 self._scrapped_msgs = 0 152 self._scrapped_msgs_sl = 0 153 154 self._last_received_ts = 0 155 self.chars_limit = self.MESSAGE_CHARS_LIMIT 156 self.receiving_hz = self.MESSAGE_HZ_LIMIT 157 self.line_limit = self.MESSAGE_LINE_LIMIT 158 self.max_displayed_msgs = self.MAX_DISPLAY_MSGS 159 self.digits_after_in_array = 2 160 161 self.enabled_message_filter = True 162 self.field_filter_fn = None 163 self._latched = False 164 self._msgs = [] 165 166 self.filterFrame.setVisible(False) 167 self.topicControlButton.clicked.connect(self.on_topic_control_btn_clicked) 168 self.clearButton.clicked.connect(self.on_clear_btn_clicked) 169 if show_only_rate: 170 self.filterButton.setVisible(False) 171 else: 172 self.filterButton.clicked.connect(self.on_filter_clicked) 173 self.showStringsCheckBox.toggled.connect(self.on_no_str_checkbox_toggled) 174 self.maxLenStringComboBox.activated[str].connect(self.combobox_reduce_ch_activated) 175 self.showArraysCheckBox.toggled.connect(self.on_no_arr_checkbox_toggled) 176 self.maxDigitsComboBox.activated[str].connect(self.combobox_reduce_digits_activated) 177 self.enableMsgFilterCheckBox.toggled.connect(self.on_enable_msg_filter_checkbox_toggled) 178 self.maxLenComboBox.activated[str].connect(self.on_combobox_chars_activated) 179 self.maxHzComboBox.activated[str].connect(self.on_combobox_hz_activated) 180 self.displayCountComboBox.activated[str].connect(self.on_combobox_count_activated) 181 self.combobox_reduce_ch_activated(self.MESSAGE_LINE_LIMIT) 182 self.on_combobox_chars_activated(self.MESSAGE_CHARS_LIMIT) 183 self.on_combobox_hz_activated(self.MESSAGE_HZ_LIMIT) 184 self.on_combobox_count_activated(self.MAX_DISPLAY_MSGS) 185 self.filterButton.setFocus() 186 self.display.setReadOnly(True) 187 self.display.document().setMaximumBlockCount(500) 188 self._blocks_in_msg = None 189 self.display.setOpenLinks(False) 190 self.display.anchorClicked.connect(self._on_display_anchorClicked) 191 192 # subscribe to the topic 193 errmsg = '' 194 try: 195 self.__msg_class = message.get_message_class(msg_type) 196 if not self.__msg_class: 197 errmsg = "Cannot load message class for [%s]. Did you build messages?" % msg_type 198 except Exception as e: 199 self.__msg_class = None 200 errmsg = "Cannot load message class for [%s]. Did you build messagest?\nError: %s" % (msg_type, utf8(e)) 201 # variables for Subscriber 202 self.msg_signal.connect(self._append_message) 203 self.sub = None 204 205 # vairables for SSH connection 206 self.ssh_output_file = None 207 self.ssh_error_file = None 208 self.ssh_input_file = None 209 self.text_signal.connect(self._append_text) 210 self.text_hz_signal.connect(self._append_text_hz) 211 self._current_msg = '' 212 self._current_errmsg = '' 213 self.text_error_signal.connect(self._append_error_text) 214 215 # decide, which connection to open 216 if use_ssh: 217 self.__msg_class = None 218 self._on_display_anchorClicked(QUrl(self._masteruri)) 219 elif self.__msg_class is None: 220 errtxt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">\n%s</pre>' % (errmsg) 221 self.display.setText('<a href="%s">open using SSH</a>' % (masteruri)) 222 self.display.append(errtxt) 223 else: 224 self.sub = rospy.Subscriber(self.topic, self.__msg_class, self._msg_handle) 225 226 self.print_hz_timer = QTimer() 227 self.print_hz_timer.timeout.connect(self._on_calc_hz) 228 self.print_hz_timer.start(1000) 229 self._start_time = time.time()
230 231 # print "======== create", self.objectName() 232 # 233 # def __del__(self): 234 # print "******* destroy", self.objectName() 235 236 # def hideEvent(self, event): 237 # self.close() 238
239 - def closeEvent(self, event):
240 if self.sub is not None: 241 self.sub.unregister() 242 del self.sub 243 try: 244 self.ssh_output_file.close() 245 self.ssh_error_file.close() 246 # send Ctrl+C to remote process 247 self.ssh_input_file.write('%s\n' % chr(3)) 248 self.ssh_input_file.close() 249 except Exception: 250 pass 251 self.finished_signal.emit(self.topic) 252 if self.parent() is None: 253 QApplication.quit()
254
255 - def create_field_filter(self, echo_nostr, echo_noarr):
256 def field_filter(val): 257 try: 258 # fields = val.__slots__ 259 # field_types = val._slot_types 260 for f, t in zip(val.__slots__, val._slot_types): 261 if echo_noarr and '[' in t: 262 continue 263 elif echo_nostr and 'string' in t: 264 continue 265 yield f 266 except Exception: 267 pass
268 return field_filter
269
270 - def on_filter_clicked(self, checked):
271 self.filterFrame.setVisible(checked)
272
273 - def on_no_str_checkbox_toggled(self, state):
274 self.maxLenStringComboBox.setEnabled(state) 275 self.field_filter_fn = self.create_field_filter(not state, not self.showArraysCheckBox.isChecked())
276
277 - def on_no_arr_checkbox_toggled(self, state):
278 self.maxDigitsComboBox.setEnabled(state) 279 self.field_filter_fn = self.create_field_filter(not self.showStringsCheckBox.isChecked(), not state)
280
281 - def combobox_reduce_ch_activated(self, ch_txt):
282 try: 283 self.line_limit = int(ch_txt) 284 except ValueError: 285 try: 286 self.line_limit = float(ch_txt) 287 except ValueError: 288 self.maxLenStringComboBox.setEditText(str(self.line_limit)) 289 self.display.clear() 290 with self.lock: 291 for msg, current_time in self._msgs: 292 self._append_message(msg, self._latched, current_time, False)
293
294 - def combobox_reduce_digits_activated(self, ch_txt):
295 try: 296 self.digits_after_in_array = int(ch_txt) 297 except ValueError: 298 self.digits_after_in_array = None 299 self.maxDigitsComboBox.setEditText('') 300 self.display.clear() 301 with self.lock: 302 for msg, current_time in self._msgs: 303 self._append_message(msg, self._latched, current_time, False)
304
305 - def on_enable_msg_filter_checkbox_toggled(self, state):
306 self.enabled_message_filter = state 307 self.maxLenComboBox.setEnabled(state) 308 self.maxHzComboBox.setEnabled(state) 309 if self.enabled_message_filter: 310 self.on_combobox_chars_activated(self.maxLenComboBox.currentText(), False) 311 self.on_combobox_hz_activated(self.maxHzComboBox.currentText(), False) 312 else: 313 self.chars_limit = 0 314 self.receiving_hz = 0 315 self.display.clear() 316 with self.lock: 317 for msg, current_time in self._msgs: 318 self._append_message(msg, self._latched, current_time, False)
319
320 - def on_combobox_chars_activated(self, chars_txt, update_display=True):
321 if not self.enabled_message_filter: 322 return 323 try: 324 self.chars_limit = int(chars_txt) 325 except ValueError: 326 try: 327 self.chars_limit = float(chars_txt) 328 except ValueError: 329 self.maxLenComboBox.setEditText(str(self.chars_limit)) 330 if update_display: 331 self.display.clear() 332 with self.lock: 333 for msg, current_time in self._msgs: 334 self._append_message(msg, self._latched, current_time, False)
335
336 - def on_combobox_hz_activated(self, hz_txt, update_display=True):
337 if not self.enabled_message_filter: 338 return 339 try: 340 self.receiving_hz = int(hz_txt) 341 except ValueError: 342 try: 343 self.receiving_hz = float(hz_txt) 344 except ValueError: 345 self.maxHzComboBox.setEditText(str(self.receiving_hz)) 346 if update_display: 347 self.display.clear() 348 with self.lock: 349 for msg, current_time in self._msgs: 350 self._append_message(msg, self._latched, current_time, False)
351
352 - def on_combobox_count_activated(self, count_txt):
353 try: 354 self.max_displayed_msgs = int(count_txt) 355 self._blocks_in_msg = None 356 except ValueError: 357 self.displayCountComboBox.setEditText(str(self.max_displayed_msgs))
358
359 - def on_clear_btn_clicked(self):
360 self.display.clear() 361 with self.lock: 362 del self._msgs[:] 363 self.message_count = 0 364 self._scrapped_msgs = 0 365 del self.times[:] 366 del self.bytes[:]
367
368 - def on_topic_control_btn_clicked(self):
369 try: 370 if self.sub is None and self.ssh_output_file is None: 371 if self.__msg_class: 372 self.sub = rospy.Subscriber(self.topic, self.__msg_class, self._msg_handle) 373 self._start_time = time.time() 374 else: 375 self._on_display_anchorClicked(QUrl(self._masteruri)) 376 self.topicControlButton.setIcon(QIcon(':/icons/deleket_deviantart_stop.png')) 377 else: 378 if self.sub is not None: 379 self.sub.unregister() 380 self.sub = None 381 elif self.ssh_output_file is not None: 382 self.ssh_output_file.close() 383 self.ssh_error_file.close() 384 self.ssh_output_file = None 385 self.topicControlButton.setIcon(QIcon(':/icons/deleket_deviantart_play.png')) 386 except Exception as e: 387 rospy.logwarn('Error while stop/play echo for topic %s: %s' % (self.topic, utf8(e)))
388
389 - def _msg_handle(self, data):
390 self.msg_signal.emit(data, (data._connection_header['latching'] != '0'))
391
392 - def _append_message(self, msg, latched, current_time=None, store=True):
393 ''' 394 Adds a label to the dialog's layout and shows the given text. 395 @param msg: the text to add to the dialog 396 @type msg: message object 397 ''' 398 if current_time is None: 399 current_time = time.time() 400 self._latched = latched 401 if store: 402 with self.lock: 403 self._msgs.append((msg, current_time)) 404 if len(self._msgs) > 25: 405 self._msgs.pop() 406 msg_len = -1 407 if (self.SHOW_BYTES or self.show_only_rate): 408 buff = None 409 try: 410 from cStringIO import StringIO # Python 2.x 411 buff = StringIO() 412 import os 413 msg.serialize(buff) 414 buff.seek(0, os.SEEK_END) 415 msg_len = buff.tell() 416 except ImportError: 417 from io import BytesIO # Python 3.x 418 buff = BytesIO() 419 msg.serialize(buff) 420 msg_len = buff.getbuffer().nbytes 421 self._count_messages(current_time, msg_len) 422 # skip messages, if they are received often then MESSAGE_HZ_LIMIT 423 if self._last_received_ts != 0 and self.receiving_hz != 0: 424 if current_time - self._last_received_ts < 1.0 / self.receiving_hz: 425 if (not latched or (latched and current_time - self._start_time > 3.0)): 426 self._scrapped_msgs += 1 427 self._scrapped_msgs_sl += 1 428 return 429 self._last_received_ts = current_time 430 if not self.show_only_rate: 431 # convert message to string and reduce line width to current limit 432 msg = self.strify_message(msg, field_filter=self.field_filter_fn) 433 if isinstance(msg, tuple): 434 msg = msg[0] 435 msg = self._trim_width(msg) 436 msg = msg.replace('<', '&lt;').replace('>', '&gt;') 437 msg_cated = False 438 if self.chars_limit != 0 and len(msg) > self.chars_limit: 439 msg = msg[0:self.chars_limit] 440 msg_cated = True 441 # create a notification about scrapped messages 442 if self._scrapped_msgs_sl > 0: 443 txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">scrapped %s message because of Hz-settings</pre>' % self._scrapped_msgs_sl 444 self.display.append(txt) 445 self._scrapped_msgs_sl = 0 446 txt = '<pre style="background-color:#FFFCCC; color:#000000;font-family:Fixedsys,Courier; padding:10px;">---------- %s --------------------\n%s</pre>' % (datetime.now().strftime("%d.%m.%Y %H:%M:%S.%f"), msg) 447 # set the count of the displayed messages on receiving the first message 448 self._update_max_msg_count(txt) 449 self.display.append(txt) 450 if msg_cated: 451 txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">message has been cut off</pre>' 452 self.display.append(txt) 453 if store: 454 self._print_status()
455
456 - def _count_messages(self, ts=time.time(), msg_len=-1):
457 ''' 458 Counts the received messages. Call this method only on receive message. 459 ''' 460 current_time = ts 461 with self.lock: 462 # time reset 463 if self.msg_t0 < 0 or self.msg_t0 > current_time: 464 self.msg_t0 = current_time 465 self.msg_tn = current_time 466 self.times = [] 467 self.bytes = [] 468 else: 469 self.times.append(current_time - self.msg_tn) 470 if msg_len > -1: 471 self.bytes.append(msg_len) 472 self.msg_tn = current_time 473 # keep only statistics for the last 5000 messages so as not to run out of memory 474 if len(self.times) > self.STATISTIC_QUEUE_LEN: 475 self.times.pop(0) 476 if len(self.bytes) > self.STATISTIC_QUEUE_LEN: 477 self.bytes.pop(0) 478 self.message_count += 1
479
480 - def _trim_width(self, msg):
481 ''' 482 reduce line width to current limit 483 :param msg: the message 484 :type msg: str 485 :return: trimmed message 486 ''' 487 result = msg 488 if self.line_limit != 0: 489 a = '' 490 for l in msg.splitlines(): 491 a = a + (l if len(l) <= self.line_limit else l[0:self.line_limit - 3] + '...') + '\n' 492 result = a 493 return result
494
495 - def _update_max_msg_count(self, txt):
496 ''' 497 set the count of the displayed messages on receiving the first message 498 :param txt: text of the message, which will be added to the document 499 :type txt: str 500 ''' 501 if self._blocks_in_msg is None: 502 td = QTextDocument(txt) 503 self._blocks_in_msg = td.blockCount() 504 self.display.document().setMaximumBlockCount(self._blocks_in_msg * self.max_displayed_msgs)
505
506 - def _on_calc_hz(self):
507 if rospy.is_shutdown(): 508 self.close() 509 return 510 if not self.show_only_rate and time.time() - self._last_received_ts > 1: 511 # create a notification about scrapped messages 512 if self._scrapped_msgs_sl > 0: 513 txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">scrapped %s message because of Hz-settings</pre>' % self._scrapped_msgs_sl 514 self._scrapped_msgs_sl = 0 515 self.display.append(txt) 516 if self.message_count == self.last_printed_count: 517 return 518 with self.lock: 519 message_rate = '' 520 message_bytes = '' 521 message_jitter = '' 522 message_window = '' 523 message_std_dev = '' 524 message_scrapped = '' 525 sum_times = sum(self.times) 526 if sum_times == 0: 527 sum_times = 1 528 if (self.SHOW_BYTES or self.show_only_rate) and self.bytes: 529 sum_bytes = sum(self.bytes) 530 avg = sum_bytes / len(self.bytes) 531 last = self.bytes[-1] 532 if avg != last: 533 message_bytes = "size[ last: %s, avg: %s ]" % (self._normilize_size_print(last), self._normilize_size_print(avg)) 534 else: 535 message_bytes = "size: %s" % (self._normilize_size_print(last)) 536 byte_rate = float(sum_bytes) / float(sum_times) 537 message_bytes += " bw: %s/s" % (self._normilize_size_print(byte_rate)) 538 # the code from ROS rostopic 539 n = len(self.times) 540 if n < 2: 541 return 542 mean = sum_times / n 543 rate = 1. / mean if mean > 0. else 0 544 message_rate = "average rate: %.3f" % rate 545 # min and max 546 if self.SHOW_JITTER or self.show_only_rate: 547 max_delta = max(self.times) 548 min_delta = min(self.times) 549 message_jitter = "jitter[ min: %.3fs max: %.3fs ]" % (min_delta, max_delta) 550 # std dev 551 self.last_printed_count = self.message_count 552 if self.SHOW_STD_DEV or self.show_only_rate: 553 std_dev = math.sqrt(sum((x - mean) ** 2 for x in self.times) / n) 554 message_std_dev = "std dev: %.5fs" % (std_dev) 555 if self.SHOW_WINDOW_SIZE or self.show_only_rate: 556 message_window = "window: %s" % (n + 1) 557 if self._scrapped_msgs > 0: 558 message_scrapped += "scrapped msgs: %s" % self._scrapped_msgs 559 self._state_message = '' 560 self._state_size_message = message_bytes 561 for msg in [message_rate, message_jitter, message_std_dev, message_window, message_scrapped]: 562 if msg: 563 if self._state_message: 564 self._state_message += ' ' 565 self._state_message += msg 566 self._print_status() 567 if self.show_only_rate: 568 self.display.append("%s %s" % (self._state_message, message_bytes))
569
570 - def _normilize_size_print(self, size):
571 if size > 999999: 572 return "%.2fMiB" % (size / 1048576.0) 573 if size > 999: 574 return "%.2fKiB" % (size / 1024.0) 575 return "%dB" % size
576
577 - def _print_status(self):
578 text = '%s messages %s' % (self.message_count, self._state_message) 579 if self._latched: 580 text = "[latched] %s" % text 581 self.statusLabel.setText(text) 582 self.statusSizeLabel.setText(self._state_size_message)
583
584 - def _append_text(self, text):
585 ''' 586 Append echo text received through the SSH. 587 ''' 588 with self.lock: 589 self._current_msg += text 590 if self._current_msg.find('---') != -1: 591 messages = self._current_msg.split('---') 592 for m in messages[:-1]: 593 current_time = time.time() 594 self._count_messages(current_time) 595 # limit the displayed text width 596 m = self._trim_width(m) 597 txt = '<pre style="background-color:#FFFCCC; color:#000000;font-family:Fixedsys,Courier; padding:10px;">---------- %s --------------------\n%s</pre>' % (datetime.now().strftime("%d.%m.%Y %H:%M:%S.%f"), m) 598 # set the count of the displayed messages on receiving the first message 599 self._update_max_msg_count(txt) 600 self.display.append(txt) 601 self._current_msg = messages[-1] 602 self._print_status()
603
604 - def _append_error_text(self, text):
605 ''' 606 Append error text received through the SSH. 607 ''' 608 with self.lock: 609 self._current_errmsg += text 610 if self._current_errmsg.find('\n') != -1: 611 messages = self._current_errmsg.split('\n') 612 for m in messages[:-1]: 613 txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">%s</pre>' % m 614 self.display.append(txt) 615 self._current_errmsg = messages[-1]
616
617 - def _append_text_hz(self, text):
618 ''' 619 Append text received through the SSH for hz view. 620 ''' 621 with self.lock: 622 self._current_msg += text 623 if self._current_msg.find('\n') != -1: 624 messages = self._current_msg.split('\n') 625 for m in messages[:-1]: 626 txt = '<div style="font-family:Fixedsys,Courier;">%s</div>' % (m) 627 self.display.append(txt) 628 self._current_msg = messages[-1]
629
630 - def _on_display_anchorClicked(self, url, user=None, pw=None):
631 try: 632 ok = False 633 if self.show_only_rate: 634 self.ssh_input_file, self.ssh_output_file, self.ssh_error_file, ok = nm.ssh().ssh_exec(url.host(), ['rostopic hz %s' % (self.topic)], user, pw, auto_pw_request=True, get_pty=True) 635 self.statusLabel.setText('connected to %s over SSH' % url.host()) 636 else: 637 nostr = '--nostr' if not self.showStringsCheckBox.isChecked() else '' 638 noarr = '--noarr' if not self.showArraysCheckBox.isChecked() else '' 639 self.ssh_input_file, self.ssh_output_file, self.ssh_error_file, ok = nm.ssh().ssh_exec(url.host(), ['rostopic echo %s %s %s' % (nostr, noarr, self.topic)], user, pw, auto_pw_request=True, get_pty=True) 640 if ok: 641 self.display.clear() 642 target = self._read_output_hz if self.show_only_rate else self._read_output 643 thread = threading.Thread(target=target, args=((self.ssh_output_file,))) 644 thread.setDaemon(True) 645 thread.start() 646 thread = threading.Thread(target=self._read_error, args=((self.ssh_error_file,))) 647 thread.setDaemon(True) 648 thread.start() 649 elif self.ssh_output_file: 650 self.ssh_output_file.close() 651 self.ssh_error_file.close() 652 except Exception as e: 653 self._append_error_text('%s\n' % e)
654
655 - def _read_output_hz(self, output_file):
656 try: 657 while not output_file.closed: 658 text = output_file.read(1) 659 if text: 660 self.text_hz_signal.emit(text) 661 except Exception: 662 pass
663
664 - def _read_output(self, output_file):
665 while not output_file.closed: 666 text = output_file.read(1) 667 if text: 668 self.text_signal.emit(text)
669
670 - def _read_error(self, error_file):
671 try: 672 while not error_file.closed: 673 text = error_file.read(1) 674 if text: 675 self.text_error_signal.emit(text) 676 except Exception: 677 pass
678 679 # ############################################################################# 680 # PARTS OF genpy/messages.py 681 # ############################################################################# 682
683 - def strify_message(self, val, indent='', time_offset=None, current_time=None, field_filter=None, fixed_numeric_width=None):
684 """ 685 Convert value to string representation 686 :param val: to convert to string representation. Most likely a Message. ``Value`` 687 :param indent: indentation. If indent is set, then the return value will have a leading \n, ``str`` 688 :param time_offset: if not None, time fields will be displayed 689 as deltas from time_offset, ``Time`` 690 691 :param current_time: currently not used. Only provided for API 692 compatibility. current_time passes in the current time with 693 respect to the message, ``Time`` 694 :param field_filter: filter the fields that are strified for Messages, ``fn(Message)->iter(str)`` 695 :returns: string (YAML) representation of message, ``str`` 696 """ 697 698 type_ = type(val) 699 if type_ in (int, long, float) and fixed_numeric_width is not None: 700 if type_ is float: 701 return ('%.' + str(fixed_numeric_width) + 'f') % val 702 else: 703 return ('%d') % val 704 elif type_ in (int, long, float, bool): 705 return utf8(val) 706 elif isstring(val): 707 # TODO: need to escape strings correctly 708 if not val: 709 return "''" 710 return val 711 elif isinstance(val, TVal): 712 if time_offset is not None and isinstance(val, Time): 713 val = val - time_offset 714 if fixed_numeric_width is not None: 715 format_str = '%d' 716 sec_str = '\n%ssecs: ' % indent + (format_str % val.secs) 717 nsec_str = '\n%snsecs: ' % indent + (format_str % val.nsecs) 718 return sec_str + nsec_str 719 else: 720 return '\n%ssecs: %s\n%snsecs: %9d' % (indent, val.secs, indent, val.nsecs) 721 722 elif type_ in (list, tuple): 723 if len(val) == 0: 724 return "[]" 725 val0 = val[0] 726 if type(val0) in (int, float) and self.digits_after_in_array is not None: 727 list_str = '[' + ''.join(self.strify_message(v, indent, time_offset, current_time, field_filter, self.digits_after_in_array) + ', ' for v in val).rstrip(', ') + ']' 728 return list_str 729 elif type(val0) in (int, float, str, bool): 730 # TODO: escape strings properly 731 return utf8(list(val)) 732 else: 733 pref = indent + '- ' 734 indent = indent + ' ' 735 return '\n' + '\n'.join([pref + self.strify_message(v, indent, time_offset, current_time, field_filter, self.digits_after_in_array) for v in val]) 736 elif isinstance(val, message.Message): 737 # allow caller to select which fields of message are strified 738 if field_filter is not None: 739 fields = list(field_filter(val)) 740 else: 741 fields = val.__slots__ 742 743 p = '%s%%s: %%s' % (indent) 744 ni = ' ' + indent 745 python_zip = None 746 if sys.hexversion > 0x03000000: # Python3 747 python_zip = zip 748 else: # Python2 749 python_zip = itertools.izip 750 slots = [] 751 for f, t in python_zip(val.__slots__, val._slot_types): 752 if f in fields: 753 cval = _convert_getattr(val, f, t) 754 slot_name = f 755 if isinstance(cval, (list, tuple)): 756 slot_name = "%s[%d]" % (f, len(cval)) 757 slots.append(p % (slot_name, self.strify_message(cval, ni, time_offset, current_time, field_filter, fixed_numeric_width))) 758 vals = '\n'.join(slots) 759 if indent: 760 return '\n' + vals 761 else: 762 return vals 763 else: 764 return utf8(val) # punt
765