1 |
1 |
//
|
2 |
|
// Colonet.java
|
|
2 |
// Colonet.java
|
3 |
3 |
//
|
4 |
4 |
|
5 |
5 |
import javax.swing.*;
|
... | ... | |
12 |
12 |
import java.io.*;
|
13 |
13 |
import java.util.*;
|
14 |
14 |
|
15 |
|
|
16 |
15 |
/**
|
17 |
|
* The Colonet Graphical User Interface Applet for use locally and over an internet connection.
|
18 |
|
* @author Gregory Tress
|
|
16 |
* The Colonet Graphical User Interface Applet for use locally and over an internet connection.
|
|
17 |
* @author Gregory Tress
|
19 |
18 |
*
|
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.
|
|
19 |
* To generate javadoc on this file or other java files, use javadoc *.java -d doc, where doc
|
|
20 |
* is the name of the folder into which the files should be written.
|
22 |
21 |
*/
|
23 |
22 |
public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener, Runnable {
|
24 |
23 |
|
25 |
24 |
// Used for images
|
26 |
|
final int CANVAS_SIZE = 500; //the applet may be slow if the canvas gets too large
|
|
25 |
final int CANVAS_SIZE = 500; //the applet may be slow if the canvas gets too large
|
27 |
26 |
final int BUFFER = 50;
|
28 |
27 |
final int RADIUS = 30;
|
29 |
28 |
|
... | ... | |
31 |
30 |
final int VECTOR_CONTROLLER_HEIGHT = 220;
|
32 |
31 |
final int VECTOR_CONTROLLER_WIDTH = 350;
|
33 |
32 |
|
34 |
|
|
35 |
33 |
// Connection
|
36 |
34 |
JTextField txtHost;
|
37 |
35 |
JTextField txtPort;
|
... | ... | |
101 |
99 |
|
102 |
100 |
Font botFont;
|
103 |
101 |
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
|
|
102 |
volatile int selectedBot; //the user has selected this bot graphically
|
|
103 |
volatile java.util.List <RobotIcon> robotIcons; //contains boundary shapes around bots for click detection
|
106 |
104 |
volatile int[] xbeeID;
|
107 |
105 |
|
108 |
106 |
Colonet self = this;
|
... | ... | |
110 |
108 |
WebcamLoader webcamLoader;
|
111 |
109 |
ColonetServerInterface csi;
|
112 |
110 |
|
113 |
|
|
114 |
111 |
public void init () {
|
115 |
112 |
// Set the default look and feel - choose one
|
116 |
|
//String laf = UIManager.getSystemLookAndFeelClassName();
|
|
113 |
//String laf = UIManager.getSystemLookAndFeelClassName();
|
117 |
114 |
String laf = UIManager.getCrossPlatformLookAndFeelClassName();
|
118 |
115 |
//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 |
|
}
|
|
116 |
try {
|
|
117 |
UIManager.setLookAndFeel(laf);
|
|
118 |
} catch (UnsupportedLookAndFeelException exc) {
|
|
119 |
System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
|
|
120 |
} catch (Exception exc) {
|
|
121 |
System.err.println ("Error loading " + laf + ": " + exc);
|
|
122 |
}
|
|
123 |
|
126 |
124 |
// We should invoke and wait to avoid browser display difficulties
|
127 |
125 |
Runnable r = new Runnable() {
|
128 |
126 |
public void run() {
|
129 |
127 |
createAndShowGUI();
|
130 |
128 |
}
|
131 |
129 |
};
|
|
130 |
|
132 |
131 |
try {
|
133 |
132 |
SwingUtilities.invokeAndWait(r);
|
134 |
133 |
} catch (InterruptedException e) {
|
... | ... | |
150 |
149 |
gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
151 |
150 |
image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE);
|
152 |
151 |
canvas = image.createGraphics();
|
153 |
|
canvas.setStroke(new BasicStroke(2)); //set pen width
|
154 |
|
panelGraph = new GraphicsPanel(image, false); //set automatic double-buffering to false. we are doing it manually.
|
|
152 |
canvas.setStroke(new BasicStroke(2)); //set pen width
|
|
153 |
panelGraph = new GraphicsPanel(image, false); //set automatic double-buffering to false. we are doing it manually.
|
155 |
154 |
panelWebcam = new WebcamPanel();
|
156 |
155 |
tabPaneMain = new JTabbedPane();
|
157 |
156 |
tabPaneMain.add(panelWebcam, "Webcam");
|
... | ... | |
282 |
281 |
|
283 |
282 |
// Message log
|
284 |
283 |
log = new JTextArea();
|
285 |
|
spLog = new JScrollPane(log,
|
286 |
|
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
|
|
284 |
spLog = new JScrollPane(log, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
|
287 |
285 |
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
288 |
286 |
spLog.setBorder(BorderFactory.createTitledBorder("Log"));
|
289 |
287 |
spLog.setPreferredSize(new Dimension(0, 120));
|
... | ... | |
341 |
339 |
btnGetXBeeIDs.addActionListener(this);
|
342 |
340 |
btnAssignID.addActionListener(this);
|
343 |
341 |
panelWebcam.addMouseListener(this);
|
344 |
|
|
345 |
342 |
}
|
346 |
343 |
|
347 |
344 |
public void run () {
|
... | ... | |
356 |
353 |
}
|
357 |
354 |
|
358 |
355 |
public void paint (Graphics g) {
|
359 |
|
super.paint(g);
|
|
356 |
super.paint(g);
|
360 |
357 |
}
|
361 |
358 |
|
362 |
359 |
public void update (Graphics g) {
|
363 |
|
paint(g);
|
|
360 |
paint(g);
|
364 |
361 |
}
|
365 |
362 |
|
366 |
363 |
/**
|
... | ... | |
387 |
384 |
|
388 |
385 |
/**
|
389 |
386 |
* Parses a String containing BOM matrix information.
|
390 |
|
* The ColonetServerInterface receives lines of the BOM matrix. (For encoding
|
391 |
|
* information, see the ColonetServerInterface documentation.) The entire matrix is passed
|
|
387 |
* The ColonetServerInterface receives lines of the BOM matrix. (For encoding
|
|
388 |
* information, see the ColonetServerInterface documentation.) The entire matrix is passed
|
392 |
389 |
* to the client when requested. This method takes a string of the form
|
393 |
390 |
* "[command code] [command code] [number of robots] [data0] [data1] ..."
|
394 |
391 |
* with tokens separated by spaces and containing no brackets.
|
395 |
392 |
* The [command code]s are predefined values identifying this String as a BOM data
|
396 |
393 |
* String, [number of robots] is an integer, and the values that follow are
|
397 |
|
* the sensor readings of the robots in order, starting with robot 0. Only [number of robots]^2
|
398 |
|
* data entries will be read. The matrix values are saved locally until the next String is parsed.
|
|
394 |
* the sensor readings of the robots in order, starting with robot 0. Only [number of robots]^2
|
|
395 |
* data entries will be read. The matrix values are saved locally until the next String is parsed.
|
399 |
396 |
*
|
400 |
397 |
*
|
401 |
398 |
* @param line the String containing BOM matrix information.
|
... | ... | |
408 |
405 |
for (int i = 0; i < num; i++) {
|
409 |
406 |
for (int j = 0; j < num; j++) {
|
410 |
407 |
String next = str[3 + i*num + j];
|
411 |
|
if (next.equals("-1"))
|
|
408 |
if (next.equals("-1")) {
|
412 |
409 |
txtMatrix.append("-");
|
413 |
|
else
|
|
410 |
} else {
|
414 |
411 |
txtMatrix.append(next);
|
415 |
|
if (j < num - 1)
|
|
412 |
}
|
|
413 |
|
|
414 |
if (j < num - 1) {
|
416 |
415 |
txtMatrix.append(" ");
|
|
416 |
}
|
417 |
417 |
}
|
418 |
|
if (i < num - 1)
|
|
418 |
|
|
419 |
if (i < num - 1) {
|
419 |
420 |
txtMatrix.append("\n");
|
|
421 |
}
|
420 |
422 |
}
|
421 |
423 |
repaint();
|
422 |
424 |
}
|
423 |
425 |
|
424 |
426 |
public void connect () {
|
425 |
|
lblConnectionStatus.setText("Status: Connecting...");
|
426 |
|
webcamLoader = new WebcamLoader(this);
|
|
427 |
lblConnectionStatus.setText("Status: Connecting...");
|
|
428 |
webcamLoader = new WebcamLoader(this);
|
427 |
429 |
dataUpdater = new DataUpdater();
|
428 |
430 |
csi = new ColonetServerInterface(this);
|
429 |
431 |
csi.connect(txtHost.getText(), txtPort.getText());
|
430 |
432 |
if (!csi.isReady()) {
|
431 |
|
lblConnectionStatus.setText("Status: Offline");
|
432 |
|
return;
|
433 |
|
}
|
434 |
|
btnConnect.setText("Disconnect");
|
435 |
|
lblConnectionStatus.setText("Status: Connected");
|
436 |
|
dataUpdater.start();
|
437 |
|
webcamLoader.start();
|
|
433 |
lblConnectionStatus.setText("Status: Offline");
|
|
434 |
} else {
|
|
435 |
btnConnect.setText("Disconnect");
|
|
436 |
lblConnectionStatus.setText("Status: Connected");
|
|
437 |
dataUpdater.start();
|
|
438 |
webcamLoader.start();
|
|
439 |
}
|
438 |
440 |
}
|
439 |
441 |
|
440 |
442 |
public void disconnect () {
|
441 |
|
lblConnectionStatus.setText("Status: Disconnecting...");
|
442 |
|
dataUpdater.interrupt();
|
443 |
|
csi.disconnect();
|
444 |
|
csi = null;
|
445 |
|
btnConnect.setText("Connect");
|
446 |
|
lblConnectionStatus.setText("Status: Disconnected");
|
|
443 |
lblConnectionStatus.setText("Status: Disconnecting...");
|
|
444 |
dataUpdater.interrupt();
|
|
445 |
csi.disconnect();
|
|
446 |
csi = null;
|
|
447 |
btnConnect.setText("Connect");
|
|
448 |
lblConnectionStatus.setText("Status: Disconnected");
|
447 |
449 |
}
|
448 |
450 |
|
449 |
451 |
/**
|
... | ... | |
460 |
462 |
|
461 |
463 |
/**
|
462 |
464 |
* Parses a String containing XBee ID values.
|
463 |
|
* The ColonetServerInterface receives Strings of XBee information. (For encoding
|
464 |
|
* information, see the ColonetServerInterface documentation.) This method takes
|
|
465 |
* The ColonetServerInterface receives Strings of XBee information. (For encoding
|
|
466 |
* information, see the ColonetServerInterface documentation.) This method takes
|
465 |
467 |
* a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
|
466 |
468 |
* with tokens separated by spaces and containing no brackets.
|
467 |
469 |
* The [command code]s are predefined values identifying this String as an XBee
|
468 |
470 |
* ID String, [number of robots] is an integer, and the values that follow are
|
469 |
|
* the IDs of the robots in order, starting with robot 0. Only [number of robots]
|
470 |
|
* will be read. The ID values are saved locally until the next String is parsed.
|
|
471 |
* the IDs of the robots in order, starting with robot 0. Only [number of robots]
|
|
472 |
* will be read. The ID values are saved locally until the next String is parsed.
|
471 |
473 |
* The purpose of having this list is to ensure that robots are properly identified for control purposes.
|
472 |
474 |
* This keeps robot identification consistent between sessions and prevents arbitrary assignment.
|
473 |
475 |
*
|
... | ... | |
479 |
481 |
String [] str = line.split(" ");
|
480 |
482 |
int num = Integer.parseInt(str[2]);
|
481 |
483 |
xbeeID = new int[num];
|
482 |
|
for (int i = 0; i < num; i++)
|
|
484 |
for (int i = 0; i < num; i++) {
|
483 |
485 |
xbeeID[i] = Integer.parseInt(str[i+3]);
|
|
486 |
}
|
484 |
487 |
|
485 |
488 |
//update the list of robots to control
|
486 |
489 |
//but save the old value first
|
487 |
490 |
Object oldSelection = cmbRobotNum.getSelectedItem();
|
488 |
491 |
cmbRobotNum.removeAllItems();
|
489 |
|
cmbRobotNum.addItem(new String(" All "));
|
490 |
|
for (int i = 0; i < num; i++)
|
|
492 |
cmbRobotNum.addItem(new String(" All "));
|
|
493 |
for (int i = 0; i < num; i++) {
|
491 |
494 |
cmbRobotNum.addItem(new String("" + xbeeID[i]));
|
|
495 |
}
|
492 |
496 |
cmbRobotNum.setSelectedItem(oldSelection);
|
493 |
497 |
repaint();
|
494 |
498 |
}
|
495 |
499 |
|
496 |
500 |
/**
|
497 |
501 |
* Parses a String containing battery information.
|
498 |
|
* The ColonetServerInterface receives Strings of battery information. (For encoding
|
499 |
|
* information, see the ColonetServerInterface documentation.) This method takes
|
|
502 |
* The ColonetServerInterface receives Strings of battery information. (For encoding
|
|
503 |
* information, see the ColonetServerInterface documentation.) This method takes
|
500 |
504 |
* a string of the form "[command code] [command code] [robot ID] [value]"
|
501 |
505 |
* with tokens separated by spaces and containing no brackets.
|
502 |
506 |
* The [command code]s are predefined values identifying this String as a battery
|
... | ... | |
513 |
517 |
int level = Integer.parseInt(str[3]);
|
514 |
518 |
int selected = -1;
|
515 |
519 |
try {
|
516 |
|
selected = Integer.parseInt((String)cmbRobotNum.getSelectedItem());
|
|
520 |
selected = Integer.parseInt((String)cmbRobotNum.getSelectedItem());
|
517 |
521 |
} catch (Exception e) {
|
|
522 |
System.out.println("Exception in parseBattery.");
|
518 |
523 |
}
|
|
524 |
|
519 |
525 |
if (selected == botNum) {
|
520 |
526 |
batteryIcon.setLevel(level);
|
521 |
527 |
}
|
... | ... | |
535 |
541 |
int x = Integer.parseInt(str[i+1]);
|
536 |
542 |
int y = Integer.parseInt(str[i+2]);
|
537 |
543 |
RobotIcon newIcon = new RobotIcon(id, x, y);
|
538 |
|
if (newIcon.id >= 0)
|
539 |
|
newIcon.color = Color.GREEN;
|
|
544 |
if (newIcon.id >= 0) {
|
|
545 |
newIcon.color = Color.GREEN;
|
|
546 |
}
|
540 |
547 |
newList.add(newIcon);
|
541 |
548 |
}
|
542 |
549 |
robotIcons = newList;
|
543 |
550 |
repaint();
|
544 |
551 |
}
|
545 |
552 |
|
546 |
|
|
547 |
553 |
//
|
548 |
554 |
// MouseListener methods
|
549 |
555 |
//
|
550 |
556 |
public void mousePressed(MouseEvent e) {
|
551 |
|
//Start a new Thread to handle the MouseEvent
|
552 |
|
(new MouseHandler(e)).start();
|
|
557 |
//Start a new Thread to handle the MouseEvent
|
|
558 |
(new MouseHandler(e)).start();
|
553 |
559 |
}
|
554 |
560 |
public void mouseExited(MouseEvent e) {
|
555 |
561 |
}
|
... | ... | |
569 |
575 |
//
|
570 |
576 |
public void keyPressed (KeyEvent e) {
|
571 |
577 |
//Start a new Thread to handle the KeyEvent
|
572 |
|
(new KeyHandler(e)).start();
|
|
578 |
(new KeyHandler(e)).start();
|
573 |
579 |
}
|
574 |
580 |
public void keyReleased (KeyEvent e) {
|
575 |
581 |
}
|
... | ... | |
585 |
591 |
}
|
586 |
592 |
|
587 |
593 |
class MouseHandler extends Thread {
|
|
594 |
MouseEvent e;
|
588 |
595 |
|
589 |
|
MouseEvent e;
|
|
596 |
public MouseHandler (MouseEvent event) {
|
|
597 |
super("MouseHandler");
|
|
598 |
this.e = event;
|
|
599 |
}
|
590 |
600 |
|
591 |
|
public MouseHandler (MouseEvent event) {
|
592 |
|
super("MouseHandler");
|
593 |
|
this.e = event;
|
594 |
|
}
|
|
601 |
public void run () {
|
|
602 |
Point pt = panelWebcam.convertClick(e);
|
595 |
603 |
|
596 |
|
public void run () {
|
|
604 |
// If we are selecting a waypoint (destination) for a specific bot
|
|
605 |
if (setWaypoint && setWaypointID >= 0) {
|
|
606 |
setWaypoint = false;
|
|
607 |
panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
|
|
608 |
if (selectedBot < 0) {
|
|
609 |
return;
|
|
610 |
}
|
597 |
611 |
|
598 |
|
Point pt = panelWebcam.convertClick(e);
|
|
612 |
RobotIcon r = robotIcons.get(selectedBot);
|
|
613 |
r.destx = pt.x;
|
|
614 |
r.desty = pt.y;
|
599 |
615 |
|
600 |
|
// If we are selecting a waypoint (destination) for a specific bot
|
601 |
|
if (setWaypoint && setWaypointID >= 0) {
|
602 |
|
setWaypoint = false;
|
603 |
|
panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
|
604 |
|
if (selectedBot < 0)
|
605 |
|
return;
|
|
616 |
if (csi != null) {
|
|
617 |
csi.sendAbsoluteMove(r.id, r.destx, r.desty);
|
606 |
618 |
|
607 |
|
RobotIcon r = robotIcons.get(selectedBot);
|
608 |
|
r.destx = pt.x;
|
609 |
|
r.desty = pt.y;
|
|
619 |
}
|
|
620 |
|
|
621 |
return;
|
|
622 |
}
|
610 |
623 |
|
611 |
|
if (csi != null)
|
612 |
|
csi.sendAbsoluteMove(r.id, r.destx, r.desty);
|
|
624 |
// Right-click also means we are moving a robot
|
|
625 |
if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
|
|
626 |
if (selectedBot < 0) {
|
|
627 |
return;
|
|
628 |
}
|
613 |
629 |
|
614 |
|
return;
|
615 |
|
}
|
|
630 |
RobotIcon r = robotIcons.get(selectedBot);
|
|
631 |
r.destx = pt.x;
|
|
632 |
r.desty = pt.y;
|
616 |
633 |
|
617 |
|
// Right-click also means we are moving a robot
|
618 |
|
if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
|
619 |
|
if (selectedBot < 0)
|
620 |
|
return;
|
|
634 |
if (csi != null) {
|
|
635 |
csi.sendAbsoluteMove(r.id, r.destx, r.desty);
|
|
636 |
}
|
621 |
637 |
|
622 |
|
RobotIcon r = robotIcons.get(selectedBot);
|
623 |
|
r.destx = pt.x;
|
624 |
|
r.desty = pt.y;
|
|
638 |
return;
|
|
639 |
}
|
625 |
640 |
|
626 |
|
if (csi != null)
|
627 |
|
csi.sendAbsoluteMove(r.id, r.destx, r.desty);
|
|
641 |
// If we are setting all waypoints
|
|
642 |
if (setWaypoint) {
|
|
643 |
setWaypoint = false;
|
|
644 |
panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
|
|
645 |
for (int i = 0; i < robotIcons.size(); i++) {
|
|
646 |
RobotIcon r = robotIcons.get(i);
|
|
647 |
r.destx = pt.x;
|
|
648 |
r.desty = pt.y;
|
|
649 |
}
|
|
650 |
|
|
651 |
return;
|
|
652 |
}
|
628 |
653 |
|
629 |
|
return;
|
630 |
|
}
|
631 |
|
|
632 |
|
// If we are setting all waypoints
|
633 |
|
if (setWaypoint) {
|
634 |
|
setWaypoint = false;
|
635 |
|
panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
|
636 |
|
for (int i = 0; i < robotIcons.size(); i++) {
|
637 |
|
RobotIcon r = robotIcons.get(i);
|
638 |
|
r.destx = pt.x;
|
639 |
|
r.desty = pt.y;
|
640 |
|
}
|
641 |
|
return;
|
642 |
|
}
|
643 |
|
|
644 |
|
// Otherwise, we are selecting a bot, or doing nothing
|
645 |
|
for (int i = 0; i < robotIcons.size(); i++) {
|
646 |
|
RobotIcon r = robotIcons.get(i);
|
647 |
|
if (r.contains(pt.x, pt.y)) {
|
648 |
|
selectedBot = i;
|
649 |
|
lblSelected.setText(" " + r.id);
|
|
654 |
// Otherwise, we are selecting a bot, or doing nothing
|
|
655 |
for (int i = 0; i < robotIcons.size(); i++) {
|
|
656 |
RobotIcon r = robotIcons.get(i);
|
|
657 |
if (r.contains(pt.x, pt.y)) {
|
|
658 |
selectedBot = i;
|
|
659 |
lblSelected.setText(" " + r.id);
|
650 |
660 |
// Try to select the clicked bot, if its XBee ID is detected.
|
651 |
661 |
for (int j = 1; j < cmbRobotNum.getItemCount(); j++) {
|
652 |
|
if (Integer.parseInt(cmbRobotNum.getItemAt(j).toString()) == robotIcons.get(selectedBot).id)
|
|
662 |
if (Integer.parseInt(cmbRobotNum.getItemAt(j).toString()) == robotIcons.get(selectedBot).id) {
|
653 |
663 |
cmbRobotNum.setSelectedIndex(j);
|
|
664 |
}
|
654 |
665 |
}
|
655 |
666 |
return;
|
656 |
|
}
|
657 |
|
}
|
|
667 |
}
|
|
668 |
}
|
658 |
669 |
|
659 |
|
repaint();
|
660 |
|
}
|
|
670 |
repaint();
|
|
671 |
}
|
661 |
672 |
}
|
662 |
673 |
|
663 |
674 |
class KeyHandler extends Thread {
|
664 |
|
|
665 |
675 |
KeyEvent e;
|
666 |
676 |
|
667 |
677 |
public KeyHandler (KeyEvent event) {
|
... | ... | |
692 |
702 |
}
|
693 |
703 |
|
694 |
704 |
class ActionHandler extends Thread {
|
|
705 |
ActionEvent e;
|
695 |
706 |
|
696 |
|
ActionEvent e;
|
|
707 |
public ActionHandler (ActionEvent event) {
|
|
708 |
super("ActionHandler");
|
|
709 |
this.e = event;
|
|
710 |
}
|
697 |
711 |
|
698 |
|
public ActionHandler (ActionEvent event) {
|
699 |
|
super("ActionHandler");
|
700 |
|
this.e = event;
|
701 |
|
}
|
|
712 |
public void run () {
|
|
713 |
Object source = e.getSource();
|
702 |
714 |
|
703 |
|
public void run () {
|
704 |
|
Object source = e.getSource();
|
|
715 |
// General Actions
|
|
716 |
if (source == btnConnect) {
|
|
717 |
if (csi == null) {
|
|
718 |
connect();
|
|
719 |
} else {
|
|
720 |
disconnect();
|
|
721 |
}
|
|
722 |
} else if (source == btnGetXBeeIDs) {
|
|
723 |
csi.sendXBeeIDRequest();
|
|
724 |
} else if (source == btnAssignID) {
|
|
725 |
String message;
|
|
726 |
if (selectedBot < 0) {
|
|
727 |
return;
|
|
728 |
}
|
|
729 |
int curID = robotIcons.get(selectedBot).id;
|
705 |
730 |
|
706 |
|
// General Actions
|
707 |
|
if (source == btnConnect) {
|
708 |
|
if (csi == null)
|
709 |
|
connect();
|
710 |
|
else
|
711 |
|
disconnect();
|
712 |
|
} else if (source == btnGetXBeeIDs) {
|
713 |
|
csi.sendXBeeIDRequest();
|
714 |
|
} else if (source == btnAssignID) {
|
715 |
|
String message;
|
716 |
|
if (selectedBot < 0)
|
717 |
|
return;
|
718 |
|
int curID = robotIcons.get(selectedBot).id;
|
719 |
|
if (curID < 0)
|
720 |
|
message = "That robot is unidentified. Please specify its ID.";
|
721 |
|
else
|
722 |
|
message = "That robot has ID " + curID + ". You may reassign it now.";
|
723 |
|
String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
|
724 |
|
if (result == null)
|
725 |
|
return;
|
726 |
|
int newID = -1;
|
727 |
|
try {
|
728 |
|
newID = Integer.parseInt(result);
|
729 |
|
} catch (Exception ex) {
|
730 |
|
csi.warn("Invalid ID.");
|
731 |
|
return;
|
732 |
|
}
|
733 |
|
// Assign new ID and update display
|
734 |
|
csi.sendIDAssignment(curID, newID);
|
735 |
|
robotIcons.get(selectedBot).id = newID;
|
736 |
|
robotIcons.get(selectedBot).color = Color.GREEN;
|
737 |
|
lblSelected.setText(" " + newID);
|
|
731 |
if (curID < 0) {
|
|
732 |
message = "That robot is unidentified. Please specify its ID.";
|
|
733 |
} else {
|
|
734 |
message = "That robot has ID " + curID + ". You may reassign it now.";
|
|
735 |
}
|
|
736 |
String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
|
|
737 |
if (result == null) {
|
|
738 |
return;
|
|
739 |
}
|
|
740 |
int newID = -1;
|
|
741 |
try {
|
|
742 |
newID = Integer.parseInt(result);
|
|
743 |
} catch (Exception ex) {
|
|
744 |
csi.warn("Invalid ID.");
|
|
745 |
return;
|
|
746 |
}
|
|
747 |
// Assign new ID and update display
|
|
748 |
csi.sendIDAssignment(curID, newID);
|
|
749 |
robotIcons.get(selectedBot).id = newID;
|
|
750 |
robotIcons.get(selectedBot).color = Color.GREEN;
|
|
751 |
lblSelected.setText(" " + newID);
|
|
752 |
} else if (source == btnF) { // Robot Movement Controls
|
|
753 |
vectorController.setMaxForward();
|
|
754 |
vectorController.sendToServer();
|
|
755 |
} else if (source == btnB) {
|
|
756 |
vectorController.setMaxReverse();
|
|
757 |
vectorController.sendToServer();
|
|
758 |
} else if (source == btnL) {
|
|
759 |
vectorController.setMaxLeft();
|
|
760 |
vectorController.sendToServer();
|
|
761 |
} else if (source == btnR) {
|
|
762 |
vectorController.setMaxRight();
|
|
763 |
vectorController.sendToServer();
|
|
764 |
} else if (source == btnActivate) {
|
|
765 |
vectorController.setZero();
|
|
766 |
vectorController.sendToServer();
|
|
767 |
} else if (source == btnCommand_MoveTo) { // Robot Commands (non-movement)
|
|
768 |
if (selectedBot < 0) {
|
|
769 |
return;
|
|
770 |
}
|
|
771 |
panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
|
|
772 |
setWaypoint = true;
|
|
773 |
setWaypointID = selectedBot;
|
|
774 |
} else if (source == btnCommand_MoveAll) {
|
|
775 |
panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
|
|
776 |
setWaypoint = true;
|
|
777 |
setWaypointID = -1;
|
|
778 |
} else if (source == btnCommand_StopTask) {
|
738 |
779 |
|
|
780 |
} else if (source == btnCommand_ResumeTask) {
|
739 |
781 |
|
740 |
|
}
|
|
782 |
} else if (source == btnCommand_ChargeNow) {
|
741 |
783 |
|
742 |
|
// Robot Movement Controls
|
743 |
|
else if (source == btnF) {
|
744 |
|
vectorController.setMaxForward();
|
745 |
|
vectorController.sendToServer();
|
746 |
|
} else if (source == btnB) {
|
747 |
|
vectorController.setMaxReverse();
|
748 |
|
vectorController.sendToServer();
|
749 |
|
} else if (source == btnL) {
|
750 |
|
vectorController.setMaxLeft();
|
751 |
|
vectorController.sendToServer();
|
752 |
|
} else if (source == btnR) {
|
753 |
|
vectorController.setMaxRight();
|
754 |
|
vectorController.sendToServer();
|
755 |
|
} else if (source == btnActivate) {
|
756 |
|
vectorController.setZero();
|
757 |
|
vectorController.sendToServer();
|
758 |
|
}
|
759 |
|
// Robot Commands (non-movement)
|
760 |
|
else if (source == btnCommand_MoveTo) {
|
761 |
|
if (selectedBot < 0)
|
762 |
|
return;
|
763 |
|
panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
|
764 |
|
setWaypoint = true;
|
765 |
|
setWaypointID = selectedBot;
|
|
784 |
} else if (source == btnCommand_StopCharging) {
|
766 |
785 |
|
767 |
|
} else if (source == btnCommand_MoveAll) {
|
768 |
|
panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
|
769 |
|
setWaypoint = true;
|
770 |
|
setWaypointID = -1;
|
|
786 |
} else if (source == btnAddTask) { // Queue Management
|
|
787 |
taskAddWindow.prompt();
|
|
788 |
} else if (source == btnRemoveTask) {
|
|
789 |
if (taskList.getSelectedIndex() >= 0) {
|
|
790 |
csi.sendQueueRemove(taskList.getSelectedIndex());
|
|
791 |
}
|
|
792 |
csi.sendQueueUpdate();
|
|
793 |
} else if (source == btnMoveTaskUp) {
|
|
794 |
csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
|
|
795 |
csi.sendQueueUpdate();
|
|
796 |
} else if (source == btnMoveTaskDown) {
|
|
797 |
csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
|
|
798 |
csi.sendQueueUpdate();
|
|
799 |
} else if (source == btnUpdateTasks) {
|
|
800 |
csi.sendQueueUpdate();
|
|
801 |
}
|
771 |
802 |
|
772 |
|
} else if (source == btnCommand_StopTask) {
|
773 |
|
|
774 |
|
} else if (source == btnCommand_ResumeTask) {
|
775 |
|
|
776 |
|
} else if (source == btnCommand_ChargeNow) {
|
777 |
|
|
778 |
|
} else if (source == btnCommand_StopCharging) {
|
779 |
|
|
780 |
|
}
|
781 |
|
|
782 |
|
// Queue Management
|
783 |
|
else if (source == btnAddTask) {
|
784 |
|
taskAddWindow.prompt();
|
785 |
|
} else if (source == btnRemoveTask) {
|
786 |
|
if (taskList.getSelectedIndex() >= 0);
|
787 |
|
csi.sendQueueRemove(taskList.getSelectedIndex());
|
788 |
|
csi.sendQueueUpdate();
|
789 |
|
} else if (source == btnMoveTaskUp) {
|
790 |
|
csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
|
791 |
|
csi.sendQueueUpdate();
|
792 |
|
} else if (source == btnMoveTaskDown) {
|
793 |
|
csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
|
794 |
|
csi.sendQueueUpdate();
|
795 |
|
} else if (source == btnUpdateTasks) {
|
796 |
|
csi.sendQueueUpdate();
|
797 |
|
}
|
798 |
|
|
799 |
|
repaint();
|
800 |
|
}
|
801 |
|
|
|
803 |
repaint();
|
|
804 |
}
|
802 |
805 |
}
|
803 |
806 |
|
804 |
807 |
/*
|
805 |
|
* DataUpdater thread.
|
806 |
|
* The purpose of this thread is to request data from the server at regular intervals.
|
|
808 |
* DataUpdater thread.
|
|
809 |
* The purpose of this thread is to request data from the server at regular intervals.
|
807 |
810 |
*
|
808 |
811 |
*/
|
809 |
812 |
class DataUpdater extends Thread {
|
... | ... | |
819 |
822 |
try {
|
820 |
823 |
//request more data
|
821 |
824 |
if (csi != null && csi.isReady()) {
|
822 |
|
csi.sendPositionRequest();
|
|
825 |
csi.sendPositionRequest();
|
823 |
826 |
csi.sendXBeeIDRequest();
|
824 |
827 |
if (cmbRobotNum.getSelectedIndex() > 0) {
|
825 |
|
String sel = (String) cmbRobotNum.getSelectedItem();
|
826 |
|
int num = Integer.parseInt(sel);
|
|
828 |
String sel = (String) cmbRobotNum.getSelectedItem();
|
|
829 |
int num = Integer.parseInt(sel);
|
827 |
830 |
csi.sendBatteryRequest(num);
|
828 |
831 |
}
|
829 |
832 |
}
|
... | ... | |
837 |
840 |
}
|
838 |
841 |
|
839 |
842 |
/*
|
840 |
|
* GraphicsPanel class
|
841 |
|
* An extension of JPanel, designed for holding an image that will be repainted regularly.
|
|
843 |
* GraphicsPanel class
|
|
844 |
* An extension of JPanel, designed for holding an image that will be repainted regularly.
|
842 |
845 |
*/
|
843 |
846 |
class GraphicsPanel extends JPanel {
|
844 |
847 |
protected Image img;
|
... | ... | |
856 |
859 |
// Place the buffered image on the screen, inside the panel
|
857 |
860 |
g.drawImage(img, 0, 0, Color.WHITE, this);
|
858 |
861 |
}
|
859 |
|
|
860 |
862 |
}
|
861 |
863 |
|
862 |
864 |
/*
|
863 |
|
* WebcamPanel class
|
864 |
|
* Enables more efficient image handling in a component-controlled environment
|
|
865 |
* WebcamPanel class
|
|
866 |
* Enables more efficient image handling in a component-controlled environment
|
865 |
867 |
*/
|
866 |
868 |
class WebcamPanel extends JPanel {
|
867 |
|
int BORDER = 16; // this is arbitrary. it makes the image look nice inside a border.
|
|
869 |
int BORDER = 16; // this is arbitrary. it makes the image look nice inside a border.
|
868 |
870 |
int BOT_RADIUS = 40;
|
869 |
871 |
volatile BufferedImage img;
|
870 |
872 |
BufferedImage buffer;
|
... | ... | |
897 |
899 |
int x = 0;
|
898 |
900 |
int y = 0;
|
899 |
901 |
|
900 |
|
if (widthRatio > heightRatio) { //height is the limiting factor
|
901 |
|
scale = heightRatio;
|
902 |
|
newHeight = maxHeight;
|
903 |
|
newWidth = (int) (img.getWidth() * scale);
|
904 |
|
y = BORDER;
|
905 |
|
x = (maxWidth - newWidth) / 2 + BORDER;
|
906 |
|
} else { //width is the limiting factor
|
907 |
|
scale = widthRatio;
|
908 |
|
newWidth = maxWidth;
|
909 |
|
newHeight = (int) (img.getHeight() * scale);
|
910 |
|
x = BORDER;
|
911 |
|
y = (maxHeight - newHeight) / 2 + BORDER;
|
|
902 |
if (widthRatio > heightRatio) { //height is the limiting factor
|
|
903 |
scale = heightRatio;
|
|
904 |
newHeight = maxHeight;
|
|
905 |
newWidth = (int) (img.getWidth() * scale);
|
|
906 |
y = BORDER;
|
|
907 |
x = (maxWidth - newWidth) / 2 + BORDER;
|
|
908 |
} else { //width is the limiting factor
|
|
909 |
scale = widthRatio;
|
|
910 |
newWidth = maxWidth;
|
|
911 |
newHeight = (int) (img.getHeight() * scale);
|
|
912 |
x = BORDER;
|
|
913 |
y = (maxHeight - newHeight) / 2 + BORDER;
|
912 |
914 |
}
|
913 |
915 |
|
914 |
916 |
// Draw everything onto the buffer
|
... | ... | |
922 |
924 |
|
923 |
925 |
// Draw Identifiers and battery levels
|
924 |
926 |
if (robotIcons != null) {
|
925 |
|
bufferedGraphics.setStroke(new BasicStroke(2));
|
926 |
|
for (int i = 0; i < robotIcons.size(); i++) {
|
927 |
|
RobotIcon r = robotIcons.get(i);
|
928 |
|
bufferedGraphics.setColor(r.color);
|
929 |
|
// Identifier circle
|
930 |
|
int px = (int) (x + r.x * scale);
|
931 |
|
int py = (int) (y + r.y * scale);
|
932 |
|
bufferedGraphics.drawOval(px-RADIUS, py-RADIUS, 2*r.RADIUS, 2*r.RADIUS);
|
933 |
|
// Battery
|
934 |
|
//if (r.battery >= 0) {
|
935 |
|
bufferedGraphics.setColor(Color.GREEN);
|
936 |
|
bufferedGraphics.fillRect(px+20, py+20, 30, 10);
|
937 |
|
bufferedGraphics.setColor(Color.BLACK);
|
938 |
|
bufferedGraphics.drawRect(px+20, py+20, 50, 10);
|
939 |
|
//}
|
940 |
|
// If the robot has a destination, draw the vector
|
941 |
|
if (r.destx >= 0) {
|
942 |
|
bufferedGraphics.drawLine(px, py, (int)(x + r.destx * scale), (int)(y + r.desty * scale));
|
943 |
|
}
|
944 |
|
}
|
|
927 |
bufferedGraphics.setStroke(new BasicStroke(2));
|
|
928 |
for (int i = 0; i < robotIcons.size(); i++) {
|
|
929 |
RobotIcon r = robotIcons.get(i);
|
|
930 |
bufferedGraphics.setColor(r.color);
|
|
931 |
// Identifier circle
|
|
932 |
int px = (int) (x + r.x * scale);
|
|
933 |
int py = (int) (y + r.y * scale);
|
|
934 |
bufferedGraphics.drawOval(px-RADIUS, py-RADIUS, 2*r.RADIUS, 2*r.RADIUS);
|
|
935 |
// Battery
|
|
936 |
//if (r.battery >= 0) {
|
|
937 |
bufferedGraphics.setColor(Color.GREEN);
|
|
938 |
bufferedGraphics.fillRect(px+20, py+20, 30, 10);
|
|
939 |
bufferedGraphics.setColor(Color.BLACK);
|
|
940 |
bufferedGraphics.drawRect(px+20, py+20, 50, 10);
|
|
941 |
//}
|
|
942 |
// If the robot has a destination, draw the vector
|
|
943 |
if (r.destx >= 0) {
|
|
944 |
bufferedGraphics.drawLine(px, py, (int)(x + r.destx * scale), (int)(y + r.desty * scale));
|
|
945 |
}
|
|
946 |
}
|
945 |
947 |
}
|
946 |
948 |
|
947 |
949 |
// Identify currently-selected robot
|
948 |
950 |
if (selectedBot >= 0) {
|
949 |
|
bufferedGraphics.setColor(Color.BLACK);
|
950 |
|
RobotIcon r = robotIcons.get(selectedBot);
|
951 |
|
int px = (int) (x + r.x * scale);
|
952 |
|
int py = (int) (y + r.y * scale);
|
953 |
|
bufferedGraphics.drawOval(px-RADIUS-6, py-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12);
|
|
951 |
bufferedGraphics.setColor(Color.BLACK);
|
|
952 |
RobotIcon r = robotIcons.get(selectedBot);
|
|
953 |
int px = (int) (x + r.x * scale);
|
|
954 |
int py = (int) (y + r.y * scale);
|
|
955 |
bufferedGraphics.drawOval(px-RADIUS-6, py-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12);
|
954 |
956 |
}
|
955 |
957 |
|
956 |
958 |
//Display buffered content
|
... | ... | |
964 |
966 |
public Point convertClick (MouseEvent e) {
|
965 |
967 |
// Calculate scaling
|
966 |
968 |
int clickx = e.getX();
|
967 |
|
int clicky = e.getY();
|
|
969 |
int clicky = e.getY();
|
968 |
970 |
int maxWidth = getWidth() - 2*BORDER;
|
969 |
971 |
int maxHeight = getHeight() - 2*BORDER;
|
970 |
972 |
double widthRatio = 1.0 * maxWidth / img.getWidth();
|
... | ... | |
975 |
977 |
int px = 0;
|
976 |
978 |
int py = 0;
|
977 |
979 |
|
978 |
|
if (widthRatio > heightRatio) { //height is the limiting factor
|
979 |
|
scale = heightRatio;
|
980 |
|
newHeight = maxHeight;
|
981 |
|
newWidth = (int) (img.getWidth() * scale);
|
982 |
|
py = clicky - BORDER;
|
983 |
|
px = clickx - BORDER - (maxWidth - newWidth) / 2;
|
984 |
|
py *= scale;
|
985 |
|
px *= scale;
|
986 |
|
} else { //width is the limiting factor
|
987 |
|
scale = widthRatio;
|
988 |
|
newWidth = maxWidth;
|
989 |
|
newHeight = (int) (img.getHeight() * scale);
|
990 |
|
px = clickx - BORDER;
|
991 |
|
py = clicky - BORDER - (maxHeight - newHeight) / 2;
|
992 |
|
px *= scale;
|
993 |
|
py *= scale;
|
|
980 |
if (widthRatio > heightRatio) { //height is the limiting factor
|
|
981 |
scale = heightRatio;
|
|
982 |
newHeight = maxHeight;
|
|
983 |
newWidth = (int) (img.getWidth() * scale);
|
|
984 |
py = clicky - BORDER;
|
|
985 |
px = clickx - BORDER - (maxWidth - newWidth) / 2;
|
|
986 |
py *= scale;
|
|
987 |
px *= scale;
|
|
988 |
} else { //width is the limiting factor
|
|
989 |
scale = widthRatio;
|
|
990 |
newWidth = maxWidth;
|
|
991 |
newHeight = (int) (img.getHeight() * scale);
|
|
992 |
px = clickx - BORDER;
|
|
993 |
py = clicky - BORDER - (maxHeight - newHeight) / 2;
|
|
994 |
px *= scale;
|
|
995 |
py *= scale;
|
994 |
996 |
}
|
995 |
997 |
|
996 |
|
return new Point(px, py);
|
|
998 |
return new Point(px, py);
|
997 |
999 |
|
998 |
1000 |
}
|
999 |
1001 |
|
1000 |
1002 |
}
|
1001 |
1003 |
|
1002 |
1004 |
/*
|
1003 |
|
* WebcamLoader class
|
1004 |
|
* Handles the loading of the webcam image.
|
|
1005 |
* WebcamLoader class
|
|
1006 |
* Handles the loading of the webcam image.
|
1005 |
1007 |
*/
|
1006 |
1008 |
class WebcamLoader extends Thread
|
1007 |
1009 |
{
|
... | ... | |
1031 |
1033 |
image.flush();
|
1032 |
1034 |
System.gc();
|
1033 |
1035 |
try {
|
1034 |
|
imagePath = new URL(IMAGE_PATH + "?rand=" + rand.nextInt(50000));
|
1035 |
|
} catch (MalformedURLException e) {
|
1036 |
|
System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
|
1037 |
|
}
|
|
1036 |
imagePath = new URL(IMAGE_PATH + "?rand=" + rand.nextInt(50000));
|
|
1037 |
} catch (MalformedURLException e) {
|
|
1038 |
System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
|
|
1039 |
}
|
1038 |
1040 |
image = ImageIO.read(imagePath);
|
1039 |
1041 |
// The MediaTracker waitForID pauses the thread until the image is loaded.
|
1040 |
1042 |
// We don't want to display a half-downloaded image.
|
... | ... | |
1057 |
1059 |
}
|
1058 |
1060 |
|
1059 |
1061 |
/*
|
1060 |
|
* RobotIcon class
|
1061 |
|
* Provides a means for graphically representing and keeping track of webcam bots.
|
|
1062 |
* RobotIcon class
|
|
1063 |
* Provides a means for graphically representing and keeping track of webcam bots.
|
1062 |
1064 |
*/
|
1063 |
1065 |
class RobotIcon {
|
1064 |
1066 |
public final int RADIUS = 30;
|
... | ... | |
1068 |
1070 |
public int destx, desty;
|
1069 |
1071 |
public int id;
|
1070 |
1072 |
public Color color;
|
1071 |
|
public int battery;
|
|
1073 |
public int battery;
|
1072 |
1074 |
|
1073 |
1075 |
public RobotIcon (int id, int x, int y) {
|
1074 |
1076 |
this.color = Color.RED;
|
... | ... | |
1081 |
1083 |
}
|
1082 |
1084 |
|
1083 |
1085 |
/**
|
1084 |
|
* Relocates this RobotIcon to a new coordinate point.
|
|
1086 |
* Relocates this RobotIcon to a new coordinate point.
|
1085 |
1087 |
*
|
1086 |
1088 |
*/
|
1087 |
1089 |
public void move (int newX, int newY) {
|
... | ... | |
1090 |
1092 |
}
|
1091 |
1093 |
|
1092 |
1094 |
/**
|
1093 |
|
* Determines if a given point is within a reasonable range of the current location
|
1094 |
|
* to be considered the same robot when moving. The threshold is determined by the
|
1095 |
|
* CLOSE value.
|
|
1095 |
* Determines if a given point is within a reasonable range of the current location
|
|
1096 |
* to be considered the same robot when moving. The threshold is determined by the
|
|
1097 |
* CLOSE value.
|
1096 |
1098 |
*
|
1097 |
|
* @returns Whether or not the given point is reasonably close to the current location.
|
|
1099 |
* @returns Whether or not the given point is reasonably close to the current location.
|
1098 |
1100 |
*
|
1099 |
1101 |
*/
|
1100 |
1102 |
public boolean isClose (int nx, int ny) {
|
... | ... | |
1103 |
1105 |
}
|
1104 |
1106 |
|
1105 |
1107 |
/**
|
1106 |
|
* Determines whether a given point is within the rectangle that circumscribes the
|
1107 |
|
* robot's circlular icon. Used for clicking on robots in webcam view.
|
1108 |
|
*
|
1109 |
|
*/
|
|
1108 |
* Determines whether a given point is within the rectangle that circumscribes the
|
|
1109 |
* robot's circlular icon. Used for clicking on robots in webcam view.
|
|
1110 |
*
|
|
1111 |
*/
|
1110 |
1112 |
public boolean contains (int px, int py) {
|
1111 |
|
Rectangle rect = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
|
1112 |
|
return rect.contains(px, py);
|
|
1113 |
Rectangle rect = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
|
|
1114 |
return rect.contains(px, py);
|
1113 |
1115 |
}
|
1114 |
1116 |
|
1115 |
1117 |
public String toString () {
|
... | ... | |
1121 |
1123 |
|
1122 |
1124 |
|
1123 |
1125 |
/*
|
1124 |
|
* VectorController class
|
1125 |
|
* Manages robot motion control graphically
|
|
1126 |
* VectorController class
|
|
1127 |
* Manages robot motion control graphically
|
1126 |
1128 |
*/
|
1127 |
1129 |
class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener {
|
1128 |
1130 |
int x, y, cx, cy;
|
... | ... | |
1262 |
1264 |
*/
|
1263 |
1265 |
|
1264 |
1266 |
//Directional commands
|
1265 |
|
if (x > cx && y == cy) { //move right
|
|
1267 |
if (x > cx && y == cy) { //move right
|
1266 |
1268 |
csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
|
1267 |
1269 |
csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
|
1268 |
|
} else if (x < cx && y == cy) { //move left
|
|
1270 |
} else if (x < cx && y == cy) { //move left
|
1269 |
1271 |
csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
|
1270 |
1272 |
csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
|
1271 |
|
} else if (x == cx && y > cy) { //move forward
|
|
1273 |
} else if (x == cx && y > cy) { //move forward
|
1272 |
1274 |
csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
|
1273 |
1275 |
csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
|
1274 |
|
} else if (x == cx && y < cy) { //move backward
|
|
1276 |
} else if (x == cx && y < cy) { //move backward
|
1275 |
1277 |
csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
|
1276 |
1278 |
csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
|
1277 |
|
} else if (x == cx && y == cy) { //stop!
|
|
1279 |
} else if (x == cx && y == cy) { //stop!
|
1278 |
1280 |
csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
|
1279 |
1281 |
csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
|
1280 |
1282 |
}
|
... | ... | |
1284 |
1286 |
}
|
1285 |
1287 |
|
1286 |
1288 |
/*
|
1287 |
|
* TaskAddWindow class
|
1288 |
|
* A window that provides a simple way to add tasks to a task queue.
|
|
1289 |
* TaskAddWindow class
|
|
1290 |
* A window that provides a simple way to add tasks to a task queue.
|
1289 |
1291 |
*/
|
1290 |
1292 |
class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
|
1291 |
1293 |
JPanel panelButtons;
|
... | ... | |
1399 |
1401 |
}
|
1400 |
1402 |
|
1401 |
1403 |
/*
|
1402 |
|
* BatteryIcon class
|
1403 |
|
* Graphical representation of battery level
|
|
1404 |
* BatteryIcon class
|
|
1405 |
* Graphical representation of battery level
|
1404 |
1406 |
*/
|
1405 |
1407 |
class BatteryIcon implements Icon {
|
1406 |
1408 |
private int width;
|
1407 |
|
private int height;
|
1408 |
|
private int level;
|
|
1409 |
private int height;
|
|
1410 |
private int level;
|
1409 |
1411 |
|
1410 |
1412 |
/**
|
1411 |
1413 |
* Constructs a new BatteryIcon with all default parameters.
|
1412 |
1414 |
* Default width and height are 50.
|
1413 |
1415 |
* Default level is 100.
|
1414 |
1416 |
*/
|
1415 |
|
public BatteryIcon(){
|
1416 |
|
this(100, 50, 50);
|
1417 |
|
}
|
|
1417 |
public BatteryIcon(){
|
|
1418 |
this(100, 50, 50);
|
|
1419 |
}
|
1418 |
1420 |
|
1419 |
1421 |
/**
|
1420 |
1422 |
* Constructs a new BatteryIcon with default width and height, and with the specified level.
|
1421 |
1423 |
* Default width and height are 50.
|
1422 |
1424 |
*/
|
1423 |
|
public BatteryIcon(int startLevel){
|
1424 |
|
this(startLevel, 50, 50);
|
1425 |
|
}
|
|
1425 |
public BatteryIcon(int startLevel){
|
|
1426 |
this(startLevel, 50, 50);
|
|
1427 |
}
|
1426 |
1428 |
|
1427 |
1429 |
/**
|
1428 |
1430 |
* Constructs a new BatteryIcon with the specified level, width, and height.
|
1429 |
1431 |
*/
|
1430 |
|
public BatteryIcon(int startLevel, int w, int h){
|
1431 |
|
level = startLevel;
|
1432 |
|
width = w;
|
1433 |
|
height = h;
|
1434 |
|
}
|
|
1432 |
public BatteryIcon(int startLevel, int w, int h){
|
|
1433 |
level = startLevel;
|
|
1434 |
width = w;
|
|
1435 |
height = h;
|
|
1436 |
}
|
1435 |
1437 |
|
1436 |
|
public void paintIcon(Component c, Graphics g, int x, int y) {
|
1437 |
|
Graphics2D g2d = (Graphics2D) g.create();
|
1438 |
|
//clear the background
|
1439 |
|
g2d.setColor(Color.WHITE);
|
1440 |
|
g2d.fillRect(x + 1, y + 1, width - 2, height - 2);
|
1441 |
|
//outline
|
1442 |
|
g2d.setColor(Color.BLACK);
|
1443 |
|
g2d.drawRect((int)(x + width*.3), y + 2, (int)(width*.4), height - 4);
|
1444 |
|
//battery life rectangle
|
|
1438 |
public void paintIcon(Component c, Graphics g, int x, int y) {
|
|
1439 |
Graphics2D g2d = (Graphics2D) g.create();
|
|
1440 |
//clear the background
|
|
1441 |
g2d.setColor(Color.WHITE);
|
|
1442 |
g2d.fillRect(x + 1, y + 1, width - 2, height - 2);
|
|
1443 |
//outline
|
|
1444 |
g2d.setColor(Color.BLACK);
|
|
1445 |
g2d.drawRect((int)(x + width*.3), y + 2, (int)(width*.4), height - 4);
|
|
1446 |
//battery life rectangle
|
1445 |
1447 |
if (level > 50)
|
1446 |
1448 |
g2d.setColor(Color.GREEN);
|
1447 |
1449 |
else if (level > 25)
|
1448 |
1450 |
g2d.setColor(Color.YELLOW);
|
1449 |
1451 |
else
|
1450 |
1452 |
g2d.setColor(Color.RED);
|
1451 |
|
int greenX = (int)(x + 1 + width*.3);
|
1452 |
|
int greenY = (int)((y+3) + Math.abs(level-100.0)*(height-6)/(100));
|
1453 |
|
int greenWidth = (int)(width*.4 - 2)+1;
|
1454 |
|
int greenHeight = 1+(int)(level-0.0)*(height-6)/(100);
|
1455 |
|
g2d.fillRect(greenX, greenY, greenWidth, greenHeight);
|
1456 |
|
//text
|
1457 |
|
g2d.setColor(Color.BLACK);
|
1458 |
|
g2d.drawString(level + "%", greenX + greenWidth/2 - 10, greenY + greenHeight/2 + 5);
|
|
1453 |
int greenX = (int)(x + 1 + width*.3);
|
|
1454 |
int greenY = (int)((y+3) + Math.abs(level-100.0)*(height-6)/(100));
|
|
1455 |
int greenWidth = (int)(width*.4 - 2)+1;
|
|
1456 |
int greenHeight = 1+(int)(level-0.0)*(height-6)/(100);
|
|
1457 |
g2d.fillRect(greenX, greenY, greenWidth, greenHeight);
|
|
1458 |
//text
|
|
1459 |
g2d.setColor(Color.BLACK);
|
|
1460 |
g2d.drawString(level + "%", greenX + greenWidth/2 - 10, greenY + greenHeight/2 + 5);
|
1459 |
1461 |
|
1460 |
|
g2d.dispose();
|
1461 |
|
}
|
|
1462 |
g2d.dispose();
|
|
1463 |
}
|
1462 |
1464 |
|
1463 |
1465 |
/**
|
1464 |
1466 |
* Sets the battery level for this BatteryIcon. The level should be given in raw form, i.e. 0-255 directly
|
... | ... | |
1466 |
1468 |
*
|
1467 |
1469 |
* @param newLevel the new battery reading from the robot that this BatteryIcon will display.
|
1468 |
1470 |
*/
|
1469 |
|
public void setLevel(int newLevel) {
|
1470 |
|
level = convert(newLevel);
|
1471 |
|
repaint();
|
1472 |
|
System.out.println("Updated level to " + level);
|
1473 |
|
}
|
|
1471 |
public void setLevel(int newLevel) {
|
|
1472 |
level = convert(newLevel);
|
|
1473 |
repaint();
|
|
1474 |
System.out.println("Updated level to " + level);
|
|
1475 |
}
|
1474 |
1476 |
|
1475 |
|
public int getIconWidth() {
|
1476 |
|
return width;
|
1477 |
|
}
|
|
1477 |
public int getIconWidth() {
|
|
1478 |
return width;
|
|
1479 |
}
|
1478 |
1480 |
|
1479 |
|
public int getIconHeight() {
|
1480 |
|
return height;
|
1481 |
|
}
|
|
1481 |
public int getIconHeight() {
|
|
1482 |
return height;
|
|
1483 |
}
|
1482 |
1484 |
|
1483 |
1485 |
/**
|
1484 |
1486 |
* Converts a robot battery reading into representable form.
|