def __init__(self): """Instantiates a SendableChooser. """ from networktables import StringArray self.choices = StringArray() self.values = [] self.defaultChoice = None self.defaultValue = None
def initTable(self, subtable): self.table = subtable from networktables import StringArray, NumberArray self.commands = StringArray() self.ids = NumberArray() self.toCancel = NumberArray() self.table.putValue("Names", self.commands) self.table.putValue("Ids", self.ids) self.table.putValue("Cancel", self.toCancel)
class Scheduler(Sendable): """The Scheduler is a singleton which holds the top-level running commands. It is in charge of both calling the command's run() method and to make sure that there are no two commands with conflicting requirements running. It is fine if teams wish to take control of the Scheduler themselves, all that needs to be done is to call Scheduler.getInstance().run() often to have Commands function correctly. However, this is already done for you if you use the CommandBased Robot template. .. seealso:: :class:`.Command` """ @staticmethod def _reset(): try: del Scheduler.instance except: pass @staticmethod def getInstance(): """Returns the Scheduler, creating it if one does not exist. :returns: the Scheduler """ if not hasattr(Scheduler, "instance"): Scheduler.instance = Scheduler() return Scheduler.instance def __init__(self): """Instantiates a Scheduler. """ hal.HALReport(hal.HALUsageReporting.kResourceType_Command, hal.HALUsageReporting.kCommand_Scheduler) # Active Commands self.commandTable = collections.OrderedDict() # The set of all Subsystems self.subsystems = set() # Whether or not we are currently adding a command self.adding = False # Whether or not we are currently disabled self.disabled = False # A list of all Commands which need to be added self.additions = [] # A list of all Buttons. It is created lazily. self.buttons = [] self.runningCommandsChanged = False def add(self, command): """Adds the command to the Scheduler. This will not add the :class:`.Command` immediately, but will instead wait for the proper time in the :meth:`run` loop before doing so. The command returns immediately and does nothing if given null. Adding a :class:`.Command` to the :class:`.Scheduler` involves the Scheduler removing any Command which has shared requirements. :param command: the command to add """ if command is not None: self.additions.append(command) def addButton(self, button): """Adds a button to the Scheduler. The Scheduler will poll the button during its :meth:`run`. :param button: the button to add """ self.buttons.append(button) def _add(self, command): """Adds a command immediately to the Scheduler. This should only be called in the :meth:`run` loop. Any command with conflicting requirements will be removed, unless it is uninterruptable. Giving None does nothing. :param command: the :class:`.Command` to add """ if command is None: return # Check to make sure no adding during adding if self.adding: warnings.warn( "Can not start command from cancel method. Ignoring: %s" % command, RuntimeWarning) return # Only add if not already in if command not in self.commandTable: # Check that the requirements can be had for lock in command.getRequirements(): if (lock.getCurrentCommand() is not None and not lock.getCurrentCommand().isInterruptible()): return # Give it the requirements self.adding = True for lock in command.getRequirements(): if lock.getCurrentCommand() is not None: lock.getCurrentCommand().cancel() self.remove(lock.getCurrentCommand()) lock.setCurrentCommand(command) self.adding = False # Add it to the list self.commandTable[command] = 1 self.runningCommandsChanged = True command.startRunning() def run(self): """Runs a single iteration of the loop. This method should be called often in order to have a functioning Command system. The loop has five stages: - Poll the Buttons - Execute/Remove the Commands - Send values to SmartDashboard - Add Commands - Add Defaults """ self.runningCommandsChanged = False if self.disabled: return # Don't run when disabled # Get button input (going backwards preserves button priority) for button in reversed(self.buttons): button() # Loop through the commands for command in list(self.commandTable): if not command.run(): self.remove(command) self.runningCommandsChanged = True # Add the new things for command in self.additions: self._add(command) self.additions.clear() # Add in the defaults for lock in self.subsystems: if lock.getCurrentCommand() is None: self._add(lock.getDefaultCommand()) lock.confirmCommand() self.updateTable() def registerSubsystem(self, system): """Registers a :class:`.Subsystem` to this Scheduler, so that the Scheduler might know if a default Command needs to be run. All :class:`.Subsystem` objects should call this. :param system: the system """ if system is not None: self.subsystems.add(system) def remove(self, command): """Removes the :class:`.Command` from the Scheduler. :param command: the command to remove """ if command is None or command not in self.commandTable: return del self.commandTable[command] for reqt in command.getRequirements(): reqt.setCurrentCommand(None) command.removed() def removeAll(self): """Removes all commands """ # TODO: Confirm that this works with "uninteruptible" commands for command in self.commandTable: for reqt in command.getRequirements(): reqt.setCurrentCommand(None) command.removed() self.commandTable.clear() def disable(self): """Disable the command scheduler. """ self.disabled = True def enable(self): """Enable the command scheduler. """ self.disabled = False def getName(self): return "Scheduler" def getType(self): return "Scheduler" def initTable(self, subtable): self.table = subtable from networktables import StringArray, NumberArray self.commands = StringArray() self.ids = NumberArray() self.toCancel = NumberArray() self.table.putValue("Names", self.commands) self.table.putValue("Ids", self.ids) self.table.putValue("Cancel", self.toCancel) def updateTable(self): table = self.getTable() if table is None: return # Get the commands to cancel self.table.retrieveValue("Cancel", self.toCancel) if self.toCancel: for command in self.commandTable: if id(command) in self.toCancel: command.cancel() self.toCancel.clear() self.table.putValue("Cancel", self.toCancel) if self.runningCommandsChanged: self.commands.clear() self.ids.clear() # Set the the running commands for command in self.commandTable: self.commands.append(command.getName()) self.ids.append(id(command)) self.table.putValue("Names", self.commands) self.table.putValue("Ids", self.ids) def getSmartDashboardType(self): return "Scheduler"
class SendableChooser(Sendable): """A useful tool for presenting a selection of options to be displayed on the SmartDashboard For instance, you may wish to be able to select between multiple autonomous modes. You can do this by putting every possible Command you want to run as an autonomous into a SendableChooser and then put it into the SmartDashboard to have a list of options appear on the laptop. Once autonomous starts, simply ask the SendableChooser what the selected value is. Example:: # This shows the user two options on the SmartDashboard chooser = wpilib.SendableChooser() chooser.addObject('option1', '1') chooser.addObject('option2', '2') wpilib.SmartDashboard.putData('Choice', chooser) # .. later, ask to see what the user selected? value = chooser.getSelected() """ # The key for the default value DEFAULT = "default" # The key for the selected option SELECTED = "selected" # The key for the option array OPTIONS = "options" # A table linking strings to the objects the represent def __init__(self): """Instantiates a SendableChooser. """ from networktables import StringArray self.choices = StringArray() self.values = [] self.defaultChoice = None self.defaultValue = None def addObject(self, name, object): """Adds the given object to the list of options. On the SmartDashboard on the desktop, the object will appear as the given name. :param name: the name of the option :param object: the option """ # if we don't have a default, set the default automatically if self.defaultChoice is None: self.addDefault(name, object) return for i, choice in enumerate(self.choices): if choice == name: self.values[i] = object return # not found self.choices.append(name) self.values.append(object) table = self.getTable() if table is not None: table.putValue(self.OPTIONS, self.choices) def addDefault(self, name, object): """Add the given object to the list of options and marks it as the default. Functionally, this is very close to addObject(...) except that it will use this as the default option if none other is explicitly selected. :param name: the name of the option :param object: the option """ if name is None: raise ValueError("Name cannot be None") self.defaultChoice = name self.defaultValue = object table = self.getTable() if table is not None: table.putString(self.DEFAULT, self.defaultChoice) self.addObject(name, object) def getSelected(self): """Returns the object associated with the selected option. If there is none selected, it will return the default. If there is none selected and no default, then it will return None. :returns: the object associated with the selected option """ table = self.getTable() if table is None: return self.defaultValue selected = table.getString(self.SELECTED, None) for i, choice in enumerate(self.choices): if choice == selected: return self.values[i] return self.defaultValue def getSmartDashboardType(self): return "String Chooser" def initTable(self, table): self.table = table if table is not None: table.putValue(self.OPTIONS, self.choices) if self.defaultChoice is not None: table.putString(self.DEFAULT, self.defaultChoice)
class Scheduler(Sendable): """The Scheduler is a singleton which holds the top-level running commands. It is in charge of both calling the command's run() method and to make sure that there are no two commands with conflicting requirements running. It is fine if teams wish to take control of the Scheduler themselves, all that needs to be done is to call Scheduler.getInstance().run() often to have Commands function correctly. However, this is already done for you if you use the CommandBased Robot template. .. seealso:: :class:`.Command` """ @staticmethod def _reset(): try: del Scheduler.instance except: pass @staticmethod def getInstance(): """Returns the Scheduler, creating it if one does not exist. :returns: the Scheduler """ if not hasattr(Scheduler, "instance"): Scheduler.instance = Scheduler() return Scheduler.instance def __init__(self): """Instantiates a Scheduler. """ hal.HALReport(hal.HALUsageReporting.kResourceType_Command, hal.HALUsageReporting.kCommand_Scheduler) # Active Commands self.commandTable = collections.OrderedDict() # The set of all Subsystems self.subsystems = set() # Whether or not we are currently adding a command self.adding = False # Whether or not we are currently disabled self.disabled = False # A list of all Commands which need to be added self.additions = [] # A list of all Buttons. It is created lazily. self.buttons = [] self.runningCommandsChanged = False def add(self, command): """Adds the command to the Scheduler. This will not add the :class:`.Command` immediately, but will instead wait for the proper time in the :meth:`run` loop before doing so. The command returns immediately and does nothing if given null. Adding a :class:`.Command` to the :class:`.Scheduler` involves the Scheduler removing any Command which has shared requirements. :param command: the command to add """ if command is not None: self.additions.append(command) def addButton(self, button): """Adds a button to the Scheduler. The Scheduler will poll the button during its :meth:`run`. :param button: the button to add """ self.buttons.append(button) def _add(self, command): """Adds a command immediately to the Scheduler. This should only be called in the :meth:`run` loop. Any command with conflicting requirements will be removed, unless it is uninterruptable. Giving None does nothing. :param command: the :class:`.Command` to add """ if command is None: return # Check to make sure no adding during adding if self.adding: warnings.warn("Can not start command from cancel method. Ignoring: %s" % command, RuntimeWarning) return # Only add if not already in if command not in self.commandTable: # Check that the requirements can be had for lock in command.getRequirements(): if (lock.getCurrentCommand() is not None and not lock.getCurrentCommand().isInterruptible()): return # Give it the requirements self.adding = True for lock in command.getRequirements(): if lock.getCurrentCommand() is not None: lock.getCurrentCommand().cancel() self.remove(lock.getCurrentCommand()) lock.setCurrentCommand(command) self.adding = False # Add it to the list self.commandTable[command] = 1 self.runningCommandsChanged = True command.startRunning() def run(self): """Runs a single iteration of the loop. This method should be called often in order to have a functioning Command system. The loop has five stages: - Poll the Buttons - Execute/Remove the Commands - Send values to SmartDashboard - Add Commands - Add Defaults """ self.runningCommandsChanged = False if self.disabled: return # Don't run when disabled # Get button input (going backwards preserves button priority) for button in reversed(self.buttons): button() # Loop through the commands for command in list(self.commandTable): if not command.run(): self.remove(command) self.runningCommandsChanged = True # Add the new things for command in self.additions: self._add(command) self.additions.clear() # Add in the defaults for lock in self.subsystems: if lock.getCurrentCommand() is None: self._add(lock.getDefaultCommand()) lock.confirmCommand() self.updateTable() def registerSubsystem(self, system): """Registers a :class:`.Subsystem` to this Scheduler, so that the Scheduler might know if a default Command needs to be run. All :class:`.Subsystem` objects should call this. :param system: the system """ if system is not None: self.subsystems.add(system) def remove(self, command): """Removes the :class:`.Command` from the Scheduler. :param command: the command to remove """ if command is None or command not in self.commandTable: return del self.commandTable[command] for reqt in command.getRequirements(): reqt.setCurrentCommand(None) command.removed() def removeAll(self): """Removes all commands """ # TODO: Confirm that this works with "uninteruptible" commands for command in self.commandTable: for reqt in command.getRequirements(): reqt.setCurrentCommand(None) command.removed() self.commandTable.clear() def disable(self): """Disable the command scheduler. """ self.disabled = True def enable(self): """Enable the command scheduler. """ self.disabled = False def getName(self): return "Scheduler" def getType(self): return "Scheduler" def initTable(self, subtable): self.table = subtable from networktables import StringArray, NumberArray self.commands = StringArray() self.ids = NumberArray() self.toCancel = NumberArray() self.table.putValue("Names", self.commands) self.table.putValue("Ids", self.ids) self.table.putValue("Cancel", self.toCancel) def updateTable(self): table = self.getTable() if table is None: return # Get the commands to cancel self.table.retrieveValue("Cancel", self.toCancel) if self.toCancel: for command in self.commandTable: if id(command) in self.toCancel: command.cancel() self.toCancel.clear() self.table.putValue("Cancel", self.toCancel) if self.runningCommandsChanged: self.commands.clear() self.ids.clear() # Set the the running commands for command in self.commandTable: self.commands.add(command.getName()) self.ids.add(id(command)) self.table.putValue("Names", self.commands) self.table.putValue("Ids", self.ids) def getSmartDashboardType(self): return "Scheduler"