View Javadoc

1   package org.lsst.ccs.drivers.ftdi;
2   
3   import java.io.IOException;
4   import java.io.InputStream;
5   import java.io.OutputStream;
6   import java.net.Socket;
7   import java.util.HashMap;
8   import java.util.Iterator;
9   import java.util.concurrent.ArrayBlockingQueue;
10  import org.lsst.ccs.utilities.conv.Convert;
11  
12  /**
13   ***************************************************************************
14   **
15   **  Remotely accesses a device which uses the FTDI chip.
16   **
17   **  @author Owen Saxton
18   **
19   ***************************************************************************
20   */
21  public class FtdiClient implements FtdiInterface {
22  
23     /**
24      ***************************************************************************
25      **
26      **  Private fields.
27      **
28      ***************************************************************************
29      */
30      private Socket srvSock;
31      private InputStream srvIn;
32      private OutputStream srvOut;
33      private HashMap<Integer, ArrayBlockingQueue> queueMap = new HashMap();
34      private ThreadLocal<Integer> threadId;
35      private ThreadLocal<ArrayBlockingQueue> threadQueue;
36      private int currThreadId = 0;
37  
38  
39     /**
40      ***************************************************************************
41      **
42      **  Inner class implementing a network reading thread.
43      **
44      ***************************************************************************
45      */
46      private class Reader extends Thread {
47  
48          @Override
49          public void run()
50          {
51              FtdiException excp = null;
52              while (true) {
53                  try {
54                      byte[] header = new byte[FtdiServer.POSN_DATA];
55                      int leng = 0, recLeng = header.length;
56                      while (leng < recLeng) {
57                          int nread = srvIn.read(header, leng, recLeng - leng);
58                          if (nread < 0) break;
59                          leng += nread;
60                      }
61                      if (leng < recLeng) {
62                          throw new FtdiException("Server disconnected");
63                      }
64                      if (Convert.bytesToInt(header, FtdiServer.POSN_MAGIC)
65                            != FtdiServer.MAGIC_NUMBER) {
66                          System.out.print("Header ="); 
67                          for (int j = 0; j < header.length; j++) {
68                              System.out.format(" %02x", header[j]);
69                          }
70                          System.out.println();
71                          throw new FtdiException("Invalid magic number");
72                      }
73                      recLeng = Convert.bytesToShort(header,
74                                                     FtdiServer.POSN_LENGTH);
75                      byte[] reply = new byte[recLeng];
76                      System.arraycopy(header, 0, reply, 0, leng);
77                      while (leng < recLeng) {
78                          int nread = srvIn.read(reply, leng, recLeng - leng);
79                          if (nread < 0) break;
80                          leng += nread;
81                      }
82                      if (leng < recLeng) {
83                          throw new FtdiException("Server disconnected");
84                      }
85                      int context = Convert.bytesToShort(reply,
86                                                         FtdiServer.POSN_CONTEXT);
87                      queueMap.get(context).offer(reply);
88                  }
89                  catch (FtdiException e) {
90                      excp = e;
91                      closeNetSilent();
92                      break;
93                  }
94                  catch (IOException e) {
95                      if (closeNetSilent()) {
96                          excp = new FtdiException(e);
97                      }
98                      break;
99                  }
100             }
101             if (excp != null) {
102                 Iterator queues = queueMap.values().iterator();
103                 while (queues.hasNext()) {
104                     ((ArrayBlockingQueue)queues.next()).offer(excp);
105                 }
106             }
107         }
108 
109     }
110 
111 
112    /**
113     ***************************************************************************
114     **
115     **  Opens a local device.
116     **
117     **  This is an invalid operation.
118     **
119     **  @param  index   The zero-based index of the FTDI device within the
120     **                  list selected by the serial argument.
121     **
122     **  @param  serial  A string which, if non-null and non-empty, restricts
123     **                  the list of available devices to those with a serial
124     **                  number containing this string.
125     **
126     **  @throws FtdiException
127     **
128     ***************************************************************************
129     */
130     @Override
131     public void open(int index, String serial) throws FtdiException
132     {
133         throw new FtdiException("Invalid local open call");
134     }
135 
136 
137    /**
138     ***************************************************************************
139     **
140     **  Opens a remote device.
141     **
142     **  @param  node    The name of the node where the device is located.
143     **
144     **  @param  index   The zero-based index of the FTDI device within the
145     **                  list selected by the serial argument.
146     **
147     **  @param  serial  A string which, if non-null and non-empty, restricts
148     **                  the list of available devices to those with a serial
149     **                  number containing this string.
150     **
151     **  @throws FtdiException
152     **
153     ***************************************************************************
154     */
155     @Override
156     public void open(String node, int index, String serial)
157         throws FtdiException
158     {
159         try {
160             srvSock = new Socket(node, FtdiServer.SERVER_PORT);
161             srvIn = srvSock.getInputStream();
162             srvOut = srvSock.getOutputStream();
163         }
164         catch (IOException e) {
165             throw new FtdiException(e);
166         }
167         queueMap.clear();
168         threadId = new ThreadLocal();
169         threadQueue = new ThreadLocal();
170         Reader rdr = new Reader();
171         rdr.setDaemon(true);
172         rdr.start();
173         byte[] bSerial = serial == null ? new byte[0] : serial.getBytes();
174         int lSerial = bSerial.length;
175         byte[] rqst = new byte[FtdiServer.POSN_SERIAL + lSerial];
176         Convert.intToBytes(index, rqst, FtdiServer.POSN_INDEX);
177         System.arraycopy(bSerial, 0, rqst, FtdiServer.POSN_SERIAL, lSerial);
178         try {
179             send(FtdiServer.FUNC_OPEN, rqst);
180             receive();
181         }
182         catch (FtdiException e) {
183             closeNetSilent();
184             throw e;
185         }
186     }
187 
188 
189    /**
190     ***************************************************************************
191     **
192     **  Closes the device.
193     **
194     **  @throws FtdiException
195     **
196     ***************************************************************************
197     */
198     @Override
199     public void close() throws FtdiException
200     {
201         byte[] rqst = new byte[FtdiServer.POSN_DATA];
202         send(FtdiServer.FUNC_CLOSE, rqst);
203         closeNet();
204     }
205 
206 
207    /**
208     ***************************************************************************
209     **
210     **  Sets the baud rate.
211     **
212     **  @param  baudrate  The baud rate ro set
213     **
214     **  @throws FtdiException
215     **
216     ***************************************************************************
217     */
218     @Override
219     public void setBaudrate(int baudrate) throws FtdiException
220     {
221         byte[] rqst = new byte[FtdiServer.POSN_BAUDRATE + 4];
222         Convert.intToBytes(baudrate, rqst, FtdiServer.POSN_BAUDRATE);
223         send(FtdiServer.FUNC_BAUDRATE, rqst);
224         receive();
225     }
226 
227 
228    /**
229     ***************************************************************************
230     **
231     **  Sets the data characteristics.
232     **
233     **  @param  wordLength  The encoded word length to set
234     **
235     **  @param  stopBits    The encoded number of stop bits to set
236     **
237     **  @param  parity      The encoded parity value to set
238     **
239     **  @throws FtdiException
240     **
241     ***************************************************************************
242     */
243     @Override
244     public void setDataCharacteristics(int wordLength, int stopBits,
245                                        int parity)
246         throws FtdiException
247     {
248         byte[] rqst = new byte[FtdiServer.POSN_PARITY + 4];
249         Convert.intToBytes(wordLength, rqst, FtdiServer.POSN_WORDLENG);
250         Convert.intToBytes(stopBits, rqst, FtdiServer.POSN_STOPBITS);
251         Convert.intToBytes(parity, rqst, FtdiServer.POSN_PARITY);
252         send(FtdiServer.FUNC_DATACHAR, rqst);
253         receive();
254     }
255 
256 
257    /**
258     ***************************************************************************
259     **
260     **  Sets the timeouts.
261     **
262     **  @param  rcveTimeout  The receive timeout to set (ms)
263     **
264     **  @param  xmitTimeout  The transmit timeout to set (ms)
265     **
266     **  @throws FtdiException
267     **
268     ***************************************************************************
269     */
270     @Override
271     public void setTimeouts(int rcveTimeout, int xmitTimeout)
272         throws FtdiException
273     {
274         byte[] rqst = new byte[FtdiServer.POSN_XMITTMO + 4];
275         Convert.intToBytes(rcveTimeout, rqst, FtdiServer.POSN_RCVETMO);
276         Convert.intToBytes(xmitTimeout, rqst, FtdiServer.POSN_XMITTMO);
277         send(FtdiServer.FUNC_TIMEOUTS, rqst);
278         receive();
279     }
280 
281 
282    /**
283     ***************************************************************************
284     **
285     **  Reads data.
286     **
287     **  Execution is blocked until either the byte array is filled, or a
288     **  timeout occurs.  In the latter case the number of bytes read will be
289     **  less than the array size.
290     **
291     **  @param  data  A byte array to receive the read data.
292     **
293     **  @return  The number of bytes read.
294     **
295     **  @throws FtdiException
296     **
297     ***************************************************************************
298     */
299     @Override
300     public int read(byte[] data) throws FtdiException
301     {
302         return read(data, 0, data.length);
303     }
304 
305 
306    /**
307     ***************************************************************************
308     **
309     **  Reads data.
310     **
311     **  Execution is blocked until either the requested number of bytes has
312     **  been read, or a timeout occurs.  In the latter case the number of
313     **  bytes read will be less than the requested number.
314     **
315     **  @param  data    A byte array to receive the read data
316     **
317     **  @param  offset  The offset in the array to the start of the data
318     **
319     **  @param  count   The maximum number of bytes to read
320     **
321     **  @return  The number of bytes read
322     **
323     **  @throws FtdiException
324     **
325     ***************************************************************************
326     */
327     @Override
328     public int read(byte[] data, int offset, int count) throws FtdiException
329     {
330         byte[] rqst = new byte[FtdiServer.POSN_READLENG + 4];
331         Convert.intToBytes(count, rqst, FtdiServer.POSN_READLENG);
332         send(FtdiServer.FUNC_READ, rqst);
333         byte[] reply = receive();
334         int nread = reply.length - FtdiServer.POSN_READDATA;
335         System.arraycopy(reply, FtdiServer.POSN_READDATA, data, offset, nread);
336 
337         return nread;
338     }
339 
340 
341    /**
342     ***************************************************************************
343     **
344     **  Writes data.
345     **
346     **  @param  data  A byte array containing the data to write.
347     **
348     **  @return  The number of bytes written.
349     **
350     **  @throws FtdiException
351     **
352     ***************************************************************************
353     */
354     @Override
355     public int write(byte[] data) throws FtdiException
356     {
357         return write(data, 0, data.length);
358     }
359 
360 
361    /**
362     ***************************************************************************
363     **
364     **  Writes data.
365     **
366     **  @param  data    A byte array containing the data to write
367     **
368     **  @param  offset  The offset in the array to the start of the data
369     **
370     **  @param  count   The number of bytes to write
371     **
372     **  @return  The number of bytes written
373     **
374     **  @throws FtdiException
375     **
376     ***************************************************************************
377     */
378     @Override
379     public int write(byte[] data, int offset, int count) throws FtdiException
380     {
381         byte[] rqst = new byte[FtdiServer.POSN_WRITEDATA + count];
382         System.arraycopy(data, offset, rqst, FtdiServer.POSN_WRITEDATA, count);
383         send(FtdiServer.FUNC_WRITE, rqst);
384 
385         return Convert.bytesToInt(receive(), FtdiServer.POSN_WRITELENG);
386     }
387 
388 
389    /**
390     ***************************************************************************
391     **
392     **  Gets the read queue status.
393     **
394     **  This is the number of bytes available for immediate read
395     **
396     **  @return  The number of bytes in the read queue
397     **
398     **  @throws FtdiException
399     **
400     ***************************************************************************
401     */
402     @Override
403     public int getQueueStatus() throws FtdiException
404     {
405         byte[] rqst = new byte[FtdiServer.POSN_DATA];
406         send(FtdiServer.FUNC_QUEUESTAT, rqst);
407 
408         return Convert.bytesToInt(receive(), FtdiServer.POSN_QUEUESTAT);
409     }
410 
411 
412    /**
413     ***************************************************************************
414     **
415     **  Gets the modem status.
416     **
417     **  @return  The modem status as a set of bits
418     **
419     **  @throws FtdiException
420     **
421     ***************************************************************************
422     */
423     @Override
424     public int getModemStatus() throws FtdiException
425     {
426         byte[] rqst = new byte[FtdiServer.POSN_DATA];
427         send(FtdiServer.FUNC_MODEMSTAT, rqst);
428 
429         return Convert.bytesToInt(receive(), FtdiServer.POSN_MODEMSTAT);
430     }
431 
432 
433    /**
434     ***************************************************************************
435     **
436     **  Sends a request to the server.
437     **
438     **  @param  rqst  A byte array containing the request.
439     **
440     **  @throws FtdiException
441     **
442     ***************************************************************************
443     */
444     private void send(int function, byte[] rqst) throws FtdiException
445     {
446         int context;
447         Integer id = threadId.get();
448         if (id == null) {
449             context = currThreadId++;
450             threadId.set(context);
451             threadQueue.set(new ArrayBlockingQueue(1));
452             queueMap.put(context, threadQueue.get());
453         }
454         else {
455             context = id;
456         }
457         Convert.intToBytes(FtdiServer.MAGIC_NUMBER, rqst,
458                            FtdiServer.POSN_MAGIC);
459         Convert.shortToBytes((short)rqst.length, rqst, FtdiServer.POSN_LENGTH);
460         Convert.shortToBytes((short)function, rqst, FtdiServer.POSN_FUNCTION);
461         Convert.shortToBytes((short)context, rqst, FtdiServer.POSN_CONTEXT);
462         try {
463             srvOut.write(rqst);
464         }
465         catch (IOException e) {
466             throw new FtdiException(e);
467         }
468     }
469 
470 
471    /**
472     ***************************************************************************
473     **
474     **  Receives a reply from the server.
475     **
476     **  @return  A byte array containing the reply.
477     **
478     **  @throws FtdiException
479     **
480     ***************************************************************************
481     */
482     private byte[] receive() throws FtdiException
483     {
484         Object replyObj = null;
485         try {
486             replyObj = threadQueue.get().take();
487         }
488         catch (InterruptedException e) {
489         }
490         if (replyObj instanceof FtdiException) {
491             throw (FtdiException)replyObj;
492         }
493         byte[] reply = (byte[])replyObj;
494         if (Convert.bytesToShort(reply, FtdiServer.POSN_FUNCTION)
495               == FtdiServer.FUNC_EXCEPTION) {
496             String text = new String(reply, FtdiServer.POSN_EXCPTEXT,
497                                      reply.length - FtdiServer.POSN_EXCPTEXT);
498             throw new FtdiException(text);
499         }
500 
501         return reply;
502     }
503 
504 
505    /**
506     ***************************************************************************
507     **
508     **  Closes the network connection silently.
509     **
510     **  @return  Whether the connection was open
511     **
512     ***************************************************************************
513     */
514     private boolean closeNetSilent()
515     {
516         boolean wasOpen = true;
517 
518         try {
519             wasOpen = closeNet();
520         }
521         catch (FtdiException e) {
522         }
523 
524         return wasOpen;
525     }
526 
527 
528    /**
529     ***************************************************************************
530     **
531     **  Closes the network connection.
532     **
533     **  @return  Whether the connection was open
534     **
535     **  @throws FtdiException
536     **
537     ***************************************************************************
538     */
539     private boolean closeNet() throws FtdiException
540     {
541         boolean wasOpen = false;
542         Exception ei = null;
543 
544         try {
545             if (srvSock != null) {
546                 wasOpen = true;
547                 srvSock.close();
548             }
549         }
550         catch (IOException e) {
551             ei = e;
552         }
553         srvSock = null;
554 
555         if (ei != null) {
556             throw new FtdiException(ei);
557         }
558 
559         return wasOpen;
560     }
561 
562 }