From: <jfa...@us...> - 2009-08-31 20:44:14
|
Revision: 23405 http://personalrobots.svn.sourceforge.net/personalrobots/?rev=23405&view=rev Author: jfaustwg Date: 2009-08-31 20:44:06 +0000 (Mon, 31 Aug 2009) Log Message: ----------- * Fix battery bar color * Draw a plug if plugged in * Add "stale" state to buttons -- greyed out if no data has been received * Shrink the icons drawn over the red/yellow/green lights * Add breaker state code * Add motors button * Add runstop/wireless runstop status * Add heuristic for rosout to determine red/yellow/green status (requires ROS 0.8) Modified Paths: -------------- pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/speech_bubble.png pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/stethoscope-32.png pkg/trunk/stacks/pr2_gui/pr2_dashboard2/src/pr2_dashboard/pr2_frame.py Added Paths: ----------- pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/electricity.png pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/green_stoplight.png pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/grey_light.png pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/grey_stoplight.png pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/plug.png pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/red_stoplight.png pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/yellow_stoplight.png Added: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/electricity.png =================================================================== (Binary files differ) Property changes on: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/electricity.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/green_stoplight.png =================================================================== (Binary files differ) Property changes on: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/green_stoplight.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/grey_light.png =================================================================== (Binary files differ) Property changes on: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/grey_light.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/grey_stoplight.png =================================================================== (Binary files differ) Property changes on: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/grey_stoplight.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/plug.png =================================================================== (Binary files differ) Property changes on: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/plug.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/red_stoplight.png =================================================================== (Binary files differ) Property changes on: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/red_stoplight.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/speech_bubble.png =================================================================== (Binary files differ) Modified: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/stethoscope-32.png =================================================================== (Binary files differ) Added: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/yellow_stoplight.png =================================================================== (Binary files differ) Property changes on: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/icons/yellow_stoplight.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: pkg/trunk/stacks/pr2_gui/pr2_dashboard2/src/pr2_dashboard/pr2_frame.py =================================================================== --- pkg/trunk/stacks/pr2_gui/pr2_dashboard2/src/pr2_dashboard/pr2_frame.py 2009-08-31 20:43:52 UTC (rev 23404) +++ pkg/trunk/stacks/pr2_gui/pr2_dashboard2/src/pr2_dashboard/pr2_frame.py 2009-08-31 20:44:06 UTC (rev 23405) @@ -46,8 +46,9 @@ import robot_monitor from robot_monitor.robot_monitor_panel import RobotMonitorPanel -from pr2_msgs.msg import PowerState -from pr2_power_board.srv import PowerBoardCommand, PowerBoardCommandRequest +from pr2_msgs.msg import PowerState, PowerBoardState +from pr2_power_board.srv import PowerBoardCommand, PowerBoardCommandRequest +import std_srvs.srv import rospy @@ -73,10 +74,13 @@ self._rosout_panel = rxtools.RosoutPanel(self) + def get_panel(self): + return self._rosout_panel + def on_close(self, evt): self.Hide() -class BatteryStateControl(wx.Window): +class PowerStateControl(wx.Window): def __init__(self, parent, id, icons_path): wx.Window.__init__(self, parent, id, wx.DefaultPosition, wx.Size(60, -1)) @@ -87,10 +91,13 @@ self._left_bitmap = wx.Bitmap(path.join(icons_path, "battery_left.png"), wx.BITMAP_TYPE_PNG) self._right_bitmap = wx.Bitmap(path.join(icons_path, "battery_right.png"), wx.BITMAP_TYPE_PNG) + self._plug_bitmap = wx.Bitmap(path.join(icons_path, "plug.png"), wx.BITMAP_TYPE_PNG) self._start_x = self._left_bitmap.GetWidth() self._end_x = self.GetSize().x - self._right_bitmap.GetWidth() self._width = self._end_x - self._start_x + self._plugged_in = False + self.Bind(wx.EVT_PAINT, self.on_paint) def on_paint(self, evt): @@ -106,9 +113,9 @@ green = [0.0, 255, 0.0] color = None - if (self._pct > 50): + if (self._pct > 0.5): color = wx.Colour(green[0], green[1], green[2]) - elif (self._pct > 30): + elif (self._pct > 0.3): color = wx.Colour(yellow[0], yellow[1], yellow[2]) else: color = wx.Colour(red[0], red[1], red[2]) @@ -122,30 +129,35 @@ dc.DrawRectangle(self._start_x, 0, self._width * (self._pct), h) dc.DrawBitmap(self._left_bitmap, 0, 0, True) dc.DrawBitmap(self._right_bitmap, self._end_x, 0, True) + + if (self._plugged_in): + dc.DrawBitmap(self._plug_bitmap, + (self._start_x + self._width) / 2.0 - (self._plug_bitmap.GetWidth() / 2.0), + self.GetSize().GetHeight() / 2.0 - (self._plug_bitmap.GetHeight() / 2.0)) - def set_battery_state(self, msg): + def set_power_state(self, msg): self._power_consumption = msg.power_consumption self._time_remaining = msg.time_remaining - print msg.relative_capacity self._pct = msg.relative_capacity / 100.0 - self._msg = msg + self._plugged_in = msg.AC_present - self.SetToolTip(wx.ToolTip("Battery: %.2f%% (%d minutes remaining)"%(self._pct, self._time_remaining))) + self.SetToolTip(wx.ToolTip("Battery: %.2f%% (%d minutes remaining)"%(self._pct * 100, self._time_remaining))) self.Refresh() class StatusControl(wx.Window): - def __init__(self, parent, id, bitmap, icons_path): + def __init__(self, parent, id, bitmap, icons_path, lights = ("green_light.png", "yellow_light.png", "red_light.png", "grey_light.png")): wx.Window.__init__(self, parent, id) self.SetSize(wx.Size(32, 32)) self._bitmap = bitmap - self._green = wx.Bitmap(path.join(icons_path, "green_light.png"), wx.BITMAP_TYPE_PNG) - self._yellow = wx.Bitmap(path.join(icons_path, "yellow_light.png"), wx.BITMAP_TYPE_PNG) - self._red = wx.Bitmap(path.join(icons_path, "red_light.png"), wx.BITMAP_TYPE_PNG) + self._green = wx.Bitmap(path.join(icons_path, lights[0]), wx.BITMAP_TYPE_PNG) + self._yellow = wx.Bitmap(path.join(icons_path, lights[1]), wx.BITMAP_TYPE_PNG) + self._red = wx.Bitmap(path.join(icons_path, lights[2]), wx.BITMAP_TYPE_PNG) + self._stale = wx.Bitmap(path.join(icons_path, lights[3]), wx.BITMAP_TYPE_PNG) - self.set_ok() + self.set_stale() self.Bind(wx.EVT_PAINT, self.on_paint) self.Bind(wx.EVT_LEFT_UP, self.on_left_up) @@ -161,8 +173,11 @@ dc = wx.BufferedPaintDC(self) dc.SetBackground(wx.Brush(self.GetBackgroundColour())) dc.Clear() - dc.DrawBitmap(self._color, 0, 0, True) - dc.DrawBitmap(self._bitmap, 0, 0, True) + + size = self.GetSize(); + + dc.DrawBitmap(self._color, (size.GetWidth() - self._color.GetWidth()) / 2.0, (size.GetHeight() - self._color.GetHeight()) / 2.0, True) + dc.DrawBitmap(self._bitmap, (size.GetWidth() - self._bitmap.GetWidth()) / 2.0, (size.GetHeight() - self._bitmap.GetHeight()) / 2.0, True) def set_ok(self): self._color = self._green @@ -176,6 +191,10 @@ self._color = self._red self.Refresh() + def set_stale(self): + self._color = self._stale + self.Refresh() + class BreakerControl(StatusControl): def __init__(self, parent, id, icons_path): StatusControl.__init__(self, parent, id, wx.Bitmap(path.join(icons_path, "electricity.png"), wx.BITMAP_TYPE_PNG), icons_path) @@ -242,6 +261,30 @@ def on_disable_all(self, evt): self.control3("disable") + def set_power_board_state_msg(self, msg): + self._power_board_state = msg + + status_msg = "OK" + + if (msg.circuit_state[0] == PowerBoardState.STATE_DISABLED or + msg.circuit_state[1] == PowerBoardState.STATE_DISABLED or + msg.circuit_state[2] == PowerBoardState.STATE_DISABLED): + self.set_error() + status_msg = "Disabled" + elif (msg.circuit_state[0] == PowerBoardState.STATE_NOPOWER or + msg.circuit_state[1] == PowerBoardState.STATE_NOPOWER or + msg.circuit_state[2] == PowerBoardState.STATE_NOPOWER): + self.set_error() + status_msg = "No Power" + elif (msg.circuit_state[0] == PowerBoardState.STATE_STANDBY or + msg.circuit_state[1] == PowerBoardState.STATE_STANDBY or + msg.circuit_state[2] == PowerBoardState.STATE_STANDBY): + self.set_warn() + status_msg = "Standby" + else: + self.set_ok() + + self.SetToolTip(wx.ToolTip("Breaker State [%s]"%(status_msg))) class PR2Frame(wx.Frame): _CONFIG_WINDOW_X="/Window/X" @@ -284,13 +327,32 @@ self._breakers_ctrl.SetToolTip(wx.ToolTip("Breakers")) sizer.Add(self._breakers_ctrl, 0) + # Motors + self._motors_button = StatusControl(self, wx.ID_ANY, wx.NullBitmap, icons_path) + self._motors_button.SetToolTip(wx.ToolTip("Motors")) + sizer.Add(self._motors_button, 0) + self._motors_button.Bind(wx.EVT_LEFT_DOWN, self.on_motors_clicked) + # Separator sizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.EXPAND) + # run-stop + self._runstop_ctrl = StatusControl(self, wx.ID_ANY, wx.NullBitmap, icons_path, lights=("green_stoplight.png", "yellow_stoplight.png", "red_stoplight.png", "grey_stoplight.png")) + self._runstop_ctrl.SetToolTip(wx.ToolTip("Runstop Status")) + sizer.Add(self._runstop_ctrl, 0) + + # Wireless run-stop + self._wireless_runstop_ctrl = StatusControl(self, wx.ID_ANY, wx.NullBitmap, icons_path, lights=("green_stoplight.png", "yellow_stoplight.png", "red_stoplight.png", "grey_stoplight.png")) + self._wireless_runstop_ctrl.SetToolTip(wx.ToolTip("Wireless Runstop Status")) + sizer.Add(self._wireless_runstop_ctrl, 0) + + # Separator + sizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.EXPAND) + # Battery State - self._battery_state_ctrl = BatteryStateControl(self, wx.ID_ANY, icons_path) - self._battery_state_ctrl.SetToolTip(wx.ToolTip("Battery")) - sizer.Add(self._battery_state_ctrl, 1, wx.EXPAND) + self._power_state_ctrl = PowerStateControl(self, wx.ID_ANY, icons_path) + self._power_state_ctrl.SetToolTip(wx.ToolTip("Battery")) + sizer.Add(self._power_state_ctrl, 1, wx.EXPAND) self._config = wx.Config("pr2_dashboard") @@ -315,6 +377,7 @@ self._mutex = threading.Lock() rospy.Subscriber("power_state", PowerState, self.power_callback) + rospy.Subscriber("power_board_state", PowerBoardState, self.power_board_callback) def on_timer(self, evt): level = self._diagnostics_frame._diagnostics_panel.get_top_level_state() @@ -322,9 +385,13 @@ self._diagnostics_button.set_error() elif (level == 1): self._diagnostics_button.set_warn() + elif (level == -1): + self._diagnostics_button.set_stale() else: self._diagnostics_button.set_ok() + self.update_rosout() + if (rospy.is_shutdown()): self.Close() @@ -336,46 +403,133 @@ self._rosout_frame.Show() self._rosout_frame.Raise() + def on_motors_clicked(self, evt): + menu = wx.Menu() + menu.Bind(wx.EVT_MENU, self.on_reset_motors, menu.Append(wx.ID_ANY, "Reset")) + menu.Bind(wx.EVT_MENU, self.on_halt_motors, menu.Append(wx.ID_ANY, "Halt")) + self.PopupMenu(menu) + + def on_reset_motors(self, evt): + reset = rospy.ServiceProxy("reset_motors", std_srvs.srv.Empty) + + try: + reset() + except rospy.ServiceException, e: + wx.MessageBox("Failed to reset the motors: service call failed with error: %s"%(e), "Error", wx.OK|wx.ICON_ERROR) + + def on_halt_motors(self, evt): + halt = rospy.ServiceProxy("halt_motors", std_srvs.srv.Empty) + + try: + halt() + except rospy.ServiceException, e: + wx.MessageBox("Failed to halt the motors: service call failed with error: %s"%(e), "Error", wx.OK|wx.ICON_ERROR) + def power_callback(self, msg): self._mutex.acquire() self._power_message = msg self._mutex.release() - wx.CallAfter(self.new_battery_message) + wx.CallAfter(self.new_power_message) - def new_battery_message(self): + def new_power_message(self): self._mutex.acquire() msg = self._power_message self._power_message = None self._mutex.release() if (msg is not None): - self._battery_state_ctrl.set_battery_state(msg) + self._power_state_ctrl.set_power_state(msg) - def load_config(self): - # Load our window options - (x, y) = self.GetPositionTuple() - (width, height) = self.GetSizeTuple() - if (self._config.HasEntry(self._CONFIG_WINDOW_X)): - x = self._config.ReadInt(self._CONFIG_WINDOW_X) - if (self._config.HasEntry(self._CONFIG_WINDOW_Y)): - y = self._config.ReadInt(self._CONFIG_WINDOW_Y) + def power_board_callback(self, msg): + self._mutex.acquire() + self._power_board_message = msg + self._mutex.release() + + wx.CallAfter(self.new_power_board_message) + + def new_power_board_message(self): + self._mutex.acquire() + msg = self._power_board_message + self._power_board_message = None + self._mutex.release() + + if (msg is not None): + self._breakers_ctrl.set_power_board_state_msg(msg) + if (not msg.run_stop): + # if the wireless stop is also off, we can't tell if the runstop is pressed or not + if (not msg.wireless_stop): + self._runstop_ctrl.set_warn() + else: + self._runstop_ctrl.set_error() + else: + self._runstop_ctrl.set_ok() + + if (not msg.wireless_stop): + self._wireless_runstop_ctrl.set_error() + else: + self._wireless_runstop_ctrl.set_ok() + + def update_rosout(self): + # Support pre-ROS-0.8, just don't have this functionality + try: + getattr(self._rosout_frame.get_panel(), "getMessageSummary") + except AttributeError: + return + + summary = self._rosout_frame.get_panel().getMessageSummary(30.0) + + if (summary.fatal or summary.error): + self._rosout_button.set_error() + elif (summary.warn): + self._rosout_button.set_warn() + else: + self._rosout_button.set_ok() - self.SetPosition((x, y)) - self.SetSize((width, height)) - def save_config(self): - config = self._config + tooltip = "" + if (summary.fatal): + tooltip += "\nFatal: %s"%(summary.fatal) + if (summary.error): + tooltip += "\nError: %s"%(summary.error) + if (summary.warn): + tooltip += "\nWarn: %s"%(summary.warn) + if (summary.info): + tooltip += "\nInfo: %s"%(summary.info) + if (summary.debug): + tooltip += "\nDebug: %s"%(summary.debug) + + if (len(tooltip) == 0): + tooltip = "Rosout: no recent activity" + else: + tooltip = "Rosout: recent activity:" + tooltip - (x, y) = self.GetPositionTuple() - (width, height) = self.GetSizeTuple() - config.WriteInt(self._CONFIG_WINDOW_X, x) - config.WriteInt(self._CONFIG_WINDOW_Y, y) + self._rosout_button.SetToolTip(wx.ToolTip(tooltip)) - config.Flush() + def load_config(self): + # Load our window options + (x, y) = self.GetPositionTuple() + (width, height) = self.GetSizeTuple() + if (self._config.HasEntry(self._CONFIG_WINDOW_X)): + x = self._config.ReadInt(self._CONFIG_WINDOW_X) + if (self._config.HasEntry(self._CONFIG_WINDOW_Y)): + y = self._config.ReadInt(self._CONFIG_WINDOW_Y) + + self.SetPosition((x, y)) + self.SetSize((width, height)) + def save_config(self): + config = self._config + + (x, y) = self.GetPositionTuple() + (width, height) = self.GetSizeTuple() + config.WriteInt(self._CONFIG_WINDOW_X, x) + config.WriteInt(self._CONFIG_WINDOW_Y, y) + + config.Flush() + def on_close(self, event): - self.save_config() - - self.Destroy() - + self.save_config() + + self.Destroy() + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |