Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (18.8 KB)

1 32 gtress
/*
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 333 gtress
/**
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 32 gtress
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 67 gtress

38
        Old packet structure:
39 38 gtress

40 107 gtress
    COMMAND PACKET STRUCTURE
41 32 gtress
        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 67 gtress
        1:  REQUEST_FROM_SERVER
49 32 gtress
        2:  # of robot
50
        3:  COLONET_REQUEST
51
        4:  ???
52 38 gtress

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 32 gtress
        */
57 38 gtress
58 76 gtress
        //General Colonet Interface
59 32 gtress
        public static final String SEND_TO_ROBOT = "0";
60 67 gtress
        public static final String REQUEST_FROM_SERVER = "1";
61 32 gtress
        public static final String RESPONSE_TO_CLIENT_REQUEST = "2";
62 76 gtress
        public static final String REQUEST_BOM_MATRIX = "144";
63 155 gtress
        public static final String REQUEST_XBEE_IDS = "145";
64 76 gtress
65 32 gtress
        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 76 gtress
        public static final String GLOBAL_DEST = "200";
69 459 gtress
        public static final String CLIENT_REQUEST_ROBOT_POSITIONS = "86";
70
        public static final String CLIENT_ASSIGN_ROBOT_ID = "87";
71 32 gtress
72 107 gtress
        //Queue instructions
73
        public static final String COLONET_QUEUE = "100";
74
        public static final String QUEUE_UPDATE = "101";
75
        public static final String QUEUE_ADD = "102";
76
        public static final String QUEUE_REMOVE = "103";
77
        public static final String QUEUE_REORDER = "104";
78
79 32 gtress
        //Use BATTERY to request battery level
80
        public static final String BATTERY = "56"; //0x38
81
82
        //MOTORS
83
        public static final String MOTORS_INIT = "23"; //0x17
84
        public static final String MOTOR1_SET = "24"; //0x18
85
        public static final String MOTOR2_SET = "25"; //0x19
86
        public static final String MOTORS_OFF = "26"; //0x1A
87
        public static final String MOVE = "27"; //0x1B
88 39 gtress
        public static final String MOVE_AVOID = "28"; //0x1C
89 32 gtress
90
        //BUZZER
91
        public static final String BUZZER_INIT = "0"; //0x00
92
        public static final String BUZZER_SET_VAL = "1"; //0x01
93
        public static final String BUZZER_SET_FREQ = "2"; //0x02
94
        public static final String BUZZER_CHIRP = "3"; //0x03
95
        public static final String BUZZER_OFF = "4"; //0x04
96
97
        //ORB
98
        public static final String ORB_INIT = "12"; //0x0C
99
        public static final String ORB_SET = "13"; //0x0D
100
        public static final String ORB_SET_COLOR = "14"; //0x0E
101
        public static final String ORB_DISABLE = "15"; //0x0F
102
        public static final String ORB_ENABLE = "16"; //0x10
103
        public static final String ORB_SET_DIO = "17"; //0x11
104
        public static final String LED_INIT = "18"; //0x12
105
        public static final String LED_USER = "19"; //0x13
106
        public static final String ORB_SET_NUM_NS = "20"; //0x14
107
        public static final String ORB_SET_NUM = "21"; //0x15
108
        public static final String ORB_SEND = "22"; //0x16
109
110 136 gtress
111 333 gtress
        Colonet colonet;  //save reference to the entire applet locally
112 32 gtress
        Socket socket;
113
        OutputStreamWriter out;
114
        BufferedReader reader;
115 76 gtress
        DataListener dataListener;
116
        JTextArea log, txtMatrix;
117 32 gtress
118 136 gtress
119 32 gtress
        /*
120
        *        FUNCTION IMPLEMENTATIONS
121
        */
122
123 333 gtress
        /**
124
        * Constructs a new ColonetServerInterface. When constructing a ColonetServerInterface, a valid Colonet object
125
        * reference must be provided to ensure that data is routed correctly.
126
        *
127
        * @param colonet The Colonet object to save locally. This reference cannot be changed once the
128
        *                ColonetSreverInterface has been contsructed.
129
        * @throws NullPointerException if colonet is null
130
        *
131
        */
132 136 gtress
        public ColonetServerInterface (Colonet colonet) {
133
                this.colonet = colonet;
134
                this.log = colonet.getLog();
135
                this.txtMatrix = colonet.getMatrixInput();
136 76 gtress
                dataListener = new DataListener();
137 32 gtress
        }
138
139
        public Socket getSocket () {
140
                return socket;
141
        }
142
143
        public OutputStreamWriter getOutputStreamWriter () {
144
                return out;
145
        }
146
147
        public BufferedReader getBufferedReader () {
148
                return reader;
149
        }
150
151
        public boolean isReady () {
152 333 gtress
                if (socket == null || out == null || reader == null)
153
                        return false;
154
                if (!socket.isConnected() || socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown())
155
                        return false;
156 32 gtress
                return true;
157
        }
158
159
        public boolean isInputReady () {
160
                try {
161
                        if (reader.ready()) return true;
162
                } catch (Exception e) {
163
                        return false;
164
                }
165
                return false;
166
        }
167
168 420 gtress
        public String getLine () throws IOException {
169
                return reader.readLine();
170 39 gtress
        }
171 38 gtress
172 32 gtress
        /**
173
         * Create socket connection to Colonet server.
174 333 gtress
         * If successful, start thread for listening for incoming data.
175 32 gtress
         */
176
        public void connect (String strHost, String strPort) {
177
                //make sure hostname and port are valid
178
                if (strHost.equals("") || strPort.equals("")) {
179
                        err("Please enter a hostname and port.");
180
                        return;
181
                }
182
                int port = 0;
183
                try {
184
                        port = Integer.parseInt(strPort);
185
                } catch (Exception e) {
186
                        err("Invalid port");
187
                        return;
188
                }
189
190 181 gtress
                //make sure we aren't already connected.
191 32 gtress
                if (socket != null && socket.isConnected()) {
192 181 gtress
                        return;
193 32 gtress
                }
194
195
                try {
196
                        socket = new Socket(strHost, port);
197 420 gtress
                        socket.setKeepAlive(true);
198 32 gtress
                } catch (UnknownHostException e) {
199
                        log.append("Unknown host exception.\n");
200
                        err("Unknown Host Exception");
201
                        return;
202
                } catch (IOException e) {
203
                        log.append("IO Exception.\n");
204 402 gtress
                        err("Could not create socket to " + strHost + ".\n" + e);
205 32 gtress
                        return;
206
                } catch (java.security.AccessControlException e) {
207
                        log.append("Access Control Exception.\n");
208 67 gtress
                        err("Permission denied by java.security.AccessControlException.\n\n"
209
                                +"You may only connect to the server from which this applet was loaded.");
210 32 gtress
                        return;
211
                }
212
                if (socket == null || !socket.isConnected()) {
213 273 gtress
                        log.append("Connection is not ready. Try connecting again.\n");
214 32 gtress
                        return;
215
                }
216
                try {
217
                        out = new OutputStreamWriter(socket.getOutputStream());
218
                        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
219
                } catch (IOException e) {
220
                        warn("Could not get transfer streams from socket connection.");
221
                }
222 76 gtress
                dataListener.start();
223 32 gtress
224
        }
225
226 420 gtress
        public void disconnect () {
227
228
        }
229
230 107 gtress
        /*
231
        *        sendString - only this method should actually write data to the output stream
232
        */
233 333 gtress
        private void sendString (String s) {
234 32 gtress
                //make sure we can send
235
                if (!this.isReady()) {
236 273 gtress
                        log.append("Could not send data.\n");
237 32 gtress
                        return;
238
                }
239
                //send packet
240
                try {
241 181 gtress
                        Thread.sleep(50);  //pause to be safe
242 107 gtress
                        out.write(s);
243 32 gtress
                        out.flush();
244 273 gtress
                        //log.append("Sent: " + s + "\n");
245 32 gtress
                } catch (IOException e) {
246 172 gtress
                        log.setText("Could not send data.\n");
247 107 gtress
                } catch (InterruptedException e) {
248 172 gtress
                        log.setText("Thread InterruptedException in sendData\n");
249 67 gtress
                }
250 32 gtress
        }
251
252 333 gtress
        /**
253
        * General send-to-server method. This method is used by other command methods, which are usually convenience
254
        * methods that simply specify arguments to this method. A command consists of a String which holds integers
255
        * separated by spaces. This method should not be used directly unless you know the format of the particular
256
        * command you are sending. If implementing a particular command for permanent use, it is recommended that
257
        * you create a new wrapper method specific to that command in the ColonetServerInterface file.
258
        *
259
        * Note that no checking is performed in this method to ensure the correct formatting of the String arguments.
260
        * If malformed commands or robot numbers are specified, the behavior of the request at the server will be
261
        * undefined and could result in server failure.
262
        *
263
        * @param s The command String in its correct format. The format of a command String is ultimately specified
264
        * by the Colonet server application and may change.
265
        * @param robotNumber The number of the robot that is the subject of the command, if any. The robot number
266
        * is specified as a single integer in a String. If the command does not have a single robot subject, this
267
        * argument can be null or an empty String, whichever is convenient.
268
        */
269 181 gtress
        public void sendData (String s, String robotNumber) {
270 107 gtress
                //create packet
271
                String packet = "";
272
                packet += ColonetServerInterface.SEND_TO_ROBOT;
273 333 gtress
                if (robotNumber != null)
274
                        packet += " " + robotNumber;
275 107 gtress
                packet += " " + ColonetServerInterface.COLONET_COMMAND;
276
                packet += " " + s;  //add  the command code here
277
                packet += "\n";
278
                sendString(packet);
279
        }
280
281 333 gtress
        /**
282
        * General request-from-server method. This method is used by other request methods, which are usually convenience
283
        * methods that simply specify arguments to this method. A request consists of a String which holds integers
284
        * separated by spaces. This method should not be used directly unless you know the format of the particular
285
        * request you are making. If implementing a particular request, it is recommended that you create a new method
286
        * specific to that request in the ColonetServerInterface file.
287
        *
288
        */
289
        private void sendRequest (String s, String robotNumber) {
290 32 gtress
                //create packet
291 107 gtress
                String packet = "";
292
                packet += ColonetServerInterface.REQUEST_FROM_SERVER;
293 270 gtress
                packet += " " + robotNumber;
294 32 gtress
                packet += " " + s;  //add  the command code here
295
                packet += "\n";
296 107 gtress
                sendString(packet);
297 32 gtress
        }
298 107 gtress
299 333 gtress
        /**
300
        * Sends a request to the server to report the entire BOM sensor matrix. The server will reply at its convenience.
301
        * No guarantee is made (end-to-end or otherwise) that the server will respond in a timely manner or at all
302
        * to any individual request.
303
        */
304 107 gtress
        public void sendSensorDataRequest () {
305
                sendRequest(ColonetServerInterface.REQUEST_BOM_MATRIX, "");
306
        }
307
308 333 gtress
        /**
309
        * Sends a request to the server to report a list of XBee IDs. The server will reply at its convenience.
310
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
311
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment.
312
        * No guarantee is made (end-to-end or otherwise) that the server will respond in a timely manner or at all
313
        * to any individual request.
314
        *
315
        * @see Colonet#parseXBeeIDs(String)
316
        */
317 136 gtress
        public void sendXBeeIDRequest () {
318
                sendRequest(ColonetServerInterface.REQUEST_XBEE_IDS, "");
319
        }
320
321 333 gtress
        /**
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 270 gtress
        public void sendBatteryRequest (int robotNum) {
334 425 gtress
                //create packet
335
                String packet = "";
336
                packet += ColonetServerInterface.SEND_TO_ROBOT;
337
                packet += " " + robotNum;
338
                packet += " " + ColonetServerInterface.COLONET_REQUEST;
339
                packet += " " + ColonetServerInterface.BATTERY;  //add  the command code here
340
                packet += "\n";
341
                sendString(packet);
342 270 gtress
        }
343
344 459 gtress
        /**
345
        * Requests a list of all robot positions and correspondind robot IDs.
346
        */
347
        public void sendPositionRequest () {
348
                sendRequest(ColonetServerInterface.CLIENT_REQUEST_ROBOT_POSITIONS, "");
349
        }
350
351
        /**
352
        * Send a robot ID assignment update to store on the server.
353
        */
354
        public void sendIDAssignment (int oldID, int newID) {
355
                String packet = "";
356
                packet += ColonetServerInterface.REQUEST_FROM_SERVER;
357
                packet += " " + oldID + " " + newID;
358
                packet += "\n";
359
                sendString(packet);
360
                txtMatrix.append("ID " + oldID + " => " + newID + "\n");
361
        }
362
363
364 107 gtress
        /*
365
        *        Queue management
366
        */
367 333 gtress
        private void sendQueueInstruction (String inst) {
368 107 gtress
                String packet = "";
369
                packet += ColonetServerInterface.COLONET_QUEUE;
370
                packet += " " + inst;
371
                packet += "\n";
372
                sendString(packet);
373
        }
374
375 333 gtress
        /**
376
        * Notifies the Colonet server to add a task to the current task queue. The Colonet server holds the canonical
377
        * task queue. Any applet can send tasks to add to the queue. Local copies of the queue in the applet are for
378
        * display purposes only, and are not authoritative when adding tasks. All clients send additions asynchronously,
379
        * and as a result no guarantee is made that a task will be added in the specificed location in the queue or at
380
        * all. If an invalid position is specified, the task will probably not be added to the queue and may cause
381
        * server failure. 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 placed. Note that this is not
385
        * guaranteed. Invalid positions may cause the task to be discarded at the server.
386
        * @param data The String containing the command code(s) for the task and any arguments necessary to fully
387
        * define the behavior of the task. This format is currently not specified. In the future, the canonical format
388
        * of the data String will ultimately be defined by the Colonet server.
389
        * @param description A String that contains a description of the task. This will be the message displayed to
390
        * the user when information about the task is requested.
391
        */
392 107 gtress
        public void sendQueueAdd (int pos, String data, String description) {
393
                String packet = "";
394
                packet += ColonetServerInterface.QUEUE_ADD;
395
                packet += " " + pos;
396
                packet += " " + data;
397
                packet += " [" + description + "]";
398
                packet += "\n";
399
                sendQueueInstruction(packet);
400
        }
401
402 333 gtress
        /**
403
        * Notifies the Colonet server to remove a task from the current task queue. The Colonet server holds the canonical
404
        * task queue. Any applet can remove a task from the queue. Local copies of the queue in the applet are for
405
        * display purposes only, and are not authoritative when removing tasks. All clients remove tasks asynchronously,
406
        * and as a result no guarantee is made that the correct task will actually be removed.
407
        * If an invalid position is specified, the state of the queue is undefined and server failure may result.
408
        * Due to the asynchronous nature of the queue, we cannot easily account for concurrent
409
        * modification failures on the client side.
410
        *
411
        * @param pos The position in the queue at which we would like the task to be removed. Note that this is not
412
        * guaranteed. Invalid positions may result in a corrupted queue.
413
        */
414 107 gtress
        public void sendQueueRemove (int pos) {
415
                String packet = "";
416
                packet += ColonetServerInterface.QUEUE_REMOVE;
417
                packet += " " + pos;
418
                packet += "\n";
419
                sendQueueInstruction(packet);
420
        }
421 32 gtress
422 333 gtress
        /**
423
        * Notifies the Colonet server to reorder tasks in the current task queue. The Colonet server holds the canonical
424
        * task queue. Any applet can reorder tasks in the queue. Local copies of the queue in the applet are for
425
        * display purposes only, and are not authoritative when reordering tasks. All clients reorder tasks asynchronously,
426
        * and as a result no guarantee is made that the correct tasks will actually be reordered.
427
        * If an invalid position is specified, the state of the queue is undefined and server failure may result.
428
        * Due to the asynchronous nature of the queue, we cannot easily account for concurrent
429
        * modification failures on the client side.
430
        *
431
        * @param pos1 The queue position of a task which we would like to reorder.
432
        * @param pos2 The queue position of a task which we would like to reorder.
433
        */
434 107 gtress
        public void sendQueueReorder (int pos1, int pos2) {
435
                String packet = "";
436
                packet += ColonetServerInterface.QUEUE_REORDER;
437
                packet += " " + pos1;
438
                packet += " " + pos2;
439
                packet += "\n";
440
                sendQueueInstruction(packet);
441
        }
442 32 gtress
443 136 gtress
        public void sendQueueUpdate () {
444
                sendQueueInstruction(ColonetServerInterface.QUEUE_UPDATE);
445
        }
446 32 gtress
447
        /**
448
         * Display informational message box on the screen. Used for casual communicaton to the user.
449
         * @param text Text to display
450
         */
451 35 gtress
        public void msg (String text) {
452 32 gtress
                JOptionPane.showMessageDialog(null, text, "Colonet", JOptionPane.INFORMATION_MESSAGE);
453
        }
454
455
        /**
456
         * Display warning message box on the screen. Used for minor alerts or exceptions.
457
         * @param text Text to display
458
         */
459 35 gtress
        public void warn (String text) {
460 32 gtress
                JOptionPane.showMessageDialog(null, text, "Colonet", JOptionPane.WARNING_MESSAGE);
461
        }
462
463
        /**
464
         * Display error message box on the screen. Used for major errors or exceptions in the program.
465
         * @param text Text to display
466
         */
467 35 gtress
        public void err (String text) {
468 32 gtress
                JOptionPane.showMessageDialog(null, text, "Colonet", JOptionPane.ERROR_MESSAGE);
469
        }
470
471 76 gtress
472
        /*
473
        *        DataListener thread.
474
        *
475
        */
476
        class DataListener extends Thread {
477
                final int DATALISTENER_DELAY = 222;
478
479
                public DataListener () {
480
                        super("Colonet DataListener");
481
                }
482
483
                public void run () {
484
                        String line;
485
                        while (true) {
486
                                try {
487 420 gtress
                                        line = reader.readLine();
488
                                        if (line == null)
489
                                            throw new IOException();
490
                                        parseData(line);
491 76 gtress
                                        Thread.sleep(DATALISTENER_DELAY);
492
                                } catch (InterruptedException e) {
493
                                        return;
494 420 gtress
                                } catch (IOException e) {
495
                                disconnect();
496
                                    colonet.disconnect();
497
                                    return;
498
                                }
499 76 gtress
                        }
500
                }
501
502
                public void parseData (String line) {
503 333 gtress
                        // Sensor Matrix
504 136 gtress
                        if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
505
                                ColonetServerInterface.REQUEST_BOM_MATRIX))
506
                                colonet.parseMatrix(line);
507 270 gtress
                        // Task Queue
508 155 gtress
                        else if (line.startsWith(ColonetServerInterface.COLONET_QUEUE))
509 136 gtress
                                colonet.parseQueue(line);
510 270 gtress
                        // XBee IDs
511 155 gtress
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
512 136 gtress
                                ColonetServerInterface.REQUEST_XBEE_IDS))
513
                                colonet.parseXBeeIDs(line);
514 270 gtress
                        // Battery
515
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
516
                                ColonetServerInterface.BATTERY))
517
                                colonet.parseBattery(line);
518 459 gtress
                        // Robot Positions
519
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
520
                                ColonetServerInterface.CLIENT_REQUEST_ROBOT_POSITIONS))
521
                                colonet.parsePositions(line);
522 333 gtress
                        // Unknown type
523
                        else
524
                                System.out.println("Got data:" + line);
525
526 76 gtress
                }
527 32 gtress
528 76 gtress
        }
529
530 420 gtress
}