Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (19.4 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
/*        Old packet structure:
31

32
                COMMAND PACKET STRUCTURE
33
        1:        SEND_TO_ROBOT
34
        2:        # of robot, or GLOBAL_DEST
35
        3:        COLONET_COMMMAND
36
        4:        message code (i.e. ORB_SET)
37
        5:        any data, as many that fit in the packet
38

39
        REQUEST PACKET STRUCTURE
40
        1:        REQUEST_FROM_SERVER
41
        2:        # of robot
42
        3:        COLONET_REQUEST
43
        4:        ???
44

45
        9/12/07 New server interface structure
46
                Client will no longer send full robot packets to the server.
47
                Commands will be defined as necessary.
48
        */
49

    
50
        //General Colonet Interface
51
        public static final String SEND_TO_ROBOT = "0";
52
        public static final String REQUEST_FROM_SERVER = "1";
53
        public static final String RESPONSE_TO_CLIENT_REQUEST = "2";
54
        public static final String REQUEST_BOM_MATRIX = "144";
55
        public static final String REQUEST_XBEE_IDS = "145";
56

    
57
        public static final String COLONET_COMMAND = "13"; //0x0D
58
        public static final String COLONET_REQUEST = "14"; //0x0E
59
        public static final String CORONET_RESPONSE = "15"; //0x0F
60
        public static final String GLOBAL_DEST = "200";
61
        public static final String CLIENT_REQUEST_ROBOT_POSITIONS = "86";
62
        public static final String CLIENT_ASSIGN_ROBOT_ID = "87";
63
  public static final String CLIENT_SET_VIRTUAL_WALL = "89";
64
        public static final String MOVE_TO_ABSOLUTE_POSITION = "83"; //0x53
65
  
66

    
67
        //Queue instructions
68
        public static final String COLONET_QUEUE = "100";
69
        public static final String QUEUE_UPDATE = "101";
70
        public static final String QUEUE_ADD = "102";
71
        public static final String QUEUE_REMOVE = "103";
72
        public static final String QUEUE_REORDER = "104";
73

    
74
        //Use BATTERY to request battery level
75
        public static final String BATTERY = "56"; //0x38
76

    
77
        //MOTORS
78
        public static final String MOTORS_INIT = "23"; //0x17
79
        public static final String MOTOR1_SET = "24"; //0x18
80
        public static final String MOTOR2_SET = "25"; //0x19
81
        public static final String MOTORS_OFF = "26"; //0x1A
82
        public static final String MOVE = "27"; //0x1B
83
        public static final String MOVE_AVOID = "28"; //0x1C
84
        public static final String MOVE_R = "29"; //0x1D
85
        public static final String MOVE_L = "30"; //0x1E
86
        public static final String MOVE_F = "31"; //0x1F
87
        public static final String MOVE_B = "32"; //0x1G
88

    
89
        //BUZZER
90
        public static final String BUZZER_INIT = "0"; //0x00
91
        public static final String BUZZER_SET_VAL = "1"; //0x01
92
        public static final String BUZZER_SET_FREQ = "2"; //0x02
93
        public static final String BUZZER_CHIRP = "3"; //0x03
94
        public static final String BUZZER_OFF = "4"; //0x04
95

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

    
109
        Colonet colonet;        //save reference to the entire applet locally
110
        Socket socket;
111
        OutputStreamWriter out;
112
        BufferedReader reader;
113
        DataListener dataListener;
114

    
115
        /*
116
        * FUNCTION IMPLEMENTATIONS
117
        */
118

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

    
133
        public Socket getSocket () {
134
                return socket;
135
        }
136

    
137
        public OutputStreamWriter getOutputStreamWriter () {
138
                return out;
139
        }
140

    
141
        public BufferedReader getBufferedReader () {
142
                return reader;
143
        }
144

    
145
        public boolean isReady () {
146
                if (socket == null || out == null || reader == null)
147
                        return false;
148
                if (!socket.isConnected() || socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown())
149
                        return false;
150
                return true;
151
        }
152

    
153
        public boolean isInputReady () {
154
                try {
155
                        if (reader.ready()) return true;
156
                } catch (Exception e) {
157
                        return false;
158
                }
159
                return false;
160
        }
161

    
162
        public String getLine () throws IOException {
163
                return reader.readLine();
164
        }
165

    
166
        /**
167
         * Create socket connection to Colonet server.
168
         * If successful, start thread for listening for incoming data.
169
         */
170
        public void connect (String strHost, String strPort) {
171
                //make sure hostname and port are valid
172
                if (strHost.equals("") || strPort.equals("")) {
173
                        err("Please enter a hostname and port.");
174
                        return;
175
                }
176
                int port = 0;
177
                try {
178
                        port = Integer.parseInt(strPort);
179
                } catch (Exception e) {
180
                        err("Invalid port");
181
                        return;
182
                }
183

    
184
                //make sure we aren't already connected.
185
                if (socket != null && socket.isConnected()) {
186
                        return;
187
                }
188

    
189
                try {
190
                        socket = new Socket(strHost, port);
191
                        socket.setKeepAlive(true);
192
                } catch (UnknownHostException e) {
193
                        err("Unknown Host Exception");
194
                        return;
195
                } catch (IOException e) {
196
                        err("Failed to connect to " + strHost + ".\n");
197
                        return;
198
                } catch (java.security.AccessControlException e) {
199
                        err("Permission denied.\n\n"
200
                                + "You may only connect to the server from which this applet was loaded.");
201
                        return;
202
                }
203

    
204
                if (socket == null || !socket.isConnected()) {
205
                        return;
206
                }
207
                try {
208
                        out = new OutputStreamWriter(socket.getOutputStream());
209
                        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
210
                } catch (IOException e) {
211
                        warn("Could not get transfer streams from socket connection.");
212
                }
213

    
214
                dataListener.start();
215
        }
216

    
217
        public void disconnect () {
218
                try {
219
                        if (reader != null) {
220
                                reader.close();
221
                        }
222
                        if (out != null) {
223
                                out.close();
224
                        }
225
                        colonet.disconnect();
226
                } catch (IOException e) {
227
                }
228
        }
229

    
230
        /*
231
        * Send a general String to the OutputStream.
232
        */
233
        private void sendString (String s) {
234
                //make sure we can send
235
                if (!this.isReady()) {
236
                        colonet.getInfoPanel().append("Could not send data.\n");
237
                        return;
238
                }
239
                //send packet
240
                try {
241
                        Thread.sleep(100);         //pause to be safe
242
                        out.write(s);
243
                        out.flush();
244
                } catch (IOException e) {
245
                        colonet.getInfoPanel().setText("Could not send data.\n");
246
                } catch (InterruptedException e) {
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
                //colonet.getInfoPanel().append("S:" + packet);
278
        }
279

    
280
        /**
281
        * General request-from-server method. This method is used by other request methods, which are usually convenience
282
        * methods that simply specify arguments to this method. A request consists of a String which holds integers
283
        * separated by spaces. This method should not be used directly unless you know the format of the particular
284
        * request you are making. If implementing a particular request, it is recommended that you create a new method
285
        * specific to that request in the ColonetServerInterface file.
286
        *
287
        */
288
        private void sendRequest (String s, String robotNumber) {
289
                //create packet
290
                String packet = "";
291
                packet += ColonetServerInterface.REQUEST_FROM_SERVER;
292
                packet += " " + robotNumber;
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
                sendRequest(ColonetServerInterface.REQUEST_XBEE_IDS, "");
318
        }
319

    
320
        /**
321
        * Sends a battery request for a specific robot to the server. The server will reply at its convenience.
322
        * Behavior is undefined if an invalid robot number is specified. The server will probably not respond in
323
        * this case.
324
        * No guarantee is made (end-to-end or otherwise) that the server will respond in a timely manner or at all
325
        * to any individual request.
326
        *
327
        * @param robotNum The number of the robot for which we are requesting the battery. Note that this value
328
        * is sent as-is to the server. No mapping to or from XBee IDs is performed. The contract for this is
329
        * currently undefined.
330
        * @see Colonet#parseBattery(String)
331
        */
332
        public void sendBatteryRequest (int robotNum) {
333
                //create packet
334
                String packet = "";
335
                packet += ColonetServerInterface.SEND_TO_ROBOT;
336
                packet += " " + robotNum;
337
                packet += " " + ColonetServerInterface.COLONET_REQUEST;
338
                packet += " " + ColonetServerInterface.BATTERY;         //add        the command code here
339
                packet += "\n";
340
                sendString(packet);
341
                //System.out.println("Sent battery request: " + packet);
342
        }
343

    
344
        /**
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 += " " + CLIENT_ASSIGN_ROBOT_ID;
358
                packet += " " + oldID + " " + newID;
359
                packet += "\n";
360
                sendString(packet);
361
        }
362

    
363
        /**
364
    * Order a robot to move to an absolute coordinate point.
365
    */
366
        public void sendAbsoluteMove (int id, int x, int y) {
367
      int x_high = (x>>8) & 0xff;
368
      int x_low = x & 0xff;
369
      int y_high = (y>>8) & 0xff;
370
      int y_low = y & 0xff;
371

    
372
                        sendData(MOVE_TO_ABSOLUTE_POSITION + " " + x_high + " " + x_low + " " + y_high + " " + y_low, "" + id);
373
        }
374
  
375
    /**
376
    * Establish a boundary for robot motion.
377
    */
378
    public void sendBoundary (int x1, int y1, int x2, int y2) {
379
        String packet = "";
380
                packet += ColonetServerInterface.REQUEST_FROM_SERVER;
381
                packet += " " + CLIENT_SET_VIRTUAL_WALL;
382
        packet += " " + x1 + " " + y1 + " " + x2 + " " + y2;
383
        packet += "\n";
384
        sendString(packet);
385
    }
386

    
387
        /*
388
        * Queue management
389
        */
390
        private void sendQueueInstruction (String inst) {
391
                String packet = "";
392
                packet += ColonetServerInterface.COLONET_QUEUE;
393
                packet += " " + inst;
394
                packet += "\n";
395
                sendString(packet);
396
        }
397

    
398
        /**
399
        * Notifies the Colonet server to add a task to the current task queue. The Colonet server holds the canonical
400
        * task queue. Any applet can send tasks to add to the queue. Local copies of the queue in the applet are for
401
        * display purposes only, and are not authoritative when adding tasks. All clients send additions asynchronously,
402
        * and as a result no guarantee is made that a task will be added in the specificed location in the queue or at
403
        * all. If an invalid position is specified, the task will probably not be added to the queue and may cause
404
        * server failure. Due to the asynchronous nature of the queue, we cannot easily account for concurrent
405
        * modification failures on the client side.
406
        *
407
        * @param pos The position in the queue at which we would like the task to be placed. Note that this is not
408
        * guaranteed. Invalid positions may cause the task to be discarded at the server.
409
        * @param data The String containing the command code(s) for the task and any arguments necessary to fully
410
        * define the behavior of the task. This format is currently not specified. In the future, the canonical format
411
        * of the data String will ultimately be defined by the Colonet server.
412
        * @param description A String that contains a description of the task. This will be the message displayed to
413
        * the user when information about the task is requested.
414
        */
415
        public void sendQueueAdd (int pos, String data, String description) {
416
                String packet = "";
417
                packet += ColonetServerInterface.QUEUE_ADD;
418
                packet += " " + pos;
419
                packet += " " + data;
420
                packet += " [" + description + "]";
421
                packet += "\n";
422
                sendQueueInstruction(packet);
423
        }
424

    
425
        /**
426
        * Notifies the Colonet server to remove a task from the current task queue. The Colonet server holds the canonical
427
        * task queue. Any applet can remove a task from the queue. Local copies of the queue in the applet are for
428
        * display purposes only, and are not authoritative when removing tasks. All clients remove tasks asynchronously,
429
        * and as a result no guarantee is made that the correct task will actually be removed.
430
        * If an invalid position is specified, the state of the queue is undefined and server failure may result.
431
        * Due to the asynchronous nature of the queue, we cannot easily account for concurrent
432
        * modification failures on the client side.
433
        *
434
        * @param pos The position in the queue at which we would like the task to be removed. Note that this is not
435
        * guaranteed. Invalid positions may result in a corrupted queue.
436
        */
437
        public void sendQueueRemove (int pos) {
438
                String packet = "";
439
                packet += ColonetServerInterface.QUEUE_REMOVE;
440
                packet += " " + pos;
441
                packet += "\n";
442
                sendQueueInstruction(packet);
443
        }
444

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

    
466
        public void sendQueueUpdate () {
467
                sendQueueInstruction(ColonetServerInterface.QUEUE_UPDATE);
468
        }
469

    
470
        /**
471
         * Display informational message box on the screen. Used for casual communicaton to the user.
472
         * @param text Text to display
473
         */
474
        public void msg (String text) {
475
                JOptionPane.showMessageDialog(colonet, text, "Colonet", JOptionPane.INFORMATION_MESSAGE);
476
        }
477

    
478
        /**
479
         * Display warning message box on the screen. Used for minor alerts or exceptions.
480
         * @param text Text to display
481
         */
482
        public void warn (String text) {
483
                JOptionPane.showMessageDialog(colonet, text, "Colonet", JOptionPane.WARNING_MESSAGE);
484
        }
485

    
486
        /**
487
         * Display error message box on the screen. Used for major errors or exceptions in the program.
488
         * @param text Text to display
489
         */
490
        public void err (String text) {
491
                JOptionPane.showMessageDialog(colonet, text, "Colonet", JOptionPane.ERROR_MESSAGE);
492
        }
493

    
494

    
495
        /*
496
        * DataListener thread.
497
        *
498
        */
499
        class DataListener extends Thread {
500
                final int DATALISTENER_DELAY = 122;
501

    
502
                public DataListener () {
503
                        super("Colonet DataListener");
504
                }
505

    
506
                public void run () {
507
                        String line;
508
                        while (true) {
509
                                try {
510
                                        line = reader.readLine();
511
                                        if (line == null) {
512
                                                throw new IOException();
513
                                        }
514
                                        parseData(line);
515
                                        Thread.sleep(DATALISTENER_DELAY);
516
                                } catch (InterruptedException e) {
517
                                        return;
518
                                } catch (IOException e) {
519
                                        disconnect();
520
                                        return;
521
                                }
522
                        }
523
                }
524

    
525
                public void parseData (String line) {
526
                        // Sensor Matrix
527
                        if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
528
                                ColonetServerInterface.REQUEST_BOM_MATRIX))
529
                                colonet.parseMatrix(line);
530
                        // Task Queue
531
                        else if (line.startsWith(ColonetServerInterface.COLONET_QUEUE))
532
                                colonet.parseQueue(line);
533
                        // XBee IDs
534
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
535
                                ColonetServerInterface.REQUEST_XBEE_IDS))
536
                                colonet.parseXBeeIDs(line);
537
                        // Battery
538
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
539
                                ColonetServerInterface.BATTERY))
540
                                colonet.parseBattery(line);
541
                        // Robot Positions
542
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
543
                                ColonetServerInterface.CLIENT_REQUEST_ROBOT_POSITIONS))
544
                                colonet.parsePositions(line);
545
                        // Unknown type
546
                        else {
547
                                System.out.println("Got unknown data: " + line + "\n");
548
                        }
549
                }
550
        }
551
}