Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (20.7 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 COLONET_COMMAND = "13"; //0x0D
55
        public static final String COLONET_REQUEST = "14"; //0x0E
56
        public static final String CORONET_RESPONSE = "15"; //0x0F
57
        public static final String GLOBAL_DEST = "200";
58
        
59
        //Common requests
60
  public static final String CLIENT_REQUEST_IMAGE = "112";
61
  
62
        public static final String CLIENT_REQUEST_ROBOT_POSITIONS = "86";
63
        public static final String CLIENT_ASSIGN_ROBOT_ID = "87";
64
  public static final String CLIENT_SET_VIRTUAL_WALL = "89";
65
  public static final String SERVER_CLEAR_VIRTUAL_WALL = "92";
66
        public static final String MOVE_TO_ABSOLUTE_POSITION = "83"; //0x53
67
        public static final String ROBOT_REPORT_ARRIVED_AT_POSITION = "93"; //0x5D
68
        public static final String REQUEST_BOM_MATRIX = "144";
69
        public static final String REQUEST_XBEE_IDS = "145";
70
        public static final String RECHARGE = "96";  //0x60
71
        public static final String RECHARGE_STOP = "97";  //0x61
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
        public static final String MOVE_R = "29"; //0x1D
91
        public static final String MOVE_L = "30"; //0x1E
92
        public static final String MOVE_F = "31"; //0x1F
93
        public static final String MOVE_B = "32"; //0x1G
94

    
95
        //BUZZER
96
        public static final String BUZZER_INIT = "0"; //0x00
97
        public static final String BUZZER_SET_VAL = "1"; //0x01
98
        public static final String BUZZER_SET_FREQ = "2"; //0x02
99
        public static final String BUZZER_CHIRP = "3"; //0x03
100
        public static final String BUZZER_OFF = "4"; //0x04
101

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

    
115
        Colonet colonet;        //save reference to the entire applet locally
116
        Socket socket;
117
        OutputStreamWriter out;
118
        BufferedReader reader;
119
        DataListener dataListener;
120

    
121
        /*
122
        * FUNCTION IMPLEMENTATIONS
123
        */
124

    
125
        /**
126
        * Constructs a new ColonetServerInterface. When constructing a ColonetServerInterface, a valid Colonet object
127
        * reference must be provided to ensure that data is routed correctly.
128
        *
129
        * @param colonet The Colonet object to save locally. This reference cannot be changed once the
130
        *                ColonetSreverInterface has been contsructed.
131
        * @throws NullPointerException if colonet is null
132
        *
133
        */
134
        public ColonetServerInterface (Colonet colonet) {
135
                this.colonet = colonet;
136
                dataListener = new DataListener();
137
        }
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
                if (socket == null || out == null || reader == null)
153
                        return false;
154
                if (!socket.isConnected() || socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown())
155
                        return false;
156
                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
        public String getLine () throws IOException {
169
                return reader.readLine();
170
        }
171

    
172
        /**
173
         * Create socket connection to Colonet server.
174
         * If successful, start thread for listening for incoming data.
175
         */
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
                //make sure we aren't already connected.
191
                if (socket != null && socket.isConnected()) {
192
                        return;
193
                }
194

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

    
210
                if (socket == null || !socket.isConnected()) {
211
                        return;
212
                }
213
                try {
214
                        out = new OutputStreamWriter(socket.getOutputStream());
215
                        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
216
                } catch (IOException e) {
217
                        warn("Could not get transfer streams from socket connection.");
218
                }
219

    
220
                dataListener.start();
221
        }
222

    
223
        public void disconnect () {
224
                try {
225
                        if (reader != null) {
226
                                reader.close();
227
                        }
228
                        if (out != null) {
229
                                out.close();
230
                        }
231
                        colonet.disconnect();
232
                } catch (IOException e) {
233
                }
234
        }
235

    
236
        /*
237
        * Send a general String to the OutputStream.
238
        */
239
        private void sendString (String s) {
240
                //make sure we can send
241
                if (!this.isReady()) {
242
                        colonet.getInfoPanel().append("Could not send data.\n");
243
                        return;
244
                }
245
                //send packet
246
                try {
247
                        Thread.sleep(100);         //pause to be safe
248
                        out.write(s);
249
                        out.flush();
250
                } catch (IOException e) {
251
                        colonet.getInfoPanel().setText("Could not send data.\n");
252
                } catch (InterruptedException e) {
253
                }
254
        }
255

    
256
        /**
257
        * General send-to-server method. This method is used by other command methods, which are usually convenience
258
        * methods that simply specify arguments to this method. A command consists of a String which holds integers
259
        * separated by spaces. This method should not be used directly unless you know the format of the particular
260
        * command you are sending. If implementing a particular command for permanent use, it is recommended that
261
        * you create a new wrapper method specific to that command in the ColonetServerInterface file.
262
        *
263
        * Note that no checking is performed in this method to ensure the correct formatting of the String arguments.
264
        * If malformed commands or robot numbers are specified, the behavior of the request at the server will be
265
        * undefined and could result in server failure.
266
        *
267
        * @param s The command String in its correct format. The format of a command String is ultimately specified
268
        * by the Colonet server application and may change.
269
        * @param robotNumber The number of the robot that is the subject of the command, if any. The robot number
270
        * is specified as a single integer in a String. If the command does not have a single robot subject, this
271
        * argument can be null or an empty String, whichever is convenient.
272
        */
273
        public void sendData (String s, String robotNumber) {
274
                //create packet
275
                String packet = "";
276
                packet += ColonetServerInterface.SEND_TO_ROBOT;
277
                if (robotNumber != null)
278
                        packet += " " + robotNumber;
279
                packet += " " + ColonetServerInterface.COLONET_COMMAND;
280
                packet += " " + s;        //add         the command code here
281
                packet += "\n";
282
                sendString(packet);
283
                //colonet.getInfoPanel().append("S:" + packet);
284
        }
285

    
286
        /**
287
        * General request-from-server method. This method is used by other request methods, which are usually convenience
288
        * methods that simply specify arguments to this method. A request consists of a String which holds integers
289
        * separated by spaces. This method should not be used directly unless you know the format of the particular
290
        * request you are making. If implementing a particular request, it is recommended that you create a new method
291
        * specific to that request in the ColonetServerInterface file.
292
        *
293
        */
294
        private void sendRequest (String s, String robotNumber) {
295
                //create packet
296
                String packet = "";
297
                packet += ColonetServerInterface.REQUEST_FROM_SERVER;
298
                packet += " " + robotNumber;
299
                packet += " " + s;        //add         the command code here
300
                packet += "\n";
301
                sendString(packet);
302
        }
303

    
304
        /**
305
        * Sends a request to the server to report the entire BOM sensor matrix. The server will reply at its convenience.
306
        * No guarantee is made (end-to-end or otherwise) that the server will respond in a timely manner or at all
307
        * to any individual request.
308
        */
309
        public void sendSensorDataRequest () {
310
                sendRequest(ColonetServerInterface.REQUEST_BOM_MATRIX, "");
311
        }
312

    
313
        /**
314
        * Sends a request to the server to report a list of XBee IDs. The server will reply at its convenience.
315
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
316
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment.
317
        * No guarantee is made (end-to-end or otherwise) that the server will respond in a timely manner or at all
318
        * to any individual request.
319
        *
320
        * @see Colonet#parseXBeeIDs(String)
321
        */
322
        public void sendXBeeIDRequest () {
323
                sendRequest(ColonetServerInterface.REQUEST_XBEE_IDS, "");
324
        }
325

    
326
        /**
327
        * Sends a battery request for a specific robot to the server. The server will reply at its convenience.
328
        * Behavior is undefined if an invalid robot number is specified. The server will probably not respond in
329
        * this case.
330
        * No guarantee is made (end-to-end or otherwise) that the server will respond in a timely manner or at all
331
        * to any individual request.
332
        *
333
        * @param robotNum The number of the robot for which we are requesting the battery. Note that this value
334
        * is sent as-is to the server. No mapping to or from XBee IDs is performed. The contract for this is
335
        * currently undefined.
336
        * @see Colonet#parseBattery(String)
337
        */
338
        public void sendBatteryRequest (int robotNum) {
339
                //create packet
340
                String packet = "";
341
                packet += ColonetServerInterface.SEND_TO_ROBOT;
342
                packet += " " + robotNum;
343
                packet += " " + ColonetServerInterface.COLONET_REQUEST;
344
                packet += " " + ColonetServerInterface.BATTERY;         //add        the command code here
345
                packet += "\n";
346
                sendString(packet);
347
                //System.out.println("Sent battery request: " + 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
        * Requests a current image from the overhead camera.
359
        */
360
        public void sendImageRequest() {
361
          sendRequest(ColonetServerInterface.CLIENT_REQUEST_IMAGE, "");
362
        }
363

    
364
        /**
365
        * Send a robot ID assignment update to store on the server.
366
        */
367
        public void sendIDAssignment (int oldID, int newID) {
368
                String packet = "";
369
                packet += ColonetServerInterface.REQUEST_FROM_SERVER;
370
                packet += " " + CLIENT_ASSIGN_ROBOT_ID;
371
                packet += " " + oldID + " " + newID;
372
                packet += "\n";
373
                sendString(packet);
374
        }
375

    
376
        /**
377
    * Order a robot to move to an absolute coordinate point.
378
    */
379
        public void sendAbsoluteMove (int id, int x, int y) {
380
      int x_high = (x>>8) & 0xff;
381
      int x_low = x & 0xff;
382
      int y_high = (y>>8) & 0xff;
383
      int y_low = y & 0xff;
384

    
385
                        sendData(MOVE_TO_ABSOLUTE_POSITION + " " + x_high + " " + x_low + " " + y_high + " " + y_low, "" + id);
386
        }
387
        
388
        /**
389
        *  Tell a robot to recharge now
390
        */
391
        public void sendRecharge (int id) {
392
          sendData(RECHARGE + "", id + "");
393
          System.out.println("Sent recharge command to ID " + id);
394
        }
395

    
396
        /**
397
        *  Tell a robot to stop recharing now
398
        */
399
        public void sendRechargeStop (int id) {
400
          sendData(RECHARGE_STOP + "", id + "");
401
          System.out.println("Sent recharge stop command to ID " + id);
402
        }
403

    
404
    /**
405
    * Establish a boundary for robot motion.
406
    */
407
    public void sendBoundary (int x1, int y1, int x2, int y2) {
408
        String packet = "";
409
                    packet += ColonetServerInterface.REQUEST_FROM_SERVER;
410
                    packet += " " + CLIENT_SET_VIRTUAL_WALL;
411
        packet += " " + x1 + " " + y1 + " " + x2 + " " + y2;
412
        packet += "\n";
413
        sendString(packet);
414
    }
415
    
416
    /**
417
    * Clear the boundary for robot motion.
418
    */
419
    public void sendBoundaryClear () {
420
        String packet = "";
421
                    packet += ColonetServerInterface.REQUEST_FROM_SERVER;
422
                    packet += " " + SERVER_CLEAR_VIRTUAL_WALL;
423
        packet += "\n";
424
        sendString(packet);
425
    }
426

    
427
        /*
428
        * Queue management
429
        */
430
        private void sendQueueInstruction (String inst) {
431
                String packet = "";
432
                packet += ColonetServerInterface.COLONET_QUEUE;
433
                packet += " " + inst;
434
                packet += "\n";
435
                sendString(packet);
436
        }
437

    
438
        /**
439
        * Notifies the Colonet server to add a task to the current task queue. The Colonet server holds the canonical
440
        * task queue. Any applet can send tasks to add to the queue. Local copies of the queue in the applet are for
441
        * display purposes only, and are not authoritative when adding tasks. All clients send additions asynchronously,
442
        * and as a result no guarantee is made that a task will be added in the specificed location in the queue or at
443
        * all. If an invalid position is specified, the task will probably not be added to the queue and may cause
444
        * server failure. Due to the asynchronous nature of the queue, we cannot easily account for concurrent
445
        * modification failures on the client side.
446
        *
447
        * @param pos The position in the queue at which we would like the task to be placed. Note that this is not
448
        * guaranteed. Invalid positions may cause the task to be discarded at the server.
449
        * @param data The String containing the command code(s) for the task and any arguments necessary to fully
450
        * define the behavior of the task. This format is currently not specified. In the future, the canonical format
451
        * of the data String will ultimately be defined by the Colonet server.
452
        * @param description A String that contains a description of the task. This will be the message displayed to
453
        * the user when information about the task is requested.
454
        */
455
        public void sendQueueAdd (int pos, String data, String description) {
456
                String packet = "";
457
                packet += ColonetServerInterface.QUEUE_ADD;
458
                packet += " " + pos;
459
                packet += " " + data;
460
                packet += " [" + description + "]";
461
                packet += "\n";
462
                sendQueueInstruction(packet);
463
        }
464

    
465
        /**
466
        * Notifies the Colonet server to remove a task from the current task queue. The Colonet server holds the canonical
467
        * task queue. Any applet can remove a task from the queue. Local copies of the queue in the applet are for
468
        * display purposes only, and are not authoritative when removing tasks. All clients remove tasks asynchronously,
469
        * and as a result no guarantee is made that the correct task will actually be removed.
470
        * If an invalid position is specified, the state of the queue is undefined and server failure may result.
471
        * Due to the asynchronous nature of the queue, we cannot easily account for concurrent
472
        * modification failures on the client side.
473
        *
474
        * @param pos The position in the queue at which we would like the task to be removed. Note that this is not
475
        * guaranteed. Invalid positions may result in a corrupted queue.
476
        */
477
        public void sendQueueRemove (int pos) {
478
                String packet = "";
479
                packet += ColonetServerInterface.QUEUE_REMOVE;
480
                packet += " " + pos;
481
                packet += "\n";
482
                sendQueueInstruction(packet);
483
        }
484

    
485
        /**
486
        * Notifies the Colonet server to reorder tasks in the current task queue. The Colonet server holds the canonical
487
        * task queue. Any applet can reorder tasks in the queue. Local copies of the queue in the applet are for
488
        * display purposes only, and are not authoritative when reordering tasks. All clients reorder tasks asynchronously,
489
        * and as a result no guarantee is made that the correct tasks will actually be reordered.
490
        * If an invalid position is specified, the state of the queue is undefined and server failure may result.
491
        * Due to the asynchronous nature of the queue, we cannot easily account for concurrent
492
        * modification failures on the client side.
493
        *
494
        * @param pos1 The queue position of a task which we would like to reorder.
495
        * @param pos2 The queue position of a task which we would like to reorder.
496
        */
497
        public void sendQueueReorder (int pos1, int pos2) {
498
                String packet = "";
499
                packet += ColonetServerInterface.QUEUE_REORDER;
500
                packet += " " + pos1;
501
                packet += " " + pos2;
502
                packet += "\n";
503
                sendQueueInstruction(packet);
504
        }
505

    
506
        public void sendQueueUpdate () {
507
                sendQueueInstruction(ColonetServerInterface.QUEUE_UPDATE);
508
        }
509

    
510
        /**
511
         * Display informational message box on the screen. Used for casual communicaton to the user.
512
         * @param text Text to display
513
         */
514
        public void msg (String text) {
515
                JOptionPane.showMessageDialog(colonet, text, "Colonet", JOptionPane.INFORMATION_MESSAGE);
516
        }
517

    
518
        /**
519
         * Display warning message box on the screen. Used for minor alerts or exceptions.
520
         * @param text Text to display
521
         */
522
        public void warn (String text) {
523
                JOptionPane.showMessageDialog(colonet, text, "Colonet", JOptionPane.WARNING_MESSAGE);
524
        }
525

    
526
        /**
527
         * Display error message box on the screen. Used for major errors or exceptions in the program.
528
         * @param text Text to display
529
         */
530
        public void err (String text) {
531
                JOptionPane.showMessageDialog(colonet, text, "Colonet", JOptionPane.ERROR_MESSAGE);
532
        }
533

    
534

    
535
        /*
536
        * DataListener thread.
537
        *
538
        */
539
        class DataListener extends Thread {
540
                final int DATALISTENER_DELAY = 122;
541

    
542
                public DataListener () {
543
                        super("Colonet DataListener");
544
                }
545

    
546
                public void run () {
547
                        String line;
548
                        while (true) {
549
                                try {
550
                                        line = reader.readLine();
551
                                        if (line == null) {
552
                                                throw new IOException();
553
                                        }
554
                                        parseData(line);
555
                                        Thread.sleep(DATALISTENER_DELAY);
556
                                } catch (InterruptedException e) {
557
                                        return;
558
                                } catch (IOException e) {
559
                                        disconnect();
560
                                        return;
561
                                }
562
                        }
563
                }
564

    
565
                public void parseData (String line) {
566
                        // Sensor Matrix
567
                        if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
568
                                ColonetServerInterface.REQUEST_BOM_MATRIX))
569
                                colonet.parseMatrix(line);
570
                        // Task Queue
571
                        else if (line.startsWith(ColonetServerInterface.COLONET_QUEUE))
572
                                colonet.parseQueue(line);
573
                        // XBee IDs
574
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
575
                                ColonetServerInterface.REQUEST_XBEE_IDS))
576
                                colonet.parseXBeeIDs(line);
577
                        // Battery
578
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
579
                                ColonetServerInterface.BATTERY))
580
                                colonet.parseBattery(line);
581
                        // Robot Positions
582
                        else if (line.startsWith(ColonetServerInterface.RESPONSE_TO_CLIENT_REQUEST + " " +
583
                                ColonetServerInterface.CLIENT_REQUEST_ROBOT_POSITIONS))
584
                                colonet.parsePositions(line);
585
      else if (line.startsWith(ColonetServerInterface.ROBOT_REPORT_ARRIVED_AT_POSITION))
586
        colonet.parseMoveUpdate(line);
587
                        // Unknown type
588
                        else {
589
                                System.out.println("Got unknown data: " + line + "\n");
590
                        }
591
                }
592
        }
593
}