3 import java.io.FileInputStream;
4 import java.io.FileNotFoundException;
5 import java.io.IOException;
6 import java.util.ArrayList;
7 import java.util.Collection;
8 import java.util.Properties;
10 public class NXTCommand implements NXTProtocol {
12 private Collection<NXTCommLogListener> fLogListeners;
14 private NXTComm nxtComm = null, nxtCommUSB = null, nxtCommBluetooth = null;
16 private static String HOME = System.getProperty("nxj.home");;
17 private static String SEP = System.getProperty("file.separator");
18 private static String PROP_FILE = HOME + SEP + "bin" + SEP
20 private static NXTCommand singleton = null;
22 private boolean verifyCommand = false;
23 private boolean open = false;
24 private static String hexChars = "01234567890abcdef";
26 private NXTCommand() {
27 fLogListeners = new ArrayList<NXTCommLogListener>();
30 public NXTInfo[] search(String name, int protocol) throws NXTCommException {
33 if (nxtComm == null) {
34 Properties props = new Properties();
37 // log("Loading " + PROP_FILE);
38 props.load(new FileInputStream(PROP_FILE));
39 } catch (FileNotFoundException e) {
40 } catch (IOException e) {
41 log("Failed to read file " + PROP_FILE + ": " + e.getMessage());
44 String os = System.getProperty("os.name");
45 boolean windows = false;
47 if (os.length() >= 7 && os.substring(0, 7).equals("Windows"))
50 // Look for USB comms driver first
51 if ((protocol & NXTCommFactory.USB) != 0) {
52 String nxtCommName = props.getProperty("NXTCommUSB",
53 "lejos.pc.comm.NXTCommLibnxt");
54 // log("NXTCommUSB = " + nxtCommName);
56 Class c = Class.forName(nxtCommName);
57 nxtCommUSB = (NXTComm) c.newInstance();
58 } catch (Throwable t) {
63 // Look for a Bluetooth one
64 String defaultDriver = (windows ? "lejos.pc.comm.NXTCommBluecove"
65 : "lejos.pc.comm.NXTCommBluez");
67 if ((protocol & NXTCommFactory.BLUETOOTH) != 0) {
68 String nxtCommName = props.getProperty("NXTCommBluetooth",
70 // log("NXTCommBluetooth = " + nxtCommName);
72 Class c = Class.forName(nxtCommName);
73 nxtCommBluetooth = (NXTComm) c.newInstance();
74 } catch (Throwable t) {
79 if (nxtCommUSB == null && nxtCommBluetooth == null) {
80 throw new NXTCommException("Cannot load a comm driver");
85 // Look for a USB one first
87 if ((protocol & NXTCommFactory.USB) != 0 && nxtCommUSB != null) {
88 nxtInfos = nxtCommUSB.search(name, protocol);
89 if (nxtInfos.length > 0) {
95 // If not found, look for a Bluetooth one
97 if ((protocol & NXTCommFactory.BLUETOOTH) != 0
98 && nxtCommBluetooth != null) {
99 nxtInfos = nxtCommBluetooth.search(name, protocol);
100 if (nxtInfos.length > 0) {
101 nxtComm = nxtCommBluetooth;
106 return new NXTInfo[0];
109 public void setNXTCommBlueTooth() {
110 if (nxtComm == null) {
111 nxtComm = NXTCommFactory.createNXTComm(NXTCommFactory.BLUETOOTH);
115 public boolean open(NXTInfo nxt) throws NXTCommException {
116 return open = nxtComm.open(nxt);
119 public void setVerify(boolean verify) {
120 verifyCommand = verify;
124 * Small helper method to send DIRECT COMMAND request to NXT and return
125 * verification result.
130 private byte sendRequest(byte[] request, int replyLen) throws IOException {
131 byte verify = 0; // default of 0 means success
133 request[0] = DIRECT_COMMAND_REPLY;
135 byte[] reply = nxtComm.sendRequest(request,
136 (request[0] == DIRECT_COMMAND_REPLY ? replyLen : 0));
137 if (request[0] == DIRECT_COMMAND_REPLY) {
144 * Small helper method to send a SYSTEM COMMAND request to NXT and return
145 * verification result.
150 private byte sendSystemRequest(byte[] request, int replyLen)
152 byte verify = 0; // default of 0 means success
154 request[0] = SYSTEM_COMMAND_REPLY;
156 byte[] reply = nxtComm.sendRequest(request,
157 (request[0] == SYSTEM_COMMAND_REPLY ? replyLen : 0));
158 if (request[0] == SYSTEM_COMMAND_REPLY) {
165 * Starts a program already on the NXT.
170 public byte startProgram(String fileName) throws IOException {
171 byte[] request = { DIRECT_COMMAND_NOREPLY, START_PROGRAM };
172 request = appendString(request, fileName);
173 return sendRequest(request, 22);
177 * Opens a file on the NXT for reading. Returns a handle number and file
178 * size, enclosed in a FileInfo object.
184 public FileInfo openRead(String fileName) throws IOException {
185 byte[] request = { SYSTEM_COMMAND_REPLY, OPEN_READ };
186 request = appendString(request, fileName); // No padding required
188 byte[] reply = nxtComm.sendRequest(request, 8);
189 FileInfo fileInfo = new FileInfo(fileName);
190 fileInfo.status = reply[2];
191 if (reply.length == 8) { // Check if all data included in reply
192 fileInfo.fileHandle = reply[3];
193 fileInfo.fileSize = (0xFF & reply[4]) | ((0xFF & reply[5]) << 8)
194 | ((0xFF & reply[6]) << 16) | ((0xFF & reply[7]) << 24);
200 * Opens a file on the NXT for writing.
204 * @return File Handle number
206 public byte openWrite(String fileName, int size) throws IOException {
207 byte[] command = { SYSTEM_COMMAND_REPLY, OPEN_WRITE };
208 byte[] asciiFileName = new byte[fileName.length()];
209 for (int i = 0; i < fileName.length(); i++)
210 asciiFileName[i] = (byte) fileName.charAt(i);
211 command = appendBytes(command, asciiFileName);
212 byte[] request = new byte[22];
213 System.arraycopy(command, 0, request, 0, command.length);
214 byte[] fileLength = { (byte) size, (byte) (size >>> 8),
215 (byte) (size >>> 16), (byte) (size >>> 24) };
216 request = appendBytes(request, fileLength);
217 byte[] reply = nxtComm.sendRequest(request, 4);
218 if (reply == null || reply.length != 4) {
219 throw new IOException("Invalid return from OPEN WRITE");
220 } else if (reply[2] != 0) {
221 if (reply[2] == (byte) 0xFB) throw new IOException("NXJ Flash Memory Full");
222 else if (reply[2] == (byte) 0xFC) throw new IOException("NXJ Directory Full");
223 else throw new IOException("OPEN WRITE failed");
225 return reply[3]; // The handle number
229 * Closes an open file.
232 * File handle number.
233 * @return Error code 0 = success
235 public byte closeFile(byte handle) throws IOException {
236 byte[] request = { SYSTEM_COMMAND_NOREPLY, CLOSE, handle };
237 return sendSystemRequest(request, 4);
240 public byte delete(String fileName) throws IOException {
241 byte[] request = { SYSTEM_COMMAND_REPLY, DELETE };
242 request = appendString(request, fileName);
243 return sendSystemRequest(request, 23);
248 * [filename].[extension], *.[extension], [filename].*, *.*
251 public FileInfo findFirst(String wildCard) throws IOException {
253 byte[] request = { SYSTEM_COMMAND_REPLY, NXJ_FIND_FIRST };
254 request = appendString(request, wildCard);
256 byte[] reply = nxtComm.sendRequest(request, 32);
257 FileInfo fileInfo = null;
258 if (reply[2] == 0 && reply.length == 32) {
259 StringBuffer name = new StringBuffer(new String(reply))
261 int lastPos = name.indexOf("\0");
262 if (lastPos < 0 || lastPos > 20) lastPos = 20;
263 name.delete(lastPos, name.length());
264 fileInfo = new FileInfo(name.toString());
266 fileInfo.fileHandle = reply[3];
267 fileInfo.fileSize = (0xFF & reply[24]) | ((0xFF & reply[25]) << 8)
268 | ((0xFF & reply[26]) << 16) | ((0xFF & reply[27]) << 24);
269 fileInfo.startPage = (0xFF & reply[28]) | ((0xFF & reply[29]) << 8)
270 | ((0xFF & reply[30]) << 16) | ((0xFF & reply[31]) << 24);
278 * Handle number from the previous found file or fromthe Find
282 public FileInfo findNext(byte handle) throws IOException {
284 byte[] request = { SYSTEM_COMMAND_REPLY, NXJ_FIND_NEXT, handle };
286 byte[] reply = nxtComm.sendRequest(request, 32);
287 FileInfo fileInfo = null;
288 if (reply[2] == 0 && reply.length == 32) {
289 StringBuffer name = new StringBuffer(new String(reply))
291 int lastPos = name.indexOf("\0");
292 if (lastPos < 0 || lastPos > 20) lastPos = 20;
293 name.delete(lastPos, name.length());
294 fileInfo = new FileInfo(name.toString());
296 fileInfo.fileHandle = reply[3];
297 fileInfo.fileSize = (0xFF & reply[24]) | ((0xFF & reply[25]) << 8)
298 | ((0xFF & reply[26]) << 16) | ((0xFF & reply[27]) << 24);
299 fileInfo.startPage = (0xFF & reply[28]) | ((0xFF & reply[29]) << 8)
300 | ((0xFF & reply[30]) << 16) | ((0xFF & reply[31]) << 24);
306 * Helper code to append a string and null terminator at the end of a
307 * command request. Should use String.concat if I could add a zero to end
314 private byte[] appendString(byte[] command, String str) {
315 byte[] buff = new byte[command.length + str.length() + 1];
316 for (int i = 0; i < command.length; i++)
317 buff[i] = command[i];
318 for (int i = 0; i < str.length(); i++)
319 buff[command.length + i] = (byte) str.charAt(i);
320 buff[command.length + str.length()] = 0;
324 private byte[] appendBytes(byte[] array1, byte[] array2) {
325 byte[] array = new byte[array1.length + array2.length];
326 System.arraycopy(array1, 0, array, 0, array1.length);
327 System.arraycopy(array2, 0, array, array1.length, array2.length);
331 public int getBatteryLevel() throws IOException {
332 byte[] request = { DIRECT_COMMAND_REPLY, GET_BATTERY_LEVEL };
333 byte[] reply = nxtComm.sendRequest(request, 5);
334 int batteryLevel = (0xFF & reply[3]) | ((0xFF & reply[4]) << 8);
339 * Call the close() command when your program ends, otherwise you will have
340 * to turn the NXT brick off/on before you run another program.
343 public void close() throws IOException {
347 byte[] request = { SYSTEM_COMMAND_NOREPLY, NXJ_DISCONNECT };
348 nxtComm.sendRequest(request, 0); // Tell NXT to disconnect
352 public byte writeFile(byte handle, byte[] data) throws IOException {
353 byte[] request = new byte[data.length + 3];
354 byte[] command = { SYSTEM_COMMAND_NOREPLY, WRITE, handle };
355 System.arraycopy(command, 0, request, 0, command.length);
356 System.arraycopy(data, 0, request, 3, data.length);
358 return sendSystemRequest(request, 6);
362 * Returns requested number of bytes from a file. File must first be opened
363 * using the openRead() command.
366 * File handle number (from openRead method)
368 * Number of bytes to read.
371 public byte[] readFile(byte handle, int length) throws IOException {
372 byte[] request = { SYSTEM_COMMAND_REPLY, READ, handle, (byte) length,
373 (byte) (length >>> 8) };
374 byte[] reply1 = nxtComm.sendRequest(request, length + 6);
375 int dataLen = (reply1[4] & 0xFF) + ((reply1[5] << 8) & 0xFF);
376 byte[] reply = new byte[dataLen];
377 for (int i = 0; i < dataLen; i++)
378 reply[i] = reply1[i + 6];
382 public byte defrag() throws IOException {
383 byte[] request = { SYSTEM_COMMAND_NOREPLY, NXJ_DEFRAG };
384 return sendSystemRequest(request, 3);
387 public String getFriendlyName() throws IOException {
388 byte[] request = { SYSTEM_COMMAND_REPLY, GET_DEVICE_INFO };
390 byte[] reply = nxtComm.sendRequest(request, 33);
392 char nameChars[] = new char[16];
395 for (int i = 0; i < 15 && reply[i + 3] != 0; i++) {
396 nameChars[i] = (char) reply[i + 3];
400 return new String(nameChars, 0, len);
403 public byte setFriendlyName(String name) throws IOException {
404 byte[] request = { SYSTEM_COMMAND_NOREPLY, SET_BRICK_NAME };
405 request = appendString(request, name);
407 return sendSystemRequest(request, 3);
410 public String getLocalAddress() throws IOException {
411 byte[] request = { SYSTEM_COMMAND_REPLY, GET_DEVICE_INFO };
412 byte[] reply = nxtComm.sendRequest(request, 33);
413 char addrChars[] = new char[14];
415 for (int i = 0; i < 7; i++) {
416 // log("Addr char " + i + " = " + (reply[i+18] &
418 addrChars[i * 2] = hexChars.charAt((reply[i + 18] >> 4) & 0xF);
419 addrChars[i * 2 + 1] = hexChars.charAt(reply[i + 18] & 0xF);
422 return new String(addrChars);
425 public InputValues getInputValues(int port) throws IOException {
426 byte [] request = {DIRECT_COMMAND_REPLY, GET_INPUT_VALUES, (byte)port};
427 byte [] reply = nxtComm.sendRequest(request, 16);
428 InputValues inputValues = new InputValues();
429 inputValues.inputPort = reply[3];
430 // 0 is false, 1 is true.
431 inputValues.valid = (reply[4] != 0);
432 // 0 is false, 1 is true.
433 inputValues.isCalibrated = (reply[5] == 0);
434 inputValues.sensorType = reply[6];
435 inputValues.sensorMode = reply[7];
436 inputValues.rawADValue = (0xFF & reply[8]) | ((0xFF & reply[9]) << 8);
437 inputValues.normalizedADValue = (0xFF & reply[10]) | ((0xFF & reply[11]) << 8);
438 inputValues.scaledValue = (short)((0xFF & reply[12]) | (reply[13] << 8));
439 inputValues.calibratedValue = (short)((0xFF & reply[14]) | (reply[15] << 8));
445 * Retrieves the current output state for a port.
446 * @param port - 0 to 3
447 * @return OutputState - returns a container object for output state variables.
449 public OutputState getOutputState(int port) throws IOException {
450 // !! Needs to check port to verify they are correct ranges.
451 byte [] request = {DIRECT_COMMAND_REPLY, GET_OUTPUT_STATE, (byte)port};
452 byte [] reply = nxtComm.sendRequest(request,25);
454 if(reply[1] != GET_OUTPUT_STATE) {
455 System.out.println("Oops! Error in NXTCommand.getOutputState.");
456 System.out.println("Return data did not match request.");
457 System.out.println("reply[0] = " + reply[0] + " reply[1] = " + reply[1] +" reply[2] = " + reply[2]);
459 OutputState outputState = new OutputState(port);
460 outputState.status = reply[2];
461 outputState.outputPort = reply[3];
462 outputState.powerSetpoint = reply[4];
463 outputState.mode = reply[5];
464 outputState.regulationMode = reply[6];
465 outputState.turnRatio = reply[7];
466 outputState.runState = reply[8];
467 outputState.tachoLimit = (0xFF & reply[9]) | ((0xFF & reply[10]) << 8)| ((0xFF & reply[11]) << 16)| ((0xFF & reply[12]) << 24);
468 outputState.tachoCount = (0xFF & reply[13]) | ((0xFF & reply[14]) << 8)| ((0xFF & reply[15]) << 16)| ((0xFF & reply[16]) << 24);
469 outputState.blockTachoCount = (0xFF & reply[17]) | ((0xFF & reply[18]) << 8)| ((0xFF & reply[19]) << 16)| ((0xFF & reply[20]) << 24);
470 outputState.rotationCount = (0xFF & reply[21]) | ((0xFF & reply[22]) << 8)| ((0xFF & reply[23]) << 16)| ((0xFF & reply[24]) << 24);
475 * @param remoteInbox 0-9
476 * @param localInbox 0-9
477 * @param remove True clears the message from the remote inbox.
480 public byte[] messageRead(byte remoteInbox, byte localInbox, boolean remove) throws IOException {
481 byte [] request = {DIRECT_COMMAND_REPLY, MESSAGE_READ, remoteInbox, localInbox, (remove ? (byte) 1 : (byte) 0)};
482 byte [] reply = nxtComm.sendRequest(request, 64);
483 byte[] message = new byte[reply[4]];
484 System.arraycopy(reply, 5, message, 0, reply[4]);
488 public static NXTCommand getSingleton() {
489 if (singleton == null)
490 singleton = new NXTCommand();
495 * register log listener
499 public void addLogListener(NXTCommLogListener listener) {
500 fLogListeners.add(listener);
504 * unregister log listener
508 public void removeLogListener(NXTCommLogListener listener) {
509 fLogListeners.remove(listener);
512 private void log(String message) {
513 for (NXTCommLogListener listener : fLogListeners) {
514 listener.logEvent(message);
518 private void log(Throwable t) {
519 for (NXTCommLogListener listener : fLogListeners) {
520 listener.logEvent(t);