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    /*----------------------------------------------------------------------------*/
007    package edu.wpi.first.wpilibj;
008    
009    import edu.wpi.first.wpilibj.communication.*;
010    import edu.wpi.first.wpilibj.parsing.IInputOutput;
011    
012    /**
013     * Provide access to the network communication data to / from the Driver Station.
014     */
015    public class DriverStation implements IInputOutput {
016    
017        /**
018         * The size of the user control data
019         */
020        public static final int USER_CONTROL_DATA_SIZE = FRCControl.USER_CONTROL_DATA_SIZE;
021        /**
022         * The size of the user status data
023         */
024        public static final int USER_STATUS_DATA_SIZE = FRCControl.USER_STATUS_DATA_SIZE;
025        /**
026         * Slot for the analog module to read the battery
027         */
028        public static final int kBatterySlot = 1;
029        /**
030         * Analog channel to read the battery
031         */
032        public static final int kBatteryChannel = 8;
033        /**
034         * Number of Joystick Ports
035         */
036        public static final int kJoystickPorts = 4;
037        /**
038         * Number of Joystick Axes
039         */
040        public static final int kJoystickAxes = 6;
041        /**
042         * Convert from raw values to volts
043         */
044        public static final double kDSAnalogInScaling = 5.0 / 1023.0;
045    
046        /**
047         * The robot alliance that the robot is a part of
048         */
049        public static class Alliance {
050    
051            /** The integer value representing this enumeration. */
052            public final int value;
053            /** The Alliance name. */
054            public final String name;
055            public static final int kRed_val = 0;
056            public static final int kBlue_val = 1;
057            public static final int kInvalid_val = 2;
058            /** alliance: Red */
059            public static final Alliance kRed = new Alliance(kRed_val, "Red");
060            /** alliance: Blue */
061            public static final Alliance kBlue = new Alliance(kBlue_val, "Blue");
062            /** alliance: Invalid */
063            public static final Alliance kInvalid = new Alliance(kInvalid_val, "invalid");
064    
065            private Alliance(int value, String name) {
066                this.value = value;
067                this.name = name;
068            }
069        } /* Alliance */
070    
071    
072        private static class DriverStationTask implements Runnable {
073    
074            private DriverStation m_ds;
075    
076            DriverStationTask(DriverStation ds) {
077                m_ds = ds;
078            }
079    
080            public void run() {
081                m_ds.task();
082            }
083        } /* DriverStationTask */
084    
085        private static DriverStation instance = new DriverStation();
086        private FRCCommonControlData m_controlData;
087        private AnalogChannel m_batteryChannel;
088        private Thread m_thread;
089        private final Object m_semaphore;
090        private final Object m_dataSem;
091        private int m_digitalOut;
092        private volatile boolean m_thread_keepalive = true;
093        private final Dashboard m_dashboardDefaultHigh;
094        private final Dashboard m_dashboardDefaultLow;
095        private IDashboard m_dashboardInUseHigh;
096        private IDashboard m_dashboardInUseLow;
097        private int m_updateNumber = 0;
098        private double m_approxMatchTimeOffset = -1.0;
099        private boolean m_userInDisabled = false;
100        private boolean m_userInAutonomous = false;
101        private boolean m_userInTeleop = false;
102        private boolean m_userInTest = false;
103        private boolean m_newControlData;
104        private final Semaphore m_packetDataAvailableSem;
105        private DriverStationEnhancedIO m_enhancedIO = new DriverStationEnhancedIO();
106    
107        /**
108         * Gets an instance of the DriverStation
109         *
110         * @return The DriverStation.
111         */
112        public static DriverStation getInstance() {
113            return DriverStation.instance;
114        }
115    
116        /**
117         * DriverStation constructor.
118         *
119         * The single DriverStation instance is created statically with the
120         * instance static member variable.
121         */
122        protected DriverStation() {
123            m_controlData = new FRCCommonControlData();
124            m_semaphore = new Object();
125            m_dataSem = new Object();
126    
127            m_dashboardInUseHigh = m_dashboardDefaultHigh = new Dashboard(m_semaphore);
128            m_dashboardInUseLow = m_dashboardDefaultLow = new Dashboard(m_semaphore);
129    
130            // m_controlData is initialized in constructor FRCCommonControlData.
131    
132            m_batteryChannel = new AnalogChannel(kBatterySlot, kBatteryChannel);
133    
134            Semaphore.Options options = new Semaphore.Options();
135            options.setPrioritySorted(true);
136            m_packetDataAvailableSem = new Semaphore(options, false);
137            FRCControl.setNewDataSem(m_packetDataAvailableSem);
138    
139            m_thread = new Thread(new DriverStationTask(this), "FRCDriverStation");
140            m_thread.setPriority((Thread.NORM_PRIORITY + Thread.MAX_PRIORITY) / 2);
141    
142            m_thread.start();
143        }
144    
145        /**
146         * Kill the thread
147         */
148        public void release() {
149            m_thread_keepalive = false;
150        }
151    
152        /**
153         * Provides the service routine for the DS polling thread.
154         */
155        private void task() {
156            int safetyCounter = 0;
157            while (m_thread_keepalive) {
158                try {
159                    m_packetDataAvailableSem.takeForever();
160                    synchronized (this) {
161                        getData();
162                        m_enhancedIO.updateData();
163                        setData();
164                    }
165                    synchronized (m_dataSem) {
166                        m_dataSem.notifyAll();
167                    }
168                    if (++safetyCounter >= 5) {
169                        //               System.out.println("Checking safety");
170                        MotorSafetyHelper.checkMotors();
171                        safetyCounter = 0;
172                    }
173                    if (m_userInDisabled) {
174                        FRCControl.observeUserProgramDisabled();
175                    }
176                    if (m_userInAutonomous) {
177                        FRCControl.observeUserProgramAutonomous();
178                    }
179                    if (m_userInTeleop) {
180                        FRCControl.observeUserProgramTeleop();
181                    }
182                    if (m_userInTest) {
183                        FRCControl.observeUserProgramTest();
184                    }
185                } catch (SemaphoreException ex) {
186                    //
187                }
188            }
189        }
190    
191        /**
192         * Wait for new data from the driver station.
193         */
194        public void waitForData() {
195            waitForData(0);
196        }
197    
198        /**
199         * Wait for new data or for timeout, which ever comes first.  If timeout is
200         * 0, wait for new data only.
201         * 
202         * @param timeout The maximum time in milliseconds to wait.
203         */
204        public void waitForData(long timeout) {
205            synchronized (m_dataSem) {
206                try {
207                    m_dataSem.wait(timeout);
208                } catch (InterruptedException ex) {
209                }
210            }
211        }
212        private static boolean lastEnabled = false;
213    
214        /**
215         * Copy data from the DS task for the user.
216         * If no new data exists, it will just be returned, otherwise
217         * the data will be copied from the DS polling loop.
218         */
219        protected synchronized void getData() {
220            FRCControl.getCommonControlData(m_controlData, Semaphore.WAIT_FOREVER);
221    
222            if (!lastEnabled && isEnabled()) {
223                // If starting teleop, assume that autonomous just took up 15 seconds
224                if (isAutonomous()) {
225                    m_approxMatchTimeOffset = Timer.getFPGATimestamp();
226                } else {
227                    m_approxMatchTimeOffset = Timer.getFPGATimestamp() - 15.0;
228                }
229            } else if (lastEnabled && !isEnabled()) {
230                m_approxMatchTimeOffset = -1.0;
231            }
232            lastEnabled = isEnabled();
233    
234            m_newControlData = true;
235        }
236    
237        /**
238         * Copy status data from the DS task for the user.
239         * This is used primarily to set digital outputs on the DS.
240         */
241        protected void setData() {
242            synchronized (m_semaphore) {
243                FRCControl.setStatusData(getBatteryVoltage(), m_digitalOut,
244                        m_updateNumber,
245                        m_dashboardInUseHigh.getBytes(),
246                        m_dashboardInUseHigh.getBytesLength(),
247                        m_dashboardInUseLow.getBytes(),
248                        m_dashboardInUseLow.getBytesLength(),
249                        Semaphore.WAIT_FOREVER);
250                m_dashboardInUseHigh.flush();
251                m_dashboardInUseLow.flush();
252            }
253        }
254    
255        /**
256         * Read the battery voltage from the specified AnalogChannel.
257         *
258         * This accessor assumes that the battery voltage is being measured
259         * through the voltage divider on an analog breakout.
260         *
261         * @return The battery voltage.
262         */
263        public double getBatteryVoltage() {
264            // The Analog bumper has a voltage divider on the battery source.
265            // Vbatt *--/\/\/\--* Vsample *--/\/\/\--* Gnd
266            //         680 Ohms            1000 Ohms
267            return m_batteryChannel.getAverageVoltage() * (1680.0 / 1000.0);
268        }
269    
270        /**
271         * Get the value of the axis on a joystick.
272         * This depends on the mapping of the joystick connected to the specified port.
273         *
274         * @param stick The joystick to read.
275         * @param axis The analog axis value to read from the joystick.
276         * @return The value of the axis on the joystick.
277         */
278        public double getStickAxis(int stick, int axis) {
279            if (axis < 1 || axis > kJoystickAxes) {
280                return 0.0;
281            }
282    
283            int value;
284            switch (stick) {
285                case 1:
286                    value = m_controlData.stick0Axes[axis - 1];
287                    break;
288                case 2:
289                    value = m_controlData.stick1Axes[axis - 1];
290                    break;
291                case 3:
292                    value = m_controlData.stick2Axes[axis - 1];
293                    break;
294                case 4:
295                    value = m_controlData.stick3Axes[axis - 1];
296                    break;
297                default:
298                    return 0.0;
299            }
300    
301            double result;
302            if (value < 0) {
303                result = ((double) value) / 128.0;
304            } else {
305                result = ((double) value) / 127.0;
306            }
307    
308            // wpi_assert(result <= 1.0 && result >= -1.0);
309            if (result > 1.0) {
310                result = 1.0;
311            } else if (result < -1.0) {
312                result = -1.0;
313            }
314            return result;
315        }
316    
317        /**
318         * The state of the buttons on the joystick.
319         * 12 buttons (4 msb are unused) from the joystick.
320         *
321         * @param stick The joystick to read.
322         * @return The state of the buttons on the joystick.
323         */
324        public int getStickButtons(final int stick) {
325            switch (stick) {
326                case 1:
327                    return m_controlData.stick0Buttons;
328                case 2:
329                    return m_controlData.stick1Buttons;
330                case 3:
331                    return m_controlData.stick2Buttons;
332                case 4:
333                    return m_controlData.stick3Buttons;
334                default:
335                    return 0;
336            }
337        }
338    
339        /**
340         * Get an analog voltage from the Driver Station.
341         * The analog values are returned as voltage values for the Driver Station analog inputs.
342         * These inputs are typically used for advanced operator interfaces consisting of potentiometers
343         * or resistor networks representing values on a rotary switch.
344         *
345         * @param channel The analog input channel on the driver station to read from. Valid range is 1 - 4.
346         * @return The analog voltage on the input.
347         */
348        public double getAnalogIn(final int channel) {
349            switch (channel) {
350                case 1:
351                    return kDSAnalogInScaling * m_controlData.analog1;
352                case 2:
353                    return kDSAnalogInScaling * m_controlData.analog2;
354                case 3:
355                    return kDSAnalogInScaling * m_controlData.analog3;
356                case 4:
357                    return kDSAnalogInScaling * m_controlData.analog4;
358                default:
359                    return 0.0;
360            }
361        }
362    
363        /**
364         * Get values from the digital inputs on the Driver Station.
365         * Return digital values from the Drivers Station. These values are typically used for buttons
366         * and switches on advanced operator interfaces.
367         * @param channel The digital input to get. Valid range is 1 - 8.
368         * @return The value of the digital input
369         */
370        public boolean getDigitalIn(final int channel) {
371            return ((m_controlData.dsDigitalIn >> (channel - 1)) & 0x1) == 0x1;
372        }
373    
374        /**
375         * Set a value for the digital outputs on the Driver Station.
376         *
377         * Control digital outputs on the Drivers Station. These values are typically used for
378         * giving feedback on a custom operator station such as LEDs.
379         *
380         * @param channel The digital output to set. Valid range is 1 - 8.
381         * @param value The state to set the digital output.
382         */
383        public void setDigitalOut(final int channel, final boolean value) {
384            m_digitalOut &= ~(0x1 << (channel - 1));
385            m_digitalOut |= ((value ? 1 : 0) << (channel - 1));
386        }
387    
388        /**
389         * Get a value that was set for the digital outputs on the Driver Station.
390         * @param channel The digital ouput to monitor. Valid range is 1 through 8.
391         * @return A digital value being output on the Drivers Station.
392         */
393        public boolean getDigitalOut(final int channel) {
394            return ((m_digitalOut >> (channel - 1)) & 0x1) == 0x1;
395        }
396    
397        /**
398         * Gets a value indicating whether the Driver Station requires the
399         * robot to be enabled.
400         *
401         * @return True if the robot is enabled, false otherwise.
402         */
403        public boolean isEnabled() {
404            return m_controlData.enabled();
405        }
406    
407        /**
408         * Gets a value indicating whether the Driver Station requires the
409         * robot to be disabled.
410         *
411         * @return True if the robot should be disabled, false otherwise.
412         */
413        public boolean isDisabled() {
414            return !m_controlData.enabled();
415        }
416    
417        /**
418         * Gets a value indicating whether the Driver Station requires the
419         * robot to be running in autonomous mode.
420         *
421         * @return True if autonomous mode should be enabled, false otherwise.
422         */
423        public boolean isAutonomous() {
424            return m_controlData.autonomous();
425        }
426        
427        /**
428         * Gets a value indicating whether the Driver Station requires the
429         * robot to be running in test mode.
430         * @return True if test mode should be enabled, false otherwise.
431         */
432        public boolean isTest() {
433            return m_controlData.testMode();
434        }
435    
436        /**
437         * Gets a value indicating whether the Driver Station requires the
438         * robot to be running in operator-controlled mode.
439         *
440         * @return True if operator-controlled mode should be enabled, false otherwise.
441         */
442        public boolean isOperatorControl() {
443            return !(m_controlData.autonomous() || m_controlData.testMode());
444        }
445    
446        /**
447         * Has a new control packet from the driver station arrived since the last time this function was called?
448         * @return True if the control data has been updated since the last call.
449         */
450        public synchronized boolean isNewControlData() {
451            boolean result = m_newControlData;
452            m_newControlData = false;
453            return result;
454        }
455    
456        /**
457         * Return the DS packet number.
458         * The packet number is the index of this set of data returned by the driver station.
459         * Each time new data is received, the packet number (included with the sent data) is returned.
460         *
461         * @return The DS packet number.
462         */
463        public int getPacketNumber() {
464            return m_controlData.packetIndex;
465        }
466    
467        /**
468         * Get the current alliance from the FMS
469         * @return the current alliance
470         */
471        public Alliance getAlliance() {
472            switch (m_controlData.dsID_Alliance) {
473                case 'R':
474                    return Alliance.kRed;
475                case 'B':
476                    return Alliance.kBlue;
477                default:
478                    return Alliance.kInvalid;
479            }
480        }
481    
482        /**
483         * Gets the location of the team's driver station controls.
484         *
485         * @return the location of the team's driver station controls: 1, 2, or 3
486         */
487        public int getLocation() {
488            return m_controlData.dsID_Position - '0';
489        }
490    
491        /**
492         * Return the team number that the Driver Station is configured for
493         * @return The team number
494         */
495        public int getTeamNumber() {
496            return m_controlData.teamID;
497        }
498    
499        /**
500         * Sets the dashboard packer to use for sending high priority user data to a
501         * dashboard receiver. This can idle or restore the default packer.
502         * (Initializing SmartDashboard sets the high priority packer in use, so
503         * beware that the default packer will then be idle. You can restore the
504         * default high priority packer by calling
505         * {@code setDashboardPackerToUseHigh(getDashboardPackerHigh())}.)
506         *
507         * @param dashboard any kind of IDashboard object
508         */
509        public void setDashboardPackerToUseHigh(IDashboard dashboard) {
510            m_dashboardInUseHigh = dashboard;
511        }
512    
513        /**
514         * Gets the default dashboard packer for sending high priority user data to
515         * a dashboard receiver. This instance stays around even after a call to
516         * {@link #setDashboardPackerToUseHigh} changes which packer is in use.
517         *
518         * @return the default Dashboard object; it may be idle
519         */
520        public Dashboard getDashboardPackerHigh() {
521            return m_dashboardDefaultHigh;
522        }
523    
524        /**
525         * Gets the dashboard packer that's currently in use for sending high
526         * priority user data to a dashboard receiver. This can be any kind of
527         * IDashboard.
528         *
529         * @return the current IDashboard object
530         */
531        public IDashboard getDashboardPackerInUseHigh() {
532            return m_dashboardInUseHigh;
533        }
534    
535        /**
536         * Sets the dashboard packer to use for sending low priority user data to a
537         * dashboard receiver. This can idle or restore the default packer.
538         *
539         * @param dashboard any kind of IDashboard object
540         */
541        public void setDashboardPackerToUseLow(IDashboard dashboard) {
542            m_dashboardInUseLow = dashboard;
543        }
544    
545        /**
546         * Gets the default dashboard packer for sending low priority user data to
547         * a dashboard receiver. This instance stays around even after a call to
548         * {@link #setDashboardPackerToUseLow} changes which packer is in use.
549         *
550         * @return the default Dashboard object; it may be idle
551         */
552        public Dashboard getDashboardPackerLow() {
553            return m_dashboardDefaultLow;
554        }
555    
556        /**
557         * Gets the dashboard packer that's currently in use for sending low
558         * priority user data to a dashboard receiver. This can be any kind of
559         * IDashboard.
560         *
561         * @return the current IDashboard object
562         */
563        public IDashboard getDashboardPackerInUseLow() {
564            return m_dashboardInUseLow;
565        }
566    
567        /**
568         * Gets the status data monitor
569         * @return The status data monitor for use with IDashboard objects which must
570         * send data across the network.
571         */
572        public Object getStatusDataMonitor() {
573            return m_semaphore;
574        }
575    
576        /**
577         * Increments the internal update number sent across the network along with
578         * status data.
579         */
580        void incrementUpdateNumber() {
581            synchronized (m_semaphore) {
582                m_updateNumber++;
583            }
584        }
585    
586        /**
587         * Is the driver station attached to a Field Management System?
588         * Note: This does not work with the Blue DS.
589         * @return True if the robot is competing on a field being controlled by a Field Management System
590         */
591        public boolean isFMSAttached() {
592            return (m_controlData.control & FRCCommonControlData.FMS_ATTATCHED) > 0;
593        }
594    
595        /**
596         * Get the interface to the enhanced IO of the new driver station.
597         * @return An enhanced IO object for the advanced features of the driver
598         * station.
599         */
600        public DriverStationEnhancedIO getEnhancedIO() {
601            return m_enhancedIO;
602        }
603    
604        /**
605         * Return the approximate match time
606         * The FMS does not currently send the official match time to the robots
607         * This returns the time since the enable signal sent from the Driver Station
608         * At the beginning of autonomous, the time is reset to 0.0 seconds
609         * At the beginning of teleop, the time is reset to +15.0 seconds
610         * If the robot is disabled, this returns 0.0 seconds
611         * Warning: This is not an official time (so it cannot be used to argue with referees)
612         * @return Match time in seconds since the beginning of autonomous
613         */
614        public double getMatchTime() {
615            if (m_approxMatchTimeOffset < 0.0) {
616                return 0.0;
617            }
618            return Timer.getFPGATimestamp() - m_approxMatchTimeOffset;
619        }
620    
621        /** Only to be used to tell the Driver Station what code you claim to be executing
622             *   for diagnostic purposes only
623             * @param entering If true, starting disabled code; if false, leaving disabled code */
624            public void InDisabled(boolean entering) {m_userInDisabled=entering;}
625    
626            /** Only to be used to tell the Driver Station what code you claim to be executing
627             *   for diagnostic purposes only
628             * @param entering If true, starting autonomous code; if false, leaving autonomous code */
629            public void InAutonomous(boolean entering) {m_userInAutonomous=entering;}
630            
631            /** Only to be used to tell the Driver Station what code you claim to be executing
632             *   for diagnostic purposes only
633             * @param entering If true, starting teleop code; if false, leaving teleop code */
634            public void InOperatorControl(boolean entering) {m_userInTeleop=entering;}
635            
636            /** Only to be used to tell the Driver Station what code you claim to be executing
637             *   for diagnostic purposes only
638             * @param entering If true, starting test code; if false, leaving test code */
639            public void InTest(boolean entering) {m_userInTeleop = entering; }
640    }