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}