root / trunk / code / projects / colonet / ColonetGUI / Colonet.java @ 453
History | View | Annotate | Download (46.4 KB)
1 | 32 | gtress | //
|
---|---|---|---|
2 | // Colonet.java
|
||
3 | //
|
||
4 | |||
5 | import javax.swing.*; |
||
6 | 320 | gtress | import javax.swing.event.*; |
7 | import javax.imageio.*; |
||
8 | 32 | gtress | import java.awt.*; |
9 | import java.awt.image.*; |
||
10 | import java.awt.event.*; |
||
11 | import java.net.*; |
||
12 | import java.io.*; |
||
13 | 427 | gtress | import java.util.*; |
14 | 32 | gtress | |
15 | |||
16 | 320 | gtress | /**
|
17 | * The Colonet Graphical User Interface Applet for use locally and over an internet connection.
|
||
18 | * @author Gregory Tress
|
||
19 | *
|
||
20 | 341 | gtress | * 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 | 320 | gtress | */
|
23 | public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener, Runnable { |
||
24 | |||
25 | 427 | gtress | // Used for images
|
26 | 320 | gtress | final int CANVAS_SIZE = 500; //the applet may be slow if the canvas gets too large |
27 | 32 | gtress | final int BUFFER = 50; |
28 | final int RADIUS = 30; |
||
29 | 341 | gtress | |
30 | //Used for the robot controller
|
||
31 | final int VECTOR_CONTROLLER_HEIGHT = 220; |
||
32 | 420 | gtress | final int VECTOR_CONTROLLER_WIDTH = 350; |
33 | 341 | gtress | |
34 | 32 | gtress | |
35 | // Connection
|
||
36 | JTextField txtHost;
|
||
37 | JTextField txtPort;
|
||
38 | JButton btnConnect;
|
||
39 | 414 | gtress | JButton btnGetXBeeIDs;
|
40 | 32 | gtress | JLabel lblConnectionStatus;
|
41 | 320 | gtress | JTextArea txtMatrix;
|
42 | 32 | gtress | JTextArea txtInfo;
|
43 | JPanel panelConnect;
|
||
44 | JPanel panelServerInterface;
|
||
45 | 427 | gtress | Socket socket;
|
46 | OutputStreamWriter out;
|
||
47 | DataUpdater dataUpdater; |
||
48 | 32 | gtress | |
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 | 320 | gtress | JPanel panelRobotDirectionButtons;
|
60 | 32 | gtress | JPanel panelRobotCommands;
|
61 | JButton btnF, btnB, btnL, btnR, btnActivate;
|
||
62 | 320 | gtress | JComboBox cmbRobotNum;
|
63 | JLabel lblBattery;
|
||
64 | 428 | gtress | JLabel lblSelected;
|
65 | 333 | gtress | BatteryIcon batteryIcon; |
66 | 320 | gtress | JPanel panelBattery;
|
67 | VectorController vectorController; |
||
68 | BufferedImage imageVectorControl;
|
||
69 | 427 | gtress | JButton btnAssignID;
|
70 | 428 | gtress | boolean setWaypoint;
|
71 | 429 | gtress | int setWaypointID;
|
72 | 428 | gtress | JButton btnCommand_MoveTo;
|
73 | 429 | gtress | JButton btnCommand_MoveAll;
|
74 | 320 | gtress | JButton btnCommand_StopTask;
|
75 | JButton btnCommand_ResumeTask;
|
||
76 | JButton btnCommand_ChargeNow;
|
||
77 | JButton btnCommand_StopCharging;
|
||
78 | 32 | gtress | |
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 | 320 | gtress | JButton btnUpdateTasks;
|
91 | TaskAddWindow taskAddWindow; |
||
92 | 32 | gtress | |
93 | 427 | gtress | //Webcam
|
94 | 320 | gtress | WebcamPanel panelWebcam; |
95 | GraphicsPanel panelGraph; |
||
96 | 32 | gtress | GraphicsConfiguration gc;
|
97 | volatile BufferedImage image; |
||
98 | volatile Graphics2D canvas; |
||
99 | int cx, cy;
|
||
100 | 320 | gtress | JTabbedPane tabPaneMain;
|
101 | 32 | gtress | |
102 | Font botFont;
|
||
103 | volatile int numBots; |
||
104 | 427 | gtress | 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 | 320 | gtress | volatile int[] xbeeID; |
107 | 32 | gtress | |
108 | 428 | gtress | Thread paintThread;
|
109 | 32 | gtress | SelectionIndicator indicator; |
110 | 320 | gtress | WebcamLoader webcamLoader; |
111 | 35 | gtress | ColonetServerInterface csi; |
112 | 32 | gtress | |
113 | |||
114 | public void init () { |
||
115 | 427 | gtress | // Set the default look and feel - choose one
|
116 | 320 | gtress | //String laf = UIManager.getSystemLookAndFeelClassName();
|
117 | String laf = UIManager.getCrossPlatformLookAndFeelClassName(); |
||
118 | //String laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
|
||
119 | 32 | gtress | 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 | 320 | gtress | System.out.println("InterruptedException in init: " + e); |
137 | 32 | gtress | } catch (java.lang.reflect.InvocationTargetException e) {
|
138 | 320 | gtress | //This could happen for various reasons if there is a problem in createAndShowGUI
|
139 | e.printStackTrace(); |
||
140 | 32 | gtress | } |
141 | } |
||
142 | |||
143 | public void destroy () { |
||
144 | 428 | gtress | try { paintThread.interrupt(); } catch (Exception e) { } |
145 | 32 | gtress | try { indicator.interrupt(); } catch (Exception e) { } |
146 | } |
||
147 | |||
148 | private synchronized void createAndShowGUI () { |
||
149 | // init graphical elements
|
||
150 | 320 | gtress | // 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 | 341 | gtress | panelGraph = new GraphicsPanel(image, false); //set automatic double-buffering to false. we are doing it manually. |
157 | 320 | gtress | panelWebcam = new WebcamPanel();
|
158 | tabPaneMain = new JTabbedPane(); |
||
159 | tabPaneMain.add(panelWebcam, "Webcam");
|
||
160 | 429 | gtress | //tabPaneMain.add(panelGraph, "Graph");
|
161 | 32 | gtress | |
162 | 320 | gtress | // Calculate center of canvas
|
163 | cx = image.getWidth() / 2;
|
||
164 | cy = image.getHeight() / 2;
|
||
165 | |||
166 | 427 | gtress | // Set up robots
|
167 | 320 | gtress | botFont = new Font("Arial", Font.PLAIN, 14); |
168 | numBots = 0;
|
||
169 | 428 | gtress | selectedBot = -1;
|
170 | 427 | gtress | robotIcons = new ArrayList <RobotIcon> (); |
171 | 320 | gtress | |
172 | 32 | gtress | // Connection area
|
173 | 320 | gtress | txtMatrix = new JTextArea(); |
174 | txtMatrix.setBorder(BorderFactory.createTitledBorder("Input Matrix")); |
||
175 | 32 | gtress | txtInfo = new JTextArea(); |
176 | txtInfo.setBorder(BorderFactory.createTitledBorder("Info")); |
||
177 | txtInfo.setEditable(false);
|
||
178 | 385 | gtress | txtHost = new JTextField("localhost"); |
179 | 32 | gtress | txtHost.setBorder(BorderFactory.createTitledBorder("Host")); |
180 | txtPort = new JTextField("10123"); |
||
181 | txtPort.setBorder(BorderFactory.createTitledBorder("Port")); |
||
182 | btnConnect = new JButton("Connect"); |
||
183 | 414 | gtress | btnGetXBeeIDs = new JButton("Get XBee IDs"); |
184 | 389 | gtress | getRootPane().setDefaultButton(btnConnect); |
185 | 32 | gtress | 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 | 414 | gtress | panelConnect.add(btnGetXBeeIDs); |
193 | 32 | gtress | panelServerInterface = new JPanel(); |
194 | panelServerInterface.setLayout(new GridLayout(2,1)); |
||
195 | panelServerInterface.add(panelConnect); |
||
196 | 320 | gtress | panelServerInterface.add(txtMatrix); |
197 | |||
198 | 32 | gtress | // Robot direction panel
|
199 | panelRobotDirection = new JPanel(); |
||
200 | 320 | gtress | panelRobotDirectionButtons = new JPanel(); |
201 | 32 | gtress | btnF = new JButton("^"); |
202 | btnB = new JButton("v"); |
||
203 | btnL = new JButton("<"); |
||
204 | btnR = new JButton(">"); |
||
205 | btnActivate = new JButton("o"); |
||
206 | 320 | gtress | 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 | 32 | gtress | |
213 | 341 | gtress | imageVectorControl = gc.createCompatibleImage(VECTOR_CONTROLLER_WIDTH, VECTOR_CONTROLLER_HEIGHT); |
214 | 320 | gtress | vectorController = new VectorController(imageVectorControl);
|
215 | panelRobotDirection.setLayout(new BorderLayout()); |
||
216 | panelRobotDirection.add(vectorController, BorderLayout.CENTER);
|
||
217 | panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
|
||
218 | |||
219 | 32 | gtress | // Robot Control and Commands
|
220 | panelRobotCommands = new JPanel(); |
||
221 | 320 | gtress | panelRobotCommands.setLayout(new GridLayout(5,2)); |
222 | cmbRobotNum = new JComboBox(); |
||
223 | 333 | gtress | // Battery subset
|
224 | 341 | gtress | batteryIcon = new BatteryIcon(25); |
225 | 333 | gtress | lblBattery = new JLabel(batteryIcon); |
226 | 428 | gtress | lblSelected = new JLabel("None"); |
227 | // Command subset
|
||
228 | setWaypoint = false;
|
||
229 | 429 | gtress | setWaypointID = -1;
|
230 | 427 | gtress | btnAssignID = new JButton("Assign ID"); |
231 | 428 | gtress | btnCommand_MoveTo = new JButton("Move to ..."); |
232 | 429 | gtress | btnCommand_MoveAll = new JButton("Move all ..."); |
233 | 320 | gtress | 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 | 428 | gtress | panelRobotCommands.add(new JLabel("Selected Icon: ")); |
242 | panelRobotCommands.add(lblSelected); |
||
243 | 427 | gtress | panelRobotCommands.add(btnAssignID); |
244 | 429 | gtress | panelRobotCommands.add(new JLabel("")); |
245 | 428 | gtress | panelRobotCommands.add(btnCommand_MoveTo); |
246 | 429 | gtress | panelRobotCommands.add(btnCommand_MoveAll); |
247 | 427 | gtress | //panelRobotCommands.add(btnCommand_StopTask);
|
248 | //panelRobotCommands.add(btnCommand_ResumeTask);
|
||
249 | //panelRobotCommands.add(btnCommand_ChargeNow);
|
||
250 | //panelRobotCommands.add(btnCommand_StopCharging);
|
||
251 | 32 | gtress | panelRobotControl = new JPanel(); |
252 | panelRobotControl.setLayout(new GridLayout(2,1)); |
||
253 | panelRobotControl.add(panelRobotDirection); |
||
254 | panelRobotControl.add(panelRobotCommands); |
||
255 | |||
256 | 333 | gtress | |
257 | 32 | gtress | // 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 | 320 | gtress | panelTaskManagerControls.setLayout(new GridLayout(1,4)); |
267 | 32 | gtress | 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 | 320 | gtress | btnUpdateTasks = new JButton("Update"); |
274 | 32 | gtress | panelTaskManagerControlsPriority.add(btnMoveTaskUp); |
275 | panelTaskManagerControlsPriority.add(btnMoveTaskDown); |
||
276 | panelTaskManagerControls.add(btnAddTask); |
||
277 | panelTaskManagerControls.add(btnRemoveTask); |
||
278 | 320 | gtress | panelTaskManagerControls.add(btnUpdateTasks); |
279 | 32 | gtress | 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 | 320 | gtress | taskAddWindow = new TaskAddWindow();
|
284 | 136 | gtress | |
285 | 32 | gtress | // 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 | 320 | gtress | spLog.setPreferredSize(new Dimension(0, 120)); |
292 | 32 | gtress | 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 | 341 | gtress | tabPaneControl.setPreferredSize(new Dimension(VECTOR_CONTROLLER_WIDTH, 0)); |
299 | 32 | gtress | 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 | 320 | gtress | //panelSouth.add(spLog);
|
308 | 32 | gtress | |
309 | 320 | gtress | // Put all elements in the ContentPane
|
310 | 32 | gtress | this.getContentPane().setLayout(new BorderLayout()); |
311 | 320 | gtress | this.getContentPane().add(tabPaneMain, BorderLayout.CENTER); |
312 | 32 | gtress | this.getContentPane().add(panelSouth, BorderLayout.SOUTH); |
313 | this.getContentPane().add(panelControl, BorderLayout.EAST); |
||
314 | this.setVisible(true); |
||
315 | |||
316 | 320 | gtress | /* Add all listeners here */
|
317 | // Task Management
|
||
318 | 32 | gtress | btnAddTask.addActionListener(this);
|
319 | btnRemoveTask.addActionListener(this);
|
||
320 | btnMoveTaskUp.addActionListener(this);
|
||
321 | btnMoveTaskDown.addActionListener(this);
|
||
322 | 320 | gtress | 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 | 428 | gtress | btnCommand_MoveTo.addActionListener(this);
|
336 | 429 | gtress | btnCommand_MoveAll.addActionListener(this);
|
337 | 320 | gtress | btnCommand_StopTask.addActionListener(this);
|
338 | btnCommand_ResumeTask.addActionListener(this);
|
||
339 | btnCommand_ChargeNow.addActionListener(this);
|
||
340 | btnCommand_StopCharging.addActionListener(this);
|
||
341 | // Other
|
||
342 | 32 | gtress | btnConnect.addActionListener(this);
|
343 | 414 | gtress | btnGetXBeeIDs.addActionListener(this);
|
344 | 427 | gtress | btnAssignID.addActionListener(this);
|
345 | panelWebcam.addMouseListener(this);
|
||
346 | 320 | gtress | |
347 | 420 | gtress | // Set up animation threads
|
348 | 32 | gtress | indicator = new SelectionIndicator(canvas);
|
349 | indicator.setRadius(RADIUS+3, 15); //a tad more than the bot radius |
||
350 | |||
351 | } |
||
352 | |||
353 | 35 | gtress | public void run () { |
354 | while (true) { |
||
355 | 32 | gtress | repaint(); |
356 | 35 | gtress | try {
|
357 | Thread.sleep(90); |
||
358 | } catch (InterruptedException e) { |
||
359 | return;
|
||
360 | } |
||
361 | 32 | gtress | } |
362 | } |
||
363 | 428 | gtress | |
364 | public void paint (Graphics g) { |
||
365 | super.paint(g);
|
||
366 | } |
||
367 | 429 | gtress | |
368 | public void update (Graphics g) { |
||
369 | paint(g); |
||
370 | } |
||
371 | 32 | gtress | |
372 | 333 | gtress | /**
|
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 | 320 | gtress | public JTextArea getLog () { |
380 | return log;
|
||
381 | } |
||
382 | |||
383 | 333 | gtress | /**
|
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 | 320 | gtress | public JTextArea getMatrixInput () { |
391 | return txtMatrix;
|
||
392 | } |
||
393 | |||
394 | 333 | gtress | /**
|
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 | 320 | gtress | 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 | 136 | gtress | } |
427 | |||
428 | } |
||
429 | |||
430 | 420 | gtress | public void connect () { |
431 | webcamLoader = new WebcamLoader(this); |
||
432 | dataUpdater = new DataUpdater();
|
||
433 | 428 | gtress | paintThread = new Thread(this, "paintThread"); |
434 | 420 | gtress | 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 | 428 | gtress | paintThread.start(); |
441 | 420 | gtress | dataUpdater.start(); |
442 | webcamLoader.start(); |
||
443 | } |
||
444 | |||
445 | public void disconnect () { |
||
446 | btnConnect.setEnabled(true);
|
||
447 | lblConnectionStatus.setText("Status: Disconnected");
|
||
448 | 428 | gtress | try { paintThread.interrupt(); } catch (Exception e) { } |
449 | 420 | gtress | try { indicator.interrupt(); } catch (Exception e) { } |
450 | |||
451 | } |
||
452 | |||
453 | 333 | gtress | /**
|
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 | 320 | gtress | public void parseQueue (String line) { |
461 | log.append("Got queue update\n");
|
||
462 | 333 | gtress | //TODO: display new queue data in tasks tab
|
463 | 320 | gtress | } |
464 | |||
465 | 333 | gtress | /**
|
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 | 320 | gtress | public void parseXBeeIDs (String line) { |
483 | 333 | gtress | |
484 | 320 | gtress | 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 | 333 | gtress | /**
|
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 | 320 | gtress | public void parseBattery (String line) { |
515 | String [] str = line.split(" "); |
||
516 | 425 | gtress | int botNum = Integer.parseInt(str[2]); |
517 | int level = Integer.parseInt(str[3]); |
||
518 | 428 | gtress | int selected = -1; |
519 | 425 | gtress | try {
|
520 | 428 | gtress | selected = Integer.parseInt((String)cmbRobotNum.getSelectedItem()); |
521 | 425 | gtress | } catch (Exception e) { |
522 | 333 | gtress | } |
523 | 428 | gtress | if (selected == botNum) {
|
524 | 425 | gtress | batteryIcon.setLevel(level); |
525 | } |
||
526 | 320 | gtress | } |
527 | |||
528 | 32 | gtress | //
|
529 | 320 | gtress | // MouseListener methods
|
530 | 32 | gtress | //
|
531 | 320 | gtress | public void mouseExited(MouseEvent e) { |
532 | } |
||
533 | public void mouseEntered(MouseEvent e) { |
||
534 | } |
||
535 | public void mouseReleased(MouseEvent e) { |
||
536 | } |
||
537 | public void mouseClicked(MouseEvent e) { |
||
538 | } |
||
539 | 35 | gtress | public void mousePressed(MouseEvent e) { |
540 | 428 | gtress | |
541 | // If we are selecting a waypoint (destination) for a specific bot
|
||
542 | 429 | gtress | if (setWaypoint && setWaypointID >= 0) { |
543 | setWaypoint = false;
|
||
544 | panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); |
||
545 | 428 | gtress | if (selectedBot < 0 || robotIcons.get(selectedBot).id < 0) |
546 | return;
|
||
547 | |||
548 | RobotIcon r = robotIcons.get(selectedBot); |
||
549 | r.destx = e.getX(); |
||
550 | r.desty = e.getY(); |
||
551 | 429 | gtress | return;
|
552 | } |
||
553 | |||
554 | // If we are setting all waypoints
|
||
555 | if (setWaypoint) {
|
||
556 | 428 | gtress | setWaypoint = false;
|
557 | panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); |
||
558 | 429 | gtress | for (int i = 0; i < robotIcons.size(); i++) { |
559 | RobotIcon r = robotIcons.get(i); |
||
560 | if (r.id < 0) |
||
561 | continue;
|
||
562 | r.destx = e.getX(); |
||
563 | r.desty = e.getY(); |
||
564 | } |
||
565 | 428 | gtress | return;
|
566 | } |
||
567 | |||
568 | // Otherwise, we are selecting a bot, or doing nothing
|
||
569 | for (int i = 0; i < robotIcons.size(); i++) { |
||
570 | RobotIcon r = robotIcons.get(i); |
||
571 | if (r.contains(e.getX(), e.getY())) {
|
||
572 | selectedBot = i; |
||
573 | if (r.id < 0) |
||
574 | lblSelected.setText("Unidentified");
|
||
575 | else
|
||
576 | lblSelected.setText(" " + r.id);
|
||
577 | } |
||
578 | } |
||
579 | repaint(); |
||
580 | 320 | gtress | } |
581 | public void mouseDragged(MouseEvent e) { |
||
582 | } |
||
583 | public void mouseMoved(MouseEvent e) { |
||
584 | } |
||
585 | 32 | gtress | |
586 | 320 | gtress | //
|
587 | // KeyListener methods
|
||
588 | //
|
||
589 | public void keyPressed (KeyEvent e) { |
||
590 | int code = e.getKeyCode();
|
||
591 | if (code == KeyEvent.VK_UP) { |
||
592 | vectorController.setMaxForward(); |
||
593 | vectorController.sendToServer(); |
||
594 | } else if (code == KeyEvent.VK_DOWN) { |
||
595 | vectorController.setMaxReverse(); |
||
596 | vectorController.sendToServer(); |
||
597 | } else if (code == KeyEvent.VK_LEFT) { |
||
598 | vectorController.setMaxLeft(); |
||
599 | vectorController.sendToServer(); |
||
600 | } else if (code == KeyEvent.VK_RIGHT) { |
||
601 | vectorController.setMaxRight(); |
||
602 | vectorController.sendToServer(); |
||
603 | } else if (code == KeyEvent.VK_S) { |
||
604 | vectorController.setZero(); |
||
605 | vectorController.sendToServer(); |
||
606 | } |
||
607 | 181 | gtress | } |
608 | 320 | gtress | public void keyReleased (KeyEvent e) { |
609 | } |
||
610 | public void keyTyped (KeyEvent e) { |
||
611 | } |
||
612 | 136 | gtress | |
613 | 320 | gtress | |
614 | //
|
||
615 | // ActionListener method
|
||
616 | //
|
||
617 | public void actionPerformed (ActionEvent e) { |
||
618 | Object source = e.getSource();
|
||
619 | 414 | gtress | |
620 | 427 | gtress | // General Actions
|
621 | 404 | gtress | if (source == btnConnect) {
|
622 | 420 | gtress | connect(); |
623 | 414 | gtress | } else if (source == btnGetXBeeIDs) { |
624 | csi.sendXBeeIDRequest(); |
||
625 | 427 | gtress | } else if (source == btnAssignID) { |
626 | String message;
|
||
627 | 428 | gtress | if (selectedBot < 0) |
628 | return;
|
||
629 | int curID = robotIcons.get(selectedBot).id;
|
||
630 | if (curID < 0) |
||
631 | message = "That robot is unidentified. Please specify its ID.";
|
||
632 | else
|
||
633 | message = "That robot has ID " + curID + ". You may reassign it now."; |
||
634 | String result = JOptionPane.showInputDialog(this, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE); |
||
635 | if (result == null) |
||
636 | return;
|
||
637 | int newID = -1; |
||
638 | try {
|
||
639 | newID = Integer.parseInt(result);
|
||
640 | } catch (Exception ex) { |
||
641 | csi.warn("Invalid ID.");
|
||
642 | return;
|
||
643 | } |
||
644 | // Assign new ID and update display
|
||
645 | robotIcons.get(selectedBot).id = newID; |
||
646 | robotIcons.get(selectedBot).color = Color.GREEN;
|
||
647 | lblSelected.setText(" " + newID);
|
||
648 | |||
649 | 427 | gtress | |
650 | 320 | gtress | } |
651 | 414 | gtress | |
652 | 320 | gtress | // Robot Movement Controls
|
653 | else if (source == btnF) { |
||
654 | vectorController.setMaxForward(); |
||
655 | vectorController.sendToServer(); |
||
656 | } else if (source == btnB) { |
||
657 | vectorController.setMaxReverse(); |
||
658 | vectorController.sendToServer(); |
||
659 | } else if (source == btnL) { |
||
660 | vectorController.setMaxLeft(); |
||
661 | vectorController.sendToServer(); |
||
662 | } else if (source == btnR) { |
||
663 | vectorController.setMaxRight(); |
||
664 | vectorController.sendToServer(); |
||
665 | } else if (source == btnActivate) { |
||
666 | vectorController.setZero(); |
||
667 | vectorController.sendToServer(); |
||
668 | } |
||
669 | // Robot Commands (non-movement)
|
||
670 | 428 | gtress | else if (source == btnCommand_MoveTo) { |
671 | 429 | gtress | if (selectedBot < 0) |
672 | return;
|
||
673 | 428 | gtress | panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); |
674 | setWaypoint = true;
|
||
675 | 429 | gtress | setWaypointID = selectedBot; |
676 | 428 | gtress | |
677 | 429 | gtress | } else if (source == btnCommand_MoveAll) { |
678 | panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); |
||
679 | setWaypoint = true;
|
||
680 | setWaypointID = -1;
|
||
681 | |||
682 | 428 | gtress | } else if (source == btnCommand_StopTask) { |
683 | 320 | gtress | |
684 | } else if (source == btnCommand_ResumeTask) { |
||
685 | |||
686 | } else if (source == btnCommand_ChargeNow) { |
||
687 | |||
688 | } else if (source == btnCommand_StopCharging) { |
||
689 | |||
690 | } |
||
691 | |||
692 | // Queue Management
|
||
693 | else if (source == btnAddTask) { |
||
694 | taskAddWindow.prompt(); |
||
695 | } else if (source == btnRemoveTask) { |
||
696 | if (taskList.getSelectedIndex() >= 0); |
||
697 | csi.sendQueueRemove(taskList.getSelectedIndex()); |
||
698 | csi.sendQueueUpdate(); |
||
699 | } else if (source == btnMoveTaskUp) { |
||
700 | csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
|
||
701 | csi.sendQueueUpdate(); |
||
702 | } else if (source == btnMoveTaskDown) { |
||
703 | csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
|
||
704 | csi.sendQueueUpdate(); |
||
705 | } else if (source == btnUpdateTasks) { |
||
706 | csi.sendQueueUpdate(); |
||
707 | } |
||
708 | } |
||
709 | |||
710 | 32 | gtress | /*
|
711 | * SelectionIndicator thread.
|
||
712 | * Graphical representation of the selection marker
|
||
713 | *
|
||
714 | * step() and draw() are synchronized methods. step() is private and
|
||
715 | * used to update the position of the crosshairs. draw() is called
|
||
716 | * externally and should only run if all calculations in step() have
|
||
717 | * been completed.
|
||
718 | */
|
||
719 | 35 | gtress | private class SelectionIndicator extends Thread { |
720 | 32 | gtress | |
721 | 320 | gtress | final int INDICATOR_DELAY = 180; |
722 | final double DTHETA = 0.4; //larger values make the marker rotate faster |
||
723 | 32 | gtress | Graphics2D g; //canvas to draw on |
724 | boolean running;
|
||
725 | |||
726 | int sx, sy; //center |
||
727 | int r, dr; //radius and width of marker |
||
728 | double theta; //current angle |
||
729 | |||
730 | volatile Polygon poly1, poly2, poly3, poly4; |
||
731 | |||
732 | int px1, py1;
|
||
733 | int rx1, ry1;
|
||
734 | int px2, py2;
|
||
735 | int rx2, ry2;
|
||
736 | int px3, py3;
|
||
737 | int rx3, ry3;
|
||
738 | int px4, py4;
|
||
739 | int rx4, ry4;
|
||
740 | |||
741 | int steps;
|
||
742 | |||
743 | 35 | gtress | public SelectionIndicator (Graphics2D g) { |
744 | 32 | gtress | super("SelectionIndicator"); |
745 | this.g = g;
|
||
746 | running = false;
|
||
747 | steps = 0;
|
||
748 | |||
749 | theta = 0;
|
||
750 | rx1 = 0; ry1 = 0; |
||
751 | px1 = 0; py1 = 0; |
||
752 | rx2 = 0; ry2 = 0; |
||
753 | px2 = 0; py2 = 0; |
||
754 | rx3 = 0; ry3 = 0; |
||
755 | px3 = 0; py3 = 0; |
||
756 | rx4 = 0; ry4 = 0; |
||
757 | px4 = 0; py4 = 0; |
||
758 | } |
||
759 | |||
760 | 35 | gtress | public synchronized void setCenter (int sx, int sy) { |
761 | 32 | gtress | if (sx == this.sx && sy == this.sy) return; |
762 | this.sx = sx;
|
||
763 | this.sy = sy;
|
||
764 | steps = 0;
|
||
765 | } |
||
766 | |||
767 | 35 | gtress | public synchronized void setRadius (int r, int dr) { |
768 | 32 | gtress | this.r = r;
|
769 | this.dr = dr;
|
||
770 | steps = 0;
|
||
771 | } |
||
772 | |||
773 | 35 | gtress | public void run () { |
774 | 32 | gtress | running = true;
|
775 | 35 | gtress | while (running) {
|
776 | 32 | gtress | step(); |
777 | 35 | gtress | try {
|
778 | Thread.sleep(INDICATOR_DELAY);
|
||
779 | } catch (InterruptedException e) { |
||
780 | running = false;
|
||
781 | return;
|
||
782 | } |
||
783 | 32 | gtress | } |
784 | } |
||
785 | |||
786 | 35 | gtress | private synchronized void step () { |
787 | 32 | gtress | Polygon poly1_new = new Polygon(); |
788 | Polygon poly2_new = new Polygon(); |
||
789 | Polygon poly3_new = new Polygon(); |
||
790 | Polygon poly4_new = new Polygon(); |
||
791 | |||
792 | //the step
|
||
793 | theta = (theta + DTHETA/Math.PI) % (Math.PI); |
||
794 | |||
795 | //the calculation
|
||
796 | //let p be the point of the pointy thing toward the center
|
||
797 | //let r be the point at the opposite side
|
||
798 | |||
799 | //recalculate radius, if it will look cool, lolz
|
||
800 | int newr = r;
|
||
801 | 38 | gtress | if (steps < 100) |
802 | 320 | gtress | newr = (int)( r + 200/(steps+1) ); |
803 | 32 | gtress | |
804 | //precompute values for dx and dy
|
||
805 | int dx_inner = (int)(newr * Math.cos(theta)); |
||
806 | int dy_inner = (int)(newr * Math.sin(theta)); |
||
807 | int dx_outer = (int)((newr+dr) * Math.cos(theta)); |
||
808 | int dy_outer = (int)((newr+dr) * Math.sin(theta)); |
||
809 | |||
810 | //calculate polygon constants
|
||
811 | int dy_poly = (int)(dr/2 * Math.cos(theta)); |
||
812 | int dx_poly = (int)(dr/2 * Math.sin(theta)); |
||
813 | |||
814 | //determine critical points
|
||
815 | 35 | gtress | //kansas city shuffle!
|
816 | 32 | gtress | px1 = sx + dx_inner; |
817 | py1 = sy - dy_inner; |
||
818 | rx1 = sx + dx_outer; |
||
819 | ry1 = sy - dy_outer; |
||
820 | px2 = sx - dx_inner; |
||
821 | py2 = sy + dy_inner; |
||
822 | rx2 = sx - dx_outer; |
||
823 | ry2 = sy + dy_outer; |
||
824 | px3 = sx - dy_inner; |
||
825 | py3 = sy - dx_inner; |
||
826 | rx3 = sx - dy_outer; |
||
827 | ry3 = sy - dx_outer; |
||
828 | px4 = sx + dy_inner; |
||
829 | py4 = sy + dx_inner; |
||
830 | rx4 = sx + dy_outer; |
||
831 | ry4 = sy + dx_outer; |
||
832 | |||
833 | //create polygons
|
||
834 | poly1_new.addPoint(px1, py1); |
||
835 | poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly); |
||
836 | poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly); |
||
837 | poly2_new.addPoint(px2, py2); |
||
838 | poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly); |
||
839 | poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly); |
||
840 | poly3_new.addPoint(px3, py3); |
||
841 | poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly); |
||
842 | poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly); |
||
843 | poly4_new.addPoint(px4, py4); |
||
844 | poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly); |
||
845 | poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly); |
||
846 | |||
847 | //reassign updated polygons
|
||
848 | poly1 = poly1_new; |
||
849 | poly2 = poly2_new; |
||
850 | poly3 = poly3_new; |
||
851 | poly4 = poly4_new; |
||
852 | |||
853 | if (steps < 300) steps++; |
||
854 | } |
||
855 | |||
856 | 35 | gtress | public synchronized void draw () { |
857 | 32 | gtress | if (!running) return; |
858 | g.setColor(Color.GRAY);
|
||
859 | //draw polygons
|
||
860 | g.fillPolygon(poly1); |
||
861 | g.fillPolygon(poly2); |
||
862 | g.fillPolygon(poly3); |
||
863 | g.fillPolygon(poly4); |
||
864 | } |
||
865 | |||
866 | } |
||
867 | |||
868 | /*
|
||
869 | 320 | gtress | * DataUpdater thread.
|
870 | * The purpose of this thread is to request data from the server at regular intervals.
|
||
871 | 32 | gtress | *
|
872 | */
|
||
873 | 320 | gtress | class DataUpdater extends Thread { |
874 | 425 | gtress | final int DATAUPDATER_DELAY = 2200; |
875 | 320 | gtress | |
876 | public DataUpdater () {
|
||
877 | super("Colonet DataUpdater"); |
||
878 | } |
||
879 | |||
880 | public void run () { |
||
881 | String line;
|
||
882 | while (true) { |
||
883 | try {
|
||
884 | //request more data
|
||
885 | if (csi != null && csi.isReady()) { |
||
886 | 333 | gtress | //csi.sendSensorDataRequest();
|
887 | 425 | gtress | csi.sendXBeeIDRequest(); |
888 | if (cmbRobotNum.getSelectedIndex() > 0) { |
||
889 | String sel = (String) cmbRobotNum.getSelectedItem(); |
||
890 | int num = Integer.parseInt(sel); |
||
891 | csi.sendBatteryRequest(num); |
||
892 | } else {
|
||
893 | 333 | gtress | csi.sendBatteryRequest(200);
|
894 | 425 | gtress | } |
895 | 320 | gtress | } |
896 | Thread.sleep(DATAUPDATER_DELAY);
|
||
897 | } catch (InterruptedException e) { |
||
898 | return;
|
||
899 | } |
||
900 | } |
||
901 | } |
||
902 | |||
903 | } |
||
904 | 136 | gtress | |
905 | 320 | gtress | /*
|
906 | * GraphicsPanel class
|
||
907 | 341 | gtress | * An extension of JPanel, designed for holding an image that will be repainted regularly.
|
908 | 320 | gtress | */
|
909 | class GraphicsPanel extends JPanel { |
||
910 | protected Image img; |
||
911 | 136 | gtress | |
912 | 320 | gtress | public GraphicsPanel (Image img) { |
913 | super();
|
||
914 | this.img = img;
|
||
915 | 136 | gtress | } |
916 | |||
917 | 341 | gtress | public GraphicsPanel (Image img, boolean isDoubleBuffered) { |
918 | 320 | gtress | super(isDoubleBuffered);
|
919 | this.img = img;
|
||
920 | } |
||
921 | |||
922 | public void paint (Graphics g) { |
||
923 | // Place the buffered image on the screen, inside the panel
|
||
924 | g.drawImage(img, 0, 0, Color.WHITE, this); |
||
925 | } |
||
926 | |||
927 | } |
||
928 | |||
929 | /*
|
||
930 | * WebcamPanel class
|
||
931 | * Enables more efficient image handling in a component-controlled environment
|
||
932 | */
|
||
933 | class WebcamPanel extends JPanel { |
||
934 | int BORDER = 16; // this is arbitrary. it makes the image look nice inside a border. |
||
935 | int BOT_RADIUS = 40; |
||
936 | volatile BufferedImage img; |
||
937 | |||
938 | public WebcamPanel () {
|
||
939 | super();
|
||
940 | } |
||
941 | |||
942 | public synchronized void setImage (BufferedImage newimg) { |
||
943 | if (img != null) { |
||
944 | img.flush(); |
||
945 | img = null;
|
||
946 | img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); |
||
947 | 429 | gtress | txtMatrix.append("c");
|
948 | 273 | gtress | } |
949 | 429 | gtress | System.gc();
|
950 | 320 | gtress | img = newimg; |
951 | 270 | gtress | } |
952 | |||
953 | 320 | gtress | public synchronized void setPoints (Point [] newpoints) { |
954 | 427 | gtress | // test code -- need to remove later
|
955 | if (newpoints == null) { |
||
956 | Point [] p = new Point [3]; |
||
957 | 428 | gtress | p[0] = new Point(200,200); |
958 | p[1] = new Point(100,100); |
||
959 | 427 | gtress | p[2] = new Point(400,300); |
960 | setPoints(p); |
||
961 | return;
|
||
962 | } |
||
963 | |||
964 | for (int i = 0; i < newpoints.length; i++) { |
||
965 | Point p = newpoints[i];
|
||
966 | boolean found = false; |
||
967 | for (int j = 0; j < robotIcons.size(); j++) { |
||
968 | 428 | gtress | RobotIcon r = robotIcons.get(j); |
969 | 427 | gtress | if (r.isClose(p.x, p.y)) {
|
970 | r.move(p.x, p.y); |
||
971 | found = true;
|
||
972 | } |
||
973 | } |
||
974 | if (!found) {
|
||
975 | RobotIcon r = new RobotIcon(p.x, p.y);
|
||
976 | robotIcons.add(r); |
||
977 | } |
||
978 | } |
||
979 | |||
980 | 273 | gtress | } |
981 | |||
982 | 320 | gtress | public synchronized void paint (Graphics g) { |
983 | |||
984 | if (img == null) |
||
985 | return;
|
||
986 | // Place the image on the screen, inside the panel
|
||
987 | g.drawImage(img, |
||
988 | BORDER, //dx1 - the x coordinate of the first corner of the destination rectangle.
|
||
989 | BORDER, //dy1 - the y coordinate of the first corner of the destination rectangle.
|
||
990 | this.getWidth() - BORDER, //dx2 - the x coordinate of the second corner of the destination rectangle. |
||
991 | this.getHeight() - BORDER, //dy2 - the y coordinate of the second corner of the destination rectangle. |
||
992 | 0, //sx1 - the x coordinate of the first corner of the source rectangle. |
||
993 | 0, //sy1 - the y coordinate of the first corner of the source rectangle. |
||
994 | image.getWidth(), //sx2 - the x coordinate of the second corner of the source rectangle.
|
||
995 | image.getHeight(), //sy2 - the y coordinate of the second corner of the source rectangle.
|
||
996 | null //observer - object to be notified as more of the image is scaled and converted. |
||
997 | ); |
||
998 | |||
999 | // Draw Identifiers
|
||
1000 | 427 | gtress | if (robotIcons == null) |
1001 | 320 | gtress | return;
|
1002 | ((Graphics2D)g).setStroke(new BasicStroke(2)); |
||
1003 | 427 | gtress | for (int i = 0; i < robotIcons.size(); i++) { |
1004 | RobotIcon r = robotIcons.get(i); |
||
1005 | g.setColor(r.color); |
||
1006 | 428 | gtress | g.drawOval(r.x-RADIUS, r.y-RADIUS, 2*r.RADIUS, 2*r.RADIUS); |
1007 | // If the robot has a destination, draw the vector
|
||
1008 | if (r.destx >= 0) { |
||
1009 | g.drawLine(r.x, r.y, r.destx, r.desty); |
||
1010 | } |
||
1011 | 320 | gtress | } |
1012 | |||
1013 | 428 | gtress | // Identify currently-selected robot
|
1014 | if (selectedBot == -1) |
||
1015 | return;
|
||
1016 | g.setColor(Color.YELLOW);
|
||
1017 | RobotIcon r = robotIcons.get(selectedBot); |
||
1018 | g.drawOval(r.x-RADIUS-6, r.y-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12); |
||
1019 | |||
1020 | 270 | gtress | } |
1021 | |||
1022 | } |
||
1023 | |||
1024 | /*
|
||
1025 | 320 | gtress | * WebcamLoader class
|
1026 | * Handles the loading of the webcam image.
|
||
1027 | 270 | gtress | */
|
1028 | 320 | gtress | class WebcamLoader extends Thread |
1029 | { |
||
1030 | final int WEBCAMLOADER_DELAY = 1000; |
||
1031 | final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg"; |
||
1032 | final String LOCATIONS_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet/locations.txt"; |
||
1033 | 270 | gtress | |
1034 | 320 | gtress | URL imagePath;
|
1035 | URI locationsPath;
|
||
1036 | |||
1037 | MediaTracker mt;
|
||
1038 | BufferedImage image;
|
||
1039 | |||
1040 | public WebcamLoader (JApplet applet) |
||
1041 | { |
||
1042 | super("ColonetWebcamLoader"); |
||
1043 | mt = new MediaTracker(applet); |
||
1044 | ImageIO.setUseCache(false); |
||
1045 | try {
|
||
1046 | imagePath = new URL(IMAGE_PATH); |
||
1047 | } catch (MalformedURLException e) { |
||
1048 | System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n"); |
||
1049 | } |
||
1050 | try {
|
||
1051 | locationsPath = new URI(LOCATIONS_PATH); |
||
1052 | } catch (URISyntaxException x) { |
||
1053 | System.out.println("Malformed URI: could not form URI from: [" + LOCATIONS_PATH + "]\n"); |
||
1054 | } |
||
1055 | |||
1056 | 270 | gtress | } |
1057 | |||
1058 | 320 | gtress | public synchronized void run () |
1059 | { |
||
1060 | 270 | gtress | while (true) { |
1061 | try {
|
||
1062 | 320 | gtress | Thread.sleep(WEBCAMLOADER_DELAY);
|
1063 | if (image != null) |
||
1064 | image.flush(); |
||
1065 | 429 | gtress | System.gc();
|
1066 | 320 | gtress | image = ImageIO.read(imagePath);
|
1067 | // The MediaTracker waitForID pauses the thread until the image is loaded.
|
||
1068 | // We don't want to display a half-downloaded image.
|
||
1069 | mt.addImage(image, 1);
|
||
1070 | mt.waitForID(1);
|
||
1071 | mt.removeImage(image); |
||
1072 | // Save
|
||
1073 | panelWebcam.setImage(image); |
||
1074 | 429 | gtress | parseLocations(locationsPath.toURL()); |
1075 | //panelWebcam.setPoints(null); //temporary simulation code
|
||
1076 | 270 | gtress | } catch (InterruptedException e) { |
1077 | return;
|
||
1078 | 320 | gtress | } catch (java.security.AccessControlException e) {
|
1079 | 389 | gtress | csi.warn("Could not load webcam.\n" + e);
|
1080 | 320 | gtress | return;
|
1081 | } catch (IOException e) { |
||
1082 | log.append("IOException while trying to load image.");
|
||
1083 | } |
||
1084 | 270 | gtress | } |
1085 | } |
||
1086 | 320 | gtress | |
1087 | private void parseLocations (URL url) { |
||
1088 | URLConnection conn = null; |
||
1089 | DataInputStream data = null; |
||
1090 | String line;
|
||
1091 | String [] lines = new String[30]; |
||
1092 | StringBuffer buf = new StringBuffer(); |
||
1093 | int i = 0; |
||
1094 | |||
1095 | try {
|
||
1096 | conn = url.openConnection(); |
||
1097 | conn.connect(); |
||
1098 | data = new DataInputStream(new BufferedInputStream(conn.getInputStream())); |
||
1099 | while ((line = data.readLine()) != null) { |
||
1100 | buf.append(line + ";");
|
||
1101 | lines[i] = line; |
||
1102 | i++; |
||
1103 | } |
||
1104 | data.close(); |
||
1105 | } catch (IOException e) { |
||
1106 | System.out.println("IOException:" + e.getMessage()); |
||
1107 | } |
||
1108 | //log.append("Got robot locations: " + buf + "\n");
|
||
1109 | |||
1110 | // Get Point values from strings
|
||
1111 | Point [] points = new Point[i]; |
||
1112 | for (int j = 0; j < i; j++) { |
||
1113 | String [] parts = lines[j].split(","); |
||
1114 | int xval = Integer.parseInt(parts[0]); |
||
1115 | int yval = Integer.parseInt(parts[1]); |
||
1116 | Point p = new Point(xval, yval); |
||
1117 | points[j] = p; |
||
1118 | } |
||
1119 | if (points.length != 0) |
||
1120 | panelWebcam.setPoints(points); |
||
1121 | |||
1122 | } |
||
1123 | } |
||
1124 | 427 | gtress | |
1125 | /*
|
||
1126 | * RobotIcon class
|
||
1127 | * Provides a means for graphically representing and keeping track of webcam bots.
|
||
1128 | */
|
||
1129 | class RobotIcon { |
||
1130 | public final int RADIUS = 30; |
||
1131 | 429 | gtress | public final int CLOSE = 70; |
1132 | 427 | gtress | |
1133 | public int x, y; |
||
1134 | 428 | gtress | public int destx, desty; |
1135 | 427 | gtress | public int id; |
1136 | public Color color; |
||
1137 | |||
1138 | public RobotIcon (int x, int y) { |
||
1139 | this.id = -1; |
||
1140 | this.color = Color.RED; |
||
1141 | this.x = x;
|
||
1142 | this.y = y;
|
||
1143 | 428 | gtress | this.destx = -1; |
1144 | this.desty = -1; |
||
1145 | 427 | gtress | } |
1146 | |||
1147 | 428 | gtress | /**
|
1148 | * Relocates this RobotIcon to a new coordinate point.
|
||
1149 | *
|
||
1150 | */
|
||
1151 | 427 | gtress | public void move (int newX, int newY) { |
1152 | this.x = newX;
|
||
1153 | this.y = newY;
|
||
1154 | } |
||
1155 | |||
1156 | 428 | gtress | /**
|
1157 | * Determines if a given point is within a reasonable range of the current location
|
||
1158 | * to be considered the same robot when moving. The threshold is determined by the
|
||
1159 | * CLOSE value.
|
||
1160 | *
|
||
1161 | * @returns Whether or not the given point is reasonably close to the current location.
|
||
1162 | *
|
||
1163 | */
|
||
1164 | 427 | gtress | public boolean isClose (int nx, int ny) { |
1165 | int dist = (int) Point.distance(this.x, this.y, nx, ny); |
||
1166 | return (dist < CLOSE);
|
||
1167 | } |
||
1168 | |||
1169 | 428 | gtress | /**
|
1170 | * Determines whether a given point is within the rectangle that circumscribes the
|
||
1171 | * robot's circlular icon. Used for clicking on robots in webcam view.
|
||
1172 | *
|
||
1173 | */
|
||
1174 | public boolean contains (int px, int py) { |
||
1175 | Rectangle rect = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS); |
||
1176 | return rect.contains(px, py);
|
||
1177 | } |
||
1178 | |||
1179 | 427 | gtress | public String toString () { |
1180 | String s = "RobotIcon at (" + x + "," + y + "), id " + id; |
||
1181 | return s;
|
||
1182 | } |
||
1183 | |||
1184 | } |
||
1185 | 311 | emullini | |
1186 | 320 | gtress | |
1187 | /*
|
||
1188 | * VectorController class
|
||
1189 | * Manages robot motion control graphically
|
||
1190 | */
|
||
1191 | 427 | gtress | class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener { |
1192 | 320 | gtress | int x, y, cx, cy;
|
1193 | int width, height;
|
||
1194 | int side;
|
||
1195 | |||
1196 | public VectorController (Image img) { |
||
1197 | super (img);
|
||
1198 | width = img.getWidth(null);
|
||
1199 | height = img.getHeight(null);
|
||
1200 | cx = img.getWidth(null)/2; |
||
1201 | cy = img.getHeight(null)/2; |
||
1202 | x = cx; |
||
1203 | y = cy; |
||
1204 | if (width < height)
|
||
1205 | side = width; |
||
1206 | else
|
||
1207 | side = height; |
||
1208 | 427 | gtress | this.addMouseListener(this); |
1209 | this.addMouseMotionListener(this); |
||
1210 | 320 | gtress | } |
1211 | |||
1212 | public void setPoint (int x, int y) { |
||
1213 | if (!isValidPoint(x, y))
|
||
1214 | return;
|
||
1215 | this.x = x;
|
||
1216 | this.y = y;
|
||
1217 | repaint(); |
||
1218 | } |
||
1219 | |||
1220 | public boolean isValidPoint (int x, int y) { |
||
1221 | double xterm = Math.pow(1.0*(x - cx)/(side/2), 2); |
||
1222 | double yterm = Math.pow(1.0*(y - cy)/(side/2), 2); |
||
1223 | return (xterm + yterm <= 1); |
||
1224 | } |
||
1225 | |||
1226 | public void notifyMouseEvent (MouseEvent e, boolean send) { |
||
1227 | if (!isValidPoint(e.getX(), e.getY()))
|
||
1228 | return;
|
||
1229 | vectorController.setPoint(e.getX(), e.getY()); |
||
1230 | vectorController.repaint(); |
||
1231 | if (send)
|
||
1232 | vectorController.sendToServer(); |
||
1233 | } |
||
1234 | |||
1235 | 427 | gtress | public void mouseExited(MouseEvent e) { |
1236 | } |
||
1237 | public void mouseEntered(MouseEvent e) { |
||
1238 | } |
||
1239 | public void mouseReleased(MouseEvent e) { |
||
1240 | this.notifyMouseEvent(e, true); |
||
1241 | } |
||
1242 | public void mouseClicked(MouseEvent e) { |
||
1243 | this.notifyMouseEvent(e, false); |
||
1244 | } |
||
1245 | public void mousePressed(MouseEvent e) { |
||
1246 | } |
||
1247 | public void mouseDragged(MouseEvent e) { |
||
1248 | vectorController.notifyMouseEvent(e, false);
|
||
1249 | } |
||
1250 | public void mouseMoved(MouseEvent e) { |
||
1251 | } |
||
1252 | |||
1253 | 320 | gtress | public int getSpeed () { |
1254 | int dx = x - cx;
|
||
1255 | int dy = y - cy;
|
||
1256 | int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) ); |
||
1257 | return v;
|
||
1258 | } |
||
1259 | |||
1260 | 341 | gtress | /**
|
1261 | * Returns the angle of the control vector in positive degrees west of north,
|
||
1262 | * or negative degrees east of north, whichever is less than or equal to
|
||
1263 | * 180 degrees total.
|
||
1264 | */
|
||
1265 | 320 | gtress | public int getAngle () { |
1266 | int dx = x - cx;
|
||
1267 | int dy = cy - y;
|
||
1268 | 341 | gtress | // find reference angle in radians
|
1269 | 320 | gtress | double theta = Math.atan2(Math.abs(dx), Math.abs(dy)); |
1270 | 341 | gtress | // transform to degrees
|
1271 | theta = theta * 180 / Math.PI; |
||
1272 | // adjust for quadrant
|
||
1273 | if (dx < 0 && dy < 0) |
||
1274 | theta = 90 + theta;
|
||
1275 | else if (dx < 0 && dy >= 0) |
||
1276 | theta = 90 - theta;
|
||
1277 | else if (dx >= 0 && dy < 0) |
||
1278 | theta = -90 - theta;
|
||
1279 | else
|
||
1280 | theta = -90 + theta;
|
||
1281 | 320 | gtress | return (int) theta; |
1282 | } |
||
1283 | |||
1284 | public void paint (Graphics g) { |
||
1285 | g.setColor(Color.BLACK);
|
||
1286 | g.fillRect(0, 0, width, height); |
||
1287 | ((Graphics2D)g).setStroke(new BasicStroke(1)); |
||
1288 | g.setColor(Color.RED);
|
||
1289 | g.drawOval(cx-side/2, cy-side/2, side, side); |
||
1290 | ((Graphics2D)g).setStroke(new BasicStroke(2)); |
||
1291 | g.setColor(Color.GREEN);
|
||
1292 | g.drawLine(cx, cy, x, y); |
||
1293 | g.fillOval(x-3, y-3, 6, 6); |
||
1294 | } |
||
1295 | |||
1296 | public void setMaxForward () { |
||
1297 | setPoint(cx, cy - (side/2) + 1); |
||
1298 | } |
||
1299 | |||
1300 | public void setMaxReverse () { |
||
1301 | setPoint(cx, cy + (side/2) - 1); |
||
1302 | } |
||
1303 | |||
1304 | public void setMaxLeft () { |
||
1305 | setPoint(cx - (side/2) + 1, cy); |
||
1306 | } |
||
1307 | |||
1308 | public void setMaxRight () { |
||
1309 | setPoint(cx + (side/2) - 1, cy); |
||
1310 | } |
||
1311 | |||
1312 | public void setZero () { |
||
1313 | setPoint(cx, cy); |
||
1314 | } |
||
1315 | |||
1316 | public void sendToServer () { |
||
1317 | 341 | gtress | System.out.println("Attempting to send angle = " + getAngle() + ", speed = " + getSpeed() + ""); |
1318 | 320 | gtress | String dest = ColonetServerInterface.GLOBAL_DEST;
|
1319 | 425 | gtress | if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0) { |
1320 | dest = (String)cmbRobotNum.getSelectedItem();
|
||
1321 | } |
||
1322 | 320 | gtress | |
1323 | if (csi != null) { |
||
1324 | 417 | gtress | /*
|
1325 | csi.sendData(ColonetServerInterface.MOVE + " " + getSpeed() + " " + getAngle(), dest);
|
||
1326 | */
|
||
1327 | |||
1328 | //Directional commands
|
||
1329 | if (x > cx && y == cy) { //move right |
||
1330 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
|
||
1331 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
|
||
1332 | } else if (x < cx && y == cy) { //move left |
||
1333 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
|
||
1334 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
|
||
1335 | } else if (x == cx && y > cy) { //move forward |
||
1336 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
|
||
1337 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
|
||
1338 | } else if (x == cx && y < cy) { //move backward |
||
1339 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
|
||
1340 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
|
||
1341 | } else if (x == cx && y == cy) { //stop! |
||
1342 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
|
||
1343 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
|
||
1344 | 320 | gtress | } |
1345 | } |
||
1346 | } |
||
1347 | |||
1348 | 270 | gtress | } |
1349 | 320 | gtress | |
1350 | /*
|
||
1351 | * TaskAddWindow class
|
||
1352 | 341 | gtress | * A window that provides a simple way to add tasks to a task queue.
|
1353 | 320 | gtress | */
|
1354 | class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener { |
||
1355 | JPanel panelButtons;
|
||
1356 | JPanel panelParameters;
|
||
1357 | JPanel panelSouth;
|
||
1358 | JPanel panelSelection;
|
||
1359 | JButton btnSubmit;
|
||
1360 | JButton btnCancel;
|
||
1361 | DefaultListModel availableListModel;
|
||
1362 | JList availableList;
|
||
1363 | JScrollPane spAvailableTasks;
|
||
1364 | JTextArea txtDescription;
|
||
1365 | JTextField txtParameters;
|
||
1366 | |||
1367 | public TaskAddWindow () {
|
||
1368 | super("Add a Task"); |
||
1369 | super.setSize(500,500); |
||
1370 | super.setLayout(new BorderLayout()); |
||
1371 | |||
1372 | // set up buttons
|
||
1373 | btnSubmit = new JButton("Submit"); |
||
1374 | btnCancel = new JButton("Cancel"); |
||
1375 | panelButtons = new JPanel(); |
||
1376 | panelButtons.setLayout(new FlowLayout()); |
||
1377 | panelButtons.add(btnSubmit); |
||
1378 | panelButtons.add(btnCancel); |
||
1379 | this.getRootPane().setDefaultButton(btnSubmit);
|
||
1380 | |||
1381 | // set up task list
|
||
1382 | availableListModel = new DefaultListModel(); |
||
1383 | availableListModel.addElement("Map the Environment");
|
||
1384 | availableListModel.addElement("Clean Up Chemical Spill");
|
||
1385 | availableListModel.addElement("Grow Plants");
|
||
1386 | availableListModel.addElement("Save the Cheerleader");
|
||
1387 | availableListModel.addElement("Save the World");
|
||
1388 | availableList = new JList(availableListModel); |
||
1389 | availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||
1390 | availableList.setSelectedIndex(-1);
|
||
1391 | spAvailableTasks = new JScrollPane(availableList); |
||
1392 | spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task")); |
||
1393 | txtDescription = new JTextArea(); |
||
1394 | txtDescription.setEditable(false);
|
||
1395 | txtDescription.setLineWrap(true);
|
||
1396 | txtDescription.setWrapStyleWord(true);
|
||
1397 | txtDescription.setBorder(BorderFactory.createTitledBorder("Description")); |
||
1398 | |||
1399 | //set up parameter area
|
||
1400 | panelParameters = new JPanel(); |
||
1401 | panelParameters.setLayout(new BorderLayout()); |
||
1402 | txtParameters = new JTextField(); |
||
1403 | panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST); |
||
1404 | panelParameters.add(txtParameters); |
||
1405 | |||
1406 | // assemble objects
|
||
1407 | panelSelection = new JPanel(); |
||
1408 | panelSelection.setLayout(new GridLayout(1,2)); |
||
1409 | panelSelection.add(spAvailableTasks); |
||
1410 | panelSelection.add(txtDescription); |
||
1411 | |||
1412 | panelSouth = new JPanel(); |
||
1413 | panelSouth.setLayout(new GridLayout(2,1)); |
||
1414 | panelSouth.add(panelParameters); |
||
1415 | panelSouth.add(panelButtons); |
||
1416 | |||
1417 | this.getContentPane().add(panelSouth, BorderLayout.SOUTH); |
||
1418 | this.getContentPane().add(panelSelection, BorderLayout.CENTER); |
||
1419 | this.setLocationRelativeTo(null); |
||
1420 | |||
1421 | // add listeners here
|
||
1422 | availableList.addListSelectionListener(this);
|
||
1423 | btnSubmit.addActionListener(this);
|
||
1424 | btnCancel.addActionListener(this);
|
||
1425 | } |
||
1426 | |||
1427 | public void prompt () { |
||
1428 | this.setVisible(true); |
||
1429 | } |
||
1430 | |||
1431 | private String getDescription (int index) { |
||
1432 | if (index < 0) |
||
1433 | return ""; |
||
1434 | switch (index) {
|
||
1435 | case 0: return "SLAM and junk"; |
||
1436 | case 1: return "I'm not sure this works"; |
||
1437 | case 2: return "Push them into the light"; |
||
1438 | 333 | gtress | case 3: return "..."; |
1439 | case 4: return "..."; |
||
1440 | 320 | gtress | |
1441 | default: return "Task not recognized"; |
||
1442 | } |
||
1443 | } |
||
1444 | |||
1445 | public void actionPerformed (ActionEvent e) { |
||
1446 | Object source = e.getSource();
|
||
1447 | if (source == btnSubmit) {
|
||
1448 | txtParameters.setText(txtParameters.getText().trim()); |
||
1449 | |||
1450 | |||
1451 | this.setVisible(false); |
||
1452 | } else if (source == btnCancel) { |
||
1453 | this.setVisible(false); |
||
1454 | } |
||
1455 | } |
||
1456 | |||
1457 | public void valueChanged (ListSelectionEvent e) { |
||
1458 | int index = availableList.getSelectedIndex();
|
||
1459 | if (index >= 0) |
||
1460 | txtDescription.setText(getDescription(index)); |
||
1461 | } |
||
1462 | |||
1463 | } |
||
1464 | |||
1465 | 427 | gtress | /*
|
1466 | * BatteryIcon class
|
||
1467 | * Graphical representation of battery level
|
||
1468 | */
|
||
1469 | 341 | gtress | class BatteryIcon implements Icon { |
1470 | 311 | emullini | private int width; |
1471 | private int height; |
||
1472 | private int level; |
||
1473 | |||
1474 | 341 | gtress | /**
|
1475 | * Constructs a new BatteryIcon with all default parameters.
|
||
1476 | * Default width and height are 50.
|
||
1477 | * Default level is 100.
|
||
1478 | */
|
||
1479 | 311 | emullini | public BatteryIcon(){
|
1480 | 341 | gtress | this(100, 50, 50); |
1481 | 311 | emullini | } |
1482 | |||
1483 | 341 | gtress | /**
|
1484 | * Constructs a new BatteryIcon with default width and height, and with the specified level.
|
||
1485 | * Default width and height are 50.
|
||
1486 | */
|
||
1487 | 311 | emullini | public BatteryIcon(int startLevel){ |
1488 | 341 | gtress | this(startLevel, 50, 50); |
1489 | 311 | emullini | } |
1490 | |||
1491 | 341 | gtress | /**
|
1492 | * Constructs a new BatteryIcon with the specified level, width, and height.
|
||
1493 | */
|
||
1494 | 311 | emullini | public BatteryIcon(int startLevel, int w, int h){ |
1495 | level = startLevel; |
||
1496 | width = w; |
||
1497 | height = h; |
||
1498 | } |
||
1499 | |||
1500 | public void paintIcon(Component c, Graphics g, int x, int y) { |
||
1501 | Graphics2D g2d = (Graphics2D) g.create(); |
||
1502 | 341 | gtress | //clear the background
|
1503 | 311 | emullini | g2d.setColor(Color.WHITE);
|
1504 | 341 | gtress | g2d.fillRect(x + 1, y + 1, width - 2, height - 2); |
1505 | 311 | emullini | //outline
|
1506 | g2d.setColor(Color.BLACK);
|
||
1507 | 341 | gtress | g2d.drawRect((int)(x + width*.3), y + 2, (int)(width*.4), height - 4); |
1508 | 311 | emullini | //battery life rectangle
|
1509 | g2d.setColor(Color.GREEN);
|
||
1510 | int greenX = (int)(x + 1 + width*.3); |
||
1511 | int greenY = (int)((y+3) + Math.abs(level-100.0)*(height-6)/(100)); |
||
1512 | int greenWidth = (int)(width*.4 - 2)+1; |
||
1513 | int greenHeight = 1+(int)(level-0.0)*(height-6)/(100); |
||
1514 | g2d.fillRect(greenX, greenY, greenWidth, greenHeight); |
||
1515 | //text
|
||
1516 | g2d.setColor(Color.BLACK);
|
||
1517 | g2d.drawString(level + "%", greenX + greenWidth/2 - 10, greenY + greenHeight/2 + 5); |
||
1518 | |||
1519 | g2d.dispose(); |
||
1520 | 425 | gtress | System.out.println("Painted icon"); |
1521 | 311 | emullini | } |
1522 | |||
1523 | 341 | gtress | /**
|
1524 | * Sets the battery level for this BatteryIcon. The level should be given in raw form, i.e. 0-255 directly
|
||
1525 | * from the robot. The value will be converted to a representative percentage automatically.
|
||
1526 | *
|
||
1527 | * @param newLevel the new battery reading from the robot that this BatteryIcon will display.
|
||
1528 | */
|
||
1529 | 333 | gtress | public void setLevel(int newLevel) { |
1530 | 341 | gtress | level = convert(newLevel); |
1531 | 425 | gtress | repaint(); |
1532 | System.out.println("Updated level to " + level); |
||
1533 | 311 | emullini | } |
1534 | 341 | gtress | |
1535 | 311 | emullini | public int getIconWidth() { |
1536 | return width;
|
||
1537 | } |
||
1538 | |||
1539 | public int getIconHeight() { |
||
1540 | return height;
|
||
1541 | } |
||
1542 | 341 | gtress | |
1543 | /**
|
||
1544 | * Converts a robot battery reading into representable form.
|
||
1545 | * Readings from the robot are returned as raw values, 0-255. This method converts the reading into a value
|
||
1546 | * from 0 to 100 so that the practical remaining charge is represented.
|
||
1547 | *
|
||
1548 | * @param level The battery level as returned by the robot.
|
||
1549 | * @returns The representable battery percentage.
|
||
1550 | */
|
||
1551 | private int convert (int level) { |
||
1552 | // TODO: make this a forreals conversion.
|
||
1553 | 425 | gtress | return (int) (100.0 * level / 128); |
1554 | 341 | gtress | } |
1555 | |||
1556 | 136 | gtress | } |
1557 | 32 | gtress | |
1558 | 320 | gtress | |
1559 | 427 | gtress | |
1560 | 32 | gtress | } |