Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / colonet / ColonetGUI / ColonetServerInterface.java @ 420

History | View | Annotate | Download (18.1 KB)

1
/*
2
*        ColonetServerInterface.java
3
*        Gregory Tress
4
*/
5

    
6
import java.net.*;
7
import java.io.*;
8
import javax.swing.JOptionPane;
9
import javax.swing.JTextArea;
10

    
11
/**
12
* The Colonet server interface class. This class handles direct communication with the Colonet server. The general
13
* contract is that this class will encapsulate the process of sending commands and requests to the server and 
14
* receiving responses. This class should provide methods to send specific commands and requests so that the 
15
* Colonet can easily communicate with the server. When data is received from the server, the ColonetServerInterface 
16
* decides how to handle it and in many cases this means simply passing it to the Colonet. 
17
* <p>
18
* The motivation is that any number of Colonet-type classes could be designed for different purposes if needed, with
19
* this class being used by each one for communication. 
20
* For instance, if an application is designed which only monitors sensor data, or that only has access to
21
* the task queue, etc., the application could easily plug in to this interface.
22
* The Colonet should generally not contain communication-specific code or Strings of command and request data. 
23
* If additional commands and requests are needed for permanent implementation, it is suggested that convenience
24
* classes be created in the ColonetServerInterface and called from the Colonet.
25
*
26
* @author Gregory Tress 
27
*/
28
public class ColonetServerInterface 
29
{
30

    
31
        /* STATIC FIELDS
32
         * Eugene Marinelli, Fan
33
         * 10/27/06
34
         *
35
         * Colonet Definitions - common definitions and structs used in all colonet 
36
         * applications
37
        
38
        Old packet structure:
39

40
    COMMAND PACKET STRUCTURE
41
        1:  SEND_TO_ROBOT
42
        2:  # of robot, or GLOBAL_DEST
43
        3:  COLONET_COMMMAND
44
        4:  message code (i.e. ORB_SET)
45
        5:  any data, as many that fit in the packet
46
        
47
        REQUEST PACKET STRUCTURE
48
        1:  REQUEST_FROM_SERVER
49
        2:  # of robot
50
        3:  COLONET_REQUEST
51
        4:  ???
52
  
53
  9/12/07 New server interface structure
54
    Client will no longer send full robot packets to the server.
55
    Commands will be defined as necessary.
56
        */
57
        
58
        //General Colonet Interface
59
        public static final String SEND_TO_ROBOT = "0";
60
        public static final String REQUEST_FROM_SERVER = "1";
61
        public static final String RESPONSE_TO_CLIENT_REQUEST = "2";
62
        public static final String REQUEST_BOM_MATRIX = "144";
63
        public static final String REQUEST_XBEE_IDS = "145";
64
        
65
        public static final String COLONET_COMMAND = "13"; //0x0D
66
        public static final String COLONET_REQUEST = "14"; //0x0E
67
        public static final String CORONET_RESPONSE = "15"; //0x0F
68
        public static final String GLOBAL_DEST = "200";
69
        
70
        //Queue instructions
71
        public static final String COLONET_QUEUE = "100";
72
        public static final String QUEUE_UPDATE = "101"; 
73
        public static final String QUEUE_ADD = "102";
74
        public static final String QUEUE_REMOVE = "103";
75
        public static final String QUEUE_REORDER = "104";
76
        
77
        //Use BATTERY to request battery level
78
        public static final String BATTERY = "56"; //0x38
79
        
80
        //MOTORS
81
        public static final String MOTORS_INIT = "23"; //0x17
82
        public static final String MOTOR1_SET = "24"; //0x18
83
        public static final String MOTOR2_SET = "25"; //0x19
84
        public static final String MOTORS_OFF = "26"; //0x1A
85
        public static final String MOVE = "27"; //0x1B
86
        public static final String MOVE_AVOID = "28"; //0x1C
87
        
88
        //BUZZER
89
        public static final String BUZZER_INIT = "0"; //0x00
90
        public static final String BUZZER_SET_VAL = "1"; //0x01
91
        public static final String BUZZER_SET_FREQ = "2"; //0x02
92
        public static final String BUZZER_CHIRP = "3"; //0x03
93
        public static final String BUZZER_OFF = "4"; //0x04
94

    
95
        //ORB
96
        public static final String ORB_INIT = "12"; //0x0C
97
        public static final String ORB_SET = "13"; //0x0D
98
        public static final String ORB_SET_COLOR = "14"; //0x0E
99
        public static final String ORB_DISABLE = "15"; //0x0F
100
        public static final String ORB_ENABLE = "16"; //0x10
101
        public static final String ORB_SET_DIO = "17"; //0x11
102
        public static final String LED_INIT = "18"; //0x12
103
        public static final String LED_USER = "19"; //0x13
104
        public static final String ORB_SET_NUM_NS = "20"; //0x14
105
        public static final String ORB_SET_NUM = "21"; //0x15
106
        public static final String ORB_SEND = "22"; //0x16
107

    
108
                
109
        Colonet colonet;  //save reference to the entire applet locally
110
        Socket socket;
111
        OutputStreamWriter out;
112
        BufferedReader reader;
113
        DataListener dataListener;
114
        JTextArea log, txtMatrix;
115
        
116
        
117
        /*
118
        *        FUNCTION IMPLEMENTATIONS
119
        */
120

    
121
        /**
122
        * Constructs a new ColonetServerInterface. When constructing a ColonetServerInterface, a valid Colonet object
123
        * reference must be provided to ensure that data is routed correctly. 
124
        *
125
        * @param colonet The Colonet object to save locally. This reference cannot be changed once the 
126
        *                ColonetSreverInterface has been contsructed.
127
        * @throws NullPointerException if colonet is null
128
        *
129
        */
130
        public ColonetServerInterface (Colonet colonet) {
131
                this.colonet = colonet;
132
                this.log = colonet.getLog();
133
                this.txtMatrix = colonet.getMatrixInput();
134
                dataListener = new DataListener();
135
        }
136

    
137
        public Socket getSocket () {
138
                return socket;
139
        }
140
        
141
        public OutputStreamWriter getOutputStreamWriter () {
142
                return out;
143
        }
144
        
145
        public BufferedReader getBufferedReader () {
146
                return reader;
147
        }
148
        
149
        public boolean isReady () {
150
                if (socket == null || out == null || reader == null)
151
                        return false;
152
                if (!socket.isConnected() || socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown())
153
                        return false;
154
                return true;
155
        }
156
        
157
        public boolean isInputReady () {
158
                try {
159
                        if (reader.ready()) return true;
160
                } catch (Exception e) {
161
                        return false;
162
                }
163
                return false;
164
        }
165
        
166
        public String getLine () throws IOException {
167
                return reader.readLine();
168
        }
169
  
170
        /**
171
         * Create socket connection to Colonet server.
172
         * If successful, start thread for listening for incoming data.
173
         */
174
        public void connect (String strHost, String strPort) {
175
                //make sure hostname and port are valid
176
                if (strHost.equals("") || strPort.equals("")) {
177
                        err("Please enter a hostname and port.");
178
                        return;
179
                }
180
                int port = 0;
181
                try {
182
                        port = Integer.parseInt(strPort);
183
                } catch (Exception e) {
184
                        err("Invalid port");
185
                        return;
186
                }
187
                
188
                //make sure we aren't already connected.
189
                if (socket != null && socket.isConnected()) {
190
                        return;
191
                }
192
                
193
                try {
194
                        socket = new Socket(strHost, port);
195
                        socket.setKeepAlive(true);
196
                } catch (UnknownHostException e) {
197
                        log.append("Unknown host exception.\n");
198
                        err("Unknown Host Exception");
199
                        return;
200
                } catch (IOException e) {
201
                        log.append("IO Exception.\n");
202
                        err("Could not create socket to " + strHost + ".\n" + e);
203
                        return;
204
                } catch (java.security.AccessControlException e) {
205
                        log.append("Access Control Exception.\n");
206
                        err("Permission denied by java.security.AccessControlException.\n\n"
207
                                +"You may only connect to the server from which this applet was loaded.");
208
                        return;
209
                }
210
                if (socket == null || !socket.isConnected()) {
211
                        log.append("Connection is not ready. Try connecting again.\n");
212
                        return;
213
                }
214
                try {
215
                        out = new OutputStreamWriter(socket.getOutputStream());
216
                        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
217
                } catch (IOException e) {
218
                        warn("Could not get transfer streams from socket connection.");
219
                }
220
                dataListener.start();
221
        
222
        }
223
        
224
        public void disconnect () {
225
            
226
        }
227
        
228
        /*
229
        *        sendString - only this method should actually write data to the output stream
230
        */
231
        private void sendString (String s) {
232
                //make sure we can send
233
                if (!this.isReady()) {
234
                        log.append("Could not send data.\n");
235
                        return;
236
                }
237
                //send packet
238
                try {
239
                        Thread.sleep(50);  //pause to be safe
240
                        out.write(s);
241
                        out.flush();
242
                        //log.append("Sent: " + s + "\n");
243
                } catch (IOException e) {
244
                        log.setText("Could not send data.\n");
245
                } catch (InterruptedException e) {
246
                        log.setText("Thread InterruptedException in sendData\n");
247
                }
248
        }
249
        
250
        /**
251
        * General send-to-server method. This method is used by other command methods, which are usually convenience
252
        * methods that simply specify arguments to this method. A command consists of a String which holds integers 
253
        * separated by spaces. This method should not be used directly unless you know the format of the particular 
254
        * command you are sending. If implementing a particular command for permanent use, it is recommended that 
255
        * you create a new wrapper method specific to that command in the ColonetServerInterface file.
256
        *
257
        * Note that no checking is performed in this method to ensure the correct formatting of the String arguments.
258
        * If malformed commands or robot numbers are specified, the behavior of the request at the server will be
259
        * undefined and could result in server failure. 
260
        *
261
        * @param s The command String in its correct format. The format of a command String is ultimately specified
262
        * by the Colonet server application and may change. 
263
        * @param robotNumber The number of the robot that is the subject of the command, if any. The robot number 
264
        * is specified as a single integer in a String. If the command does not have a single robot subject, this 
265
        * argument can be null or an empty String, whichever is convenient. 
266
        */
267
        public void sendData (String s, String robotNumber) { 
268
                //create packet
269
                String packet = "";
270
                packet += ColonetServerInterface.SEND_TO_ROBOT;
271
                if (robotNumber != null)
272
                        packet += " " + robotNumber;
273
                packet += " " + ColonetServerInterface.COLONET_COMMAND;
274
                packet += " " + s;  //add  the command code here
275
                packet += "\n";
276
                sendString(packet);
277
        }
278
        
279
        /**
280
        * General request-from-server method. This method is used by other request methods, which are usually convenience
281
        * methods that simply specify arguments to this method. A request consists of a String which holds integers 
282
        * separated by spaces. This method should not be used directly unless you know the format of the particular 
283
        * request you are making. If implementing a particular request, it is recommended that you create a new method
284
        * specific to that request in the ColonetServerInterface file.
285
        *
286
        */
287
        private void sendRequest (String s, String robotNumber) {
288
                //create packet
289
                String packet = "";
290
                packet += ColonetServerInterface.REQUEST_FROM_SERVER;
291
                packet += " " + robotNumber;
292
                //packet += " " + ColonetServerInterface.COLONET_REQUEST;
293
                packet += " " + s;  //add  the command code here
294
                packet += "\n";
295
                sendString(packet);
296
        }
297
        
298
        /** 
299
        * Sends a request to the server to report the entire BOM sensor matrix. The server will reply at its convenience. 
300
        * No guarantee is made (end-to-end or otherwise) that the server will respond in a timely manner or at all
301
        * to any individual request.
302
        */
303
        public void sendSensorDataRequest () {
304
                sendRequest(ColonetServerInterface.REQUEST_BOM_MATRIX, "");
305
        }
306
        
307
        /**
308
        * Sends a request to the server to report a list of XBee IDs. The server will reply at its convenience. 
309
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
310
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment. 
311
        * No guarantee is made (end-to-end or otherwise) that the server will respond in a timely manner or at all
312
        * to any individual request.
313
        *
314
        * @see Colonet#parseXBeeIDs(String)
315
        */
316
        public void sendXBeeIDRequest () {
317
                System.out.println("Sending XBee ID request");
318
                sendRequest(ColonetServerInterface.REQUEST_XBEE_IDS, "");
319
        }
320
        
321
        /**
322
        * Sends a battery request for a specific robot to the server. The server will reply at its convenience. 
323
        * Behavior is undefined if an invalid robot number is specified. The server will probably not respond in 
324
        * this case.
325
        * No guarantee is made (end-to-end or otherwise) that the server will respond in a timely manner or at all
326
        * to any individual request.
327
        *
328
        * @param robotNum The number of the robot for which we are requesting the battery. Note that this value
329
        * is sent as-is to the server. No mapping to or from XBee IDs is performed. The contract for this is
330
        * currently undefined.
331
        * @see Colonet#parseBattery(String)
332
        */
333
        public void sendBatteryRequest (int robotNum) {
334
                sendData(ColonetServerInterface.BATTERY, "" + robotNum);
335
        }
336
        
337
        /*
338
        *        Queue management
339
        */
340
        private void sendQueueInstruction (String inst) {
341
                String packet = "";
342
                packet += ColonetServerInterface.COLONET_QUEUE;
343
                packet += " " + inst;
344
                packet += "\n";
345
                sendString(packet);
346
        }
347
        
348
        /**
349
        * Notifies the Colonet server to add a task to the current task queue. The Colonet server holds the canonical 
350
        * task queue. Any applet can send tasks to add to the queue. Local copies of the queue in the applet are for
351
        * display purposes only, and are not authoritative when adding tasks. All clients send additions asynchronously,
352
        * and as a result no guarantee is made that a task will be added in the specificed location in the queue or at 
353
        * all. If an invalid position is specified, the task will probably not be added to the queue and may cause
354
        * server failure. Due to the asynchronous nature of the queue, we cannot easily account for concurrent
355
        * modification failures on the client side.
356
        *
357
        * @param pos The position in the queue at which we would like the task to be placed. Note that this is not 
358
        * guaranteed. Invalid positions may cause the task to be discarded at the server.
359
        * @param data The String containing the command code(s) for the task and any arguments necessary to fully 
360
        * define the behavior of the task. This format is currently not specified. In the future, the canonical format
361
        * of the data String will ultimately be defined by the Colonet server.
362
        * @param description A String that contains a description of the task. This will be the message displayed to
363
        * the user when information about the task is requested. 
364
        */
365
        public void sendQueueAdd (int pos, String data, String description) {
366
                String packet = "";
367
                packet += ColonetServerInterface.QUEUE_ADD;
368
                packet += " " + pos;
369
                packet += " " + data;
370
                packet += " [" + description + "]";
371
                packet += "\n";
372
                sendQueueInstruction(packet);
373
        }
374
        
375
        /**
376
        * Notifies the Colonet server to remove a task from the current task queue. The Colonet server holds the canonical 
377
        * task queue. Any applet can remove a task from the queue. Local copies of the queue in the applet are for
378
        * display purposes only, and are not authoritative when removing tasks. All clients remove tasks asynchronously,
379
        * and as a result no guarantee is made that the correct task will actually be removed. 
380
        * If an invalid position is specified, the state of the queue is undefined and server failure may result.
381
        * Due to the asynchronous nature of the queue, we cannot easily account for concurrent
382
        * modification failures on the client side.
383
        *
384
        * @param pos The position in the queue at which we would like the task to be removed. Note that this is not 
385
        * guaranteed. Invalid positions may result in a corrupted queue.
386
        */
387
        public void sendQueueRemove (int pos) {
388
                String packet = "";
389
                packet += ColonetServerInterface.QUEUE_REMOVE;
390
                packet += " " + pos;
391
                packet += "\n";
392
                sendQueueInstruction(packet);
393
        }
394

    
395
        /**
396
        * Notifies the Colonet server to reorder tasks in the current task queue. The Colonet server holds the canonical 
397
        * task queue. Any applet can reorder tasks in the queue. Local copies of the queue in the applet are for
398
        * display purposes only, and are not authoritative when reordering tasks. All clients reorder tasks asynchronously,
399
        * and as a result no guarantee is made that the correct tasks will actually be reordered. 
400
        * If an invalid position is specified, the state of the queue is undefined and server failure may result.
401
        * Due to the asynchronous nature of the queue, we cannot easily account for concurrent
402
        * modification failures on the client side.
403
        *
404
        * @param pos1 The queue position of a task which we would like to reorder.
405
        * @param pos2 The queue position of a task which we would like to reorder.
406
        */
407
        public void sendQueueReorder (int pos1, int pos2) {
408
                String packet = "";
409
                packet += ColonetServerInterface.QUEUE_REORDER;
410
                packet += " " + pos1;
411
                packet += " " + pos2;
412
                packet += "\n";
413
                sendQueueInstruction(packet);
414
        }
415

    
416
        public void sendQueueUpdate () {
417
                sendQueueInstruction(ColonetServerInterface.QUEUE_UPDATE);
418
        }
419
        
420
        /**
421
         * Display informational message box on the screen. Used for casual communicaton to the user.
422
         * @param text Text to display
423
         */
424
        public void msg (String text) {
425
                JOptionPane.showMessageDialog(null, text, "Colonet", JOptionPane.INFORMATION_MESSAGE);
426
        }
427
        
428
        /**
429
         * Display warning message box on the screen. Used for minor alerts or exceptions.
430
         * @param text Text to display
431
         */
432
        public void warn (String text) {
433
                JOptionPane.showMessageDialog(null, text, "Colonet", JOptionPane.WARNING_MESSAGE);
434
        }
435
        
436
        /**
437
         * Display error message box on the screen. Used for major errors or exceptions in the program.
438
         * @param text Text to display
439
         */
440
        public void err (String text) {
441
                JOptionPane.showMessageDialog(null, text, "Colonet", JOptionPane.ERROR_MESSAGE);
442
        }
443
        
444
                
445
        /*
446
        *        DataListener thread.
447
        *
448
        */
449
        class DataListener extends Thread {
450
                final int DATALISTENER_DELAY = 222;
451
                
452
                public DataListener () {
453
                        super("Colonet DataListener");
454
                }
455
                
456
                public void run () {
457
                        String line;
458
                        while (true) { 
459
                                try {
460
                                        line = reader.readLine();
461
                                        if (line == null)
462
                                            throw new IOException();
463
                                        parseData(line);
464
                                        Thread.sleep(DATALISTENER_DELAY);
465
                                } catch (InterruptedException e) {
466
                                        return;
467
                                } catch (IOException e) {
468
                                disconnect();
469
                                    colonet.disconnect();
470
                                    return;
471
                                }
472
                        }
473
                }
474
                
475
                public void parseData (String line) {
476
                        // Sensor Matrix
477
                        if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
478
                                ColonetServerInterface.REQUEST_BOM_MATRIX))
479
                                colonet.parseMatrix(line);
480
                        // Task Queue
481
                        else if (line.startsWith(ColonetServerInterface.COLONET_QUEUE))
482
                                colonet.parseQueue(line);
483
                        // XBee IDs
484
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
485
                                ColonetServerInterface.REQUEST_XBEE_IDS))
486
                                colonet.parseXBeeIDs(line);
487
                        // Battery
488
                        else if (line.charAt(0) == Integer.parseInt(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST)
489
                                && line.charAt(2) == Integer.parseInt(ColonetServerInterface.BATTERY))
490
                                colonet.parseBattery(line);
491
                        /*
492
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
493
                                ColonetServerInterface.BATTERY))
494
                                colonet.parseBattery(line);
495
                        */
496
                        // Unknown type
497
                        else
498
                                System.out.println("Got data:" + line);
499
                                
500
                }
501

    
502
        }
503

    
504
}