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