Project

General

Profile

Revision 476

renamed colonetgui to client

View differences:

trunk/code/projects/colonet/ColonetGUI/index.xhtml
1
<?xml version="1.0"?>
2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
3
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
4

  
5
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
6
  <head>
7
    <title>Colony Control Applet - Carnegie Mellon Robotics Club</title>
8
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
9
    <link rel="stylesheet" type="text/css" href="colonetstyle.css" />
10
  </head>
11

  
12
  <body>
13

  
14
    <div class="top">
15
      <h3>Colonet</h3>
16
      <img src="http://roboclub8.frc.ri.cmu.edu/mainwiki/roboclublogo.png" alt="RoboClub Logo" />
17
    </div>
18

  
19
    <div class="app">
20
      <p>
21
	<object class = "applet"
22
	   archive = "Colonet.jar"
23
	   type = "application/x-java-applet"
24
	   width = "950"
25
	   height = "550"
26
	   codebase = "."
27
	   codetype = "application/x-java-applet"
28
	   name = "Colonet"
29
	   title = "Colonet" >
30
	  <param name="type" value="application/x-java-applet" /> 
31
	  <param name = "code" value = "Colonet.class" />
32
	  <param name = "cache_option" value = "No" />
33
	</object>
34
	<br />
35
	Requires Java 5.
36
	<br />
37
      </p>
38
    </div>
39

  
40
    <div class="bottom">
41
      <p><b>Colony Project Home page:</b>&nbsp;<a href="http://www.robotcolony.org/">www.robotcolony.org</a></p>
42

  
43
      <p><b>Validator:</b><br />
44
	<a href="http://validator.w3.org/check?uri=referer">
45
	  <img src="http://www.w3.org/Icons/valid-xhtml11-blue"
46
	       alt="Valid XHTML 1.0 Transitional"
47
	       height="31"
48
	       width="88"
49
	       />
50
	</a>
51
	<br />
52
      </p>
53

  
54
    </div>
55

  
56
  </body>
57
</html>
trunk/code/projects/colonet/ColonetGUI/Colonet.java
1
//
2
//  Colonet.java
3
//
4

  
5
import javax.swing.*;
6
import javax.swing.event.*;
7
import javax.imageio.*;
8
import java.awt.*;
9
import java.awt.image.*;
10
import java.awt.event.*;
11
import java.net.*;
12
import java.io.*;
13
import java.util.*;
14

  
15

  
16
/**
17
*	The Colonet Graphical User Interface Applet for use locally and over an internet connection.
18
*	@author Gregory Tress
19
*	
20
*	To generate javadoc on this file or other java files, use javadoc *.java -d doc, where doc
21
*	is the name of the folder into which the files should be written.
22
*/
23
public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener, Runnable {
24

  
25
	// Used for images
26
	final int CANVAS_SIZE = 500;  //the applet may be slow if the canvas gets too large
27
	final int BUFFER = 50;
28
	final int RADIUS = 30;
29
	
30
	//Used for the robot controller
31
	final int VECTOR_CONTROLLER_HEIGHT = 220;
32
	final int VECTOR_CONTROLLER_WIDTH = 350;
33
	
34

  
35
	// Connection
36
	JTextField txtHost;				
37
	JTextField txtPort;				
38
	JButton btnConnect;	
39
	JButton btnGetXBeeIDs;
40
	JLabel lblConnectionStatus;
41
	JTextArea txtMatrix;
42
	JTextArea txtInfo; 
43
	JPanel panelConnect;
44
	JPanel panelServerInterface;
45
	Socket socket;					
46
	OutputStreamWriter out;
47
	DataUpdater dataUpdater;  
48
	
49
	// South
50
	JPanel panelSouth;
51
	JTextArea log;
52
	JScrollPane spLog;
53
	
54
	// Control
55
	JPanel panelControl;
56
	JTabbedPane tabPaneControl;
57
	JPanel panelRobotControl;
58
	JPanel panelRobotDirection;
59
	JPanel panelRobotDirectionButtons;
60
	JPanel panelRobotCommands;
61
	JButton btnF, btnB, btnL, btnR, btnActivate;
62
	JComboBox cmbRobotNum;
63
	JLabel lblBattery;
64
	JLabel lblSelected;
65
	BatteryIcon batteryIcon;
66
	JPanel panelBattery;
67
	VectorController vectorController;
68
	BufferedImage imageVectorControl;
69
	JButton btnAssignID;
70
	boolean setWaypoint;
71
	int setWaypointID;
72
	JButton btnCommand_MoveTo;
73
	JButton btnCommand_MoveAll;
74
	JButton btnCommand_StopTask;
75
	JButton btnCommand_ResumeTask;
76
	JButton btnCommand_ChargeNow;
77
	JButton btnCommand_StopCharging;
78
	
79
	// Task Manager
80
	JPanel panelTaskManager;
81
	JScrollPane spTaskManager;
82
	JPanel panelTaskManagerControls;
83
	JPanel panelTaskManagerControlsPriority;
84
	DefaultListModel taskListModel;
85
	JList taskList;
86
	JButton btnAddTask;
87
	JButton btnRemoveTask;
88
	JButton btnMoveTaskUp;
89
	JButton btnMoveTaskDown;
90
	JButton btnUpdateTasks;
91
	TaskAddWindow taskAddWindow;
92
	
93
	//Webcam
94
	WebcamPanel panelWebcam;
95
	GraphicsPanel panelGraph;
96
	GraphicsConfiguration gc;
97
	volatile BufferedImage image;
98
	volatile Graphics2D canvas;
99
	int cx, cy;
100
	JTabbedPane tabPaneMain;
101
	
102
	Font botFont;
103
	volatile int numBots;
104
	volatile int selectedBot;  //the user has selected this bot graphically
105
	volatile java.util.List <RobotIcon> robotIcons;  //contains boundary shapes around bots for click detection
106
	volatile int[] xbeeID;
107
	
108
	Thread paintThread;
109
	SelectionIndicator indicator;
110
	WebcamLoader webcamLoader;
111
	ColonetServerInterface csi;
112

  
113
	
114
	public void init () {
115
		// Set the default look and feel - choose one
116
        //String laf = UIManager.getSystemLookAndFeelClassName();
117
		String laf = UIManager.getCrossPlatformLookAndFeelClassName();
118
		//String laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
119
        try {
120
            UIManager.setLookAndFeel(laf);
121
        } catch (UnsupportedLookAndFeelException exc) {
122
            System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
123
        } catch (Exception exc) {
124
            System.err.println ("Error loading " + laf + ": " + exc);
125
        }
126
		// We should invoke and wait to avoid browser display difficulties
127
		Runnable r = new Runnable() {
128
			public void run() {
129
				createAndShowGUI();
130
			}
131
		};
132
		try {
133
			SwingUtilities.invokeAndWait(r);
134
		} catch (InterruptedException e) {
135
			//Not really sure why we would be in this situation
136
			System.out.println("InterruptedException in init: " + e);
137
		} catch (java.lang.reflect.InvocationTargetException e) {
138
			//This could happen for various reasons if there is a problem in createAndShowGUI
139
			e.printStackTrace();
140
		}
141
	}
142
	
143
	public void destroy () {
144
		try { paintThread.interrupt(); } catch (Exception e) { }
145
		try { indicator.interrupt(); } catch (Exception e) { }
146
	}
147

  
148
	private synchronized void createAndShowGUI () {
149
		// init graphical elements
150
		// Get the graphics configuration of the screen to create a buffer
151
		gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
152
			.getDefaultScreenDevice().getDefaultConfiguration();
153
		image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE);
154
		canvas = image.createGraphics();
155
		canvas.setStroke(new BasicStroke(2));  //set pen width
156
		panelGraph = new GraphicsPanel(image, false);  //set automatic double-buffering to false. we are doing it manually.
157
		panelWebcam = new WebcamPanel();
158
		tabPaneMain = new JTabbedPane();
159
		tabPaneMain.add(panelWebcam, "Webcam");
160
		//tabPaneMain.add(panelGraph, "Graph");
161
		
162
		// Calculate center of canvas
163
		cx = image.getWidth() / 2;
164
		cy = image.getHeight() / 2;
165
		
166
		// Set up robots
167
		botFont = new Font("Arial", Font.PLAIN, 14);
168
		numBots = 0;
169
		selectedBot = -1;
170
		robotIcons = new ArrayList <RobotIcon> ();
171
		
172
		// Connection area
173
		txtMatrix = new JTextArea();
174
		txtMatrix.setBorder(BorderFactory.createTitledBorder("Input Matrix"));
175
		txtInfo = new JTextArea();
176
		txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
177
		txtInfo.setEditable(false);
178
		txtHost = new JTextField(this.getDocumentBase().getHost());
179
		txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
180
		txtPort = new JTextField("10123");
181
		txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
182
		btnConnect = new JButton("Connect");
183
		btnGetXBeeIDs = new JButton("Get XBee IDs");
184
		getRootPane().setDefaultButton(btnConnect);
185
		lblConnectionStatus = new JLabel("Status: Offline");
186
		panelConnect = new JPanel();
187
		panelConnect.setLayout(new GridLayout(6,1));
188
		panelConnect.add(lblConnectionStatus);
189
		panelConnect.add(txtHost);
190
		panelConnect.add(txtPort);
191
		panelConnect.add(btnConnect);
192
		//panelConnect.add(btnGetXBeeIDs);
193
		panelServerInterface = new JPanel();
194
		panelServerInterface.setLayout(new GridLayout(2,1));
195
		panelServerInterface.add(panelConnect);
196
		panelServerInterface.add(txtMatrix);
197
	
198
		// Robot direction panel
199
		panelRobotDirection = new JPanel();
200
		panelRobotDirectionButtons = new JPanel();
201
		btnF = new JButton("^");
202
		btnB = new JButton("v");
203
		btnL = new JButton("<");
204
		btnR = new JButton(">");
205
		btnActivate = new JButton("o");
206
		panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
207
		panelRobotDirectionButtons.add(btnActivate);
208
		panelRobotDirectionButtons.add(btnF);
209
		panelRobotDirectionButtons.add(btnB);
210
		panelRobotDirectionButtons.add(btnL);
211
		panelRobotDirectionButtons.add(btnR);
212
		
213
		imageVectorControl = gc.createCompatibleImage(VECTOR_CONTROLLER_WIDTH, VECTOR_CONTROLLER_HEIGHT);
214
		vectorController = new VectorController(imageVectorControl);
215
		panelRobotDirection.setLayout(new BorderLayout());
216
		panelRobotDirection.add(vectorController, BorderLayout.CENTER);
217
		panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
218
		
219
		// Robot Control and Commands
220
		panelRobotCommands = new JPanel();
221
		panelRobotCommands.setLayout(new GridLayout(5,2));
222
		cmbRobotNum = new JComboBox();
223
		// Battery subset
224
		batteryIcon = new BatteryIcon(25);
225
		lblBattery = new JLabel(batteryIcon);
226
		lblSelected = new JLabel("None");
227
		// Command subset
228
		setWaypoint = false;
229
		setWaypointID = -1;
230
		btnAssignID = new JButton("Assign ID");
231
		btnCommand_MoveTo = new JButton("Move to ...");
232
		btnCommand_MoveAll = new JButton("Move all ...");
233
		btnCommand_StopTask = new JButton("Stop Current Task");
234
		btnCommand_ResumeTask = new JButton("Resume Current Task");
235
		btnCommand_ChargeNow = new JButton("Recharge Now");
236
		btnCommand_StopCharging = new JButton("Stop Recharging");
237
		panelRobotCommands.add(new JLabel("Select Robot to Control: "));
238
		panelRobotCommands.add(cmbRobotNum);
239
		panelRobotCommands.add(new JLabel("Battery Level: "));
240
		panelRobotCommands.add(lblBattery);
241
		panelRobotCommands.add(new JLabel("Selected Icon: "));
242
		panelRobotCommands.add(lblSelected);
243
		panelRobotCommands.add(btnAssignID);
244
		panelRobotCommands.add(new JLabel(""));
245
		panelRobotCommands.add(btnCommand_MoveTo);
246
		panelRobotCommands.add(btnCommand_MoveAll);
247
		//panelRobotCommands.add(btnCommand_StopTask);
248
		//panelRobotCommands.add(btnCommand_ResumeTask);
249
		//panelRobotCommands.add(btnCommand_ChargeNow);
250
		//panelRobotCommands.add(btnCommand_StopCharging);
251
		panelRobotControl = new JPanel();
252
		panelRobotControl.setLayout(new GridLayout(2,1));
253
		panelRobotControl.add(panelRobotDirection);
254
		panelRobotControl.add(panelRobotCommands);
255
		
256
		
257
		// Task Manager
258
		panelTaskManager = new JPanel();
259
		panelTaskManager.setLayout(new BorderLayout());
260
		taskListModel = new DefaultListModel();
261
		taskList = new JList(taskListModel);
262
		taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
263
		taskList.setSelectedIndex(0);
264
		spTaskManager = new JScrollPane(taskList);
265
		panelTaskManagerControls = new JPanel();
266
		panelTaskManagerControls.setLayout(new GridLayout(1,4));
267
		panelTaskManagerControlsPriority = new JPanel();
268
		panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
269
		btnAddTask = new JButton("Add...");
270
		btnRemoveTask = new JButton("Remove");
271
		btnMoveTaskUp = new JButton("^");
272
		btnMoveTaskDown = new JButton("v");
273
		btnUpdateTasks = new JButton("Update");
274
		panelTaskManagerControlsPriority.add(btnMoveTaskUp);
275
		panelTaskManagerControlsPriority.add(btnMoveTaskDown);
276
		panelTaskManagerControls.add(btnAddTask);
277
		panelTaskManagerControls.add(btnRemoveTask);
278
		panelTaskManagerControls.add(btnUpdateTasks);
279
		panelTaskManagerControls.add(panelTaskManagerControlsPriority);
280
		panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
281
		panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
282
		panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
283
		taskAddWindow = new TaskAddWindow();
284
		
285
		// Message log
286
		log = new JTextArea();
287
		spLog = new JScrollPane(log,
288
			ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 
289
			ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
290
		spLog.setBorder(BorderFactory.createTitledBorder("Log"));
291
		spLog.setPreferredSize(new Dimension(0, 120));
292
		log.setEditable(false);
293
		
294
		// Main control mechanism
295
		panelControl = new JPanel();
296
		panelControl.setLayout(new GridLayout(1,1));
297
		tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
298
		tabPaneControl.setPreferredSize(new Dimension(VECTOR_CONTROLLER_WIDTH, 0));
299
		tabPaneControl.addTab("Connection", panelServerInterface);
300
		tabPaneControl.addTab("Robots", panelRobotControl);
301
		tabPaneControl.addTab("Tasks", panelTaskManager);
302
		panelControl.add(tabPaneControl);
303
		
304
		// Set up elements in the south
305
		panelSouth = new JPanel();
306
		panelSouth.setLayout(new GridLayout(1,2));
307
		//panelSouth.add(spLog);
308

  
309
		// Put all elements in the ContentPane
310
		this.getContentPane().setLayout(new BorderLayout());
311
		this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
312
		this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
313
		this.getContentPane().add(panelControl, BorderLayout.EAST);
314
		this.setVisible(true);
315
		
316
		/* Add all listeners here */
317
		// Task Management
318
		btnAddTask.addActionListener(this);
319
		btnRemoveTask.addActionListener(this);
320
		btnMoveTaskUp.addActionListener(this);
321
		btnMoveTaskDown.addActionListener(this);
322
		btnUpdateTasks.addActionListener(this);
323
		// Robot Control
324
		btnF.addActionListener(this);
325
		btnB.addActionListener(this);
326
		btnL.addActionListener(this);
327
		btnR.addActionListener(this);
328
		btnF.addKeyListener(this);
329
		btnB.addKeyListener(this);
330
		btnL.addKeyListener(this);
331
		btnR.addKeyListener(this);
332
		btnActivate.addActionListener(this);
333
		btnActivate.addKeyListener(this);
334
		cmbRobotNum.addKeyListener(this);
335
		btnCommand_MoveTo.addActionListener(this);
336
		btnCommand_MoveAll.addActionListener(this);
337
		btnCommand_StopTask.addActionListener(this);
338
		btnCommand_ResumeTask.addActionListener(this);
339
		btnCommand_ChargeNow.addActionListener(this);
340
		btnCommand_StopCharging.addActionListener(this);
341
		// Other
342
		btnConnect.addActionListener(this);
343
		btnGetXBeeIDs.addActionListener(this);
344
		btnAssignID.addActionListener(this);
345
		panelWebcam.addMouseListener(this);	
346
				
347
		// Set up animation threads
348
		indicator = new SelectionIndicator(canvas);
349
		indicator.setRadius(RADIUS+3, 15);  //a tad more than the bot radius
350
	
351
	}
352
	
353
	public void run () {
354
		while (true) {
355
			repaint();
356
			try { 
357
				Thread.sleep(90);
358
			} catch (InterruptedException e) {
359
				return;
360
			}
361
		}
362
	}
363
	
364
	public void paint (Graphics g) {
365
	    super.paint(g);
366
	}
367
	
368
	public void update (Graphics g) {
369
	    paint(g);
370
	}
371
		
372
	/** 
373
	* Gets the JTextArea used for storing the activity log. This method returns a reference to the 
374
	* JTextArea that stores the log. The log can contain any activity that is revelant to the use
375
	* of the applet, and may optionally display debugging information.
376
	*
377
	* @return the JTextArea where BOM matrix information is stored.
378
	*/ 
379
	public JTextArea getLog () {
380
		return log;
381
	}
382
	
383
	/** 
384
	* Gets the JTextArea used for storing the BOM matrix data. This method returns a reference to the 
385
	* JTextArea that stores the BOM matrix. The values in the matrix are stored as integers separated 
386
	* by spaces, and the lines should be separated by a newline.
387
	* 
388
	* @return the JTextArea where BOM matrix information is stored.
389
	*/
390
	public JTextArea getMatrixInput () {
391
		return txtMatrix;
392
	}
393
	
394
	/**
395
	* Parses a String containing BOM matrix information.
396
	* The ColonetServerInterface receives lines of the BOM matrix.  (For encoding 
397
	* information, see the ColonetServerInterface documentation.)  The entire matrix is passed
398
	* to the client when requested. This method takes a string of the form 
399
	* "[command code] [command code] [number of robots] [data0] [data1] ..."
400
	* with tokens separated by spaces and containing no brackets.  
401
	* The [command code]s are predefined values identifying this String as a BOM data
402
	* String, [number of robots] is an integer, and the values that follow are 
403
	* the sensor readings of the robots in order, starting with robot 0.  Only [number of robots]^2
404
	* data entries will be read.  The matrix values are saved locally until the next String is parsed.
405
	* 
406
	*
407
	* @param line the String containing BOM matrix information.
408
	* @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots]^2 data entries in the String
409
	*/
410
	public void parseMatrix (String line) {
411
		txtMatrix.setText("");
412
		String [] str = line.split(" ");
413
		int num = Integer.parseInt(str[2]);
414
		for (int i = 0; i < num; i++) {
415
			for (int j = 0; j < num; j++) {
416
				String next = str[3 + i*num + j];
417
				if (next.equals("-1"))
418
					txtMatrix.append("-");
419
				else 
420
					txtMatrix.append(next);
421
				if (j < num - 1) 
422
					txtMatrix.append(" ");
423
			}
424
			if (i < num - 1) 
425
				txtMatrix.append("\n");
426
		}
427
		
428
	}
429
	
430
	public void connect () {
431
	    webcamLoader = new WebcamLoader(this);
432
		dataUpdater = new DataUpdater();
433
		paintThread = new Thread(this, "paintThread");
434
		csi = new ColonetServerInterface(this);
435
		csi.connect(txtHost.getText(), txtPort.getText());
436
		if (!csi.isReady())
437
			return;
438
		btnConnect.setEnabled(false);
439
		lblConnectionStatus.setText("Status: Connected");
440
		//paintThread.start();
441
		dataUpdater.start();
442
		webcamLoader.start();
443
	}
444
	
445
	public void disconnect () {
446
	    btnConnect.setEnabled(true);
447
	    lblConnectionStatus.setText("Status: Disconnected");
448
	    try { paintThread.interrupt(); } catch (Exception e) { }
449
		try { indicator.interrupt(); } catch (Exception e) { }
450
		
451
	}
452
	
453
	/**
454
	* Parses a String containing a task queue update.
455
	* Format is currently not specified.
456
	* This method currently does nothing.
457
	*
458
	* @param line the String containing task queue update information.
459
	*/
460
	public void parseQueue (String line) {
461
		log.append("Got queue update\n");
462
		//TODO: display new queue data in tasks tab
463
	}
464
	
465
	/**
466
	* Parses a String containing XBee ID values.
467
	* The ColonetServerInterface receives Strings of XBee information.  (For encoding 
468
	* information, see the ColonetServerInterface documentation.)  This method takes
469
	* a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
470
	* with tokens separated by spaces and containing no brackets.  
471
	* The [command code]s are predefined values identifying this String as an XBee
472
	* ID String, [number of robots] is an integer, and the values that follow are 
473
	* the IDs of the robots in order, starting with robot 0.  Only [number of robots] 
474
	* will be read.  The ID values are saved locally until the next String is parsed.
475
	* The purpose of having this list is to ensure that robots are properly identified for control purposes.
476
	* This keeps robot identification consistent between sessions and prevents arbitrary assignment. 
477
	*
478
	* @param line the String containing XBee ID information.
479
	* @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots] IDs in the String
480
	* @see ColonetServerInterface#sendXBeeIDRequest()
481
	*/
482
	public void parseXBeeIDs (String line) {
483
	
484
		String [] str = line.split(" ");
485
		int num = Integer.parseInt(str[2]);
486
		xbeeID = new int[num];
487
		for (int i = 0; i < num; i++)
488
			xbeeID[i] = Integer.parseInt(str[i+3]);
489
		
490
		//update the list of robots to control
491
		//but save the old value first
492
		Object oldSelection = cmbRobotNum.getSelectedItem();
493
		cmbRobotNum.removeAllItems();
494
		cmbRobotNum.addItem(new String("   All   "));
495
		for (int i = 0; i < num; i++)
496
			cmbRobotNum.addItem(new String("" + xbeeID[i]));
497
		cmbRobotNum.setSelectedItem(oldSelection);
498
	}
499
	
500
	/**
501
	* Parses a String containing battery information.
502
	* The ColonetServerInterface receives Strings of battery information.  (For encoding 
503
	* information, see the ColonetServerInterface documentation.)  This method takes
504
	* a string of the form "[command code] [command code] [robot ID] [value]"
505
	* with tokens separated by spaces and containing no brackets.  
506
	* The [command code]s are predefined values identifying this String as a battery
507
	* information String, [robot ID] is an integer, and [value] is a battery measurement.
508
	* This updates the batery information for a single robot.
509
	* 
510
	*
511
	* @param line the String containing battery information.
512
	* @see ColonetServerInterface#sendBatteryRequest(int)
513
	*/
514
	public void parseBattery (String line) {
515
		String [] str = line.split(" ");
516
		int botNum = Integer.parseInt(str[2]);
517
		int level = Integer.parseInt(str[3]);
518
		int selected = -1;
519
		try { 
520
		    selected = Integer.parseInt((String)cmbRobotNum.getSelectedItem());
521
		} catch (Exception e) {
522
		}
523
		if (selected == botNum) {
524
			batteryIcon.setLevel(level);
525
		}
526
	}
527
	
528
	/**
529
	* Parses a String containing visual robot position information along with 
530
	* canonical ID assignments.
531
	*/
532
	public void parsePositions (String line) {
533
		String [] str = line.split(" ");
534
		java.util.List <RobotIcon> newList = new ArrayList <RobotIcon> ();
535
		
536
		for (int i = 2; i < str.length; i+=3) {
537
			int id = Integer.parseInt(str[i]);
538
			int x = Integer.parseInt(str[i+1]);
539
			int y = Integer.parseInt(str[i+2]);
540
			RobotIcon newIcon = new RobotIcon(id, x, y);
541
			newList.add(newIcon);
542
		}
543
		
544
		robotIcons = newList;
545
	
546
	}
547
	
548
	
549
	//
550
	// MouseListener methods
551
	//
552
	public void mouseExited(MouseEvent e) {
553
	}
554
	public void mouseEntered(MouseEvent e) {
555
	}
556
	public void mouseReleased(MouseEvent e) {
557
	}
558
	public void mouseClicked(MouseEvent e) {
559
	}
560
	public void mousePressed(MouseEvent e) {
561
	
562
	    // If we are selecting a waypoint (destination) for a specific bot
563
	    if (setWaypoint && setWaypointID  >= 0) {
564
	        setWaypoint = false;
565
	        panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
566
	        if (selectedBot < 0)
567
	            return;
568
	        
569
	        RobotIcon r = robotIcons.get(selectedBot);
570
	        r.destx = e.getX();
571
	        r.desty = e.getY();
572
	        
573
	        csi.sendAbsoluteMove(r.id, r.destx, r.desty);
574
	        
575
	        return;
576
	    }
577
	    
578
	    // If we are setting all waypoints
579
	    if (setWaypoint) {
580
	        setWaypoint = false;
581
	        panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
582
	        for (int i = 0; i < robotIcons.size(); i++) {
583
	            RobotIcon r = robotIcons.get(i);
584
	            r.destx = e.getX();
585
	            r.desty = e.getY();
586
	        }
587
	        return;
588
	    }
589
	    
590
	    // Otherwise, we are selecting a bot, or doing nothing
591
		for (int i = 0; i < robotIcons.size(); i++) {
592
		    RobotIcon r = robotIcons.get(i);
593
		    if (r.contains(e.getX(), e.getY())) {
594
		        selectedBot = i;
595
		        lblSelected.setText(" " + r.id);
596
		    }
597
		}
598
		repaint();
599
	}
600
	public void mouseDragged(MouseEvent e) {
601
	}
602
	public void mouseMoved(MouseEvent e) {
603
	}
604
	
605
	//
606
	// KeyListener methods
607
	//
608
	public void keyPressed (KeyEvent e) {
609
		int code = e.getKeyCode();
610
		if (code == KeyEvent.VK_UP) {
611
			vectorController.setMaxForward();
612
			vectorController.sendToServer();
613
		} else if (code == KeyEvent.VK_DOWN) {
614
			vectorController.setMaxReverse();
615
			vectorController.sendToServer();
616
		} else if (code == KeyEvent.VK_LEFT) {
617
			vectorController.setMaxLeft();
618
			vectorController.sendToServer();
619
		} else if (code == KeyEvent.VK_RIGHT) {
620
			vectorController.setMaxRight();
621
			vectorController.sendToServer();
622
		} else if (code == KeyEvent.VK_S) {
623
			vectorController.setZero();
624
			vectorController.sendToServer();
625
		}
626
	}
627
	public void keyReleased (KeyEvent e) {
628
	}
629
	public void keyTyped (KeyEvent e) {
630
	}
631
	
632
	
633
	//
634
	// ActionListener method
635
	//
636
	public void actionPerformed (ActionEvent e) {
637
		Object source = e.getSource();
638
		
639
		// General Actions
640
		if (source == btnConnect) {
641
			connect();
642
		} else if (source == btnGetXBeeIDs) {
643
			csi.sendXBeeIDRequest();
644
		} else if (source == btnAssignID) {
645
		    String message;
646
		    if (selectedBot < 0)
647
		        return;
648
		    int curID = robotIcons.get(selectedBot).id;
649
		    if (curID < 0)
650
		        message = "That robot is unidentified. Please specify its ID.";
651
		    else
652
		        message = "That robot has ID " + curID + ". You may reassign it now.";
653
		    String result = JOptionPane.showInputDialog(this, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
654
		    if (result == null)
655
		        return;
656
	        int newID = -1;
657
		    try {
658
		        newID = Integer.parseInt(result);
659
		    } catch (Exception ex) {
660
		        csi.warn("Invalid ID.");
661
		        return;
662
		    }
663
		    // Assign new ID and update display  
664
			csi.sendIDAssignment(curID, newID);
665
		    robotIcons.get(selectedBot).id = newID;
666
		    robotIcons.get(selectedBot).color = Color.GREEN;
667
		    lblSelected.setText(" " + newID);
668
		    
669
			
670
		}
671
		
672
		// Robot Movement Controls
673
		else if (source == btnF) {
674
			vectorController.setMaxForward();
675
			vectorController.sendToServer();
676
		} else if (source == btnB) {
677
			vectorController.setMaxReverse();
678
			vectorController.sendToServer();
679
		} else if (source == btnL) {
680
			vectorController.setMaxLeft();
681
			vectorController.sendToServer();
682
		} else if (source == btnR) {
683
			vectorController.setMaxRight();
684
			vectorController.sendToServer();
685
		} else if (source == btnActivate) {
686
			vectorController.setZero();
687
			vectorController.sendToServer();
688
		}
689
		// Robot Commands (non-movement)
690
		else if (source == btnCommand_MoveTo) {
691
		    if (selectedBot < 0)
692
		        return;
693
		    panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
694
		    setWaypoint = true;
695
		    setWaypointID = selectedBot;
696
		    		
697
		} else if (source == btnCommand_MoveAll) {
698
		    panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
699
		    setWaypoint = true;
700
		    setWaypointID = -1;
701
		    		
702
		} else if (source == btnCommand_StopTask) {
703
		
704
		} else if (source == btnCommand_ResumeTask) {
705
		
706
		} else if (source == btnCommand_ChargeNow) {
707
		
708
		} else if (source == btnCommand_StopCharging) {
709
		
710
		}
711
			
712
		// Queue Management
713
		else if (source == btnAddTask) {
714
			taskAddWindow.prompt();
715
		} else if (source == btnRemoveTask) {
716
			if (taskList.getSelectedIndex() >= 0);
717
				csi.sendQueueRemove(taskList.getSelectedIndex());
718
			csi.sendQueueUpdate();
719
		} else if (source == btnMoveTaskUp) {
720
			csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
721
			csi.sendQueueUpdate();
722
		} else if (source == btnMoveTaskDown) {
723
			csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
724
			csi.sendQueueUpdate();
725
		} else if (source == btnUpdateTasks) {
726
			csi.sendQueueUpdate();
727
		}
728
	}
729
	
730
	/*
731
	*	SelectionIndicator thread.
732
	*	Graphical representation of the selection marker
733
	*
734
	*	step() and draw() are synchronized methods. step() is private and 
735
	*	used to update the position of the crosshairs. draw() is called 
736
	*	externally and should only run if all calculations in step() have
737
	*	been completed.
738
	*/
739
	private class SelectionIndicator extends Thread {
740
	
741
		final int INDICATOR_DELAY = 180;
742
		final double DTHETA = 0.4;    //larger values make the marker rotate faster
743
		Graphics2D g;   //canvas to draw on
744
		boolean running;
745
		
746
		int sx, sy;		//center
747
		int r, dr;		//radius and width of marker
748
		double theta;   //current angle
749
		
750
		volatile Polygon poly1, poly2, poly3, poly4;
751
		
752
		int px1, py1;
753
		int rx1, ry1;
754
		int px2, py2;
755
		int rx2, ry2;
756
		int px3, py3;
757
		int rx3, ry3;
758
		int px4, py4;
759
		int rx4, ry4;
760
		
761
		int steps;
762
	
763
		public SelectionIndicator (Graphics2D g) {
764
			super("SelectionIndicator");
765
			this.g = g;
766
			running = false;
767
			steps = 0;
768
			
769
			theta = 0;
770
			rx1 = 0; ry1 = 0;
771
			px1 = 0; py1 = 0;
772
			rx2 = 0; ry2 = 0;
773
			px2 = 0; py2 = 0;
774
			rx3 = 0; ry3 = 0;
775
			px3 = 0; py3 = 0;
776
			rx4 = 0; ry4 = 0;
777
			px4 = 0; py4 = 0;
778
		}
779
		
780
		public synchronized void setCenter (int sx, int sy) {
781
			if (sx == this.sx && sy == this.sy) return;
782
			this.sx = sx;
783
			this.sy = sy;
784
			steps = 0;
785
		}
786
		
787
		public synchronized void setRadius (int r, int dr) {
788
			this.r = r;
789
			this.dr = dr;
790
			steps = 0;
791
		}
792
		
793
		public void run () {
794
			running = true;
795
			while (running) {
796
				step();
797
				try { 
798
					Thread.sleep(INDICATOR_DELAY);
799
				} catch (InterruptedException e) {
800
					running = false;
801
					return;
802
				}
803
			}
804
		}
805
		
806
		private synchronized void step () {
807
			Polygon poly1_new = new Polygon();
808
			Polygon poly2_new = new Polygon();
809
			Polygon poly3_new = new Polygon();
810
			Polygon poly4_new = new Polygon();
811
		
812
			//the step
813
			theta = (theta + DTHETA/Math.PI) % (Math.PI);
814
			
815
			//the calculation
816
			//let p be the point of the pointy thing toward the center
817
			//let r be the point at the opposite side
818
			
819
			//recalculate radius, if it will look cool, lolz
820
			int newr = r;
821
			if (steps < 100)
822
			newr = (int)( r + 200/(steps+1) );
823
			
824
			//precompute values for dx and dy
825
			int dx_inner = (int)(newr * Math.cos(theta));
826
			int dy_inner = (int)(newr * Math.sin(theta));
827
			int dx_outer = (int)((newr+dr) * Math.cos(theta));
828
			int dy_outer = (int)((newr+dr) * Math.sin(theta));
829
			
830
			//calculate polygon constants
831
			int dy_poly = (int)(dr/2 * Math.cos(theta));
832
			int dx_poly = (int)(dr/2 * Math.sin(theta));
833
			
834
			//determine critical points
835
			//kansas city shuffle!
836
			px1 = sx + dx_inner;
837
			py1 = sy - dy_inner;
838
			rx1 = sx + dx_outer;
839
			ry1 = sy - dy_outer;
840
			px2 = sx - dx_inner;
841
			py2 = sy + dy_inner;
842
			rx2 = sx - dx_outer;
843
			ry2 = sy + dy_outer;
844
			px3 = sx - dy_inner;
845
			py3 = sy - dx_inner;
846
			rx3 = sx - dy_outer;
847
			ry3 = sy - dx_outer;
848
			px4 = sx + dy_inner;
849
			py4 = sy + dx_inner;
850
			rx4 = sx + dy_outer;
851
			ry4 = sy + dx_outer;
852
			
853
			//create polygons
854
			poly1_new.addPoint(px1, py1);
855
			poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly);
856
			poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly);
857
			poly2_new.addPoint(px2, py2);
858
			poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly);
859
			poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly);
860
			poly3_new.addPoint(px3, py3);
861
			poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly);
862
			poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly);
863
			poly4_new.addPoint(px4, py4);
864
			poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly);
865
			poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly);
866
			
867
			//reassign updated polygons
868
			poly1 = poly1_new;
869
			poly2 = poly2_new;
870
			poly3 = poly3_new;
871
			poly4 = poly4_new;
872
		
873
			if (steps < 300) steps++;
874
		}
875
		
876
		public synchronized void draw () {
877
			if (!running) return;
878
			g.setColor(Color.GRAY);
879
			//draw polygons
880
			g.fillPolygon(poly1);
881
			g.fillPolygon(poly2);
882
			g.fillPolygon(poly3);
883
			g.fillPolygon(poly4);
884
		}
885
	
886
	}
887
	
888
	/*
889
	*	DataUpdater thread.
890
	*   The purpose of this thread is to request data from the server at regular intervals.
891
	*
892
	*/
893
	class DataUpdater extends Thread {
894
		final int DATAUPDATER_DELAY = 2200;
895
		
896
		public DataUpdater () {
897
			super("Colonet DataUpdater");
898
		}
899
		
900
		public void run () {
901
			String line;
902
			while (true) {
903
				try {
904
					//request more data
905
					if (csi != null && csi.isReady()) {
906
                        csi.sendPositionRequest();
907
						csi.sendXBeeIDRequest();
908
						if (cmbRobotNum.getSelectedIndex() > 0) {
909
						    String sel = (String) cmbRobotNum.getSelectedItem();
910
						    int num = Integer.parseInt(sel);
911
							csi.sendBatteryRequest(num);
912
						}
913
					}
914
					Thread.sleep(DATAUPDATER_DELAY);
915
				} catch (InterruptedException e) {
916
					return;
917
				} 
918
			}
919
		}
920

  
921
	}
922
	
923
	/*
924
	*	GraphicsPanel class
925
	*	An extension of JPanel, designed for holding an image that will be repainted regularly.
926
	*/
927
	class GraphicsPanel extends JPanel {
928
		protected Image img;
929
	
930
		public GraphicsPanel (Image img) {
931
			super();
932
			this.img = img;
933
		}
934
		
935
		public GraphicsPanel (Image img, boolean isDoubleBuffered) {
936
			super(isDoubleBuffered);
937
			this.img = img;
938
		}
939
		
940
		public void paint (Graphics g) {
941
			// Place the buffered image on the screen, inside the panel
942
			g.drawImage(img, 0, 0, Color.WHITE, this);
943
		}
944
	
945
	}
946
	
947
	/*
948
	*	WebcamPanel class
949
	*	Enables more efficient image handling in a component-controlled environment
950
	*/
951
	class WebcamPanel extends JPanel {
952
		int BORDER = 16;  // this is arbitrary. it makes the image look nice inside a border.
953
		int BOT_RADIUS = 40;
954
		volatile BufferedImage img;
955
	
956
		public WebcamPanel () {
957
			super();
958
		}
959
		
960
		public void setImage (BufferedImage newimg) {
961
			if (img != null) {
962
				img.flush();
963
			}
964
			System.gc();
965
			img = newimg;
966
		}
967
		
968
				
969
		public void paint (Graphics g) {
970
			
971
			if (img == null)
972
				return;
973
			// Place the image on the screen, inside the panel
974
			
975
			int maxWidth = getWidth() - 2*BORDER;
976
			int maxHeight = getHeight() - 2*BORDER;
977
			double widthRatio = 1.0 * maxWidth / img.getWidth();
978
			double heightRatio = 1.0 * maxHeight / img.getHeight();
979
			int newWidth = 0;
980
			int newHeight = 0;
981
			int x = 0;
982
			int y = 0;
983
			
984
			if (widthRatio > heightRatio) {  //height is the limiting factor
985
			    newHeight = maxHeight;
986
			    newWidth = img.getWidth() * maxHeight / img.getHeight();
987
			    y = BORDER;
988
			    x = (maxWidth - newWidth) / 2 + BORDER;
989
			} else {  //width is the limiting factor
990
			    newWidth = maxWidth;
991
			    newHeight = img.getHeight() * maxWidth / img.getWidth();
992
			    x = BORDER;
993
			    y = (maxHeight - newHeight) / 2 + BORDER;
994
			}
995
			
996
			Image imgScaled = img.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST);
997
			g.drawImage(imgScaled, x, y, this);
998
			
999
						
1000
			// Draw Identifiers
1001
			if (robotIcons == null)
1002
				return;
1003
			
1004
			((Graphics2D)g).setStroke(new BasicStroke(2));
1005
			for (int i = 0; i < robotIcons.size(); i++) {
1006
				RobotIcon r = robotIcons.get(i);
1007
				g.setColor(r.color);
1008
				if (widthRatio > heightRatio) {
1009
				    
1010
				
1011
				} else {
1012
				    
1013
				}
1014
				g.drawOval(r.x-RADIUS, r.y-RADIUS, 2*r.RADIUS, 2*r.RADIUS);
1015
				// If the robot has a destination, draw the vector
1016
				if (r.destx >= 0) {
1017
				    g.drawLine(r.x, r.y, r.destx, r.desty);
1018
				}
1019
			}
1020
			
1021
			// Identify currently-selected robot
1022
			if (selectedBot == -1)
1023
			    return;
1024
			g.setColor(Color.YELLOW);
1025
			RobotIcon r = robotIcons.get(selectedBot);
1026
			g.drawOval(r.x-RADIUS-6, r.y-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12);
1027
			
1028
		}
1029
	
1030
	}
1031
	
1032
	/*
1033
	*	WebcamLoader class
1034
	*	Handles the loading of the webcam image.
1035
	*/
1036
	class WebcamLoader extends Thread 
1037
	{
1038
		final int WEBCAMLOADER_DELAY = 300;
1039
		final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg";
1040
				
1041
		URL imagePath;
1042
		
1043
		MediaTracker mt;
1044
		BufferedImage image;
1045
		Random rand;
1046
		
1047
		public WebcamLoader (JApplet applet)
1048
		{
1049
			super("ColonetWebcamLoader");
1050
			mt = new MediaTracker(applet);
1051
			ImageIO.setUseCache(false);
1052
			rand = new Random();
1053
		}
1054
		
1055
		public void run ()
1056
		{
1057
			while (true) {
1058
				try {
1059
					Thread.sleep(WEBCAMLOADER_DELAY);
1060
					if (image != null) 
1061
						image.flush();
1062
					System.gc();
1063
					try {
1064
				        imagePath = new URL(IMAGE_PATH + "?rand=" + rand.nextInt(50000));
1065
			        } catch (MalformedURLException e) {
1066
				        System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
1067
			        }
1068
					image = ImageIO.read(imagePath);
1069
					// The MediaTracker waitForID pauses the thread until the image is loaded.
1070
					// We don't want to display a half-downloaded image.
1071
					mt.addImage(image, 1);
1072
					mt.waitForID(1);
1073
					mt.removeImage(image);
1074
					// Save
1075
					panelWebcam.setImage(image);
1076
				} catch (InterruptedException e) {
1077
					return;
1078
				} catch (java.security.AccessControlException e) {
1079
					csi.warn("Could not load webcam.\n" + e);
1080
					return;
1081
				} catch (IOException e) {
1082
					log.append("IOException while trying to load image.");
1083
				}
1084
			}
1085
		}
1086
		
1087
	}
1088
	
1089
	/*
1090
	*  RobotIcon class
1091
	*  Provides a means for graphically representing and keeping track of webcam bots.
1092
	*/
1093
	class RobotIcon {
1094
		public final int RADIUS = 30;
1095
		public final int CLOSE = 80;
1096
		
1097
		public int x, y;
1098
		public int destx, desty;
1099
		public int id;
1100
		public Color color;
1101
		
1102
		public RobotIcon (int id, int x, int y) {
1103
			this.color = Color.RED;
1104
			this.x = x;
1105
			this.y = y;
1106
			this.id = id;
1107
			this.destx = -1;
1108
			this.desty = -1;
1109
		}
1110
		
1111
		/**
1112
		*  Relocates this RobotIcon to a new coordinate point.
1113
		*
1114
		*/
1115
		public void move (int newX, int newY) {
1116
			this.x = newX;
1117
			this.y = newY;
1118
		}
1119
		
1120
		/**
1121
		*  Determines if a given point is within a reasonable range of the current location
1122
		*  to be considered the same robot when moving. The threshold is determined by the 
1123
		*  CLOSE value.
1124
		*
1125
		*  @returns Whether or not the given point is reasonably close to the current location.
1126
		*
1127
		*/
1128
		public boolean isClose (int nx, int ny) {
1129
			int dist = (int) Point.distance(this.x, this.y, nx, ny);
1130
			return (dist < CLOSE);
1131
		}
1132
		
1133
		/**
1134
		*  Determines whether a given point is within the rectangle that circumscribes the
1135
		*  robot's circlular icon. Used for clicking on robots in webcam view.
1136
        *
1137
        */
1138
		public boolean contains (int px, int py) {
1139
		    Rectangle rect = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
1140
		    return rect.contains(px, py);
1141
		}
1142
		
1143
		public String toString () {
1144
			String s = "RobotIcon at (" + x + "," + y + "), id " + id;
1145
			return s;
1146
		}
1147
		
1148
	}
1149

  
1150
	
1151
	/*
1152
	*	VectorController class
1153
	*	Manages robot motion control graphically
1154
	*/
1155
	class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener {
1156
		int x, y, cx, cy;
1157
		int width, height;
1158
		int side;
1159
		
1160
		public VectorController (Image img) {
1161
			super (img);
1162
			width = img.getWidth(null);
1163
			height = img.getHeight(null);
1164
			cx = img.getWidth(null)/2;
1165
			cy = img.getHeight(null)/2;
1166
			x = cx;
1167
			y = cy;
1168
			if (width < height)
1169
				side = width;
1170
			else
1171
				side = height;
1172
			this.addMouseListener(this);
1173
			this.addMouseMotionListener(this);
1174
		}
1175
		
1176
		public void setPoint (int x, int y) {
1177
			if (!isValidPoint(x, y))
1178
				return;
1179
			this.x = x;
1180
			this.y = y;
1181
			repaint();
1182
		}
1183
		
1184
		public boolean isValidPoint (int x, int y) {
1185
			double xterm = Math.pow(1.0*(x - cx)/(side/2), 2);
1186
			double yterm = Math.pow(1.0*(y - cy)/(side/2), 2);
1187
			return (xterm + yterm <= 1);
1188
		}
1189
		
1190
		public void notifyMouseEvent (MouseEvent e, boolean send) {
1191
			if (!isValidPoint(e.getX(), e.getY()))
1192
				return;
1193
			vectorController.setPoint(e.getX(), e.getY());
1194
			vectorController.repaint();
1195
			if (send)
1196
				vectorController.sendToServer();
1197
		}
1198
		
1199
		public void mouseExited(MouseEvent e) {
1200
		}
1201
		public void mouseEntered(MouseEvent e) {
1202
		}
1203
		public void mouseReleased(MouseEvent e) {
1204
			this.notifyMouseEvent(e, true);
1205
		}
1206
		public void mouseClicked(MouseEvent e) {
1207
			this.notifyMouseEvent(e, false);
1208
		}
1209
		public void mousePressed(MouseEvent e) {
1210
		}
1211
		public void mouseDragged(MouseEvent e) {
1212
			vectorController.notifyMouseEvent(e, false);
1213
		}
1214
		public void mouseMoved(MouseEvent e) {
1215
		}
1216
		
1217
		public int getSpeed () {
1218
			int dx = x - cx;
1219
			int dy = y - cy;
1220
			int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
1221
			return v;
1222
		}
1223
		
1224
		/** 
1225
		* Returns the angle of the control vector in positive degrees west of north,
1226
		* or negative degrees east of north, whichever is less than or equal to
1227
		* 180 degrees total.
1228
		*/
1229
		public int getAngle () {
1230
			int dx = x - cx;
1231
			int dy = cy - y;
1232
			// find reference angle in radians
1233
			double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
1234
			// transform to degrees
1235
			theta = theta * 180 / Math.PI;
1236
			// adjust for quadrant
1237
			if (dx < 0 && dy < 0)
1238
				theta = 90 + theta;
1239
			else if (dx < 0 && dy >= 0)
1240
				theta = 90 - theta;
1241
			else if (dx >= 0 && dy < 0)
1242
				theta = -90 - theta;
1243
			else
1244
				theta = -90 + theta;
1245
			return (int) theta;
1246
		}
1247
		
1248
		public void paint (Graphics g) {
1249
			g.setColor(Color.BLACK);
1250
			g.fillRect(0, 0, width, height);
1251
			((Graphics2D)g).setStroke(new BasicStroke(1));
1252
			g.setColor(Color.RED);
1253
			g.drawOval(cx-side/2, cy-side/2, side, side);
1254
			((Graphics2D)g).setStroke(new BasicStroke(2));
1255
			g.setColor(Color.GREEN);
1256
			g.drawLine(cx, cy, x, y);
1257
			g.fillOval(x-3, y-3, 6, 6);
1258
		}
1259
		
1260
		public void setMaxForward () {
1261
			setPoint(cx, cy - (side/2) + 1);
1262
		}
1263
		
1264
		public void setMaxReverse () {
1265
			setPoint(cx, cy + (side/2) - 1);
1266
		}
1267
		
1268
		public void setMaxLeft () {
1269
			setPoint(cx - (side/2) + 1, cy);
1270
		}
1271
		
1272
		public void setMaxRight () {
1273
			setPoint(cx + (side/2) - 1, cy);
1274
		}
1275
		
1276
		public void setZero () {
1277
			setPoint(cx, cy);
1278
		}
1279
		
1280
		public void sendToServer () {
1281
			System.out.println("Attempting to send angle = " + getAngle() + ", speed = " + getSpeed() + "");
1282
			String dest = ColonetServerInterface.GLOBAL_DEST;
1283
			if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0) {
1284
				dest = (String)cmbRobotNum.getSelectedItem();
1285
			}
1286
			
1287
			if (csi != null) {
1288
				/*
1289
				csi.sendData(ColonetServerInterface.MOVE + " " + getSpeed() + " " + getAngle(), dest);
1290
				*/
1291
				
1292
				//Directional commands
1293
				if (x > cx && y == cy) {  //move right
1294
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
1295
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
1296
				} else if (x < cx && y == cy) {  //move left
1297
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
1298
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
1299
				} else if (x == cx && y > cy) {  //move forward
1300
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
1301
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
1302
				} else if (x == cx && y < cy) {  //move backward
1303
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
1304
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
1305
				} else if (x == cx && y == cy) {  //stop!
1306
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
1307
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
1308
				}
1309
			}
1310
		}
1311
	
1312
	}
1313
	
1314
	/*
1315
	*	TaskAddWindow class
1316
	*	A window that provides a simple way to add tasks to a task queue.
1317
	*/
1318
	class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
1319
		JPanel panelButtons;
1320
		JPanel panelParameters;
1321
		JPanel panelSouth;
1322
		JPanel panelSelection;
1323
		JButton btnSubmit;
1324
		JButton btnCancel;
1325
		DefaultListModel availableListModel;
1326
		JList availableList;
1327
		JScrollPane spAvailableTasks;
1328
		JTextArea txtDescription;
1329
		JTextField txtParameters;
1330
		
1331
		public TaskAddWindow () {
1332
			super("Add a Task");
1333
			super.setSize(500,500);
1334
			super.setLayout(new BorderLayout());
1335
			
1336
			// set up buttons
1337
			btnSubmit = new JButton("Submit");
1338
			btnCancel = new JButton("Cancel");
1339
			panelButtons = new JPanel();
1340
			panelButtons.setLayout(new FlowLayout());
1341
			panelButtons.add(btnSubmit);
1342
			panelButtons.add(btnCancel);
1343
			this.getRootPane().setDefaultButton(btnSubmit);
1344
			
1345
			// set up task list
1346
			availableListModel = new DefaultListModel();
1347
			availableListModel.addElement("Map the Environment");
1348
			availableListModel.addElement("Clean Up Chemical Spill");
1349
			availableListModel.addElement("Grow Plants");
1350
			availableListModel.addElement("Save the Cheerleader");
1351
			availableListModel.addElement("Save the World");
1352
			availableList = new JList(availableListModel);
1353
			availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1354
			availableList.setSelectedIndex(-1);
1355
			spAvailableTasks = new JScrollPane(availableList);
1356
			spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task"));
1357
			txtDescription = new JTextArea();
1358
			txtDescription.setEditable(false);
1359
			txtDescription.setLineWrap(true);
1360
			txtDescription.setWrapStyleWord(true);
1361
			txtDescription.setBorder(BorderFactory.createTitledBorder("Description"));
1362
			
1363
			//set up parameter area
1364
			panelParameters = new JPanel();
1365
			panelParameters.setLayout(new BorderLayout());
1366
			txtParameters = new JTextField();
1367
			panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST);
1368
			panelParameters.add(txtParameters);
1369
			
1370
			// assemble objects
1371
			panelSelection = new JPanel();
1372
			panelSelection.setLayout(new GridLayout(1,2));
1373
			panelSelection.add(spAvailableTasks);
1374
			panelSelection.add(txtDescription);
1375
			
1376
			panelSouth = new JPanel();
1377
			panelSouth.setLayout(new GridLayout(2,1));
1378
			panelSouth.add(panelParameters);
1379
			panelSouth.add(panelButtons);
1380
			
1381
			this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
1382
			this.getContentPane().add(panelSelection, BorderLayout.CENTER);
1383
			this.setLocationRelativeTo(null);
1384
			
1385
			// add listeners here
1386
			availableList.addListSelectionListener(this);
1387
			btnSubmit.addActionListener(this);
1388
			btnCancel.addActionListener(this);
1389
		}
1390
		
1391
		public void prompt () {
1392
			this.setVisible(true);
1393
		}
1394
		
1395
		private String getDescription (int index) {
1396
			if (index < 0)
1397
				return "";
1398
			switch (index) {
1399
				case 0: return "SLAM and junk";
1400
				case 1: return "I'm not sure this works";
1401
				case 2: return "Push them into the light";
1402
				case 3: return "...";
1403
				case 4: return "...";
1404
			
1405
				default: return "Task not recognized";
1406
			}
1407
		}
1408
		
1409
		public void actionPerformed (ActionEvent e) {
1410
			Object source = e.getSource();
1411
			if (source == btnSubmit) {
1412
				txtParameters.setText(txtParameters.getText().trim());
1413
				
1414
				
1415
				this.setVisible(false);
1416
			} else if (source == btnCancel) {
1417
				this.setVisible(false);
1418
			}
1419
		}
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff