NetworkTable issues...

Hello,

We are attempting to use the NetworkTable components to communicate data between our Robot and a Java program running on a laptop. However we are running into an issue where some of the values that we are trying to query from the Java program running on the laptop cause an exception to be thrown. It is a NullReferenceException that seems to be coming from the Reader when try to call table.getValue() or table.getNumber(). At the point that we are reading the value it has been written from the Robot side. We’re using the SmartDashboard as the table. We are interested in moving to a table of our name but we are unsure of what is involved in setting that up. Additionally, I noticed in some examples in other posting on this forum that people have used the beginTransaction method on the NetworkTable object. However, in the Java libraries we have there doesn’t seem to be such a method. I’ve checked for updates but didn’t find any. Is it possible what we’re running is out of date?

Thanks,
Rob

Would it be possible for you to provide a stack trace. If you want to use your own network table you can see an example of what you are trying to do here http://wpilib.screenstepslive.com/s/3120/m/7912/l/80205-writing-a-simple-networktables-program-in-c-and-java-with-a-java-client-pc-side
Transactions were removed in this years version of the library as it was rewritten and not many people seemed to use them.

Hi,

Thanks for the reply. Unfortunately I’m at home now and I don’t have a call stack with me but I remember it was down in the call to get the value of one of the keys (e.g. table.getValue()). Let me layout some details about how the code is structured and perhaps someone might be able to spot what is going wrong. There are three pieces to the vision solution, a RoboRealm script, a Java application running on a PC (called the RobotVisionTargetServer), and a command based Robot program running on the Robot. The RobotVisionTargetServer communicates with RoboRealm using the a TCP socket connection in order to request image data. That part is working correctly. The communication between the Robot and the RobotVisionServer is where the NetworkTable comes into play.

On the Robot the interaction begins as follows with the Robot requesting that targeting of a specific goal type begin:

    table = NetworkTable.getTable("SmartDashboard");
    this.goalType = goalType;
    table.addTableListener(this);
    requestId = System.currentTimeMillis();
    table.putNumber("TARGET_TYPE_REQUEST_ID", (double)requestId);
    table.putNumber("TARGET_TYPE_REQUEST", (double)goalType);
    System.out.println("Published requestId");

Notice that it registers a listener which is how it picks up the response that is supposed to be written by the RobotVisionServer application. However, it never gets that far because of the problem that we run into with the RobotVisionServer. The RobotVisionServer initializes interaction with the NetworkTable as follows. Note that address is “10.39.50.2” which is the address of our Robot. (Is the Robot address the correct one to use?)

public RobotVisionServer(String address) {
NetworkTable.setClientMode();
NetworkTable.setIPAddress(address);
NetworkTable table = NetworkTable.getTable(“SmartDashboard”);

    if (table == null) {
        // TBD: What to do if we can't get the table
        // Does getTable return null or throw an exception.
    }
    
    table.addTableListener(this);
}

The RobotVersionServer instance also adds itself as a table listener so that it can act on the requests passed over by the Robot. One of the issues that we’re running into is when a request from the Robot is detected by the RobotVisionServer and we’re trying to read one of the keys as is indicated in the code snippet below…

public void valueChanged(ITable source, String key, Object value, boolean isNew) {
System.out.println("ValueChanged: " + key + " Value: " + value + " new: " + isNew);

    // Is this a key we care about?
    if (key.equals("TARGET_TYPE_REQUEST")) {
        
        // Expecting a double value.
        if (value instanceof Double) {
            double reqGoal = ((Double)value).doubleValue();

	// *** This line causes an exception to be thrown
	Object v = table.getValue("TARGET_TYPE_REQUEST_ID");
	.
	.	
	.
	
        }
        else {
            System.out.println("Received goal targeting request but value is not a Double as expected.");
        }
    }
}

I tried changing the table.getValue(…) to something simpler: table.getNumber(“TARGET_TYPE_REQUEST_ID”) but the same exception occurs. I’m not sure what the issue is but one of the assumptions I made is that because the Robot is writing the key “TARGET_TYPE_REQUEST_ID” first followed by the key “TARGET_TYPE_REQUEST” that when the RobotVisionServer application is notified of the change to “TARGET_TYPE_REQUEST” that the “TARGET_TYPE_REQUEST_ID” change would have already arrived and been made available. If this isn’t correct how can I ensure both values are available together since transactions have been removed?

If I comment out the getValue of the “TARGET_TYPE_REQUEST_ID” the code will run further but runs into trouble when it then tries to publish the resulting angle of the target it has detected. A similar exception occurs except this time it on writing a result out to the NetworkTable. Here’s a snippet showing the write code. All the types passed to putNumber are double primitives. The very first putNumber causes an exception:

	    // Exception is thrown when the line below is executed.
                table.putNumber("TARGET_TYPE_RESULT", mapGoalTypeToDouble(goalType));
                table.putNumber("TARGET_TYPE_RESULT_ID",  currRequestId);
                table.putNumber(TARGET_AVG_DISTANCE_RESULT , avgDistance);
                table.putNumber("TARGET_ANGLE_RESULT", angle);

Note that these put calls are called from a Java Thread that my program creates. I have a lock object which is used for synchronization and is acquired when either the change notification performs the getValue key calls or the putNumber calls are made. (I didn’t show the synchronization here to keep the code simple). Are there any threading issues to be aware of when using the NetworkTable from multiple threads?

I’m at my wits end since the clock is running out. Any help is greatly appreciated in trying to shoot down this issue. Would we better off not using the “SmartDashboard” table?

Regards,
Rob Saccone
Mentor Team 3950

Hi Rob,

Thanks for the additional information. Unfortunately there is no guarantee in the order which values are delivered. If you want to guarantee that you receive both values together than you can use arrays (see code below) which allow for transaction like behavior but only with keys of the same type. The NumberArray acts just like an array list and supports simple operations such as add, set, clear and remove. Doing operations on array object do not send any values until putValue is used. retrieveValue will atomically retrieve values from network tables. DO NOT attempt to use the value in the ValueChanged callback. You must always use retrieveValue to get an array.

As for multithreading, networktables should be thread safe, HOWEVER the ValueChanged callback will be called while an internal lock is held on the internal data store. This means that you cannot do any kind of put or get from another thread (but you can do it from inside of the callback) so if you attempt to take a lock inside of the ValueChanged method make sure that another thread will not be holding the same lock and attempt to use networktables or your code will deadlock. One other consideration is try to make sure that you don’t do a put on networktables inside of ValueChanged that will cause ValueChanged to be called again as this will cause recursive call of ValueChanged.

You can use whatever table you want. If you put values to the SmartDashboard table then they will show up on the Smart Dashboard but otherwise there is no reason either way.

Also you can just do getDouble. You don’t have to do getValue and then do the type check.

–Mitchell Wills

//to sent value
NumberArray data= new NumberArray();
table.putValue(“mykey”, data);
//values in date are now valid

=======================================

//to retrieve value
NumberArray data= new NumberArray();
//put values in the array
table.retrieveValue(“mykey”, data);

Hi Mitchell

Thanks for the additional information. I will take a careful look at the threading issues you mentioned to be sure the code is safe. Could any of that cause the null reference exception we’re seeing? Any more ideas as why that might be coming up in the Java program running on the laptop when we try to do a getXXX or a putXXX? That’s really a showstopper for us at this point as we can’t publish our result back to the Robot. I will also try and get a stack capture when I go back up to the school Tues evening.

Regards,
Rob

Sorry I can’t be of more help, but without a stack trace i’m not really sure. The only NullPointerException that I can think of would come from passing a null value to putXXX, but besides that you should get a TableKeyNotDefinedException if the key you provided doesn’t currently have a value. Also the user thread should never get to the reader objects as there are separate read and write threads.

One more thing I want to double check, using to the Robot’s address for the NetworkTable in the program running on the laptop is correct?

Thanks,
Rob

Yes that is the address of the server which the Network Tables client should connect to.

Hi Mitchell,

I took a run up to the school and I figured out the issue as soon I got a stack trace. We had a local variable that was shadowing an instance variable so the instance variable for the NetworkTable never got initialized! Hence the null reference exception when the instance variable for the table was being referenced later. I’m going to get the code refactored to avoid the deadlock scenario that you pointed out.

One thing I noticed is that the getDouble api is struck out by NetBeans. I guess it has been deprecated in favor of getNumber. Also there is a variation on getNumber which takes not only the key but a double as well. The JavaDoc I have is not very descriptive but I’m assuming the double parameter is returned if the key doesn’t exist in the table. Sound right?

Thanks again,
Rob

Yes that is correct. The added parameter is the default return value if the key doesn’t exist. getDouble and getInt and their put functions were deprecated in favor of get/setNumber to simplify the data types.