Seamonsters-2605.github.io

Making a Dashboard with REMI

We will be using the remi library to make a simple webpage that runs on the robot and has buttons to make the robot move forward and stop moving.

If you are using a school laptop, you will need to turn off wifi to run the dashboard because otherwise the computer will block it.

import remi.gui as gui
import seamonsters as sea

This tells the python interpreter that you are going to use the remi.gui library and the seamonsters library in your code. To refrence those libraries in the code, you would write gui or sea because that is what it is imported as.

class PracticeDashboard(sea.Dashboard):

This is creating the dashboard class that holds all of the buttons and widgets of the dashboard. The sea.Dashboard inside of the parenthesis is the class that you are using as a template for your code.

def main(self, robot, appCallback):

This defines the function main() that all dashboard classes need to have. It takes in three arguments (the things in the parenthesis)

root = gui.VBox(gui.Label("Drive Controls"), width = 600, margin = "0px auto")

This declares the root widget to be returned later, adds a label to it, and modifies some of the html properties of it.

The VBox and HBox are containers that can have other widgets in them. Widgets contained in a VBox are alligned vertically and the ones in an HBox are alligned horizontally (“V” for vertical and “H” for horizontal).

driveBox = gui.VBox()
root.append(driveBox)

This creates a new VBox and adds it too the root widget by using the append function

driveForwardButton = gui.Button("Drive Forward")
driveForwardButton.set_on_click_listener(robot.c_driveForward)     driveBox.append(driveForwardButton)
stopButton = gui.Button("Stop")
stopButton.set_on_click_listener(robot.c_stop)
driveBox.append(stopButton)

Here, another button is created. This one says “Stop” on it and calls a different function on the robot when it is pressed (c_stop). The button is added to the driveBox.

appCallback(self)
return root

appCallback(self) tells the computer that the dashboard was initialized and adds it to the robot. return root gives the robot the full GUI that was put together within the main function.

import remi.gui as gui
import seamonsters as sea

class PracticeDashboard(sea.Dashboard):

    def main(self, robot, appCallback):

        root = gui.VBox(gui.Label("Drive Controls"), width = 600, margin = "0px auto")  

        driveBox = gui.VBox()
        root.append(driveBox)

        driveForwardButton = gui.Button("Drive Forward")
        driveForwardButton.set_on_click_listener(robot.c_driveForward)
        driveBox.append(driveForwardButton)

        stopButton = gui.Button("Stop")
        stopButton.set_on_click_listener(robot.c_stop)
        driveBox.append(stopButton)

        appCallback(self)
        return root

This is what the dashboard code should look like.

Setting up the Dashboard on the Robot

Open up robot.py, we are going to take a look at how the robot creates and manages the dashboard and its callbacks.

import dashboard

This is found right at the top of robot.py and it allows the code we wrote in dashboard.py to be used in this file.

self.app = None
sea.startDashboard(self, dashboard.PracticeDashboard)

This is found at the end of robotInit. First it declares self.app which is the dashboard and sets it to None. Then startDashboard starts the PracticeDashboard we wrote in dashboard.py and attaches it to the robot.

def teleop(self):
    yield from self.updateDashboardGenerator()
def updateDashboardGenerator(self):
    if self.app is not None:
        self.app.clearEvents()
    while True:
        v = None
        if self.app is not None:
            v = self.app.doEvents()
        yield v

teleop starts updateDashboardGenerator which goes through the events queued by the dashboard and executes them. Since updateDashboardGenerator has a yield in it, it is a generator. If you wanted to run a second generator at the same time (like if you wanted to be able to drive the robot manually) you would use sea.parallel() in telop and it will run all the generators inputted at the same time.

@sea.queuedDashboardEvent
def c_driveForward(self, button):
    self.drivetrain.drive(16, math.pi/2, 0)

@sea.queuedDashboardEvent
def c_stop(self, button):
    self.drivetrain.drive(0,0,0)

These are the two functions called when buttons are pressed because they were set as the callback when the buttons were defined in the dashboard. @sea.queuedDashboardEvent adds the function to the dashboard event queue when it is called. Then it runs when the dashboard is updated (as described above).