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 008 package edu.wpi.first.wpilibj; 009 010 import edu.wpi.first.wpilibj.can.*; 011 import edu.wpi.first.wpilibj.communication.Semaphore; 012 import edu.wpi.first.wpilibj.communication.UsageReporting; 013 import edu.wpi.first.wpilibj.livewindow.LiveWindow; 014 import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; 015 import edu.wpi.first.wpilibj.tables.ITable; 016 import edu.wpi.first.wpilibj.tables.ITableListener; 017 018 public class CANJaguar implements MotorSafety, PIDOutput, SpeedController, LiveWindowSendable { 019 020 // The internal PID control loop in the Jaguar runs at 1kHz. 021 public static final int kControllerRate = 1000; 022 public static final double kApproxBusVoltage = 12.0; 023 024 /** 025 * Mode determines how the Jaguar is controlled 026 */ 027 public static class ControlMode { 028 029 public final int value; 030 static final int kPercentVbus_val = 0; 031 static final int kCurrent_val = 1; 032 static final int kSpeed_val = 2; 033 static final int kPosition_val = 3; 034 static final int kVoltage_val = 4; 035 public static final ControlMode kPercentVbus = new ControlMode(kPercentVbus_val); 036 public static final ControlMode kCurrent = new ControlMode(kCurrent_val); 037 public static final ControlMode kSpeed = new ControlMode(kSpeed_val); 038 public static final ControlMode kPosition = new ControlMode(kPosition_val); 039 public static final ControlMode kVoltage = new ControlMode(kVoltage_val); 040 041 private ControlMode(int value) { 042 this.value = value; 043 } 044 } 045 046 /** 047 * Faults reported by the Jaguar 048 */ 049 public static class Faults { 050 051 public final int value; 052 static final int kCurrentFault_val = 1; 053 static final int kTemperatureFault_val = 2; 054 static final int kBusVoltageFault_val = 4; 055 static final int kGateDriverFault_val = 8; 056 public static final Faults kCurrentFault = new Faults(kCurrentFault_val); 057 public static final Faults kTemperatureFault = new Faults(kTemperatureFault_val); 058 public static final Faults kBusVoltageFault = new Faults(kBusVoltageFault_val); 059 public static final Faults kGateDriverFault = new Faults(kGateDriverFault_val); 060 061 private Faults(int value) { 062 this.value = value; 063 } 064 } 065 066 /** 067 * Limit switch masks 068 */ 069 public static class Limits { 070 071 public final int value; 072 static final int kForwardLimit_val = 1; 073 static final int kReverseLimit_val = 2; 074 public static final Limits kForwardLimit = new Limits(kForwardLimit_val); 075 public static final Limits kReverseLimit = new Limits(kReverseLimit_val); 076 077 private Limits(int value) { 078 this.value = value; 079 } 080 } 081 082 /** 083 * Determines which sensor to use for position reference. 084 */ 085 public static class PositionReference { 086 087 public final byte value; 088 static final byte kQuadEncoder_val = 0; 089 static final byte kPotentiometer_val = 1; 090 static final byte kNone_val = (byte)0xFF; 091 public static final PositionReference kQuadEncoder = new PositionReference(kQuadEncoder_val); 092 public static final PositionReference kPotentiometer = new PositionReference(kPotentiometer_val); 093 public static final PositionReference kNone = new PositionReference(kNone_val); 094 095 private PositionReference(byte value) { 096 this.value = value; 097 } 098 } 099 100 /** 101 * Determines which sensor to use for speed reference. 102 */ 103 public static class SpeedReference { 104 105 public final byte value; 106 static final byte kEncoder_val = 0; 107 static final byte kInvEncoder_val = 2; 108 static final byte kQuadEncoder_val = 3; 109 static final byte kNone_val = (byte)0xFF; 110 public static final SpeedReference kEncoder = new SpeedReference(kEncoder_val); 111 public static final SpeedReference kInvEncoder = new SpeedReference(kInvEncoder_val); 112 public static final SpeedReference kQuadEncoder = new SpeedReference(kQuadEncoder_val); 113 public static final SpeedReference kNone = new SpeedReference(kNone_val); 114 115 private SpeedReference(byte value) { 116 this.value = value; 117 } 118 } 119 120 /** 121 * Determines how the Jaguar behaves when sending a zero signal. 122 */ 123 public static class NeutralMode { 124 125 public final byte value; 126 static final byte kJumper_val = 0; 127 static final byte kBrake_val = 1; 128 static final byte kCoast_val = 2; 129 public static final NeutralMode kJumper = new NeutralMode(kJumper_val); 130 public static final NeutralMode kBrake = new NeutralMode(kBrake_val); 131 public static final NeutralMode kCoast = new NeutralMode(kCoast_val); 132 133 private NeutralMode(byte value) { 134 this.value = value; 135 } 136 } 137 138 /** 139 * Determines which sensor to use for position reference. 140 */ 141 public static class LimitMode { 142 143 public final byte value; 144 static final byte kSwitchInputsOnly_val = 0; 145 static final byte kSoftPositionLimit_val = 1; 146 public static final LimitMode kSwitchInputsOnly = new LimitMode(kSwitchInputsOnly_val); 147 public static final LimitMode kSoftPostionLimits = new LimitMode(kSoftPositionLimit_val); 148 149 private LimitMode(byte value) { 150 this.value = value; 151 } 152 } 153 154 private final Object m_transactionMutex = new Object(); 155 private byte m_deviceNumber; 156 private ControlMode m_controlMode; 157 private double m_maxOutputVoltage; 158 //private Semaphore m_receiveSemaphore; 159 private MotorSafetyHelper m_safetyHelper; 160 private static final byte[] kNoData = new byte[0]; 161 162 private final static int swap16(int x) { 163 return ((((x) >>> 8) & 0x00FF) | (((x) << 8) & 0xFF00)); 164 } 165 166 private final static long swap32(long x) { 167 return ((((x) >> 24) & 0x000000FF) | (((x) >> 8) & 0x0000FF00) | (((x) << 8) & 0x00FF0000) | (((x) << 24) & 0xFF000000)); 168 } 169 170 private final static int swap16(int x, byte[] buffer) { 171 buffer[0] = (byte) x; 172 buffer[1] = (byte) ((x >>> 8) & 0x00FF); 173 return ((((x) >>> 8) & 0x00FF) | (((x) << 8) & 0xFF00)); 174 } 175 176 private final static long swap32(long x, byte[] buffer) { 177 buffer[0] = (byte) x; 178 buffer[1] = (byte) ((x >>> 8) & 0x00FF); 179 buffer[2] = (byte) ((x >>> 16) & 0x00FF); 180 buffer[3] = (byte) ((x >>> 24) & 0x00FF); 181 return ((((x) >> 24) & 0x000000FF) | (((x) >> 8) & 0x0000FF00) | (((x) << 8) & 0x00FF0000) | (((x) << 24) & 0xFF000000)); 182 } 183 184 private final static int swap16(byte[] buffer) { 185 return ((((buffer[1]) >>> 8) & 0x00FF) | (((buffer[0]) << 8) & 0xFF00)); 186 } 187 188 private final static long swap32(byte[] buffer) { 189 return ((((buffer[3]) >> 24) & 0x000000FF) | (((buffer[2]) >> 8) & 0x0000FF00) | (((buffer[1]) << 8) & 0x00FF0000) | (((buffer[0]) << 24) & 0xFF000000)); 190 } 191 192 /** 193 * Pack 16-bit data in little-endian byte order 194 * @param data The data to be packed 195 * @param buffer The buffer to pack into 196 * @param offset The offset into data to pack the variable 197 */ 198 private static final void pack16(short data, byte[] buffer, int offset) { 199 buffer[offset] = (byte) (data & 0xFF); 200 buffer[offset + 1] = (byte) ((data >>> 8) & 0xFF); 201 } 202 203 /** 204 * Pack 32-bit data in little-endian byte order 205 * @param data The data to be packed 206 * @param buffer The buffer to pack into 207 * @param offset The offset into data to pack the variable 208 */ 209 private static final void pack32(int data, byte[] buffer, int offset) { 210 buffer[offset] = (byte) (data & 0xFF); 211 buffer[offset + 1] = (byte) ((data >>> 8) & 0xFF); 212 buffer[offset + 2] = (byte) ((data >>> 16) & 0xFF); 213 buffer[offset + 3] = (byte) ((data >>> 24) & 0xFF); 214 } 215 216 /** 217 * Unpack 16-bit data from a buffer in little-endian byte order 218 * @param buffer The buffer to unpack from 219 * @param offset The offset into he buffer to unpack 220 * @return The data that was unpacked 221 */ 222 private static final short unpack16(byte[] buffer, int offset) { 223 return (short) (((int) buffer[offset] & 0xFF) | (short) ((buffer[offset + 1] << 8)) & 0xFF00); 224 } 225 226 /** 227 * Unpack 32-bit data from a buffer in little-endian byte order 228 * @param buffer The buffer to unpack from 229 * @param offset The offset into he buffer to unpack 230 * @return The data that was unpacked 231 */ 232 private static final int unpack32(byte[] buffer, int offset) { 233 return ((int) buffer[offset] & 0xFF) | ((buffer[offset + 1] << 8) & 0xFF00) | 234 ((buffer[offset + 2] << 16) & 0xFF0000) | ((buffer[offset + 3] << 24) & 0xFF000000); 235 } 236 private static final int kFullMessageIDMask = 0x1FFFFFC0; 237 238 /** 239 * Common initialization code called by all constructors. 240 */ 241 private void initCANJaguar() throws CANTimeoutException { 242 if (m_deviceNumber < 1 || m_deviceNumber > 63) { 243 throw new RuntimeException("Invalid CAN device number \"" + 244 m_deviceNumber + "\" - must be between 1 and 63."); 245 } 246 247 // VxWorks semaphore for calling async CAN receive API. 248 //Semaphore.Options options = new Semaphore.Options(); 249 //options.setPrioritySorted(true); 250 //m_receiveSemaphore = new Semaphore(options, false); 251 252 int fwVer = getFirmwareVersion(); 253 if (fwVer >= CANJaguarVersionException.kMinRDKFirmwareVersion || 254 fwVer < CANJaguarVersionException.kMinLegalFIRSTFirmwareVersion) 255 { 256 throw new CANJaguarVersionException(m_deviceNumber, fwVer); 257 } 258 259 switch (m_controlMode.value) { 260 case ControlMode.kPercentVbus_val: 261 case ControlMode.kVoltage_val: 262 enableControl(); 263 break; 264 default: 265 break; 266 } 267 m_safetyHelper = new MotorSafetyHelper(this); 268 269 UsageReporting.report(UsageReporting.kResourceType_CANJaguar, m_deviceNumber, m_controlMode.value); 270 LiveWindow.addActuator("CANJaguar", m_deviceNumber, 0, this); 271 } 272 273 /** 274 * Constructor 275 * Default to percent Vbus control mode. 276 * @param deviceNumber The address of the Jaguar on the CAN bus. 277 */ 278 public CANJaguar(int deviceNumber) throws CANTimeoutException { 279 m_deviceNumber = (byte) deviceNumber; 280 m_controlMode = ControlMode.kPercentVbus; 281 m_maxOutputVoltage = kApproxBusVoltage; 282 initCANJaguar(); 283 } 284 285 /** 286 * Constructor 287 * @param deviceNumber The address of the Jaguar on the CAN bus. 288 * @param controlMode The control mode that the Jaguar will run in. 289 */ 290 public CANJaguar(int deviceNumber, ControlMode controlMode) throws CANTimeoutException { 291 m_deviceNumber = (byte) deviceNumber; 292 m_controlMode = controlMode; 293 m_maxOutputVoltage = kApproxBusVoltage; 294 initCANJaguar(); 295 } 296 297 /** 298 * Set the output set-point value. 299 * 300 * The scale and the units depend on the mode the Jaguar is in. 301 * In PercentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar). 302 * In Voltage Mode, the outputValue is in Volts. 303 * In Current Mode, the outputValue is in Amps. 304 * In Speed Mode, the outputValue is in Rotations/Minute. 305 * In Position Mode, the outputValue is in Rotations. 306 * 307 * @param outputValue The set-point to sent to the motor controller. 308 */ 309 public void setX(double outputValue) throws CANTimeoutException { 310 setX(outputValue, (byte) 0); 311 } 312 313 /** 314 * Set the output set-point value. 315 * 316 * Needed by the SpeedControl interface (swallows CANTimeoutExceptions). 317 * 318 * @deprecated Use setX instead. 319 * @param outputValue The set-point to sent to the motor controller. 320 */ 321 public void set(double outputValue) { 322 set(outputValue, (byte) 0); 323 } 324 325 /** 326 * Set the output set-point value. 327 * 328 * The scale and the units depend on the mode the Jaguar is in. 329 * In PercentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar). 330 * In Voltage Mode, the outputValue is in Volts. 331 * In Current Mode, the outputValue is in Amps. 332 * In Speed Mode, the outputValue is in Rotations/Minute. 333 * In Position Mode, the outputValue is in Rotations. 334 * 335 * @param outputValue The set-point to sent to the motor controller. 336 * @param syncGroup The update group to add this set() to, pending updateSyncGroup(). If 0, update immediately. 337 */ 338 public void setX(double outputValue, byte syncGroup) throws CANTimeoutException { 339 int messageID = 0; 340 byte[] dataBuffer = new byte[8]; 341 byte dataSize = 0; 342 343 if (!m_safetyHelper.isAlive()) { 344 enableControl(); 345 } 346 347 switch (m_controlMode.value) { 348 case ControlMode.kPercentVbus_val: 349 messageID = JaguarCANProtocol.LM_API_VOLT_T_SET; 350 if (outputValue > 1.0) outputValue = 1.0; 351 if (outputValue < -1.0) outputValue = -1.0; 352 packPercentage(dataBuffer, outputValue); 353 dataSize = 2; 354 break; 355 case ControlMode.kSpeed_val: { 356 messageID = JaguarCANProtocol.LM_API_SPD_T_SET; 357 dataSize = packFXP16_16(dataBuffer, outputValue); 358 } 359 break; 360 case ControlMode.kPosition_val: { 361 messageID = JaguarCANProtocol.LM_API_POS_T_SET; 362 dataSize = packFXP16_16(dataBuffer, outputValue); 363 } 364 break; 365 case ControlMode.kCurrent_val: { 366 messageID = JaguarCANProtocol.LM_API_ICTRL_T_SET; 367 dataSize = packFXP8_8(dataBuffer, outputValue); 368 } 369 break; 370 case ControlMode.kVoltage_val: { 371 messageID = JaguarCANProtocol.LM_API_VCOMP_T_SET; 372 dataSize = packFXP8_8(dataBuffer, outputValue); 373 } 374 break; 375 default: 376 return; 377 } 378 if (syncGroup != 0) { 379 dataBuffer[dataSize] = syncGroup; 380 dataSize++; 381 } 382 setTransaction(messageID, dataBuffer, dataSize); 383 m_safetyHelper.feed(); 384 } 385 386 /** 387 * Set the output set-point value. 388 * 389 * Needed by the SpeedControl interface (swallows CANTimeoutExceptions). 390 * 391 * @deprecated Use setX instead. 392 * @param outputValue The set-point to sent to the motor controller. 393 * @param syncGroup The update group to add this set() to, pending updateSyncGroup(). If 0, update immediately. 394 */ 395 public void set(double outputValue, byte syncGroup) { 396 try { 397 setX(outputValue, syncGroup); 398 } catch (CANTimeoutException e) {} 399 } 400 401 /** 402 * Get the recently set outputValue setpoint. 403 * 404 * The scale and the units depend on the mode the Jaguar is in. 405 * In PercentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar). 406 * In Voltage Mode, the outputValue is in Volts. 407 * In Current Mode, the outputValue is in Amps. 408 * In Speed Mode, the outputValue is in Rotations/Minute. 409 * In Position Mode, the outputValue is in Rotations. 410 * 411 * @return The most recently set outputValue setpoint. 412 */ 413 public double getX() throws CANTimeoutException { 414 byte[] dataBuffer = new byte[8]; 415 byte dataSize = 0; 416 417 switch (m_controlMode.value) { 418 case ControlMode.kPercentVbus_val: 419 dataSize = getTransaction(JaguarCANProtocol.LM_API_VOLT_SET, dataBuffer); 420 if (dataSize == 2) { 421 return unpackPercentage(dataBuffer); 422 } 423 break; 424 case ControlMode.kSpeed_val: 425 dataSize = getTransaction(JaguarCANProtocol.LM_API_SPD_SET, dataBuffer); 426 if (dataSize == 4) { 427 return unpackFXP16_16(dataBuffer); 428 } 429 break; 430 case ControlMode.kPosition_val: 431 dataSize = getTransaction(JaguarCANProtocol.LM_API_POS_SET, dataBuffer); 432 if (dataSize == 4) { 433 return unpackFXP16_16(dataBuffer); 434 } 435 break; 436 case ControlMode.kCurrent_val: 437 dataSize = getTransaction(JaguarCANProtocol.LM_API_ICTRL_SET, dataBuffer); 438 if (dataSize == 2) { 439 return unpackFXP8_8(dataBuffer); 440 } 441 break; 442 case ControlMode.kVoltage_val: 443 dataSize = getTransaction(JaguarCANProtocol.LM_API_VCOMP_SET, dataBuffer); 444 if (dataSize == 2) { 445 return unpackFXP8_8(dataBuffer); 446 } 447 break; 448 } 449 return 0.0; 450 451 } 452 453 /** 454 * Get the recently set outputValue setpoint. 455 * 456 * Needed by the SpeedControl interface (swallows CANTimeoutExceptions). 457 * 458 * @deprecated Use getX instead. 459 * @return The most recently set outputValue setpoint. 460 */ 461 public double get() { 462 try { 463 return getX(); 464 } catch (CANTimeoutException e) { 465 return 0.0; 466 } 467 } 468 469 /** 470 * Common interface for disabling a motor. 471 * 472 * Needed by the SpeedControl interface (swallows CANTimeoutExceptions). 473 * 474 * @deprecated Use disableControl instead. 475 */ 476 public void disable() { 477 try { 478 disableControl(); 479 } catch (CANTimeoutException e) {} 480 } 481 482 /** 483 * Write out the PID value as seen in the PIDOutput base object. 484 * 485 * @deprecated Use setX instead. 486 * @param output Write out the percentage voltage value as was computed by the PIDController 487 */ 488 public void pidWrite(double output) { 489 if (m_controlMode == ControlMode.kPercentVbus) { 490 set(output); 491 } else { 492 // TODO: Error... only percent vbus mode supported for PID API 493 } 494 } 495 496 byte packPercentage(byte[] buffer, double value) { 497 short intValue = (short) (value * 32767.0); 498 swap16(intValue, buffer); 499 return 2; 500 } 501 502 byte packFXP8_8(byte[] buffer, double value) { 503 short intValue = (short) (value * 256.0); 504 swap16(intValue, buffer); 505 return 2; 506 } 507 508 byte packFXP16_16(byte[] buffer, double value) { 509 int intValue = (int) (value * 65536.0); 510 swap32(intValue, buffer); 511 return 4; 512 } 513 514 byte packINT16(byte[] buffer, short value) { 515 swap16(value, buffer); 516 return 2; 517 } 518 519 byte packINT32(byte[] buffer, int value) { 520 swap32(value, buffer); 521 return 4; 522 } 523 524 double unpackPercentage(byte[] buffer) { 525 return unpack16(buffer,0) / 32767.0; 526 } 527 528 double unpackFXP8_8(byte[] buffer) { 529 return unpack16(buffer,0) / 256.0; 530 } 531 532 double unpackFXP16_16(byte[] buffer) { 533 return unpack32(buffer,0) / 65536.0; 534 } 535 536 int unpackINT16(byte[] buffer) { 537 return unpack16(buffer,0); 538 } 539 540 long unpackINT32(byte[] buffer) { 541 return unpack32(buffer,0); 542 } 543 private static final byte[] sendTrustedDataBuffer = new byte[JaguarCANDriver.kMaxMessageDataSize]; 544 545 /** 546 * Send a message on the CAN bus through the CAN driver in FRC_NetworkCommunication 547 * 548 * Trusted messages require a 2-byte token at the beginning of the data payload. 549 * If the message being sent is trusted, make space for the token. 550 * 551 * @param messageID The messageID to be used on the CAN bus 552 * @param data The up to 8 bytes of data to be sent with the message 553 * @param dataSize Specify how much of the data in "data" to send 554 */ 555 protected static void sendMessage(int messageID, byte[] data, int dataSize) throws CANTimeoutException { 556 final int[] kTrustedMessages = { 557 JaguarCANProtocol.LM_API_VOLT_T_EN, JaguarCANProtocol.LM_API_VOLT_T_SET, 558 JaguarCANProtocol.LM_API_SPD_T_EN, JaguarCANProtocol.LM_API_SPD_T_SET, 559 JaguarCANProtocol.LM_API_VCOMP_T_EN, JaguarCANProtocol.LM_API_VCOMP_T_SET, 560 JaguarCANProtocol.LM_API_POS_T_EN, JaguarCANProtocol.LM_API_POS_T_SET, 561 JaguarCANProtocol.LM_API_ICTRL_T_EN, JaguarCANProtocol.LM_API_ICTRL_T_SET}; 562 563 byte i; 564 for (i = 0; i < kTrustedMessages.length; i++) { 565 if ((kFullMessageIDMask & messageID) == kTrustedMessages[i]) { 566 sendTrustedDataBuffer[0] = 0; 567 sendTrustedDataBuffer[1] = 0; 568 // Make sure the data will still fit after adjusting for the token. 569 if (dataSize > JaguarCANDriver.kMaxMessageDataSize - 2) { 570 throw new RuntimeException("CAN message has too much data."); 571 } 572 573 byte j; 574 for (j = 0; j < dataSize; j++) { 575 sendTrustedDataBuffer[j + 2] = data[j]; 576 } 577 578 JaguarCANDriver.sendMessage(messageID, sendTrustedDataBuffer, dataSize + 2); 579 return; 580 } 581 } 582 JaguarCANDriver.sendMessage(messageID, data, dataSize); 583 } 584 585 /** 586 * Receive a message from the CAN bus through the CAN driver in FRC_NetworkCommunication 587 * 588 * @param messageID The messageID to read from the CAN bus 589 * @param data The up to 8 bytes of data that was received with the message 590 * @param timeout Specify how long to wait for a message (in seconds) 591 */ 592 protected static byte receiveMessage(int messageID, byte[] data, double timeout) throws CANTimeoutException { 593 JaguarCANDriver canDriver = new JaguarCANDriver(); 594 byte dataSize = canDriver.receiveMessage(messageID, data, timeout); 595 return dataSize; 596 } 597 598 protected static byte receiveMessage(int messageID, byte[] data) throws CANTimeoutException { 599 return receiveMessage(messageID, data, 0.01); 600 } 601 602 /** 603 * Execute a transaction with a Jaguar that sets some property. 604 * 605 * Jaguar always acks when it receives a message. If we don't wait for an ack, 606 * the message object in the Jaguar could get overwritten before it is handled. 607 * 608 * @param messageID The messageID to be used on the CAN bus (device number is added internally) 609 * @param data The up to 8 bytes of data to be sent with the message 610 * @param dataSize Specify how much of the data in "data" to send 611 */ 612 protected byte setTransaction(int messageID, byte[] data, byte dataSize) throws CANTimeoutException { 613 int ackMessageID = JaguarCANProtocol.LM_API_ACK | m_deviceNumber; 614 615 // Make sure we don't have more than one transaction with the same Jaguar outstanding. 616 synchronized (m_transactionMutex) { 617 // Throw away any stale acks. 618 try { 619 receiveMessage(ackMessageID, kNoData, 0.0); 620 } 621 catch(CANTimeoutException e) {} 622 // Send the message with the data. 623 sendMessage(messageID | m_deviceNumber, data, dataSize); 624 // Wait for an ack. 625 dataSize = receiveMessage(ackMessageID, kNoData); 626 } 627 return dataSize; 628 } 629 630 /** 631 * Execute a transaction with a Jaguar that gets some property. 632 * 633 * Jaguar always generates a message with the same message ID when replying. 634 * 635 * @param messageID The messageID to read from the CAN bus (device number is added internally) 636 * @param data The up to 8 bytes of data that was received with the message 637 * @return Indicates how much data was received 638 */ 639 protected byte getTransaction(int messageID, byte[] data) throws CANTimeoutException { 640 int targetedMessageID = messageID | m_deviceNumber; 641 byte dataSize = 0; 642 // Make sure we don't have more than one transaction with the same Jaguar outstanding. 643 synchronized (m_transactionMutex) { 644 // Send the message requesting data. 645 sendMessage(targetedMessageID, kNoData, 0); 646 // Caller may have set bit31 for remote frame transmission so clear invalid bits[31-29] 647 targetedMessageID &= 0x1FFFFFFF; 648 // Wait for the data. 649 dataSize = receiveMessage(targetedMessageID, data); 650 } 651 return dataSize; 652 } 653 654 /** 655 * Set the reference source device for speed controller mode. 656 * 657 * Choose encoder as the source of speed feedback when in speed control mode. 658 * 659 * @param reference Specify a SpeedReference. 660 */ 661 public void setSpeedReference(SpeedReference reference) throws CANTimeoutException { 662 byte[] dataBuffer = new byte[8]; 663 664 dataBuffer[0] = reference.value; 665 setTransaction(JaguarCANProtocol.LM_API_SPD_REF, dataBuffer, (byte) 1); 666 } 667 668 /** 669 * Get the reference source device for speed controller mode. 670 * 671 * @return A SpeedReference indicating the currently selected reference device for speed controller mode. 672 */ 673 public SpeedReference getSpeedReference() throws CANTimeoutException { 674 byte[] dataBuffer = new byte[8]; 675 byte dataSize = 0; 676 677 dataSize = getTransaction(JaguarCANProtocol.LM_API_SPD_REF, dataBuffer); 678 if (dataSize == 1) { 679 switch (dataBuffer[0]) { 680 case SpeedReference.kEncoder_val: 681 return SpeedReference.kEncoder; 682 case SpeedReference.kInvEncoder_val: 683 return SpeedReference.kInvEncoder; 684 case SpeedReference.kQuadEncoder_val: 685 return SpeedReference.kQuadEncoder; 686 case SpeedReference.kNone_val: 687 return SpeedReference.kNone; 688 } 689 } 690 return SpeedReference.kNone; 691 } 692 693 /** 694 * Set the reference source device for position controller mode. 695 * 696 * Choose between using and encoder and using a potentiometer 697 * as the source of position feedback when in position control mode. 698 * 699 * @param reference Specify a PositionReference. 700 */ 701 public void setPositionReference(PositionReference reference) throws CANTimeoutException { 702 byte[] dataBuffer = new byte[8]; 703 704 dataBuffer[0] = reference.value; 705 setTransaction(JaguarCANProtocol.LM_API_POS_REF, dataBuffer, (byte) 1); 706 } 707 708 /** 709 * Get the reference source device for position controller mode. 710 * 711 * @return A PositionReference indicating the currently selected reference device for position controller mode. 712 */ 713 public PositionReference getPositionReference() throws CANTimeoutException { 714 byte[] dataBuffer = new byte[8]; 715 byte dataSize = 0; 716 717 dataSize = getTransaction(JaguarCANProtocol.LM_API_POS_REF, dataBuffer); 718 if (dataSize == 1) { 719 switch (dataBuffer[0]) { 720 case PositionReference.kPotentiometer_val: 721 return PositionReference.kPotentiometer; 722 case PositionReference.kQuadEncoder_val: 723 return PositionReference.kQuadEncoder; 724 case PositionReference.kNone_val: 725 return PositionReference.kNone; 726 } 727 } 728 return PositionReference.kNone; 729 } 730 731 /** 732 * Set the P, I, and D constants for the closed loop modes. 733 * 734 * @param p The proportional gain of the Jaguar's PID controller. 735 * @param i The integral gain of the Jaguar's PID controller. 736 * @param d The differential gain of the Jaguar's PID controller. 737 */ 738 public void setPID(double p, double i, double d) throws CANTimeoutException { 739 byte[] dataBuffer = new byte[8]; 740 byte dataSize; 741 742 switch (m_controlMode.value) { 743 case ControlMode.kPercentVbus_val: 744 case ControlMode.kVoltage_val: 745 // TODO: Error, Not Valid 746 break; 747 case ControlMode.kSpeed_val: 748 dataSize = packFXP16_16(dataBuffer, p); 749 setTransaction(JaguarCANProtocol.LM_API_SPD_PC, dataBuffer, dataSize); 750 dataSize = packFXP16_16(dataBuffer, i); 751 setTransaction(JaguarCANProtocol.LM_API_SPD_IC, dataBuffer, dataSize); 752 dataSize = packFXP16_16(dataBuffer, d); 753 setTransaction(JaguarCANProtocol.LM_API_SPD_DC, dataBuffer, dataSize); 754 break; 755 case ControlMode.kPosition_val: 756 dataSize = packFXP16_16(dataBuffer, p); 757 setTransaction(JaguarCANProtocol.LM_API_POS_PC, dataBuffer, dataSize); 758 dataSize = packFXP16_16(dataBuffer, i); 759 setTransaction(JaguarCANProtocol.LM_API_POS_IC, dataBuffer, dataSize); 760 dataSize = packFXP16_16(dataBuffer, d); 761 setTransaction(JaguarCANProtocol.LM_API_POS_DC, dataBuffer, dataSize); 762 break; 763 case ControlMode.kCurrent_val: 764 dataSize = packFXP16_16(dataBuffer, p); 765 setTransaction(JaguarCANProtocol.LM_API_ICTRL_PC, dataBuffer, dataSize); 766 dataSize = packFXP16_16(dataBuffer, i); 767 setTransaction(JaguarCANProtocol.LM_API_ICTRL_IC, dataBuffer, dataSize); 768 dataSize = packFXP16_16(dataBuffer, d); 769 setTransaction(JaguarCANProtocol.LM_API_ICTRL_DC, dataBuffer, dataSize); 770 break; 771 } 772 } 773 774 /** 775 * Get the Proportional gain of the controller. 776 * 777 * @return The proportional gain. 778 */ 779 public double getP() throws CANTimeoutException { 780 byte[] dataBuffer = new byte[8]; 781 byte dataSize = 0; 782 783 switch (m_controlMode.value) { 784 case ControlMode.kPercentVbus_val: 785 case ControlMode.kVoltage_val: 786 // TODO: Error, Not Valid 787 break; 788 case ControlMode.kSpeed_val: 789 dataSize = getTransaction(JaguarCANProtocol.LM_API_SPD_PC, dataBuffer); 790 if (dataSize == 4) { 791 return unpackFXP16_16(dataBuffer); 792 } 793 break; 794 case ControlMode.kPosition_val: 795 dataSize = getTransaction(JaguarCANProtocol.LM_API_POS_PC, dataBuffer); 796 if (dataSize == 4) { 797 return unpackFXP16_16(dataBuffer); 798 } 799 break; 800 case ControlMode.kCurrent_val: 801 dataSize = getTransaction(JaguarCANProtocol.LM_API_ICTRL_PC, dataBuffer); 802 if (dataSize == 4) { 803 return unpackFXP16_16(dataBuffer); 804 } 805 break; 806 } 807 return 0.0; 808 } 809 810 /** 811 * Get the Intregral gain of the controller. 812 * 813 * @return The integral gain. 814 */ 815 public double getI() throws CANTimeoutException { 816 byte[] dataBuffer = new byte[8]; 817 byte dataSize = 0; 818 819 switch (m_controlMode.value) { 820 case ControlMode.kPercentVbus_val: 821 case ControlMode.kVoltage_val: 822 // TODO: Error, Not Valid 823 break; 824 case ControlMode.kSpeed_val: 825 dataSize = getTransaction(JaguarCANProtocol.LM_API_SPD_IC, dataBuffer); 826 if (dataSize == 4) { 827 return unpackFXP16_16(dataBuffer); 828 } 829 break; 830 case ControlMode.kPosition_val: 831 dataSize = getTransaction(JaguarCANProtocol.LM_API_POS_IC, dataBuffer); 832 if (dataSize == 4) { 833 return unpackFXP16_16(dataBuffer); 834 } 835 break; 836 case ControlMode.kCurrent_val: 837 dataSize = getTransaction(JaguarCANProtocol.LM_API_ICTRL_IC, dataBuffer); 838 if (dataSize == 4) { 839 return unpackFXP16_16(dataBuffer); 840 } 841 break; 842 } 843 return 0.0; 844 } 845 846 /** 847 * Get the Differential gain of the controller. 848 * 849 * @return The differential gain. 850 */ 851 public double getD() throws CANTimeoutException { 852 byte[] dataBuffer = new byte[8]; 853 byte dataSize = 0; 854 855 switch (m_controlMode.value) { 856 case ControlMode.kPercentVbus_val: 857 case ControlMode.kVoltage_val: 858 // TODO: Error, Not Valid 859 break; 860 case ControlMode.kSpeed_val: 861 dataSize = getTransaction(JaguarCANProtocol.LM_API_SPD_DC, dataBuffer); 862 if (dataSize == 4) { 863 return unpackFXP16_16(dataBuffer); 864 } 865 break; 866 case ControlMode.kPosition_val: 867 dataSize = getTransaction(JaguarCANProtocol.LM_API_POS_DC, dataBuffer); 868 if (dataSize == 4) { 869 return unpackFXP16_16(dataBuffer); 870 } 871 break; 872 case ControlMode.kCurrent_val: 873 dataSize = getTransaction(JaguarCANProtocol.LM_API_ICTRL_DC, dataBuffer); 874 if (dataSize == 4) { 875 return unpackFXP16_16(dataBuffer); 876 } 877 break; 878 } 879 return 0.0; 880 } 881 882 /** 883 * Enable the closed loop controller. 884 * 885 * Start actually controlling the output based on the feedback. 886 */ 887 public void enableControl() throws CANTimeoutException { 888 enableControl(0.0); 889 } 890 891 /** 892 * Enable the closed loop controller. 893 * 894 * Start actually controlling the output based on the feedback. 895 * If starting a position controller with an encoder reference, 896 * use the encoderInitialPosition parameter to initialize the 897 * encoder state. 898 * @param encoderInitialPosition Encoder position to set if position with encoder reference. Ignored otherwise. 899 */ 900 public void enableControl(double encoderInitialPosition) throws CANTimeoutException { 901 byte[] dataBuffer = new byte[8]; 902 byte dataSize = 0; 903 904 switch (m_controlMode.value) { 905 case ControlMode.kPercentVbus_val: 906 setTransaction(JaguarCANProtocol.LM_API_VOLT_T_EN, dataBuffer, dataSize); 907 break; 908 case ControlMode.kSpeed_val: 909 setTransaction(JaguarCANProtocol.LM_API_SPD_T_EN, dataBuffer, dataSize); 910 break; 911 case ControlMode.kPosition_val: 912 dataSize = packFXP16_16(dataBuffer, encoderInitialPosition); 913 setTransaction(JaguarCANProtocol.LM_API_POS_T_EN, dataBuffer, dataSize); 914 break; 915 case ControlMode.kCurrent_val: 916 setTransaction(JaguarCANProtocol.LM_API_ICTRL_T_EN, dataBuffer, dataSize); 917 break; 918 case ControlMode.kVoltage_val: 919 setTransaction(JaguarCANProtocol.LM_API_VCOMP_T_EN, dataBuffer, dataSize); 920 break; 921 } 922 } 923 924 /** 925 * Disable the closed loop controller. 926 * 927 * Stop driving the output based on the feedback. 928 */ 929 public void disableControl() throws CANTimeoutException { 930 byte[] dataBuffer = new byte[8]; 931 byte dataSize = 0; 932 933 switch (m_controlMode.value) { 934 case ControlMode.kPercentVbus_val: 935 setTransaction(JaguarCANProtocol.LM_API_VOLT_DIS, dataBuffer, dataSize); 936 break; 937 case ControlMode.kSpeed_val: 938 setTransaction(JaguarCANProtocol.LM_API_SPD_DIS, dataBuffer, dataSize); 939 break; 940 case ControlMode.kPosition_val: 941 setTransaction(JaguarCANProtocol.LM_API_POS_DIS, dataBuffer, dataSize); 942 break; 943 case ControlMode.kCurrent_val: 944 setTransaction(JaguarCANProtocol.LM_API_ICTRL_DIS, dataBuffer, dataSize); 945 break; 946 case ControlMode.kVoltage_val: 947 setTransaction(JaguarCANProtocol.LM_API_VCOMP_DIS, dataBuffer, dataSize); 948 break; 949 } 950 } 951 952 /** 953 * Change the control mode of this Jaguar object. 954 * 955 * After changing modes, configure any PID constants or other settings needed 956 * and then enableControl() to actually change the mode on the Jaguar. 957 * 958 * @param controlMode The new mode. 959 */ 960 public void changeControlMode(ControlMode controlMode) throws CANTimeoutException { 961 // Disable the previous mode 962 disableControl(); 963 964 // Update the local mode 965 m_controlMode = controlMode; 966 967 UsageReporting.report(UsageReporting.kResourceType_CANJaguar, m_deviceNumber, m_controlMode.value); 968 } 969 970 /** 971 * Get the active control mode from the Jaguar. 972 * 973 * Ask the Jag what mode it is in. 974 * 975 * @return ControlMode that the Jag is in. 976 */ 977 public ControlMode getControlMode() throws CANTimeoutException { 978 byte[] dataBuffer = new byte[8]; 979 byte dataSize = 0; 980 981 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_CMODE, dataBuffer); 982 if (dataSize == 1) 983 { 984 switch (dataBuffer[0]) { 985 case ControlMode.kPercentVbus_val: 986 return ControlMode.kPercentVbus; 987 case ControlMode.kCurrent_val: 988 return ControlMode.kCurrent; 989 case ControlMode.kSpeed_val: 990 return ControlMode.kSpeed; 991 case ControlMode.kPosition_val: 992 return ControlMode.kPosition; 993 case ControlMode.kVoltage_val: 994 return ControlMode.kVoltage; 995 } 996 } 997 return ControlMode.kPercentVbus; 998 } 999 1000 /** 1001 * Get the voltage at the battery input terminals of the Jaguar. 1002 * 1003 * @return The bus voltage in Volts. 1004 */ 1005 public double getBusVoltage() throws CANTimeoutException { 1006 byte[] dataBuffer = new byte[8]; 1007 byte dataSize = 0; 1008 1009 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_VOLTBUS, dataBuffer); 1010 if (dataSize == 2) { 1011 return unpackFXP8_8(dataBuffer); 1012 } 1013 return 0.0; 1014 } 1015 1016 /** 1017 * Get the voltage being output from the motor terminals of the Jaguar. 1018 * 1019 * @return The output voltage in Volts. 1020 */ 1021 public double getOutputVoltage() throws CANTimeoutException { 1022 byte[] dataBuffer = new byte[8]; 1023 byte dataSize = 0; 1024 1025 // Read the volt out which is in Volts units. 1026 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_VOUT, dataBuffer); 1027 if (dataSize == 2) { 1028 return unpackFXP8_8(dataBuffer); 1029 } 1030 return 0.0; 1031 } 1032 1033 /** 1034 * Get the current through the motor terminals of the Jaguar. 1035 * 1036 * @return The output current in Amps. 1037 */ 1038 public double getOutputCurrent() throws CANTimeoutException { 1039 byte[] dataBuffer = new byte[8]; 1040 byte dataSize; 1041 1042 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_CURRENT, dataBuffer); 1043 if (dataSize == 2) { 1044 return unpackFXP8_8(dataBuffer); 1045 } 1046 return 0.0; 1047 1048 } 1049 1050 /** 1051 * Get the internal temperature of the Jaguar. 1052 * 1053 * @return The temperature of the Jaguar in degrees Celsius. 1054 */ 1055 public double getTemperature() throws CANTimeoutException { 1056 byte[] dataBuffer = new byte[8]; 1057 byte dataSize; 1058 1059 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_TEMP, dataBuffer); 1060 if (dataSize == 2) { 1061 return unpackFXP8_8(dataBuffer); 1062 } 1063 return 0.0; 1064 } 1065 1066 /** 1067 * Get the position of the encoder or potentiometer. 1068 * 1069 * @return The position of the motor based on the configured feedback. 1070 */ 1071 public double getPosition() throws CANTimeoutException { 1072 byte[] dataBuffer = new byte[8]; 1073 byte dataSize; 1074 1075 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_POS, dataBuffer); 1076 if (dataSize == 4) { 1077 return unpackFXP16_16(dataBuffer); 1078 } 1079 return 0.0; 1080 } 1081 1082 /** 1083 * Get the speed of the encoder. 1084 * 1085 * @return The speed of the motor in RPM based on the configured feedback. 1086 */ 1087 public double getSpeed() throws CANTimeoutException { 1088 byte[] dataBuffer = new byte[8]; 1089 byte dataSize; 1090 1091 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_SPD, dataBuffer); 1092 if (dataSize == 4) { 1093 return unpackFXP16_16(dataBuffer); 1094 } 1095 return 0.0; 1096 } 1097 1098 /** 1099 * Get the status of the forward limit switch. 1100 * 1101 * @return The motor is allowed to turn in the forward direction when true. 1102 */ 1103 public boolean getForwardLimitOK() throws CANTimeoutException { 1104 byte[] dataBuffer = new byte[8]; 1105 byte dataSize; 1106 1107 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_LIMIT, dataBuffer); 1108 if (dataSize == 1) { 1109 return (dataBuffer[0] & Limits.kForwardLimit_val) != 0; 1110 } 1111 return false; 1112 } 1113 1114 /** 1115 * Get the status of the reverse limit switch. 1116 * 1117 * @return The motor is allowed to turn in the reverse direction when true. 1118 */ 1119 public boolean getReverseLimitOK() throws CANTimeoutException { 1120 byte[] dataBuffer = new byte[8]; 1121 byte dataSize; 1122 1123 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_LIMIT, dataBuffer); 1124 if (dataSize == 1) { 1125 return (dataBuffer[0] & Limits.kReverseLimit_val) != 0; 1126 } 1127 return false; 1128 } 1129 1130 /** 1131 * Get the status of any faults the Jaguar has detected. 1132 * 1133 * @return A bit-mask of faults defined by the "Faults" enum class. 1134 */ 1135 public short getFaults() throws CANTimeoutException { 1136 byte[] dataBuffer = new byte[8]; 1137 byte dataSize; 1138 1139 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_FAULT, dataBuffer); 1140 if (dataSize == 2) { 1141 return (short)unpackINT16(dataBuffer); 1142 } 1143 return 0; 1144 } 1145 1146 /** 1147 * Check if the Jaguar's power has been cycled since this was last called. 1148 * 1149 * This should return true the first time called after a Jaguar power up, 1150 * and false after that. 1151 * 1152 * @return The Jaguar was power cycled since the last call to this function. 1153 */ 1154 public boolean getPowerCycled() throws CANTimeoutException { 1155 byte[] dataBuffer = new byte[8]; 1156 byte dataSize; 1157 1158 dataSize = getTransaction(JaguarCANProtocol.LM_API_STATUS_POWER, dataBuffer); 1159 if (dataSize == 1) { 1160 boolean powerCycled = dataBuffer[0] != 0; 1161 1162 // Clear the power cycled bit now that we've accessed it 1163 if (powerCycled) { 1164 dataBuffer[0] = 1; 1165 setTransaction(JaguarCANProtocol.LM_API_STATUS_POWER, dataBuffer, (byte) 1); 1166 } 1167 1168 return powerCycled; 1169 } 1170 return false; 1171 } 1172 1173 /** 1174 * Set the maximum voltage change rate. 1175 * 1176 * When in percent voltage output mode, the rate at which the voltage changes can 1177 * be limited to reduce current spikes. Set this to 0.0 to disable rate limiting. 1178 * 1179 * @param rampRate The maximum rate of voltage change in Percent Voltage mode in V/s. 1180 */ 1181 public void setVoltageRampRate(double rampRate) throws CANTimeoutException { 1182 byte[] dataBuffer = new byte[8]; 1183 byte dataSize; 1184 1185 switch (m_controlMode.value) { 1186 case ControlMode.kPercentVbus_val: 1187 dataSize = packPercentage(dataBuffer, rampRate / (m_maxOutputVoltage * kControllerRate)); 1188 setTransaction(JaguarCANProtocol.LM_API_VOLT_SET_RAMP, dataBuffer, dataSize); 1189 break; 1190 case ControlMode.kVoltage_val: 1191 dataSize = packFXP8_8(dataBuffer, rampRate / kControllerRate); 1192 setTransaction(JaguarCANProtocol.LM_API_VCOMP_IN_RAMP, dataBuffer, dataSize); 1193 break; 1194 default: 1195 return; 1196 } 1197 } 1198 1199 /** 1200 * Get the version of the firmware running on the Jaguar. 1201 * 1202 * @return The firmware version. 0 if the device did not respond. 1203 */ 1204 public int getFirmwareVersion() throws CANTimeoutException { 1205 byte[] dataBuffer = new byte[8]; 1206 byte dataSize; 1207 1208 // Set the MSB to tell the 2CAN that this is a remote message. 1209 dataSize = getTransaction(0x80000000 | JaguarCANProtocol.CAN_MSGID_API_FIRMVER, dataBuffer); 1210 if (dataSize == 4) { 1211 return (int)unpackINT32(dataBuffer); 1212 } 1213 return 0; 1214 } 1215 1216 /** 1217 * Get the version of the Jaguar hardware. 1218 * 1219 * @return The hardware version. 1: Jaguar, 2: Black Jaguar 1220 */ 1221 public byte getHardwareVersion() throws CANTimeoutException { 1222 byte[] dataBuffer = new byte[8]; 1223 byte dataSize; 1224 1225 dataSize = getTransaction(JaguarCANProtocol.LM_API_HWVER, dataBuffer); 1226 if (dataSize == 1 + 1) { 1227 if (dataBuffer[0] == m_deviceNumber) { 1228 return dataBuffer[1]; 1229 } 1230 } 1231 // Assume Gray Jag if there is no response 1232 return JaguarCANProtocol.LM_HWVER_JAG_1_0; 1233 } 1234 1235 /** 1236 * Configure what the controller does to the H-Bridge when neutral (not driving the output). 1237 * 1238 * This allows you to override the jumper configuration for brake or coast. 1239 * 1240 * @param mode Select to use the jumper setting or to override it to coast or brake. 1241 */ 1242 public void configNeutralMode(NeutralMode mode) throws CANTimeoutException { 1243 byte[] dataBuffer = new byte[8]; 1244 1245 dataBuffer[0] = mode.value; 1246 setTransaction(JaguarCANProtocol.LM_API_CFG_BRAKE_COAST, dataBuffer, (byte) 1); 1247 } 1248 1249 /** 1250 * Configure how many codes per revolution are generated by your encoder. 1251 * 1252 * @param codesPerRev The number of counts per revolution in 1X mode. 1253 */ 1254 public void configEncoderCodesPerRev(int codesPerRev) throws CANTimeoutException { 1255 byte[] dataBuffer = new byte[8]; 1256 byte dataSize; 1257 1258 dataSize = packINT16(dataBuffer, (short)codesPerRev); 1259 setTransaction(JaguarCANProtocol.LM_API_CFG_ENC_LINES, dataBuffer, dataSize); 1260 } 1261 1262 /** 1263 * Configure the number of turns on the potentiometer. 1264 * 1265 * There is no special support for continuous turn potentiometers. 1266 * Only integer numbers of turns are supported. 1267 * 1268 * @param turns The number of turns of the potentiometer 1269 */ 1270 public void configPotentiometerTurns(int turns) throws CANTimeoutException { 1271 byte[] dataBuffer = new byte[8]; 1272 byte dataSize; 1273 1274 dataSize = packINT16(dataBuffer, (short)turns); 1275 setTransaction(JaguarCANProtocol.LM_API_CFG_POT_TURNS, dataBuffer, dataSize); 1276 } 1277 1278 /** 1279 * Configure Soft Position Limits when in Position Controller mode. 1280 * 1281 * When controlling position, you can add additional limits on top of the limit switch inputs 1282 * that are based on the position feedback. If the position limit is reached or the 1283 * switch is opened, that direction will be disabled. 1284 * 1285 * @param forwardLimitPosition The position that if exceeded will disable the forward direction. 1286 * @param reverseLimitPosition The position that if exceeded will disable the reverse direction. 1287 */ 1288 public void configSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition) throws CANTimeoutException { 1289 byte[] dataBuffer = new byte[8]; 1290 byte dataSize; 1291 1292 dataSize = packFXP16_16(dataBuffer, forwardLimitPosition); 1293 dataBuffer[dataSize++] = (forwardLimitPosition > reverseLimitPosition) ? (byte) 1 : (byte) 0; 1294 setTransaction(JaguarCANProtocol.LM_API_CFG_LIMIT_FWD, dataBuffer, dataSize); 1295 1296 dataSize = packFXP16_16(dataBuffer, reverseLimitPosition); 1297 dataBuffer[dataSize++] = forwardLimitPosition <= reverseLimitPosition ? (byte) 1 : (byte) 0; 1298 setTransaction(JaguarCANProtocol.LM_API_CFG_LIMIT_REV, dataBuffer, dataSize); 1299 1300 dataBuffer[0] = LimitMode.kSoftPositionLimit_val; 1301 setTransaction(JaguarCANProtocol.LM_API_CFG_LIMIT_MODE, dataBuffer, (byte) 1); 1302 } 1303 1304 /** 1305 * Disable Soft Position Limits if previously enabled. 1306 * 1307 * Soft Position Limits are disabled by default. 1308 */ 1309 public void disableSoftPositionLimits() throws CANTimeoutException { 1310 byte[] dataBuffer = new byte[8]; 1311 1312 dataBuffer[0] = LimitMode.kSwitchInputsOnly_val; 1313 setTransaction(JaguarCANProtocol.LM_API_CFG_LIMIT_MODE, dataBuffer, (byte) 1); 1314 } 1315 1316 /** 1317 * Configure the maximum voltage that the Jaguar will ever output. 1318 * 1319 * This can be used to limit the maximum output voltage in all modes so that 1320 * motors which cannot withstand full bus voltage can be used safely. 1321 * 1322 * @param voltage The maximum voltage output by the Jaguar. 1323 */ 1324 public void configMaxOutputVoltage(double voltage) throws CANTimeoutException { 1325 byte[] dataBuffer = new byte[8]; 1326 byte dataSize; 1327 1328 m_maxOutputVoltage = voltage; 1329 dataSize = packFXP8_8(dataBuffer, voltage); 1330 setTransaction(JaguarCANProtocol.LM_API_CFG_MAX_VOUT, dataBuffer, dataSize); 1331 } 1332 1333 /** 1334 * Configure how long the Jaguar waits in the case of a fault before resuming operation. 1335 * 1336 * Faults include over temerature, over current, and bus under voltage. 1337 * The default is 3.0 seconds, but can be reduced to as low as 0.5 seconds. 1338 * 1339 * @param faultTime The time to wait before resuming operation, in seconds. 1340 */ 1341 public void configFaultTime(double faultTime) throws CANTimeoutException { 1342 byte[] dataBuffer = new byte[8]; 1343 byte dataSize; 1344 1345 // Message takes ms 1346 dataSize = packINT16(dataBuffer, (short) (faultTime * 1000.0)); 1347 setTransaction(JaguarCANProtocol.LM_API_CFG_FAULT_TIME, dataBuffer, dataSize); 1348 } 1349 1350 /** 1351 * Update all the motors that have pending sets in the syncGroup. 1352 * 1353 * @param syncGroup A bitmask of groups to generate synchronous output. 1354 */ 1355 public static void updateSyncGroup(byte syncGroup) throws CANTimeoutException { 1356 byte[] dataBuffer = new byte[8]; 1357 1358 dataBuffer[0] = syncGroup; 1359 sendMessage(JaguarCANProtocol.CAN_MSGID_API_SYNC, dataBuffer, 1); 1360 } 1361 1362 1363 public void setExpiration(double timeout) { 1364 m_safetyHelper.setExpiration(timeout); 1365 } 1366 1367 public double getExpiration() { 1368 return m_safetyHelper.getExpiration(); 1369 } 1370 1371 public boolean isAlive() { 1372 return m_safetyHelper.isAlive(); 1373 } 1374 1375 public boolean isSafetyEnabled() { 1376 return m_safetyHelper.isSafetyEnabled(); 1377 } 1378 1379 public void setSafetyEnabled(boolean enabled) { 1380 m_safetyHelper.setSafetyEnabled(enabled); 1381 } 1382 1383 public String getDescription() { 1384 return "CANJaguar ID "+m_deviceNumber; 1385 } 1386 1387 /** 1388 * Common interface for stopping a motor. 1389 * 1390 * @deprecated Use disableControl instead. 1391 */ 1392 public void stopMotor() { 1393 try { 1394 disableControl(); 1395 } catch (CANTimeoutException e) {} 1396 } 1397 1398 /* 1399 * Live Window code, only does anything if live window is activated. 1400 */ 1401 public String getSmartDashboardType(){ 1402 return "Speed Controller"; 1403 } 1404 private ITable m_table; 1405 private ITableListener m_table_listener; 1406 1407 /** 1408 * {@inheritDoc} 1409 */ 1410 public void initTable(ITable subtable) { 1411 m_table = subtable; 1412 updateTable(); 1413 } 1414 1415 /** 1416 * {@inheritDoc} 1417 */ 1418 public void updateTable() { 1419 if (m_table != null) { 1420 m_table.putNumber("Value", get()); 1421 } 1422 } 1423 1424 /** 1425 * {@inheritDoc} 1426 */ 1427 public ITable getTable(){ 1428 return m_table; 1429 } 1430 1431 /** 1432 * {@inheritDoc} 1433 */ 1434 public void startLiveWindowMode() { 1435 set(0); // Stop for safety 1436 m_table_listener = new ITableListener() { 1437 public void valueChanged(ITable itable, String key, Object value, boolean bln) { 1438 set(((Double) value).doubleValue()); 1439 } 1440 }; 1441 m_table.addTableListener("Value", m_table_listener, true); 1442 } 1443 1444 /** 1445 * {@inheritDoc} 1446 */ 1447 public void stopLiveWindowMode() { 1448 set(0); // Stop for safety 1449 // TODO: Broken, should only remove the listener from "Value" only. 1450 m_table.removeTableListener(m_table_listener); 1451 } 1452 }