Retrieving the Driver's Station IP (2019 Version)

Is there a simple way to retrieve the IP address of the Driver’s Station such that

  • The same API call works regardless of ethernet (10.* network) or USB (172.* network)
  • We don’t have to detect the network ourselves. We call a method, it gives us the IP
  • We don’t want to use static IP’s.

Is this possible in the current Java API? Is there a utility method that a team has made to detect the network itself?

No, there is no standard way provided to do this. What are you trying to do? There’s probably ways to solve the problem that don’t involve this.

Assuming you mean finding the Driver Station computer’s IP from the robot when the DS application is actually running on the computer and connected, it’s possible to do by calling the netstat command: netstat -t -n shows active TCP connections; if the DS is connected there will be a line where the local port is 1740 and the state is ESTABLISHED. The foreign address in that line is the DS IP address. E.g. for this output:

Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 127.0.0.1:43213         127.0.0.1:45150         ESTABLISHED 
tcp        0      0 192.168.1.106:1740      192.168.1.110:50354     ESTABLISHED 
tcp        0      0 127.0.0.1:45150         127.0.0.1:43213         ESTABLISHED 

The driver station IP here is 192.168.1.110.

If you know a NetworkTables application is running on the DS computer, you can also find out the connected clients via the NetworkTables API and do appropriate filtering based on your network configuration.

Trying to connect a custom networking code library that supports compression, high-frequency cycles (batched into lower-rate packets), etc. The hope is to migrate the actual comms layer to a MQ-based framework that supports multi-casting, but keep the batching/compression.

This isn’t strictly for the field; it’s more for the various states of prototypes that need logging in the lab. The only piece that’s missing is a reliable way to connect the comms code to the DS laptop regardless of how the DS is connected to the robot.

Thanks for the tip to netstat.

If the connection goes the other direction, then you can use mDNS to find the roboRIO.

On that note, if you know what hostname your driver station has, you can probably also use mDNS from the robot to find the laptop.

This code works fairly well; we will have it in a thread that polls once per second for DriverStation.getInstance().isDSAttached() to return true.

I think an additional grep on the appropriate port will get the EXACT Ip of the DS, but in our case we’re just trying to multicast UDP (for now).

If switching from USB to Ethernet/Wifi, it DOES require a RIO reset. Yet this is rare.

Thanks again for the tip.


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.io.FilenameUtils;

/**
 * Utility class to get the local IP by running a netstat. 
 * @see GetLocalIP#getIp()
 */
public final class GetLocalIP {

    /**
     * In windows we want to run in the git bash shell. Update to the correct
     * directory
     */
    private static final String kWindowsShell = "C:\\Program Files\\Git\\bin\\bash";
    private static final String kWindowsCommand = "robot/src/main/resources/sampleOutput.sh";
    private static final String kUnixShell = "/bin/sh";
    private static final String UNIX_SCRIPT = "netstat -t -n | grep tcp | grep -v 127.0.0.1 | awk '{print $5}' | awk -F: '{print $1}'";

    private static final String kShell;
    private static final String kScript;

    /**
     * Regex provided by:
     * https://www.mkyong.com/regular-expressions/how-to-validate-ip-address-with-regular-expression/
     */
    private static final String kIpAddressRegex = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
            + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
            + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";

    private static final Pattern kIPPattern = Pattern.compile(kIpAddressRegex);

    static {
        String os = System.getProperty("os.name");
        String winScript = null;
        if (os.toLowerCase().contains("windows")) {
            String userDir = System.getProperty("user.dir");

            // Git-bash uses unix file system representation.
            String userDirUnix = FilenameUtils.separatorsToUnix(userDir);

            winScript = userDirUnix + "/" + kWindowsCommand;
            kShell = kWindowsShell;
            kScript = winScript;
        } else {
            kShell = kUnixShell;
            kScript = UNIX_SCRIPT;
        }
    }

    /**
     * Method to get the local IP of the DS
     * @return
     *  An optional that will contain the IP if it was sucessfully retrieved. Otherwise it will be 
     * empty. It will never be null.
     */
    public static Optional<String> getIp() {
        //TODO - clean this up
        Optional<String> returnVal = Optional.of(getAllIps().get(0));

        return returnVal;
    }

    /**
     * @return a list of all non-localhost IP's attached to the robot at this time
     */
    public static List<String> getAllIps() {
        List<String> results = new ArrayList<>();
        ProcessBuilder procBuild = new ProcessBuilder(Arrays.asList(kShell, "-c", kScript));
        procBuild.redirectErrorStream(true);

        try {
            Process proc = procBuild.start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            results.addAll(getIPFromInputStream(reader));

        } catch (IOException e) {
            e.printStackTrace();
        }
        return results;
    }

    /**
     * Method to extract the IP from a process's InputStreamReader. This method will
     * keep reading from the passed in reader until all IPs are found. Then the method 
     * will return the first match. If there are no IPs or any other errors, this method 
     * will return an empty list
     * 
     * @param reader the reader from the process's inputstream. If this is null, 
     * the method will return an empty optional
     */
    protected static List<String> getIPFromInputStream(BufferedReader reader) {

        if (reader != null) {
            Set<String> allLines = new LinkedHashSet<>();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    Matcher matcher = kIPPattern.matcher(line);
                    if (matcher.matches()) {
                        allLines.add(line);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            return allLines.stream().collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    /**
     * Private class so this does not get instantiated
     */
    private GetLocalIP() {

    }

    public static void main(String[] pArgs) {
        Optional<String> ip = getIp();
        if (ip.isPresent()) {
            System.out.println("IP: " + ip.get());
        } else {
            System.out.println("No IP");
        }
    }
}