Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / colonet / client / ColonetServerInterface.java @ 517

History | View | Annotate | Download (19.2 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
        public static final String CLIENT_REQUEST_ROBOT_POSITIONS = "86";
70
        public static final String CLIENT_ASSIGN_ROBOT_ID = "87";
71
        public static final String MOVE_TO_ABSOLUTE_POSITION = "83"; //0x53
72
        
73
        //Queue instructions
74
        public static final String COLONET_QUEUE = "100";
75
        public static final String QUEUE_UPDATE = "101"; 
76
        public static final String QUEUE_ADD = "102";
77
        public static final String QUEUE_REMOVE = "103";
78
        public static final String QUEUE_REORDER = "104";
79
        
80
        //Use BATTERY to request battery level
81
        public static final String BATTERY = "56"; //0x38
82
        
83
        //MOTORS
84
        public static final String MOTORS_INIT = "23"; //0x17
85
        public static final String MOTOR1_SET = "24"; //0x18
86
        public static final String MOTOR2_SET = "25"; //0x19
87
        public static final String MOTORS_OFF = "26"; //0x1A
88
        public static final String MOVE = "27"; //0x1B
89
        public static final String MOVE_AVOID = "28"; //0x1C
90
        
91
        //BUZZER
92
        public static final String BUZZER_INIT = "0"; //0x00
93
        public static final String BUZZER_SET_VAL = "1"; //0x01
94
        public static final String BUZZER_SET_FREQ = "2"; //0x02
95
        public static final String BUZZER_CHIRP = "3"; //0x03
96
        public static final String BUZZER_OFF = "4"; //0x04
97

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

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

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

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

    
434
        /**
435
        * Notifies the Colonet server to reorder tasks in the current task queue. The Colonet server holds the canonical 
436
        * task queue. Any applet can reorder tasks in the queue. Local copies of the queue in the applet are for
437
        * display purposes only, and are not authoritative when reordering tasks. All clients reorder tasks asynchronously,
438
        * and as a result no guarantee is made that the correct tasks will actually be reordered. 
439
        * If an invalid position is specified, the state of the queue is undefined and server failure may result.
440
        * Due to the asynchronous nature of the queue, we cannot easily account for concurrent
441
        * modification failures on the client side.
442
        *
443
        * @param pos1 The queue position of a task which we would like to reorder.
444
        * @param pos2 The queue position of a task which we would like to reorder.
445
        */
446
        public void sendQueueReorder (int pos1, int pos2) {
447
                String packet = "";
448
                packet += ColonetServerInterface.QUEUE_REORDER;
449
                packet += " " + pos1;
450
                packet += " " + pos2;
451
                packet += "\n";
452
                sendQueueInstruction(packet);
453
        }
454

    
455
        public void sendQueueUpdate () {
456
                sendQueueInstruction(ColonetServerInterface.QUEUE_UPDATE);
457
        }
458
        
459
        /**
460
         * Display informational message box on the screen. Used for casual communicaton to the user.
461
         * @param text Text to display
462
         */
463
        public void msg (String text) {
464
                JOptionPane.showMessageDialog(colonet, text, "Colonet", JOptionPane.INFORMATION_MESSAGE);
465
        }
466
        
467
        /**
468
         * Display warning message box on the screen. Used for minor alerts or exceptions.
469
         * @param text Text to display
470
         */
471
        public void warn (String text) {
472
                JOptionPane.showMessageDialog(colonet, text, "Colonet", JOptionPane.WARNING_MESSAGE);
473
        }
474
        
475
        /**
476
         * Display error message box on the screen. Used for major errors or exceptions in the program.
477
         * @param text Text to display
478
         */
479
        public void err (String text) {
480
                JOptionPane.showMessageDialog(colonet, text, "Colonet", JOptionPane.ERROR_MESSAGE);
481
        }
482
        
483
                
484
        /*
485
        *        DataListener thread.
486
        *
487
        */
488
        class DataListener extends Thread {
489
                final int DATALISTENER_DELAY = 222;
490
                
491
                public DataListener () {
492
                        super("Colonet DataListener");
493
                }
494
                
495
                public void run () {
496
                        String line;
497
                        while (true) { 
498
                                try {
499
                                        line = reader.readLine();
500
                                        if (line == null)
501
                                            throw new IOException();
502
                                        parseData(line);
503
                                        Thread.sleep(DATALISTENER_DELAY);
504
                                } catch (InterruptedException e) {
505
                                        return;
506
                                } catch (IOException e) {
507
                                disconnect();
508
                                    colonet.disconnect();
509
                                    return;
510
                                }
511
                        }
512
                }
513
                
514
                public void parseData (String line) {
515
                        // Sensor Matrix
516
                        if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
517
                                ColonetServerInterface.REQUEST_BOM_MATRIX))
518
                                colonet.parseMatrix(line);
519
                        // Task Queue
520
                        else if (line.startsWith(ColonetServerInterface.COLONET_QUEUE))
521
                                colonet.parseQueue(line);
522
                        // XBee IDs
523
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
524
                                ColonetServerInterface.REQUEST_XBEE_IDS))
525
                                colonet.parseXBeeIDs(line);
526
                        // Battery
527
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
528
                                ColonetServerInterface.BATTERY))
529
                                colonet.parseBattery(line);
530
                        // Robot Positions
531
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
532
                                ColonetServerInterface.CLIENT_REQUEST_ROBOT_POSITIONS))
533
                                colonet.parsePositions(line);
534
                        // Unknown type
535
                        else {
536
                                //txtMatrix.setText("Got unknown data: " + line + "\n");
537
                        }
538
                }
539

    
540
        }
541

    
542
}