package lejos.pc.comm;
import java.io.*;
import java.util.*;
/**
* Connects to a NXT using Bluetooth or USB (or either) and supplies input and output
* data streams.
*
* @author Lawrie Griffiths and Roger Glassey
*/
public class NXTConnector extends NXTCommLoggable
{
private DataInputStream dataIn;
private DataOutputStream dataOut;
private InputStream is;
private OutputStream os;
private NXTInfo nxtInfo;
private NXTInfo[] nxtInfos;
private NXTComm nxtCommUSB = null, nxtCommBluetooth = null, nxtComm = null;
private boolean debugOn = false;
/**
* Connect to any NXT over any protocol in PACKET mode
*
* @return 0 true iff the open succeeded
*/
public boolean connectTo() {
return connectTo(null, null, NXTCommFactory.ALL_PROTOCOLS, NXTComm.PACKET);
}
/**
* Connect to any NXT over any protocol specifying mode
* @param mode the NXTComm mode (PACKET, LCP, or RAW)
*
* @return 0 true iff the open succeeded
*/
public boolean connectTo(int mode) {
return connectTo(null, null, NXTCommFactory.ALL_PROTOCOLS, mode);
}
/**
* Connect to a specified NXT in packet mode
*
* @param nxt the name of the NXT to connect to or null for any
* @param addr the address of the NXT to connect to or null
* @param protocols the protocols to use
* @return true iff the open succeeded
*/
public boolean connectTo(String nxt, String addr, int protocols) {
return connectTo(nxt, addr, protocols, NXTComm.PACKET);
}
/**
* Search for a NXT
*
* @param nxt the name of the NXT to connect to or null for any
* @param addr the address of the NXT to connect to or null
* @param protocols the protocols to use
* @return 0 if opened successfully, -1 if failed, 1 if there is a list to choose from
*/
public NXTInfo[] search(String nxt, String addr, int protocols)
{
String name = (nxt == null || nxt.length() == 0 ? nxt: "Unknown");
String searchParam = (nxt == null || nxt.length() == 0 || nxt.equals("*") ? null : nxt);
String searchFor = (nxt == null || nxt.length() == 0 ? "any NXT" : nxt);
Properties props = null;
// reset the relevant instance variables
nxtComm = null;
nxtInfos = new NXTInfo[0];
debug("Protocols = " + protocols);
debug("Search Param = " + searchParam);
// Try USB first
if ((protocols & NXTCommFactory.USB) != 0) {
try {
nxtComm = nxtCommUSB = NXTCommFactory.createNXTComm(NXTCommFactory.USB);
} catch (NXTCommException e) {
log("Failed to load USB comms driver: " + e.getMessage());
}
if (addr != null && addr.length() > 0) {
log("Using USB device with address = " + addr);
nxtInfo = new NXTInfo(NXTCommFactory.USB, name, addr);
nxtInfos = new NXTInfo[1];
nxtInfos[0] = nxtInfo;
} else if (nxtComm != null){
debug("Searching for " + searchFor + " using USB");
try {
nxtInfos = nxtComm.search(searchParam, NXTCommFactory.USB);
if (nxtInfos.length == 0)
debug((searchParam == null ? "No NXT found using USB: " : (searchParam + " not found using USB: ")) + "Is the NXT switched on and the USB cable connected?");
} catch (NXTCommException ex) {
log("Search Failed: " + ex.getMessage());
}
}
}
if (nxtInfos.length > 0) return nxtInfos;
// If nothing found on USB, try Bluetooth
if ((protocols & NXTCommFactory.BLUETOOTH) != 0) {
// Load Bluetooth driver
try {
nxtComm = nxtCommBluetooth = NXTCommFactory.createNXTComm(NXTCommFactory.BLUETOOTH);
} catch (NXTCommException e) {
log("Failed to load Bluetooth comms driver: " + e.getMessage());
return nxtInfos;
}
// If address specified, connect by address
if (addr != null && addr.length() > 0) {
log("Using Bluetooth device with address = " + addr);
nxtInfo = new NXTInfo(NXTCommFactory.BLUETOOTH, name, addr);
nxtInfos = new NXTInfo[1];
nxtInfos[0] = nxtInfo;
return nxtInfos;
}
// Get known NXT names and addresses from the properties file
try {
props = NXTCommFactory.getNXJCache();
// Create an array of NXTInfos from the properties
if (props.size() > 0 && !(nxt != null && nxt.equals("*"))) {
Hashtable<String,String> nxtNames = new Hashtable<String,String>();
Enumeration<?> enProps = props.propertyNames();
debug("Searching cache file for known Bluetooth devices");
// Populate hashTable from NXT_<name> entries, filtering by name, if supplied
for (; enProps.hasMoreElements(); ) {
// Get property name
String propName = (String)enProps.nextElement();
if (propName.startsWith("NXT_")) {
String nxtName = propName.substring(4);
String nxtAddr = (String)props.get(propName);
if (searchParam == null || nxtName.equals(nxt)) {
debug("Found " + nxtName + " " + nxtAddr + " in cache file");
nxtNames.put(nxtName, nxtAddr);
}
}
}
debug("Found " + nxtNames.size() + " matching NXTs in cache file");
// If any found, create the NXTInfo array from the hashtable
if (nxtNames.size() > 0) {
nxtInfos = new NXTInfo[nxtNames.size()];
Enumeration<?> enNXTs = nxtNames.keys();
int i=0;
for (; enNXTs.hasMoreElements(); ) {
String ne = (String)enNXTs.nextElement();
nxtInfos[i++] = new NXTInfo(NXTCommFactory.BLUETOOTH, ne, nxtNames.get(ne));
}
}
} else {
debug("No NXTs found in cache file");
}
} catch (NXTCommException ex) {
log("Failed to load cache file");
}
// If none found, do a Bluetooth inquiry
if (nxtInfos.length == 0) {
log("Searching for " + searchFor + " using Bluetooth inquiry");
try {
nxtInfos = nxtComm.search(searchParam, NXTCommFactory.BLUETOOTH);
} catch (NXTCommException ex) {
log("Search Failed: " + ex.getMessage());
}
debug("Inquiry found " + nxtInfos.length + " NXTs");
// Save the results in the properties file
for(int i=0;i<nxtInfos.length;i++) {
log("Name " + i + " = " + nxtInfos[i].name);
log("Address " + i + " = " + nxtInfos[i].deviceAddress);
props.put("NXT_" + nxtInfos[i].name, nxtInfos[i].deviceAddress);
}
debug("Saving cached names");
try {
NXTCommFactory.saveNXJCache(props,"Results from Bluetooth inquiry");
} catch (IOException ex) {
log("Failed to write cache file: " + ex.getMessage());
}
}
}
// If nothing found, log a message
if (nxtInfos.length == 0) {
log("Failed to find any NXTs");
}
return nxtInfos;
}
/**
* Connect to a NXT
*
* @param nxt the name of the NXT to connect to or null for any
* @param addr the address of the NXT to connect to or null
* @param protocols the protocols to use
* @return 0 if opened successfully, -1 if failed, 1 if there is a list to choose from
*/
public boolean connectTo(String nxt, String addr, int protocols, int mode)
{
boolean opened = false;
// reset all the instance variables associated with the connection
nxtInfo = null;
is = null;
os = null;
dataIn = null;
dataOut = null;
// Search for matching NXTs
search(nxt, addr, protocols);
// Try each available NXT in turn
for(int i=0;i<nxtInfos.length;i++) {
try {
debug("Connecting to " + nxtInfos[i].name + " " + nxtInfos[i].deviceAddress + " in mode " + mode);
opened = nxtComm.open(nxtInfos[i], mode);
if (opened) {
nxtInfo = nxtInfos[i];
log("Connected to " + nxtInfo.name);
break;
} else {
log("Failed to open " + nxtInfos[i].name + " " + nxtInfos[i].deviceAddress);
}
} catch (NXTCommException ex) {
log("Exception in open: " + ex.getMessage());
}
}
if (!opened) {
log("Failed to connect to any NXT");
return false;
}
setStreams();
return true;
}
/**
* Connect to a NXT using a NXTInfo
* @param nxtInfo
* @param mode
* @return true iff the connection succeeded
*/
public boolean connectTo(NXTInfo nxtInfo, int mode) {
this.nxtInfo = nxtInfo;
if (nxtInfo.protocol == NXTCommFactory.USB ) {
if (nxtCommUSB == null) {
try {
nxtComm = nxtCommUSB = NXTCommFactory.createNXTComm(NXTCommFactory.USB);
} catch (NXTCommException e) {
log("Failed to load USB comms driver: " + e.getMessage());
return false;
}
}
nxtComm = nxtCommUSB;
}
if (nxtInfo.protocol == NXTCommFactory.BLUETOOTH ) {
if (nxtCommBluetooth == null) {
try {
nxtComm = nxtCommBluetooth = NXTCommFactory.createNXTComm(NXTCommFactory.BLUETOOTH);
} catch (NXTCommException e) {
log("Failed to load Bluetooth comms driver: " + e.getMessage());
return false;
}
}
nxtComm = nxtCommBluetooth;
}
try {
boolean opened = nxtComm.open(nxtInfo, mode);
if (!opened) {
log("Failed to connect to the specified NXT");
return false;
}
setStreams();
return true;
} catch (NXTCommException e) {
log("Exception connecting to NXT: " + e.getMessage());
return false;
}
}
/**
* Connect to a device by URL
* @param deviceURL
* @param mode the mode (NXTComm.LCP or NXTComm.PACKET)
* @return true iff the connection succeeded
*/
public boolean connectTo(String deviceURL, int mode) {
String protocolString = "";
int colonIndex = deviceURL.indexOf(':');
if (colonIndex >= 0) {
protocolString = deviceURL.substring(0,colonIndex);
}
String addr = null;
String name = null;
int protocols = NXTCommFactory.ALL_PROTOCOLS;
if (protocolString.equals("btspp")) protocols = NXTCommFactory.BLUETOOTH;
if (protocolString.equals("usb")) protocols = NXTCommFactory.USB;
if (colonIndex >= 0) colonIndex +=2; // Skip "//"
String nameString = deviceURL.substring(colonIndex+1);
boolean isAddress = nameString.startsWith("00");
if (isAddress) {
addr = nameString;
name = "Unknown";
} else {
name = nameString;
addr = null;
}
return connectTo(name, addr, protocols, mode);
}
/**
* Connect to a device by URL in packet mode
* @param deviceURL
* @return true iff the connection succeeded
*/
public boolean connectTo(String deviceURL) {
return connectTo(deviceURL, NXTComm.PACKET);
}
private void setStreams() {
is = nxtComm.getInputStream();
dataIn = new DataInputStream(nxtComm.getInputStream());
os = nxtComm.getOutputStream();
dataOut = new DataOutputStream(os);
}
/**
* @return the InputStream for this connection;
*/
public InputStream getInputStream() {return is;}
/**
* @return the DataInputStream for this connection;
*/
public DataInputStream getDataIn() {return dataIn;}
/**
* @return the OutputSteram for this connection;
*/
public OutputStream getOutputStream() {return os;}
/**
* @return the DataOutputStream for this connection
*/
public DataOutputStream getDataOut() {return dataOut;}
/**
* @return the NXTInfo for this connection
*/
public NXTInfo getNXTInfo () {return nxtInfo;}
/**
* @return the array of NXTInfos for this connection
*/
public NXTInfo[] getNXTInfos () {return nxtInfos;}
/**
* @return the NXTComm for this connection
*/
public NXTComm getNXTComm () {return nxtComm;}
/**
* Close the connection
*
* @throws IOException
*/
public void close() throws IOException {
if (nxtComm != null) nxtComm.close();
}
private void debug(String msg) {
if (debugOn) log(msg);
}
/**
* Set debugging on or off
*
* @param debug true for on, false for off
*/
public void setDebug(boolean debug) {
debugOn = debug;
}
}