Refnum like interface for CANJaguars

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;
    }
}