001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2017. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj.livewindow;
009
010import java.util.Enumeration;
011import java.util.Hashtable;
012import java.util.Vector;
013
014import edu.wpi.first.wpilibj.command.Scheduler;
015import edu.wpi.first.wpilibj.networktables.NetworkTable;
016import edu.wpi.first.wpilibj.tables.ITable;
017
018
019/**
020 * The LiveWindow class is the public interface for putting sensors and actuators on the
021 * LiveWindow.
022 */
023public class LiveWindow {
024
025  private static Vector sensors = new Vector();
026  // private static Vector actuators = new Vector();
027  private static Hashtable components = new Hashtable();
028  private static ITable livewindowTable;
029  private static ITable statusTable;
030  private static boolean liveWindowEnabled = false;
031  private static boolean firstTime = true;
032
033  /**
034   * Initialize all the LiveWindow elements the first time we enter LiveWindow mode. By holding off
035   * creating the NetworkTable entries, it allows them to be redefined before the first time in
036   * LiveWindow mode. This allows default sensor and actuator values to be created that are replaced
037   * with the custom names from users calling addActuator and addSensor.
038   */
039  private static void initializeLiveWindowComponents() {
040    System.out.println("Initializing the components first time");
041    livewindowTable = NetworkTable.getTable("LiveWindow");
042    statusTable = livewindowTable.getSubTable("~STATUS~");
043    for (Enumeration e = components.keys(); e.hasMoreElements(); ) {
044      LiveWindowSendable component = (LiveWindowSendable) e.nextElement();
045      LiveWindowComponent liveWindowComponent = (LiveWindowComponent) components.get(component);
046      String subsystem = liveWindowComponent.getSubsystem();
047      String name = liveWindowComponent.getName();
048      System.out.println("Initializing table for '" + subsystem + "' '" + name + "'");
049      livewindowTable.getSubTable(subsystem).putString("~TYPE~", "LW Subsystem");
050      ITable table = livewindowTable.getSubTable(subsystem).getSubTable(name);
051      table.putString("~TYPE~", component.getSmartDashboardType());
052      table.putString("Name", name);
053      table.putString("Subsystem", subsystem);
054      component.initTable(table);
055      if (liveWindowComponent.isSensor()) {
056        sensors.addElement(component);
057      }
058    }
059  }
060
061  /**
062   * Set the enabled state of LiveWindow. If it's being enabled, turn off the scheduler and remove
063   * all the commands from the queue and enable all the components registered for LiveWindow. If
064   * it's being disabled, stop all the registered components and reenable the scheduler. TODO: add
065   * code to disable PID loops when enabling LiveWindow. The commands should reenable the PID loops
066   * themselves when they get rescheduled. This prevents arms from starting to move around, etc.
067   * after a period of adjusting them in LiveWindow mode.
068   */
069  public static void setEnabled(boolean enabled) {
070    if (liveWindowEnabled != enabled) {
071      if (enabled) {
072        System.out.println("Starting live window mode.");
073        if (firstTime) {
074          initializeLiveWindowComponents();
075          firstTime = false;
076        }
077        Scheduler.getInstance().disable();
078        Scheduler.getInstance().removeAll();
079        for (Enumeration e = components.keys(); e.hasMoreElements(); ) {
080          LiveWindowSendable component = (LiveWindowSendable) e.nextElement();
081          component.startLiveWindowMode();
082        }
083      } else {
084        System.out.println("stopping live window mode.");
085        for (Enumeration e = components.keys(); e.hasMoreElements(); ) {
086          LiveWindowSendable component = (LiveWindowSendable) e.nextElement();
087          component.stopLiveWindowMode();
088        }
089        Scheduler.getInstance().enable();
090      }
091      liveWindowEnabled = enabled;
092      statusTable.putBoolean("LW Enabled", enabled);
093    }
094  }
095
096  /**
097   * The run method is called repeatedly to keep the values refreshed on the screen in test mode.
098   */
099  public static void run() {
100    updateValues();
101  }
102
103  /**
104   * Add a Sensor associated with the subsystem and with call it by the given name.
105   *
106   * @param subsystem The subsystem this component is part of.
107   * @param name      The name of this component.
108   * @param component A LiveWindowSendable component that represents a sensor.
109   */
110  public static void addSensor(String subsystem, String name, LiveWindowSendable component) {
111    components.put(component, new LiveWindowComponent(subsystem, name, true));
112  }
113
114  /**
115   * Add Sensor to LiveWindow. The components are shown with the type and channel like this: Gyro[1]
116   * for a gyro object connected to the first analog channel.
117   *
118   * @param moduleType A string indicating the type of the module used in the naming (above)
119   * @param channel    The channel number the device is connected to
120   * @param component  A reference to the object being added
121   */
122  public static void addSensor(String moduleType, int channel, LiveWindowSendable component) {
123    addSensor("Ungrouped", moduleType + "[" + channel + "]", component);
124    if (sensors.contains(component)) {
125      sensors.removeElement(component);
126    }
127    sensors.addElement(component);
128  }
129
130  /**
131   * Add an Actuator associated with the subsystem and with call it by the given name.
132   *
133   * @param subsystem The subsystem this component is part of.
134   * @param name      The name of this component.
135   * @param component A LiveWindowSendable component that represents a actuator.
136   */
137  public static void addActuator(String subsystem, String name, LiveWindowSendable component) {
138    components.put(component, new LiveWindowComponent(subsystem, name, false));
139  }
140
141  /**
142   * Add Actuator to LiveWindow. The components are shown with the module type, slot and channel
143   * like this: Servo[1,2] for a servo object connected to the first digital module and PWM port 2.
144   *
145   * @param moduleType A string that defines the module name in the label for the value
146   * @param channel    The channel number the device is plugged into (usually PWM)
147   * @param component  The reference to the object being added
148   */
149  public static void addActuator(String moduleType, int channel, LiveWindowSendable component) {
150    addActuator("Ungrouped", moduleType + "[" + channel + "]", component);
151  }
152
153  /**
154   * Add Actuator to LiveWindow. The components are shown with the module type, slot and channel
155   * like this: Servo[1,2] for a servo object connected to the first digital module and PWM port 2.
156   *
157   * @param moduleType   A string that defines the module name in the label for the value
158   * @param moduleNumber The number of the particular module type
159   * @param channel      The channel number the device is plugged into (usually PWM)
160   * @param component    The reference to the object being added
161   */
162  public static void addActuator(String moduleType, int moduleNumber, int channel,
163                                 LiveWindowSendable component) {
164    addActuator("Ungrouped", moduleType + "[" + moduleNumber + "," + channel + "]", component);
165  }
166
167  /**
168   * Puts all sensor values on the live window.
169   */
170  private static void updateValues() {
171    // TODO: gross - needs to be sped up
172    for (int i = 0; i < sensors.size(); i++) {
173      LiveWindowSendable lws = (LiveWindowSendable) sensors.elementAt(i);
174      lws.updateTable();
175    }
176    // TODO: Add actuators?
177    // TODO: Add better rate limiting.
178  }
179}