Source code for wpilib.livewindow

# validated: 2017-12-09 EN f9bece2ffbf7 edu/wpi/first/wpilibj/livewindow/LiveWindow.java
# ----------------------------------------------------------------------------
#  Copyright (c) 2008-2017 FIRST. All Rights Reserved.                        
#  Open Source Software - may be modified and shared by FRC teams. The code   
#  must be accompanied by the FIRST BSD license file in the root directory of 
#  the project.                                                               
# ----------------------------------------------------------------------------
import threading
from networktables import NetworkTablesInstance
from .sendablebuilder import SendableBuilder

import warnings
import logging
logger = logging.getLogger(__name__)

__all__ = ["LiveWindow"]


class Component:
    def __init__(self, sendable, parent):
        self.sendable = sendable
        self.parent = parent
        self.builder = SendableBuilder()
        self.firstTime = True
        self.telemetryEnabled = True


class _LiveWindowComponent:
    """A LiveWindow component is a device (sensor or actuator) that should be
    added to the SmartDashboard in test mode. The components are cached until
    the first time the robot enters Test mode. This allows the components to
    be inserted, then renamed."""
    def __init__(self, subsystem, name, isSensor):
        self.subsystem = subsystem
        self.name = str(name)
        self.isSensor = isSensor

[docs]class LiveWindow: """The public interface for putting sensors and actuators on the LiveWindow.""" components = {} _liveWindowTable = None _statusTable = None _enabledEntry = None startLiveWindow = False liveWindowEnabled = False telemetryEnabled = True mutex = threading.RLock()
[docs] @classmethod def liveWindowTable(cls): if cls._liveWindowTable is None: cls._liveWindowTable = NetworkTablesInstance.getDefault().getTable("LiveWindow") return cls._liveWindowTable
[docs] @classmethod def statusTable(cls): if cls._statusTable is None: cls._statusTable = cls.liveWindowTable().getSubTable(".status") return cls._statusTable
[docs] @classmethod def enabledEntry(cls): if cls._enabledEntry is None: cls._enabledEntry = cls.statusTable().getEntry("LW Enabled") return cls._enabledEntry
@classmethod def _reset(cls): cls.components = {} cls._liveWindowTable = None cls._statusTable = None cls._enabledEntry = None cls.startLiveWindow = False cls.liveWindowEnabled = False cls.telemetryEnabled = True
[docs] @classmethod def isEnabled(cls): return cls.liveWindowEnabled
[docs] @classmethod def setEnabled(cls, enabled): """Set the enabled state of LiveWindow. If it's being enabled, turn off the scheduler and remove all the commands from the queue and enable all the components registered for LiveWindow. If it's being disabled, stop all the registered components and reenable the scheduler. TODO: add code to disable PID loops when enabling LiveWindow. The commands should reenable the PID loops themselves when they get rescheduled. This prevents arms from starting to move around, etc. after a period of adjusting them in LiveWindow mode. """ from .command import Scheduler if cls.liveWindowEnabled != enabled: scheduler = Scheduler.getInstance() if enabled: logger.info("Starting live window mode.") scheduler.disable() scheduler.removeAll() else: logger.info("Stopping live window mode.") for component in cls.components.values(): component.builder.stopLiveWindowMode() scheduler.enable() cls.startLiveWindow = enabled cls.liveWindowEnabled = enabled cls.enabledEntry().setBoolean(enabled)
[docs] @classmethod def run(cls): """The run method is called repeatedly to keep the values refreshed on the screen in test mode. .. deprecated:: 2018.0.0 No longer required """ warnings.warn("run is deprecated. It is no longer required.", DeprecationWarning, stacklevel=2) cls.updateValues()
[docs] @classmethod def addSensor(cls, subsystem, name, component): """Add a Sensor associated with the subsystem and with call it by the given name. :param subsystem: The subsystem this component is part of. :param name: The name of this component. :param component: A LiveWindowSendable component that represents a sensor. .. deprecated:: 2018.0.0 Use :meth:`.Sendable.setName` instead. """ warnings.warn("addSensor is deprecated. " + "Use Sendable.setName(subsystem, name) instead.", DeprecationWarning, stacklevel=2) with cls.mutex: cls.add(component) component.setName(subsystem, name)
[docs] @classmethod def addActuator(cls, subsystem, name, component): """Add an Actuator associated with the subsystem and with call it by the given name. :param subsystem: The subsystem this component is part of. :param name: The name of this component. :param component: A LiveWindowSendable component that represents a actuator. .. deprecated:: 2018.0.0 Use :meth:`.Sendable.setName` instead. """ warnings.warn("addActuator is deprecated. " + "Use Sendable.setName(subsystem, name) instead.", DeprecationWarning, stacklevel=2) with cls.mutex: cls.add(component) component.setName(subsystem, name)
[docs] @classmethod def addSensorChannel(cls, moduleType, channel, component): """Add Sensor to LiveWindow. The components are shown with the type and channel like this: Gyro[0] for a gyro object connected to the first analog channel. :param moduleType: A string indicating the type of the module used in the naming (above) :param channel: The channel number the device is connected to :param component: A reference to the object being added .. deprecated:: 2018.0.0 Use :meth:`.SendableBase.setName` instead. """ warnings.warn("addSensorChannel is deprecated. "+ "Use SendableBase.setName(moduleType, channel) instead.", DeprecationWarning, stacklevel=2) cls.add(component) component.setName("Ungrouped", "%s[%s]" % (moduleType, channel))
[docs] @classmethod def addActuatorChannel(cls, moduleType, channel, component): """Add Actuator to LiveWindow. The components are shown with the module type, slot and channel like this: Servo[0,2] for a servo object connected to the first digital module and PWM port 2. :param moduleType: A string that defines the module name in the label for the value :param channel: The channel number the device is plugged into (usually PWM) :param component: The reference to the object being added .. deprecated:: 2018.0.0 Use :meth:`.SendableBase.setName` instead. """ warnings.warn("addActuatorChannel is deprecated. " + "Use SendableBase.setName(moduleType, channel) instead.", DeprecationWarning, stacklevel=2) cls.add(component) component.setName("Ungrouped", "%s[%s]" % (moduleType, channel))
[docs] @classmethod def addActuatorModuleChannel(cls, moduleType, moduleNumber, channel, component): """Add Actuator to LiveWindow. The components are shown with the module type, slot and channel like this: Servo[0,2] for a servo object connected to the first digital module and PWM port 2. :param moduleType: A string that defines the module name in the label for the value :param moduleNumber: The number of the particular module type :param channel: The channel number the device is plugged into (usually PWM) :param component: The reference to the object being added .. deprecated:: 2018.0.0 Use :meth:`.SendableBase.setName` instead. """ warnings.warn("addActuatorModuleChannel is deprecated. " + "Use SendableBase.setName(moduleType, moduleNumber, channel) instead.", DeprecationWarning, stacklevel=2) cls.add(component) component.setName("Ungrouped", "%s[%s,%s]" % (moduleType, moduleNumber, channel))
[docs] @classmethod def add(cls, sendable): """ Add a component to the LiveWindow. :param sendable: component to add """ with cls.mutex: if sendable not in cls.components: cls.components[sendable] = Component(sendable, None)
[docs] @classmethod def addChild(cls, parent, child): """ Add a child component to a component. :param parent: parent component :param child: child component """ with cls.mutex: component = cls.components.get(child, None) if component is None: component = Component(None, parent) cls.components[child] = component else: component.parent = parent component.telemetryEnabled = False
[docs] @classmethod def remove(cls, sendable): """ Remove a component from the LiveWindow. @param sendable component to remove """ with cls.mutex: if sendable in cls.components: component = cls.components[sendable] del cls.components[sendable] if cls.isEnabled(): component.builder.stopLiveWindowMode()
[docs] @classmethod def enableTelemetry(cls, sendable): """ Enable telemetry for a single component. :param sendable: component """ with cls.mutex: cls.telemetryEnabled = True component = cls.components.get(sendable, None) if component is not None: component.telemetryEnabled = True
[docs] @classmethod def disableTelemetry(cls, sendable): """ Disable telemetry for a single component. :param sendable: component """ with cls.mutex: component = cls.components.get(sendable, None) if component is not None: component.telemetryEnabled = False
[docs] @classmethod def disableAllTelemetry(cls): """ Disable ALL telemetry """ with cls.mutex: cls.telemetryEnabled = False for component in cls.components.values(): component.telemetryEnabled = False
[docs] @classmethod def updateValues(cls): with cls.mutex: # only do this if either LiveWindow mode or telemetry is enabled if not cls.liveWindowEnabled and not cls.telemetryEnabled: return for component in cls.components.values(): if component.sendable is not None and component.parent is None and (cls.liveWindowEnabled or component.telemetryEnabled): if component.firstTime: # By holding off creating the NetworkTable entries, it allows the # components to be redefined. This allows default sensor and actuator # values to be created that are replaced with the custom names from # users calling setName. name = component.sendable.getName() if name == "": continue subsystem = component.sendable.getSubsystem() ssTable = cls.liveWindowTable().getSubTable(subsystem) if name == subsystem: table = ssTable else: table = ssTable.getSubTable(name) table.getEntry(".name").setString(name) component.builder.setTable(table) component.sendable.initSendable(component.builder) ssTable.getEntry(".type").setString("LW Subsystem") component.firstTime = False if cls.startLiveWindow: component.builder.startLiveWindowMode() component.builder.updateTable() cls.startLiveWindow = False