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.ServerSocket;
7   import java.net.Socket;
8   import java.util.concurrent.ArrayBlockingQueue;
9   import java.util.logging.Level;
10  import java.util.logging.Logger;
11  import org.lsst.ccs.utilities.conv.Convert;
12  
13  /**
14   ***************************************************************************
15   **
16   **  Serves access to a device which uses the FTDI chip
17   **
18   **  @author Owen Saxton
19   **
20   ***************************************************************************
21   */
22  public class FtdiServer extends Thread {
23  
24     /**
25      ***************************************************************************
26      **
27      **  Package constants
28      **
29      ***************************************************************************
30      */
31      final static int
32          SERVER_PORT    = 9001,
33          MAGIC_NUMBER   = 0x71e5f39c,
34  
35          FUNC_EXCEPTION = 0,
36          FUNC_OPEN      = 1,
37          FUNC_CLOSE     = 2,
38          FUNC_READ      = 3,
39          FUNC_WRITE     = 4,
40          FUNC_TIMEOUTS  = 5,
41          FUNC_BAUDRATE  = 6,
42          FUNC_DATACHAR  = 7,
43          FUNC_QUEUESTAT = 8,
44          FUNC_MODEMSTAT = 9,
45  
46          POSN_MAGIC     = 0,
47          POSN_LENGTH    = POSN_MAGIC + 4,
48          POSN_FUNCTION  = POSN_LENGTH + 2,
49          POSN_CONTEXT   = POSN_FUNCTION + 2,
50          POSN_DATA      = POSN_CONTEXT + 2,
51          POSN_EXCPTEXT  = POSN_DATA,
52          POSN_INDEX     = POSN_DATA,
53          POSN_SERIAL    = POSN_INDEX + 4,
54          POSN_READLENG  = POSN_DATA,
55          POSN_READDATA  = POSN_DATA,
56          POSN_WRITEDATA = POSN_DATA,
57          POSN_WRITELENG = POSN_DATA,
58          POSN_RCVETMO   = POSN_DATA,
59          POSN_XMITTMO   = POSN_RCVETMO + 4,
60          POSN_BAUDRATE  = POSN_DATA,
61          POSN_WORDLENG  = POSN_DATA,
62          POSN_STOPBITS  = POSN_WORDLENG + 4,
63          POSN_PARITY    = POSN_STOPBITS + 4,
64          POSN_QUEUESTAT = POSN_DATA,
65          POSN_MODEMSTAT = POSN_DATA;
66  
67     /**
68      ***************************************************************************
69      **
70      **  Private fields
71      **
72      ***************************************************************************
73      */
74      private final static Logger log = Logger.getLogger("ftdiServer");
75      private Socket cliSock;
76      private InputStream cliIn;
77      private OutputStream cliOut;
78      private FtdiLocal ftdi = new FtdiLocal();
79      private ArrayBlockingQueue<byte[]> readQ = new ArrayBlockingQueue(2);
80  
81  
82     /**
83      ***************************************************************************
84      **
85      **  Runs a device reader thread
86      **
87      ***************************************************************************
88      */
89      private class Reader extends Thread {
90  
91          @Override
92          public void run()
93          {
94              IOException excp = null;
95              while (true) {
96                  byte[] rqst;
97                  try {
98                      rqst = readQ.take();
99                  }
100                 catch (InterruptedException e) {
101                     break;
102                 }
103                 try {
104                     int leng = Convert.bytesToInt(rqst, POSN_READLENG);
105                     byte[] data = new byte[leng];
106                     int nread = ftdi.read(data);
107                     byte[] reply = new byte[POSN_READDATA + nread];
108                     System.arraycopy(data, 0, reply, POSN_READDATA, nread);
109                     send(rqst, reply);
110                 }
111                 catch (FtdiException ef) {
112                     try {
113                         sendException(rqst, ef);
114                     }
115                     catch (IOException ei) {
116                         excp = ei;
117                         break;
118                     }
119                 }
120                 catch (IOException ei) {
121                     excp = ei;
122                     break;
123                 }
124             }
125 
126             if (excp != null) {
127                 if (closeNetSilent()) {
128                     log.warning(excp.toString());
129                 }
130                 try {
131                     ftdi.close();
132                 }
133                 catch (FtdiException e) {
134                 }
135             }
136         }
137 
138     }
139 
140 
141    /**
142     ***************************************************************************
143     **
144     **  Constructor
145     **
146     ***************************************************************************
147     */
148     public FtdiServer(Socket sock) throws IOException
149     {
150         cliSock = sock;
151         cliIn = sock.getInputStream();
152         cliOut = sock.getOutputStream();
153     }
154 
155 
156    /**
157     ***************************************************************************
158     **
159     **  Main program
160     **
161     ***************************************************************************
162     */
163     public static void main(String[] args) throws IOException
164     {
165         log.setLevel(Level.INFO);
166         log.info("Started server");
167         ServerSocket sSock = new ServerSocket(SERVER_PORT, 10);
168         while (true) {
169             Socket sock = sSock.accept();
170             FtdiServer srvr = new FtdiServer(sock);
171             srvr.setDaemon(true);
172             srvr.start();
173         }
174     }
175 
176 
177    /**
178     ***************************************************************************
179     **
180     **  Runs a server thread
181     **
182     ***************************************************************************
183     */
184     @Override
185     public void run()
186     {
187         String client = cliSock.getInetAddress().getHostName();
188         int port = cliSock.getPort();
189         log.info("Opened connection to " + client + ":" + port);
190         Reader rdr = new Reader();
191         rdr.setDaemon(true);
192         rdr.start();
193         IOException excp = null;
194         boolean running = true;
195 
196         while (running) {
197             byte rqst[] = null;
198             try {
199                 byte reply[];
200                 rqst = receive();
201                 int rLeng = Convert.bytesToShort(rqst, POSN_LENGTH);
202                 int func = Convert.bytesToShort(rqst, POSN_FUNCTION);
203 
204                 switch (func) {
205 
206                 case FUNC_OPEN:
207                     int index = Convert.bytesToInt(rqst, POSN_INDEX);
208                     String serial = null;
209                     if (rLeng > POSN_SERIAL) {
210                         serial = new String(rqst, POSN_SERIAL,
211                                             rLeng - POSN_SERIAL);
212                     }
213                     ftdi.open(index, serial);
214                     sendAck(rqst);
215                     break;
216 
217                 case FUNC_CLOSE:
218                     ftdi.close();
219                     closeNet();
220                     running = false;
221                     break;
222 
223                 case FUNC_READ:
224                     readQ.offer(rqst);
225                     break;
226 
227                 case FUNC_WRITE:
228                     int nwrite = ftdi.write(rqst, POSN_WRITEDATA,
229                                             rLeng - POSN_WRITEDATA);
230                     reply = new byte[POSN_WRITELENG + 4];
231                     Convert.intToBytes(nwrite, reply, POSN_WRITELENG);
232                     send(rqst, reply);
233                     break;
234 
235                 case FUNC_TIMEOUTS:
236                     int rcveTmo = Convert.bytesToInt(rqst, POSN_RCVETMO);
237                     int xmitTmo = Convert.bytesToInt(rqst, POSN_XMITTMO);
238                     ftdi.setTimeouts(rcveTmo, xmitTmo);
239                     sendAck(rqst);
240                     break;
241 
242                 case FUNC_BAUDRATE:
243                     int baudrate = Convert.bytesToInt(rqst, POSN_BAUDRATE);
244                     ftdi.setBaudrate(baudrate);
245                     sendAck(rqst);
246                     break;
247 
248                 case FUNC_DATACHAR:
249                     int wordleng = Convert.bytesToInt(rqst, POSN_WORDLENG);
250                     int stopbits = Convert.bytesToInt(rqst, POSN_STOPBITS);
251                     int parity = Convert.bytesToInt(rqst, POSN_PARITY);
252                     ftdi.setDataCharacteristics(wordleng, stopbits, parity);
253                     sendAck(rqst);
254                     break;
255 
256                 case FUNC_QUEUESTAT:
257                     reply = new byte[POSN_QUEUESTAT + 4];
258                     Convert.intToBytes(ftdi.getQueueStatus(), reply,
259                                        POSN_QUEUESTAT);
260                     send(rqst, reply);
261                     break;
262 
263                 case FUNC_MODEMSTAT:
264                     reply = new byte[POSN_MODEMSTAT + 4];
265                     Convert.intToBytes(ftdi.getModemStatus(), reply,
266                                        POSN_MODEMSTAT);
267                     send(rqst, reply);
268                     break;
269 
270                 default:
271                 }
272             }
273             catch (FtdiException ef) {
274                 try {
275                     sendException(rqst, ef);
276                 }
277                 catch (IOException ei) {
278                     excp = ei;
279                     running = false;
280                 }
281             }
282             catch (IOException ei) {
283                 excp = ei;
284                 running = false;
285             }
286         }
287 
288         if (excp != null) {
289             if (closeNetSilent()) {
290                 log.warning(excp.toString());
291             }
292             try {
293                 ftdi.close();
294             }
295             catch (FtdiException ef) {
296             }
297         }
298 
299         rdr.interrupt();
300         log.info("Closed connection to " + client + ":" + port);
301     }
302 
303 
304    /**
305     ***************************************************************************
306     **
307     **  Receives a request from the client
308     **
309     ***************************************************************************
310     */
311     private byte[] receive() throws IOException
312     {
313         byte[] header = new byte[POSN_DATA];
314         int leng = 0, recLeng = header.length;
315         while (leng < recLeng) {
316             int nread = cliIn.read(header, leng, recLeng - leng);
317             if (nread < 0) break;
318             leng += nread;
319         }
320         if (leng < recLeng) {
321             throw new IOException("Client disconnected");
322         }
323         if (Convert.bytesToInt(header, POSN_MAGIC) != MAGIC_NUMBER) {
324             throw new IOException("Invalid magic number");
325         }
326         recLeng = Convert.bytesToShort(header, POSN_LENGTH);
327         byte[] rqst = new byte[recLeng];
328         System.arraycopy(header, 0, rqst, 0, leng);
329         while (leng < recLeng) {
330             int nread = cliIn.read(rqst, leng, recLeng - leng);
331             if (nread < 0) break;
332             leng += nread;
333         }
334         if (leng < recLeng) {
335             throw new IOException("Client disconnected");
336         }
337 
338         return rqst;
339     }
340 
341 
342    /**
343     ***************************************************************************
344     **
345     **  Sends a reply to the client
346     **
347     ***************************************************************************
348     */
349     private void send(byte[] rqst, byte[] reply) throws IOException
350     {
351         Convert.intToBytes(MAGIC_NUMBER, reply, POSN_MAGIC);
352         Convert.shortToBytes((short)reply.length, reply, POSN_LENGTH);
353         System.arraycopy(rqst, POSN_FUNCTION, reply, POSN_FUNCTION, 2);
354         System.arraycopy(rqst, POSN_CONTEXT, reply, POSN_CONTEXT, 2);
355         cliOut.write(reply);
356     }
357 
358 
359    /**
360     ***************************************************************************
361     **
362     **  Sends an acknowledgment to the client
363     **
364     ***************************************************************************
365     */
366     private void sendAck(byte[] rqst) throws IOException
367     {
368         byte[] reply = new byte[POSN_DATA];
369         send(rqst, reply);
370     }
371 
372 
373    /**
374     ***************************************************************************
375     **
376     **  Sends an exception response to the client
377     **
378     ***************************************************************************
379     */
380     private void sendException(byte[] rqst, Exception e) throws IOException
381     {
382         if (rqst == null) return;
383         byte[] text = e.getMessage().getBytes();
384         byte[] reply = new byte[POSN_EXCPTEXT + text.length];
385         System.arraycopy(text, 0, reply, POSN_EXCPTEXT, text.length);
386         Convert.shortToBytes((short)FUNC_EXCEPTION, rqst, POSN_FUNCTION);
387         send(rqst, reply);
388     }
389 
390 
391    /**
392     ***************************************************************************
393     **
394     **  Closes the network connections silently
395     **
396     ***************************************************************************
397     */
398     private boolean closeNetSilent()
399     {
400         boolean wasOpen = true;
401 
402         try {
403             wasOpen = closeNet();
404         }
405         catch (IOException e) {
406         }
407 
408         return wasOpen;
409     }
410 
411 
412    /**
413     ***************************************************************************
414     **
415     **  Closes the network connections
416     **
417     ***************************************************************************
418     */
419     private boolean closeNet() throws IOException
420     {
421         boolean wasOpen = false;
422         try {
423             if (cliSock != null) {
424                 wasOpen = true;
425                 cliSock.close();
426             }
427         }
428         finally {
429             cliSock = null;
430         }
431 
432         return wasOpen;
433     }
434 
435 }