001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2012. 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/*----------------------------------------------------------------------------*/
007package edu.wpi.first.wpilibj;
008
009import java.nio.IntBuffer;
010import java.nio.ByteBuffer;
011import java.nio.ByteOrder;
012
013import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary;
014import edu.wpi.first.wpilibj.communication.HALControlWord;
015import edu.wpi.first.wpilibj.communication.HALAllianceStationID;
016import edu.wpi.first.wpilibj.hal.HALUtil;
017import edu.wpi.first.wpilibj.hal.PowerJNI;
018
019/**
020 * Provide access to the network communication data to / from the Driver Station.
021 */
022public class DriverStation implements RobotState.Interface {
023
024    /**
025     * Number of Joystick Ports
026     */
027    public static final int kJoystickPorts = 6;
028        
029        private class HALJoystickButtons {
030                public int buttons;
031                public byte count;
032        }
033
034    /**
035     * The robot alliance that the robot is a part of
036     */
037    public enum Alliance { Red, Blue, Invalid }
038
039    private static final double JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL = 1.0;
040    private double m_nextMessageTime = 0.0;
041
042    private static class DriverStationTask implements Runnable {
043
044        private DriverStation m_ds;
045
046        DriverStationTask(DriverStation ds) {
047            m_ds = ds;
048        }
049
050        public void run() {
051            m_ds.task();
052        }
053    } /* DriverStationTask */
054
055    private static DriverStation instance = new DriverStation();
056
057    private short[][] m_joystickAxes = new short[kJoystickPorts][FRCNetworkCommunicationsLibrary.kMaxJoystickAxes];
058    private short[][] m_joystickPOVs = new short[kJoystickPorts][FRCNetworkCommunicationsLibrary.kMaxJoystickPOVs];
059    private HALJoystickButtons[] m_joystickButtons = new HALJoystickButtons[kJoystickPorts];
060
061    private Thread m_thread;
062    private final Object m_dataSem;
063    private volatile boolean m_thread_keepalive = true;
064    private boolean m_userInDisabled = false;
065    private boolean m_userInAutonomous = false;
066    private boolean m_userInTeleop = false;
067    private boolean m_userInTest = false;
068    private boolean m_newControlData;
069    private final ByteBuffer m_packetDataAvailableMutex;
070    private final ByteBuffer m_packetDataAvailableSem;
071
072    /**
073     * Gets an instance of the DriverStation
074     *
075     * @return The DriverStation.
076     */
077    public static DriverStation getInstance() {
078        return DriverStation.instance;
079    }
080
081    /**
082     * DriverStation constructor.
083     *
084     * The single DriverStation instance is created statically with the
085     * instance static member variable.
086     */
087    protected DriverStation() {
088        m_dataSem = new Object();
089                for(int i=0; i<kJoystickPorts; i++)
090                {
091                        m_joystickButtons[i] = new HALJoystickButtons();
092                }
093
094        m_packetDataAvailableMutex = HALUtil.initializeMutexNormal();
095        m_packetDataAvailableSem = HALUtil.initializeMultiWait();
096        FRCNetworkCommunicationsLibrary.setNewDataSem(m_packetDataAvailableSem);
097
098        m_thread = new Thread(new DriverStationTask(this), "FRCDriverStation");
099        m_thread.setPriority((Thread.NORM_PRIORITY + Thread.MAX_PRIORITY) / 2);
100
101        m_thread.start();
102    }
103
104    /**
105     * Kill the thread
106     */
107    public void release() {
108        m_thread_keepalive = false;
109    }
110
111    /**
112     * Provides the service routine for the DS polling thread.
113     */
114    private void task() {
115        int safetyCounter = 0;
116        while (m_thread_keepalive) {
117                HALUtil.takeMultiWait(m_packetDataAvailableSem, m_packetDataAvailableMutex, 0);
118                synchronized (this) {
119                getData();
120            }
121            synchronized (m_dataSem) {
122                m_dataSem.notifyAll();
123            }
124            if (++safetyCounter >= 4) {
125                MotorSafetyHelper.checkMotors();
126                safetyCounter = 0;
127            }
128            if (m_userInDisabled) {
129                FRCNetworkCommunicationsLibrary.FRCNetworkCommunicationObserveUserProgramDisabled();
130            }
131            if (m_userInAutonomous) {
132                FRCNetworkCommunicationsLibrary.FRCNetworkCommunicationObserveUserProgramAutonomous();
133            }
134            if (m_userInTeleop) {
135                FRCNetworkCommunicationsLibrary.FRCNetworkCommunicationObserveUserProgramTeleop();
136            }
137            if (m_userInTest) {
138                FRCNetworkCommunicationsLibrary.FRCNetworkCommunicationObserveUserProgramTest();
139            }
140        }
141    }
142
143    /**
144     * Wait for new data from the driver station.
145     */
146    public void waitForData() {
147        waitForData(0);
148    }
149
150    /**
151     * Wait for new data or for timeout, which ever comes first.  If timeout is
152     * 0, wait for new data only.
153     *
154     * @param timeout The maximum time in milliseconds to wait.
155     */
156    public void waitForData(long timeout) {
157        synchronized (m_dataSem) {
158            try {
159                m_dataSem.wait(timeout);
160            } catch (InterruptedException ex) {
161            }
162        }
163    }
164    /**
165     * Copy data from the DS task for the user.
166     * If no new data exists, it will just be returned, otherwise
167     * the data will be copied from the DS polling loop.
168     */
169    protected synchronized void getData() {
170
171        // Get the status of all of the joysticks
172        for(byte stick = 0; stick < kJoystickPorts; stick++) {
173            m_joystickAxes[stick] = FRCNetworkCommunicationsLibrary.HALGetJoystickAxes(stick);
174            m_joystickPOVs[stick] = FRCNetworkCommunicationsLibrary.HALGetJoystickPOVs(stick);
175                        ByteBuffer countBuffer = ByteBuffer.allocateDirect(1);
176                        m_joystickButtons[stick].buttons = FRCNetworkCommunicationsLibrary.HALGetJoystickButtons((byte)stick, countBuffer);
177                        m_joystickButtons[stick].count = countBuffer.get();
178        }
179
180        m_newControlData = true;
181    }
182
183    /**
184     * Read the battery voltage.
185     *
186     * @return The battery voltage in Volts.
187     */
188    public double getBatteryVoltage() {
189        IntBuffer status = ByteBuffer.allocateDirect(4).asIntBuffer();
190        float voltage = PowerJNI.getVinVoltage(status);
191        HALUtil.checkStatus(status);
192
193        return voltage;
194    }
195
196        /**
197         * Reports errors related to unplugged joysticks
198         * Throttles the errors so that they don't overwhelm the DS
199         */
200    private void reportJoystickUnpluggedError(String message) {
201        double currentTime = Timer.getFPGATimestamp();
202        if (currentTime > m_nextMessageTime) {
203            reportError(message, false);
204            m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
205        }
206    }
207
208    /**
209     * Get the value of the axis on a joystick.
210     * This depends on the mapping of the joystick connected to the specified port.
211     *
212     * @param stick The joystick to read.
213     * @param axis The analog axis value to read from the joystick.
214     * @return The value of the axis on the joystick.
215     */
216    public synchronized double getStickAxis(int stick, int axis) {
217        if(stick < 0 || stick >= kJoystickPorts) {
218            throw new RuntimeException("Joystick index is out of range, should be 0-5");
219        }
220
221        if (axis < 0 || axis >= FRCNetworkCommunicationsLibrary.kMaxJoystickAxes) {
222            throw new RuntimeException("Joystick axis is out of range");
223        }
224
225        if (axis >= m_joystickAxes[stick].length) {
226                        reportJoystickUnpluggedError("WARNING: Joystick axis " + axis + " on port " + stick + " not available, check if controller is plugged in\n");
227            return 0.0;
228        }
229
230        byte value = (byte)m_joystickAxes[stick][axis];
231
232        if(value < 0) {
233            return value / 128.0;
234        } else {
235            return value / 127.0;
236        }
237    }
238
239    /**
240    * Returns the number of axes on a given joystick port
241    *
242    * @param stick The joystick port number
243        * @return The number of axes on the indicated joystick
244    */
245    public synchronized int getStickAxisCount(int stick){
246
247        if(stick < 0 || stick >= kJoystickPorts) {
248            throw new RuntimeException("Joystick index is out of range, should be 0-5");
249        }
250        
251        return m_joystickAxes[stick].length;
252    }
253
254    /**
255     * Get the state of a POV on the joystick.
256     *
257     * @return the angle of the POV in degrees, or -1 if the POV is not pressed.
258     */
259    public synchronized int getStickPOV(int stick, int pov) {
260        if(stick < 0 || stick >= kJoystickPorts) {
261            throw new RuntimeException("Joystick index is out of range, should be 0-5");
262        }
263
264        if (pov < 0 || pov >= FRCNetworkCommunicationsLibrary.kMaxJoystickPOVs) {
265            throw new RuntimeException("Joystick POV is out of range");
266        }
267
268        if (pov >= m_joystickPOVs[stick].length) {
269                        reportJoystickUnpluggedError("WARNING: Joystick POV " + pov + " on port " + stick + " not available, check if controller is plugged in\n");
270            return 0;
271        }
272
273        return m_joystickPOVs[stick][pov];
274    }
275
276    /**
277    * Returns the number of POVs on a given joystick port
278    *
279    * @param stick The joystick port number
280        * @return The number of POVs on the indicated joystick
281    */
282    public synchronized int getStickPOVCount(int stick){
283
284        if(stick < 0 || stick >= kJoystickPorts) {
285            throw new RuntimeException("Joystick index is out of range, should be 0-5");
286        }
287
288        return m_joystickPOVs[stick].length;
289    }
290
291    /**
292     * The state of the buttons on the joystick.
293     *
294     * @param stick The joystick to read.
295     * @return The state of the buttons on the joystick.
296     */
297    public synchronized int getStickButtons(final int stick) {
298        if(stick < 0 || stick >= kJoystickPorts) {
299            throw new RuntimeException("Joystick index is out of range, should be 0-3");
300        }
301
302        return m_joystickButtons[stick].buttons;
303    }
304
305    /**
306     * The state of one joystick button. Button indexes begin at 1.
307     *
308     * @param stick The joystick to read.
309     * @param button The button index, beginning at 1.
310     * @return The state of the joystick button.
311     */
312    public synchronized boolean getStickButton(final int stick, byte button) {
313        if(stick < 0 || stick >= kJoystickPorts) {
314            throw new RuntimeException("Joystick index is out of range, should be 0-3");
315        }
316                
317
318                if(button > m_joystickButtons[stick].count) {
319                        reportJoystickUnpluggedError("WARNING: Joystick Button " + button + " on port " + stick + " not available, check if controller is plugged in\n");
320            return false;
321                }
322                if(button <= 0)
323                {
324                        reportJoystickUnpluggedError("ERROR: Button indexes begin at 1 in WPILib for C++ and Java\n");
325                        return false;
326                }
327                return ((0x1 << (button - 1)) & m_joystickButtons[stick].buttons) != 0;
328    }
329
330    /**
331    * Gets the number of buttons on a joystick
332    *
333    * @param  stick The joystick port number
334        * @return The number of buttons on the indicated joystick
335    */
336    public synchronized int getStickButtonCount(int stick){
337
338        if(stick < 0 || stick >= kJoystickPorts) {
339            throw new RuntimeException("Joystick index is out of range, should be 0-5");
340        }
341        
342        
343        return m_joystickButtons[stick].count;
344    }
345
346    /**
347     * Gets a value indicating whether the Driver Station requires the
348     * robot to be enabled.
349     *
350     * @return True if the robot is enabled, false otherwise.
351     */
352    public boolean isEnabled() {
353                HALControlWord controlWord = FRCNetworkCommunicationsLibrary.HALGetControlWord();
354        return controlWord.getEnabled() && controlWord.getDSAttached();
355    }
356
357    /**
358     * Gets a value indicating whether the Driver Station requires the
359     * robot to be disabled.
360     *
361     * @return True if the robot should be disabled, false otherwise.
362     */
363    public boolean isDisabled() {
364        return !isEnabled();
365    }
366
367    /**
368     * Gets a value indicating whether the Driver Station requires the
369     * robot to be running in autonomous mode.
370     *
371     * @return True if autonomous mode should be enabled, false otherwise.
372     */
373    public boolean isAutonomous() {
374                HALControlWord controlWord = FRCNetworkCommunicationsLibrary.HALGetControlWord();
375        return controlWord.getAutonomous();
376    }
377
378    /**
379     * Gets a value indicating whether the Driver Station requires the
380     * robot to be running in test mode.
381     * @return True if test mode should be enabled, false otherwise.
382     */
383    public boolean isTest() {
384                HALControlWord controlWord = FRCNetworkCommunicationsLibrary.HALGetControlWord();
385        return controlWord.getTest();
386    }
387
388    /**
389     * Gets a value indicating whether the Driver Station requires the
390     * robot to be running in operator-controlled mode.
391     *
392     * @return True if operator-controlled mode should be enabled, false otherwise.
393     */
394    public boolean isOperatorControl() {
395        return !(isAutonomous() || isTest());
396    }
397        
398        /**
399     * Gets a value indicating whether the FPGA outputs are enabled. The outputs may be disabled
400         * if the robot is disabled or e-stopped, the watdhog has expired, or if the roboRIO browns out.
401     *
402     * @return True if the FPGA outputs are enabled.
403     */
404        public boolean isSysActive() {
405                ByteBuffer status = ByteBuffer.allocateDirect(4);
406                status.order(ByteOrder.LITTLE_ENDIAN);
407                boolean retVal = FRCNetworkCommunicationsLibrary.HALGetSystemActive(status.asIntBuffer());
408                HALUtil.checkStatus(status.asIntBuffer());
409                return retVal;
410        }
411        
412        /**
413     * Check if the system is browned out.
414         * 
415     * @return True if the system is browned out
416     */
417        public boolean isBrownedOut() {
418                ByteBuffer status = ByteBuffer.allocateDirect(4);
419                status.order(ByteOrder.LITTLE_ENDIAN);
420                boolean retVal = FRCNetworkCommunicationsLibrary.HALGetBrownedOut(status.asIntBuffer());
421                HALUtil.checkStatus(status.asIntBuffer());
422                return retVal;
423        }
424
425    /**
426     * Has a new control packet from the driver station arrived since the last time this function was called?
427     * @return True if the control data has been updated since the last call.
428     */
429    public synchronized boolean isNewControlData() {
430        boolean result = m_newControlData;
431        m_newControlData = false;
432        return result;
433    }
434
435    /**
436     * Get the current alliance from the FMS
437     * @return the current alliance
438     */
439    public Alliance getAlliance() {
440                HALAllianceStationID allianceStationID = FRCNetworkCommunicationsLibrary.HALGetAllianceStation();
441                if(allianceStationID == null) {
442                        return Alliance.Invalid;
443                }
444                
445        switch (allianceStationID) {
446            case Red1:
447            case Red2:
448            case Red3:
449                return Alliance.Red;
450
451            case Blue1:
452            case Blue2:
453            case Blue3:
454                return Alliance.Blue;
455
456            default:
457                return Alliance.Invalid;
458        }
459    }
460
461    /**
462     * Gets the location of the team's driver station controls.
463     *
464     * @return the location of the team's driver station controls: 1, 2, or 3
465     */
466    public int getLocation() {
467                HALAllianceStationID allianceStationID = FRCNetworkCommunicationsLibrary.HALGetAllianceStation();
468                if(allianceStationID == null) {
469                        return 0;
470                }
471        switch (allianceStationID) {
472            case Red1:
473            case Blue1:
474                return 1;
475
476            case Red2:
477            case Blue2:
478                return 2;
479
480            case Blue3:
481            case Red3:
482                return 3;
483
484            default:
485                return 0;
486        }
487    }
488
489    /**
490     * Is the driver station attached to a Field Management System?
491     * Note: This does not work with the Blue DS.
492     * @return True if the robot is competing on a field being controlled by a Field Management System
493     */
494    public boolean isFMSAttached() {
495                HALControlWord controlWord = FRCNetworkCommunicationsLibrary.HALGetControlWord();
496        return controlWord.getFMSAttached();
497    }
498        
499        public boolean isDSAttached() {
500                HALControlWord controlWord = FRCNetworkCommunicationsLibrary.HALGetControlWord();
501                return controlWord.getDSAttached();
502        }
503
504        /**
505         * Return the approximate match time
506         * The FMS does not send an official match time to the robots, but does send an approximate match time.
507         * The value will count down the time remaining in the current period (auto or teleop).
508         * Warning: This is not an official time (so it cannot be used to dispute ref calls or guarantee that a function
509         * will trigger before the match ends)
510         * The Practice Match function of the DS approximates the behaviour seen on the field.
511         * @return Time remaining in current match period (auto or teleop) in seconds 
512         */
513    public double getMatchTime() {
514        return FRCNetworkCommunicationsLibrary.HALGetMatchTime();
515    }
516        
517        /**
518         * Report error to Driver Station.
519         * Also prints error to System.err
520         * Optionally appends Stack trace to error message
521         * @param printTrace If true, append stack trace to error string
522         */
523        public static void reportError(String error, boolean printTrace) {
524                String errorString = error;
525                if(printTrace) {
526                        errorString += " at ";
527                        StackTraceElement[] traces = Thread.currentThread().getStackTrace();
528                        for (int i=2; i<traces.length; i++)
529                        {
530                                errorString += traces[i].toString() + "\n";
531                        }
532                }
533                System.err.println(errorString);
534                HALControlWord controlWord = FRCNetworkCommunicationsLibrary.HALGetControlWord();
535                if(controlWord.getDSAttached()) {
536                        FRCNetworkCommunicationsLibrary.HALSetErrorData(errorString);
537                }
538        }
539
540    /** Only to be used to tell the Driver Station what code you claim to be executing
541     *   for diagnostic purposes only
542     * @param entering If true, starting disabled code; if false, leaving disabled code */
543    public void InDisabled(boolean entering) {
544        m_userInDisabled=entering;
545    }
546
547    /** Only to be used to tell the Driver Station what code you claim to be executing
548    *   for diagnostic purposes only
549     * @param entering If true, starting autonomous code; if false, leaving autonomous code */
550    public void InAutonomous(boolean entering) {
551        m_userInAutonomous=entering;
552    }
553
554    /** Only to be used to tell the Driver Station what code you claim to be executing
555    *   for diagnostic purposes only
556     * @param entering If true, starting teleop code; if false, leaving teleop code */
557    public void InOperatorControl(boolean entering) {
558        m_userInTeleop=entering;
559    }
560
561    /** Only to be used to tell the Driver Station what code you claim to be executing
562     *   for diagnostic purposes only
563     * @param entering If true, starting test code; if false, leaving test code */
564    public void InTest(boolean entering) {
565        m_userInTest = entering;
566    }
567}