OSDN Git Service

lejos_NXJ_win32_0_5_0beta.zip
[nxt-jsp/lejos_nxj.git] / nxtOSEK / lejos_nxj / src / java / pccomms / lejos / pc / comm / NXTCommand.java
1 package lejos.pc.comm;
2
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;
9
10 public class NXTCommand implements NXTProtocol {
11
12         private Collection<NXTCommLogListener> fLogListeners;
13
14         private NXTComm nxtComm = null, nxtCommUSB = null, nxtCommBluetooth = null;
15
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
19                         + "nxj.properties";
20         private static NXTCommand singleton = null;
21
22         private boolean verifyCommand = false;
23         private boolean open = false;
24         private static String hexChars = "01234567890abcdef";
25
26         private NXTCommand() {
27                 fLogListeners = new ArrayList<NXTCommLogListener>();
28         }
29
30         public NXTInfo[] search(String name, int protocol) throws NXTCommException {
31                 NXTInfo[] nxtInfos;
32
33                 if (nxtComm == null) {
34                         Properties props = new Properties();
35
36                         try {
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());
42                         }
43
44                         String os = System.getProperty("os.name");
45                         boolean windows = false;
46
47                         if (os.length() >= 7 && os.substring(0, 7).equals("Windows"))
48                                 windows = true;
49
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);
55                                 try {
56                                         Class c = Class.forName(nxtCommName);
57                                         nxtCommUSB = (NXTComm) c.newInstance();
58                                 } catch (Throwable t) {
59                                         log(t);
60                                 }
61                         }
62
63                         // Look for a Bluetooth one
64                         String defaultDriver = (windows ? "lejos.pc.comm.NXTCommBluecove"
65                                         : "lejos.pc.comm.NXTCommBluez");
66
67                         if ((protocol & NXTCommFactory.BLUETOOTH) != 0) {
68                                 String nxtCommName = props.getProperty("NXTCommBluetooth",
69                                                 defaultDriver);
70                                 // log("NXTCommBluetooth = " + nxtCommName);
71                                 try {
72                                         Class c = Class.forName(nxtCommName);
73                                         nxtCommBluetooth = (NXTComm) c.newInstance();
74                                 } catch (Throwable t) {
75                                         log(t);
76                                 }
77                         }
78
79                         if (nxtCommUSB == null && nxtCommBluetooth == null) {
80                                 throw new NXTCommException("Cannot load a comm driver");
81                         }
82
83                 }
84
85                 // Look for a USB one first
86
87                 if ((protocol & NXTCommFactory.USB) != 0 && nxtCommUSB != null) {
88                         nxtInfos = nxtCommUSB.search(name, protocol);
89                         if (nxtInfos.length > 0) {
90                                 nxtComm = nxtCommUSB;
91                                 return nxtInfos;
92                         }
93                 }
94
95                 // If not found, look for a Bluetooth one
96
97                 if ((protocol & NXTCommFactory.BLUETOOTH) != 0
98                                 && nxtCommBluetooth != null) {
99                         nxtInfos = nxtCommBluetooth.search(name, protocol);
100                         if (nxtInfos.length > 0) {
101                                 nxtComm = nxtCommBluetooth;
102                                 return nxtInfos;
103                         }
104                 }
105
106                 return new NXTInfo[0];
107         }
108
109         public void setNXTCommBlueTooth() {
110                 if (nxtComm == null) {
111                         nxtComm = NXTCommFactory.createNXTComm(NXTCommFactory.BLUETOOTH);
112                 }
113         }
114
115         public boolean open(NXTInfo nxt) throws NXTCommException {
116                 return open = nxtComm.open(nxt);
117         }
118
119         public void setVerify(boolean verify) {
120                 verifyCommand = verify;
121         }
122
123         /**
124          * Small helper method to send DIRECT COMMAND request to NXT and return
125          * verification result.
126          * 
127          * @param request
128          * @return
129          */
130         private byte sendRequest(byte[] request, int replyLen) throws IOException {
131                 byte verify = 0; // default of 0 means success
132                 if (verifyCommand)
133                         request[0] = DIRECT_COMMAND_REPLY;
134
135                 byte[] reply = nxtComm.sendRequest(request,
136                                 (request[0] == DIRECT_COMMAND_REPLY ? replyLen : 0));
137                 if (request[0] == DIRECT_COMMAND_REPLY) {
138                         verify = reply[2];
139                 }
140                 return verify;
141         }
142
143         /**
144          * Small helper method to send a SYSTEM COMMAND request to NXT and return
145          * verification result.
146          * 
147          * @param request
148          * @return
149          */
150         private byte sendSystemRequest(byte[] request, int replyLen)
151                         throws IOException {
152                 byte verify = 0; // default of 0 means success
153                 if (verifyCommand)
154                         request[0] = SYSTEM_COMMAND_REPLY;
155
156                 byte[] reply = nxtComm.sendRequest(request,
157                                 (request[0] == SYSTEM_COMMAND_REPLY ? replyLen : 0));
158                 if (request[0] == SYSTEM_COMMAND_REPLY) {
159                         verify = reply[2];
160                 }
161                 return verify;
162         }
163
164         /**
165          * Starts a program already on the NXT.
166          * 
167          * @param fileName
168          * @return
169          */
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);
174         }
175
176         /**
177          * Opens a file on the NXT for reading. Returns a handle number and file
178          * size, enclosed in a FileInfo object.
179          * 
180          * @param fileName
181          *            e.g. "Woops.rso"
182          * @return
183          */
184         public FileInfo openRead(String fileName) throws IOException {
185                 byte[] request = { SYSTEM_COMMAND_REPLY, OPEN_READ };
186                 request = appendString(request, fileName); // No padding required
187                                                                                                         // apparently
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);
195                 }
196                 return fileInfo;
197         }
198
199         /**
200          * Opens a file on the NXT for writing.
201          * 
202          * @param fileName
203          *            e.g. "Woops.rso"
204          * @return File Handle number
205          */
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");
224                 }
225                 return reply[3]; // The handle number
226         }
227
228         /**
229          * Closes an open file.
230          * 
231          * @param handle
232          *            File handle number.
233          * @return Error code 0 = success
234          */
235         public byte closeFile(byte handle) throws IOException {
236                 byte[] request = { SYSTEM_COMMAND_NOREPLY, CLOSE, handle };
237                 return sendSystemRequest(request, 4);
238         }
239
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);
244         }
245
246         /**
247          * @param wildCard
248          *            [filename].[extension], *.[extension], [filename].*, *.*
249          * @return
250          */
251         public FileInfo findFirst(String wildCard) throws IOException {
252
253                 byte[] request = { SYSTEM_COMMAND_REPLY, NXJ_FIND_FIRST };
254                 request = appendString(request, wildCard);
255
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))
260                                         .delete(0, 4);
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());
265                         fileInfo.status = 0;
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);
271
272                 }
273                 return fileInfo;
274         }
275
276         /**
277          * @param handle
278          *            Handle number from the previous found file or fromthe Find
279          *            First command.
280          * @return
281          */
282         public FileInfo findNext(byte handle) throws IOException {
283
284                 byte[] request = { SYSTEM_COMMAND_REPLY, NXJ_FIND_NEXT, handle };
285
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))
290                                         .delete(0, 4);
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());
295                         fileInfo.status = 0;
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);
301                 }
302                 return fileInfo;
303         }
304
305         /**
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
308          * somehow.
309          * 
310          * @param command
311          * @param str
312          * @return
313          */
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;
321                 return buff;
322         }
323
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);
328                 return array;
329         }
330
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);
335                 return batteryLevel;
336         }
337
338         /**
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.
341          * 
342          */
343         public void close() throws IOException {
344                 if (!open)
345                         return;
346                 open = false;
347                 byte[] request = { SYSTEM_COMMAND_NOREPLY, NXJ_DISCONNECT };
348                 nxtComm.sendRequest(request, 0); // Tell NXT to disconnect
349                 nxtComm.close();
350         }
351
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);
357
358                 return sendSystemRequest(request, 6);
359         }
360
361         /**
362          * Returns requested number of bytes from a file. File must first be opened
363          * using the openRead() command.
364          * 
365          * @param handle
366          *            File handle number (from openRead method)
367          * @param length
368          *            Number of bytes to read.
369          * @return
370          */
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];
379                 return reply;
380         }
381
382         public byte defrag() throws IOException {
383                 byte[] request = { SYSTEM_COMMAND_NOREPLY, NXJ_DEFRAG };
384                 return sendSystemRequest(request, 3);
385         }
386
387         public String getFriendlyName() throws IOException {
388                 byte[] request = { SYSTEM_COMMAND_REPLY, GET_DEVICE_INFO };
389
390                 byte[] reply = nxtComm.sendRequest(request, 33);
391
392                 char nameChars[] = new char[16];
393                 int len = 0;
394
395                 for (int i = 0; i < 15 && reply[i + 3] != 0; i++) {
396                         nameChars[i] = (char) reply[i + 3];
397                         len++;
398                 }
399
400                 return new String(nameChars, 0, len);
401         }
402
403         public byte setFriendlyName(String name) throws IOException {
404                 byte[] request = { SYSTEM_COMMAND_NOREPLY, SET_BRICK_NAME };
405                 request = appendString(request, name);
406
407                 return sendSystemRequest(request, 3);
408         }
409
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];
414
415                 for (int i = 0; i < 7; i++) {
416                         // log("Addr char " + i + " = " + (reply[i+18] &
417                         // 0xFF));
418                         addrChars[i * 2] = hexChars.charAt((reply[i + 18] >> 4) & 0xF);
419                         addrChars[i * 2 + 1] = hexChars.charAt(reply[i + 18] & 0xF);
420                 }
421
422                 return new String(addrChars);
423         }
424         
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));
440                 
441                 return inputValues;
442         }
443         
444         /**
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.
448          */
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);
453                 
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]);
458                 }
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);
471                 return outputState;
472         }
473         
474         /**
475          * @param remoteInbox 0-9
476          * @param localInbox 0-9
477          * @param remove True clears the message from the remote inbox.
478          * @return
479          */
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]);
485                 return message;
486         }
487         
488         public static NXTCommand getSingleton() {
489                 if (singleton == null)
490                         singleton = new NXTCommand();
491                 return singleton;
492         }
493
494         /**
495          * register log listener
496          * 
497          * @param listener
498          */
499         public void addLogListener(NXTCommLogListener listener) {
500                 fLogListeners.add(listener);
501         }
502
503         /**
504          * unregister log listener
505          * 
506          * @param listener
507          */
508         public void removeLogListener(NXTCommLogListener listener) {
509                 fLogListeners.remove(listener);
510         }
511
512         private void log(String message) {
513                 for (NXTCommLogListener listener : fLogListeners) {
514                         listener.logEvent(message);
515                 }
516         }
517
518         private void log(Throwable t) {
519                 for (NXTCommLogListener listener : fLogListeners) {
520                         listener.logEvent(t);
521                 }
522         }
523         
524 }