Note that I have not tested this yet
After playing around with Labview a little bit, I learned that I liked the idea of Refnums (identifier strings that can be used to describe and identify objects).
And after using Java and CAN on this years robot, I wanted something that would not use the bus for every single call to the getter methods.
So, I made a simple class that uses a Hashtable (Stores the instances of CANJaguar with a unique and descriptive name (like a refnum)) and a TimerTask that updates the data in the local cache at around 50 hertz.
Here is a simple example that uses the class (a simple arcade drive teleoperated)
package edu.wpi.first.wpilibj.templates;
import edu.wpi.first.wpilibj.CAN.CANBus;
import edu.wpi.first.wpilibj.IterativeRobot;
import edu.wpi.first.wpilibj.Joystick;
public class RobotTemplate extends IterativeRobot {
CANBus bus = new CANBus();
Joystick arcade = new Joystick(1);
public void robotInit()
{
bus.addJaguar("LeftDrive", 2);
bus.addJaguar("RightDrive", 3);
}
public void autonomousPeriodic() {
}
public void teleopPeriodic() {
// Arcade drive implementation
double speed = -arcade.getY();
double turn = arcade.getX();
double left = speed + turn;
double right = speed - turn;
if(left > 1){ left = 1;}
else if(left < -1){ left = -1;}
if(right > 1){ right = 1;}
else if(right < -1){ right = -1;}
// End arcade drive implementation
bus.setPercentOutputVoltage("LeftDrive", left);
bus.setPercentOutputVoltage("RightDrive", right);
}
}
and here is the actual class (Note that I made a slight change to the JaguarCANProtocol class, I added
public static final int LM_STATUS_FAULT_BUSERR = 0xFF;
so that I could signal to the main execution loop that there was an error in either transmission or reception.):
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.wpilibj.CAN;
import java.util.Timer;
import edu.wpi.first.wpilibj.util.UncleanStatusException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.TimerTask;
/**
* Keeps track of all CANJaguar instances on CAN bus using unique Names.
* Use this to access information about a jaguar without acessing the bus directly
* This is a local cache so that data from the bus is not accessed except by one thread.
* Will update the data around 50 times a second depending on the # of devices
* on the bus, and on the device used to cummunocate with the Jaguars (ie 2CAN or black jag)
*
* As far as controlling the JAguar goes, this only supports the Voltage control mode (for now)
* If other modes are needed, then use the getjaguar method.
* (Note that controlling the jaguar outside of this class will not provide any exception handling)
*
* @author biojae
*/
public class CANBus
{
// Keep an instance of each CANJaguar, (Generics not supported by squawk)
private Hashtable devices = new Hashtable();
// Keep a local copy of status information on each jaguar so as to reduce data throughput on CAN
private Hashtable statuses = new Hashtable();
//Update the device information ~50 times a second (depending on # of devices)
private Timer updateThread;
public CANBus()
{
TimerTask update = new TimerTask()
{
public void run()
{
for (Enumeration name = devices.keys() ; name.hasMoreElements() ;)
{
String jagName = name.nextElement().toString();
CANJaguar currentJag = (CANJaguar) devices.get(jagName);
CANJaguarInfo currentInfo = (CANJaguarInfo) statuses.get(jagName);
try{
currentJag.set(currentInfo.outputPercentVoltage);
currentInfo.BusVoltage = currentJag.getBusVoltage();
currentInfo.OutVoltage = currentJag.getOutputVoltage();
currentInfo.Temperature = currentJag.getTemperature();
currentInfo.outCurrent = currentJag.getOutputCurrent();
currentInfo.position = currentJag.getPosition();
currentInfo.speed = currentJag.getSpeed();
currentInfo.faults = currentJag.getFaults();
}catch(UncleanStatusException errorOnBus)
{
errorOnBus.printStackTrace();
currentInfo.faults = JaguarCANProtocol.LM_STATUS_FAULT_BUSERR;
}
statuses.put(jagName, currentInfo);
}
}
};
updateThread = new Timer();
updateThread.scheduleAtFixedRate(update, 0, 20);
}
public void setPercentOutputVoltage(String name, double set)
{
if(statuses.contains(name))
{
((CANJaguarInfo)statuses.get(name)).outputPercentVoltage = set;
}
}
public boolean addJaguar(String name, int ID)
{
try{
devices.put(name, new CANJaguar(ID));
statuses.put(name, new CANJaguarInfo());
}catch(UncleanStatusException CANBusError)
{
devices.remove(name);
statuses.remove(name);
CANBusError.printStackTrace();
return false;
}
return true;
}
public double getTemperature(String name)
{
if(devices.contains(name))
{
return ((CANJaguarInfo)statuses.get(name)).Temperature;
}
return -1;
}
public double getBusVoltage(String name)
{
if(devices.contains(name))
{
return ((CANJaguarInfo)statuses.get(name)).BusVoltage;
}
return -1;
}
public double getOutputVoltage(String name)
{
if(devices.contains(name))
{
return ((CANJaguarInfo)statuses.get(name)).OutVoltage;
}
return -1;
}
public double getOutpuCurrent(String name)
{
if(devices.contains(name))
{
return ((CANJaguarInfo)statuses.get(name)).outCurrent;
}
return -1;
}
public double getPosition(String name)
{
if(devices.contains(name))
{
return ((CANJaguarInfo)statuses.get(name)).position;
}
return -1;
}
public double getSpeed(String name)
{
if(devices.contains(name))
{
return ((CANJaguarInfo)statuses.get(name)).speed;
}
return -1;
}
public short getFaults(String name)
{
if(devices.contains(name))
{
return ((CANJaguarInfo)statuses.get(name)).faults;
}
return -1;
}
public CANJaguar getJaguar(String name)
{
if(devices.contains(name))
{
return (CANJaguar) devices.get(name);
}
return null;
}
}