示例#1
0
class PlexilPlanSelectionGUI(Plugin):

  #sets up our signal for the sub callback
  monitor_signal = pyqtSignal(['QString']) 

  def __init__(self, context):
    '''init initializes the widget and sets up our subscriber, publisher and event handlers'''
    super(PlexilPlanSelectionGUI, self).__init__(context)
    self.setObjectName('PlexilPlanSelectionGUI')

    # Process standalone plugin command-line arguments
    parser = ArgumentParser()
    parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", help="Put plugin in silent mode")
    args, unknowns = parser.parse_known_args(context.argv())

    # Find resources and Create QWidget
    self._widget = QWidget()
    ui_file = os.path.join(rospkg.RosPack().get_path('ow_plexil'), 'rqt_plexil_plan_selection', 'resource', 'plexil_plan_selection.ui')
    loadUi(ui_file, self._widget)
    self._widget.setObjectName('PlexilPlanSelectionUI')
    if context.serial_number() > 1:
      self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
    context.add_widget(self._widget)

    #set window icon
    icon = os.path.join(rospkg.RosPack().get_path('ow_plexil'), 'rqt_plexil_plan_selection', 'resource', 'icon.png')
    self._widget.setWindowIcon(QIcon(icon))
    #pub and sub setup
    self.status_publisher = rospy.Publisher('plexil_plan_selection_status', String, queue_size=20)
    self.status_subscriber = rospy.Subscriber('plexil_plan_selection_status', String, self.status_callback)
    #client placeholder
    self.plan_select_client = None
  
    #Qt signal to modify GUI from callback
    self.monitor_signal[str].connect(self.monitor_status)

    #populates the plan list, shows different plans based off of what launch file is running
    if rospy.get_param('owlat_flag', False):
      owlat_plan_dir = os.path.join(rospkg.RosPack().get_path('ow_plexil'), 'src', 'plans', 'owlat_plans')
      self.populate_plan_list(owlat_plan_dir)
    else:
      ow_plan_dir = os.path.join(rospkg.RosPack().get_path('ow_plexil'), 'src', 'plans')
      self.populate_plan_list(ow_plan_dir)
 
    #sets up tables
    self._widget.sentPlansTable.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
    self._widget.planQueueTable.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
    self._widget.sentPlansTable.insertColumn(0)
    self._widget.sentPlansTable.insertColumn(1)
    self._widget.planQueueTable.insertColumn(0)
    #sets up event listeners
    self._widget.addButton.clicked[bool].connect(self.handle_add_button_clicked)
    self._widget.removeButton.clicked[bool].connect(self.handle_remove_button_clicked)
    self._widget.upButton.clicked[bool].connect(self.handle_up_button_clicked)
    self._widget.downButton.clicked[bool].connect(self.handle_down_button_clicked)
    self._widget.sendButton.clicked[bool].connect(self.handle_send_button_clicked)
    self._widget.resetButton.clicked[bool].connect(self.handle_reset_button_clicked)

  def populate_plan_list(self, plan_dir):
    '''Finds all .ple plexil plans in the plans directory and stores them in the widget list.'''
    plan_list = []
    #get all plans with extension .ple
    for p in os.listdir(plan_dir):
      if p.endswith(".ple"):
        plan_list.append(p.rsplit(".")[0])
    #add to list
    self._widget.planList.addItems(plan_list)
    self._widget.planList.sortItems()

  def monitor_status(self, feedback):
    '''Signal from callback calls this function to do the work to avoid threading issued with the GUI,
     changes the status on the sent plans table to match the current plan status. Plan can be Pending,
     Running, Finished or Failed.'''
    num_rows = self._widget.sentPlansTable.rowCount()
    #if completed and previously running we set status as finished
    if feedback == "COMPLETE": 
      for i in range(num_rows):
        current_status = self._widget.sentPlansTable.item(i,1).text()
        if current_status == "Running...":
          self._widget.sentPlansTable.item(i, 1).setText("Finished")
          self._widget.sentPlansTable.item(i, 1).setBackground(QColor(0,128,0))
          break
    else:
      #splitting into plan name and status
      status = feedback.rsplit(":")[0]
      running_plan = feedback.rsplit(":")[1].rsplit(".")[0]
      #we set status to running or Failed depending on status 
      for i in range(num_rows):
        plan_name = self._widget.sentPlansTable.item(i,0).text()
        current_status = self._widget.sentPlansTable.item(i,1).text()
        if plan_name == running_plan and current_status == "Pending...":
          if status == "SUCCESS":
            self._widget.sentPlansTable.item(i, 1).setText("Running...")
            break 
          else:
            self._widget.sentPlansTable.item(i, 1).setText("Failed")
            self._widget.sentPlansTable.item(i, 1).setBackground(QColor(230,38,0))
            break 
    return
   
  def status_callback(self, msg):
    '''Callback from status subscriber. Sends the msg to the function monitor_signal for further 
     processing in order to prevent threading issues in the GUI.'''
    #have to use a signal here or else GUI wont update
    feedback_string = str(msg.data)
    self.monitor_signal.emit(feedback_string)

  def handle_add_button_clicked(self, checked):
    '''When the add button is clicked this function moves any selected items to the plan queue table.'''
    #get selected items
    selected_plans = self._widget.planList.selectedItems()
    #create a new row in the queue table for each selection and insert it
    for i in selected_plans:
      row_position = self._widget.planQueueTable.rowCount()
      self._widget.planQueueTable.insertRow(row_position)
      self._widget.planQueueTable.setItem(row_position-1, 1, QTableWidgetItem(i.text()))
    self._widget.planList.selectionModel().clear()
    self._widget.planQueueTable.resizeColumnsToContents()
    return

  def handle_remove_button_clicked(self, checked):
    '''When the remove button is clicked this function deletes any selected items from the plan queue table.'''
    selected_rows = self._widget.planQueueTable.selectedItems()
    for i in selected_rows:
      self._widget.planQueueTable.removeRow(self._widget.planQueueTable.row(i))
    self._widget.planQueueTable.selectionModel().clear()
    return

  def handle_up_button_clicked(self, checked):
    '''When up button is clicked this function moves the selected item up in the queue table.'''
    selected_row = self._widget.planQueueTable.currentRow()
    #checks we are not at top of list
    if selected_row <= 0:
      return
    #switches the two rows and puts selection on previously selected row
    else:
      belowTxt = self._widget.planQueueTable.item(selected_row,0).text()
      aboveTxt = self._widget.planQueueTable.item(selected_row-1,0).text()
      self._widget.planQueueTable.item(selected_row, 0).setText(aboveTxt)
      self._widget.planQueueTable.item(selected_row-1, 0).setText(belowTxt)
      self._widget.planQueueTable.selectionModel().clear()
      self._widget.planQueueTable.setCurrentItem(self._widget.planQueueTable.item(selected_row-1,0))
      return

  def handle_down_button_clicked(self, checked):
    '''When down button is clicked this function moves the selected item down in the queue table.'''
    selected_row = self._widget.planQueueTable.currentRow()
    num_rows = self._widget.planQueueTable.rowCount()

    #checks we are not at bottom of list
    if selected_row >= num_rows-1 or selected_row < 0:
      return
    #switches the two rows and puts selection on previously selected row
    else:
      belowTxt = self._widget.planQueueTable.item(selected_row+1,0).text()
      aboveTxt = self._widget.planQueueTable.item(selected_row,0).text()
      self._widget.planQueueTable.item(selected_row+1, 0).setText(aboveTxt)
      self._widget.planQueueTable.item(selected_row, 0).setText(belowTxt)
      self._widget.planQueueTable.selectionModel().clear()
      self._widget.planQueueTable.setCurrentItem(self._widget.planQueueTable.item(selected_row+1,0))
      return

  def handle_send_button_clicked(self, checked):
    '''When send button is clicked any items in the plan queue table are sent (in order) to the sent plans table.
     It then publishes a string array of these plans so that the ow_plexil_plan_selection node can run them 
     sequentially. If the subscriber is not connected a popup box reminding the user to run the ow_plexil node will show up.'''
    if self.check_client_set_up() == False:
      return

    num_rows = self._widget.planQueueTable.rowCount()
    plans_sent = []
    #creates a new row in the sent table for each item in the queue table and inserts it
    for i in range(num_rows):
      plan_name = self._widget.planQueueTable.item(0,0).text()
      plans_sent.append(str(plan_name + ".plx"))
      self._widget.planQueueTable.removeRow(0)
      
      row_position = self._widget.sentPlansTable.rowCount()
      self._widget.sentPlansTable.insertRow(row_position)
      self._widget.sentPlansTable.setItem(row_position, 0, QTableWidgetItem(plan_name))
      self._widget.sentPlansTable.setItem(row_position, 1, QTableWidgetItem("Pending..."))
      self._widget.sentPlansTable.resizeColumnsToContents()

    # Create msg and send to subscriber for plan execution
    self.plan_select_client("ADD", plans_sent)
    return

  def handle_reset_button_clicked(self, checked):
    '''When reset button is clicked all plans in the sent plans table are deleted. It also publishes a string command 
     RESET letting the ow_plexil_plan_selection node know that any plans in its queue should also be deleted.'''
    if self.check_client_set_up() == False:
      return
    num_rows = self._widget.sentPlansTable.rowCount()
    #deletes each row pressent in sentplanstable
    for i in range(num_rows):
      self._widget.sentPlansTable.removeRow(0)
    self.plan_select_client("RESET", [])
    return

  def check_client_set_up(self):
    '''Checks to see if the client is initialized, if not we check if the server is running before initializing the client.
    If server is not yet running we send a popup informing the user and return False.'''
    #checks to see if we have connected to service yet
    if self.plan_select_client == None:
      #we get list of services and see if any match plexil_plan_selection
      services = rosservice.get_service_list()
      service_running = [i for i in services if "plexil_plan_selection" in i]
      #if none exist we send a popup and return
      if len(service_running) == 0:
        popup = QMessageBox()
        popup.setWindowTitle("ow_plexil service not yet connected")
        popup.setText("ow_plexil plan selection service not connected yet, please make sure the ow_plexil launch file is running.")
        popup.exec_()
        return False
      else:
        #client setup
        rospy.wait_for_service('plexil_plan_selection')
        self.plan_select_client = rospy.ServiceProxy('plexil_plan_selection', PlanSelection)
        return True
    else:
      return True


  def shutdown_plugin(self):
    '''handles shutdown procedures for the plugin, unregisters the publisher and subscriber.'''
    self.status_subscriber.unregister()
    pass
示例#2
0
class ratrollerGuiPlugin(Plugin):
    def __init__(self, context):
        super(ratrollerGuiPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('DyrosRedGUI')
        self._publisher = rospy.Publisher('/dyros_red/command',
                                          String,
                                          queue_size=10)
        self._publisher2 = rospy.Publisher('/dyros_red/com_command',
                                           dyros_red_msgs.msg.ComCommand,
                                           queue_size=10)
        self._publisher3 = rospy.Publisher(
            '/mujoco_ros_interface/sim_command_con2sim', String, queue_size=10)

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('dyros_red_gui'),
                               'resource', 'DyrosRed.ui')
        icon_file = os.path.join(rospkg.RosPack().get_path('dyros_red_gui'),
                                 'resource', 'Dyros_Logo3.png')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('DyrosRedUi')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
            self._widget.setWindowIcon(QtGui.QIcon(icon_file))
        # Add widget to the user interface
        context.add_widget(self._widget)
        self._widget.gravity_button.pressed.connect(self.gravity_button)
        self._widget.task_button.pressed.connect(self.task_button)
        self._widget.contact_button.pressed.connect(self.contact_button)
        self._widget.data_button.pressed.connect(self.data_button)
        self._widget.com_send_button.pressed.connect(self.com_command_sender)
        self._widget.pause_button.pressed.connect(self.pause_button)
        self._widget.slowmotion_button.pressed.connect(self.slowmotion_button)
        self._widget.reset_button.pressed.connect(self.reset_button)

        self.sub = rospy.Subscriber('/dyros_red/point',
                                    geometry_msgs.msg.PolygonStamped,
                                    self.sub_cb)

    def sub_cb(self, msg):
        self._widget.label.setText(str(round(msg.polygon.points[0].x, 6)))
        self._widget.label_2.setText(str(round(msg.polygon.points[0].y, 6)))
        self._widget.label_3.setText(str(round(msg.polygon.points[0].z, 6)))
        self._widget.label_14.setText(str(round(msg.polygon.points[3].x, 6)))
        self._widget.label_15.setText(str(round(msg.polygon.points[3].y, 6)))
        self._widget.label_16.setText(str(round(msg.polygon.points[3].z, 6)))

        self._widget.label_22.setText(str(round(msg.polygon.points[4].x, 6)))
        self._widget.label_23.setText(str(round(msg.polygon.points[4].y, 6)))

        self._widget.label_24.setText(str(round(msg.polygon.points[5].x, 6)))
        self._widget.label_27.setText(str(round(msg.polygon.points[5].y, 6)))

    def pause_button(self):
        self.send_msg2("pause")

    def slowmotion_button(self):
        self.send_msg2("mjslowmotion")

    def reset_button(self):
        self.send_msg2("mjreset")

    def com_command_sender(self):
        if self._widget.text_pos.text().replace(
                '.', '',
                1).isdigit() and self._widget.text_time.text().replace(
                    '.', '', 1).isdigit():
            print("Sending COM command mgs")
            com_command_msg = dyros_red_msgs.msg.ComCommand()
            c_pos = float(self._widget.text_pos.text())
            if c_pos < 0 and c_pos > 1:
                print("com pos value must between 0 ~ 1")
            list = [0, c_pos, 1]
            list.sort()
            com_command_msg.ratio = list[1]
            t_time = float(self._widget.text_time.text())
            if t_time < 0:
                print("traj time is negative. changing it to positive")
                t_time = -t_time
            com_command_msg.time = t_time
            c_height = float(self._widget.text_height.text())
            com_command_msg.height = c_height
            com_command_msg.angle = float(self._widget.text_angle.text())
            idx = self._widget.comboBox.currentIndex()
            com_command_msg.mode = idx
            self._publisher2.publish(com_command_msg)

        else:
            print("Commands need to be float and positive ! ")

    def data_button(self):
        self.send_msg("data")

    def gravity_button(self):
        self.send_msg("gravity")

    def task_button(self):
        self.send_msg("task")

    def contact_button(self):
        self.send_msg("contact")

    def send_msg(self, msg):
        self._publisher.publish(msg)

    def send_msg2(self, msg):
        self._publisher3.publish(msg)

    def _unregister_publisher(self):
        if self._publisher is not None:
            self._publisher.unregister()
            self._publisher = None
        if self._publisher2 is not None:
            self._publisher2.unregister()
            self._publisher2 = None

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        self._unregister_publisher()
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass