001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) 2008-2018 FIRST. 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.HashMap; 011import java.util.Map; 012 013import edu.wpi.first.networktables.NetworkTable; 014import edu.wpi.first.networktables.NetworkTableEntry; 015import edu.wpi.first.networktables.NetworkTableInstance; 016import edu.wpi.first.wpilibj.command.Scheduler; 017import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl; 018import edu.wpi.first.wpilibj.Sendable; 019 020 021/** 022 * The LiveWindow class is the public interface for putting sensors and actuators on the 023 * LiveWindow. 024 */ 025public class LiveWindow { 026 private static class Component { 027 Component(Sendable sendable, Sendable parent) { 028 m_sendable = sendable; 029 m_parent = parent; 030 } 031 032 final Sendable m_sendable; 033 Sendable m_parent; 034 final SendableBuilderImpl m_builder = new SendableBuilderImpl(); 035 boolean m_firstTime = true; 036 boolean m_telemetryEnabled = true; 037 } 038 039 private static final Map<Object, Component> components = new HashMap<>(); 040 private static final NetworkTable liveWindowTable = 041 NetworkTableInstance.getDefault().getTable("LiveWindow"); 042 private static final NetworkTable statusTable = liveWindowTable.getSubTable(".status"); 043 private static final NetworkTableEntry enabledEntry = statusTable.getEntry("LW Enabled"); 044 private static boolean startLiveWindow = false; 045 private static boolean liveWindowEnabled = false; 046 private static boolean telemetryEnabled = true; 047 048 public static synchronized boolean isEnabled() { 049 return liveWindowEnabled; 050 } 051 052 /** 053 * Set the enabled state of LiveWindow. If it's being enabled, turn off the scheduler and remove 054 * all the commands from the queue and enable all the components registered for LiveWindow. If 055 * it's being disabled, stop all the registered components and reenable the scheduler. TODO: add 056 * code to disable PID loops when enabling LiveWindow. The commands should reenable the PID loops 057 * themselves when they get rescheduled. This prevents arms from starting to move around, etc. 058 * after a period of adjusting them in LiveWindow mode. 059 */ 060 public static synchronized void setEnabled(boolean enabled) { 061 if (liveWindowEnabled != enabled) { 062 Scheduler scheduler = Scheduler.getInstance(); 063 if (enabled) { 064 System.out.println("Starting live window mode."); 065 scheduler.disable(); 066 scheduler.removeAll(); 067 } else { 068 System.out.println("stopping live window mode."); 069 for (Component component : components.values()) { 070 component.m_builder.stopLiveWindowMode(); 071 } 072 scheduler.enable(); 073 } 074 startLiveWindow = enabled; 075 liveWindowEnabled = enabled; 076 enabledEntry.setBoolean(enabled); 077 } 078 } 079 080 /** 081 * The run method is called repeatedly to keep the values refreshed on the screen in test mode. 082 * @deprecated No longer required 083 */ 084 @Deprecated 085 public static void run() { 086 updateValues(); 087 } 088 089 /** 090 * Add a Sensor associated with the subsystem and with call it by the given name. 091 * 092 * @param subsystem The subsystem this component is part of. 093 * @param name The name of this component. 094 * @param component A LiveWindowSendable component that represents a sensor. 095 * @deprecated Use {@link Sendable#setName(String, String)} instead. 096 */ 097 @Deprecated 098 public static synchronized void addSensor(String subsystem, String name, Sendable component) { 099 add(component); 100 component.setName(subsystem, name); 101 } 102 103 /** 104 * Add Sensor to LiveWindow. The components are shown with the type and channel like this: Gyro[1] 105 * for a gyro object connected to the first analog channel. 106 * 107 * @param moduleType A string indicating the type of the module used in the naming (above) 108 * @param channel The channel number the device is connected to 109 * @param component A reference to the object being added 110 * @deprecated Use {@link edu.wpi.first.wpilibj.SensorBase#setName(String, int)} instead. 111 */ 112 @Deprecated 113 public static void addSensor(String moduleType, int channel, Sendable component) { 114 add(component); 115 component.setName("Ungrouped", moduleType + "[" + channel + "]"); 116 } 117 118 /** 119 * Add an Actuator associated with the subsystem and with call it by the given name. 120 * 121 * @param subsystem The subsystem this component is part of. 122 * @param name The name of this component. 123 * @param component A LiveWindowSendable component that represents a actuator. 124 * @deprecated Use {@link Sendable#setName(String, String)} instead. 125 */ 126 @Deprecated 127 public static synchronized void addActuator(String subsystem, String name, Sendable component) { 128 add(component); 129 component.setName(subsystem, name); 130 } 131 132 /** 133 * Add Actuator to LiveWindow. The components are shown with the module type, slot and channel 134 * like this: Servo[1,2] for a servo object connected to the first digital module and PWM port 2. 135 * 136 * @param moduleType A string that defines the module name in the label for the value 137 * @param channel The channel number the device is plugged into (usually PWM) 138 * @param component The reference to the object being added 139 * @deprecated Use {@link edu.wpi.first.wpilibj.SensorBase#setName(String, int)} instead. 140 */ 141 @Deprecated 142 public static void addActuator(String moduleType, int channel, Sendable component) { 143 add(component); 144 component.setName("Ungrouped", moduleType + "[" + channel + "]"); 145 } 146 147 /** 148 * Add Actuator to LiveWindow. The components are shown with the module type, slot and channel 149 * like this: Servo[1,2] for a servo object connected to the first digital module and PWM port 2. 150 * 151 * @param moduleType A string that defines the module name in the label for the value 152 * @param moduleNumber The number of the particular module type 153 * @param channel The channel number the device is plugged into (usually PWM) 154 * @param component The reference to the object being added 155 * @deprecated Use {@link edu.wpi.first.wpilibj.SensorBase#setName(String, int, int)} instead. 156 */ 157 @Deprecated 158 public static void addActuator(String moduleType, int moduleNumber, int channel, 159 Sendable component) { 160 add(component); 161 component.setName("Ungrouped", moduleType + "[" + moduleNumber + "," + channel + "]"); 162 } 163 164 /** 165 * Add a component to the LiveWindow. 166 * 167 * @param sendable component to add 168 */ 169 public static synchronized void add(Sendable sendable) { 170 components.putIfAbsent(sendable, new Component(sendable, null)); 171 } 172 173 /** 174 * Add a child component to a component. 175 * 176 * @param parent parent component 177 * @param child child component 178 */ 179 public static synchronized void addChild(Sendable parent, Object child) { 180 Component component = components.get(child); 181 if (component == null) { 182 component = new Component(null, parent); 183 components.put(child, component); 184 } else { 185 component.m_parent = parent; 186 } 187 component.m_telemetryEnabled = false; 188 } 189 190 /** 191 * Remove a component from the LiveWindow. 192 * 193 * @param sendable component to remove 194 */ 195 public static synchronized void remove(Sendable sendable) { 196 Component component = components.remove(sendable); 197 if (component != null && isEnabled()) { 198 component.m_builder.stopLiveWindowMode(); 199 } 200 } 201 202 /** 203 * Enable telemetry for a single component. 204 * 205 * @param sendable component 206 */ 207 public static synchronized void enableTelemetry(Sendable sendable) { 208 // Re-enable global setting in case disableAllTelemetry() was called. 209 telemetryEnabled = true; 210 Component component = components.get(sendable); 211 if (component != null) { 212 component.m_telemetryEnabled = true; 213 } 214 } 215 216 /** 217 * Disable telemetry for a single component. 218 * 219 * @param sendable component 220 */ 221 public static synchronized void disableTelemetry(Sendable sendable) { 222 Component component = components.get(sendable); 223 if (component != null) { 224 component.m_telemetryEnabled = false; 225 } 226 } 227 228 /** 229 * Disable ALL telemetry. 230 */ 231 public static synchronized void disableAllTelemetry() { 232 telemetryEnabled = false; 233 for (Component component : components.values()) { 234 component.m_telemetryEnabled = false; 235 } 236 } 237 238 /** 239 * Tell all the sensors to update (send) their values. 240 * 241 * <p>Actuators are handled through callbacks on their value changing from the 242 * SmartDashboard widgets. 243 */ 244 public static synchronized void updateValues() { 245 // Only do this if either LiveWindow mode or telemetry is enabled. 246 if (!liveWindowEnabled && !telemetryEnabled) { 247 return; 248 } 249 250 for (Component component : components.values()) { 251 if (component.m_sendable != null && component.m_parent == null 252 && (liveWindowEnabled || component.m_telemetryEnabled)) { 253 if (component.m_firstTime) { 254 // By holding off creating the NetworkTable entries, it allows the 255 // components to be redefined. This allows default sensor and actuator 256 // values to be created that are replaced with the custom names from 257 // users calling setName. 258 String name = component.m_sendable.getName(); 259 if (name.isEmpty()) { 260 continue; 261 } 262 String subsystem = component.m_sendable.getSubsystem(); 263 NetworkTable ssTable = liveWindowTable.getSubTable(subsystem); 264 NetworkTable table; 265 // Treat name==subsystem as top level of subsystem 266 if (name.equals(subsystem)) { 267 table = ssTable; 268 } else { 269 table = ssTable.getSubTable(name); 270 } 271 table.getEntry(".name").setString(name); 272 component.m_builder.setTable(table); 273 component.m_sendable.initSendable(component.m_builder); 274 ssTable.getEntry(".type").setString("LW Subsystem"); 275 276 component.m_firstTime = false; 277 } 278 279 if (startLiveWindow) { 280 component.m_builder.startLiveWindowMode(); 281 } 282 component.m_builder.updateTable(); 283 } 284 } 285 286 startLiveWindow = false; 287 } 288}