root / branches / autonomous_recharging / code / projects / colonet / ColonetGUI / Colonet.java @ 956
History | View | Annotate | Download (23.1 KB)
1 | 32 | gtress | //
|
---|---|---|---|
2 | // Colonet.java
|
||
3 | //
|
||
4 | |||
5 | import javax.swing.*; |
||
6 | import java.awt.*; |
||
7 | import java.awt.image.*; |
||
8 | import java.awt.event.*; |
||
9 | import java.net.*; |
||
10 | import java.io.*; |
||
11 | import java.util.Random; |
||
12 | import java.applet.*; |
||
13 | |||
14 | public class Colonet extends JApplet implements ActionListener, MouseListener, Runnable { |
||
15 | |||
16 | final int CANVAS_SIZE = 500; //don't make this too large, or the applet will be slow. |
||
17 | final int BUFFER = 50; |
||
18 | final int RADIUS = 30; |
||
19 | |||
20 | // Connection
|
||
21 | JTextField txtHost;
|
||
22 | JTextField txtPort;
|
||
23 | JButton btnConnect;
|
||
24 | JButton btnGraph;
|
||
25 | JLabel lblConnectionStatus;
|
||
26 | 76 | gtress | JTextArea txtMatrix;
|
27 | 32 | gtress | JTextArea txtInfo;
|
28 | JPanel panelConnect;
|
||
29 | JPanel panelServerInterface;
|
||
30 | |||
31 | // Stats
|
||
32 | JLabel lblBattery;
|
||
33 | JLabel lblTokenPasses;
|
||
34 | JLabel lblHastToken;
|
||
35 | JPanel panelStats;
|
||
36 | |||
37 | // South
|
||
38 | JPanel panelSouth;
|
||
39 | JTextArea log;
|
||
40 | JScrollPane spLog;
|
||
41 | |||
42 | // Control
|
||
43 | JPanel panelControl;
|
||
44 | JTabbedPane tabPaneControl;
|
||
45 | JPanel panelRobotControl;
|
||
46 | JPanel panelRobotDirection;
|
||
47 | JPanel panelRobotCommands;
|
||
48 | JButton btnF, btnB, btnL, btnR, btnActivate;
|
||
49 | 67 | gtress | JComboBox cmbRobotNum;
|
50 | 32 | gtress | |
51 | // Task Manager
|
||
52 | JPanel panelTaskManager;
|
||
53 | JScrollPane spTaskManager;
|
||
54 | JPanel panelTaskManagerControls;
|
||
55 | JPanel panelTaskManagerControlsPriority;
|
||
56 | DefaultListModel taskListModel;
|
||
57 | JList taskList;
|
||
58 | JButton btnAddTask;
|
||
59 | JButton btnRemoveTask;
|
||
60 | JButton btnMoveTaskUp;
|
||
61 | JButton btnMoveTaskDown;
|
||
62 | |||
63 | // Graphics
|
||
64 | JPanel panel;
|
||
65 | GraphicsConfiguration gc;
|
||
66 | volatile BufferedImage image; |
||
67 | volatile Graphics2D canvas; |
||
68 | int cx, cy;
|
||
69 | |||
70 | Socket socket;
|
||
71 | OutputStreamWriter out; //TODO: add a BufferedWriter |
||
72 | 76 | gtress | DataUpdater dataUpdater; |
73 | 32 | gtress | |
74 | Font botFont;
|
||
75 | Random random = new Random(); |
||
76 | volatile int tokenLoc; //the token is currently here |
||
77 | volatile int numBots; |
||
78 | volatile int selectedBot; //the user has selected this bot |
||
79 | volatile Rectangle[] botRect; //contains boundary shapes around bots for click detection |
||
80 | |||
81 | Thread drawThread;
|
||
82 | 76 | gtress | Simulator simulator; |
83 | 32 | gtress | SelectionIndicator indicator; |
84 | PacketMonitor packetMonitor; |
||
85 | 35 | gtress | ColonetServerInterface csi; |
86 | 32 | gtress | |
87 | |||
88 | public void init () { |
||
89 | // set the default look and feel
|
||
90 | String laf = UIManager.getSystemLookAndFeelClassName(); |
||
91 | try {
|
||
92 | UIManager.setLookAndFeel(laf);
|
||
93 | } catch (UnsupportedLookAndFeelException exc) { |
||
94 | System.err.println ("Warning: UnsupportedLookAndFeel: " + laf); |
||
95 | } catch (Exception exc) { |
||
96 | System.err.println ("Error loading " + laf + ": " + exc); |
||
97 | } |
||
98 | // We should invoke and wait to avoid browser display difficulties
|
||
99 | Runnable r = new Runnable() { |
||
100 | public void run() { |
||
101 | createAndShowGUI(); |
||
102 | } |
||
103 | }; |
||
104 | try {
|
||
105 | SwingUtilities.invokeAndWait(r);
|
||
106 | } catch (InterruptedException e) { |
||
107 | //Not really sure why we would be in this situation
|
||
108 | System.out.println(e);
|
||
109 | } catch (java.lang.reflect.InvocationTargetException e) {
|
||
110 | //This should never happen. Seriously.
|
||
111 | System.out.println(e);
|
||
112 | } |
||
113 | } |
||
114 | |||
115 | public void destroy () { |
||
116 | try { drawThread.interrupt(); } catch (Exception e) { } |
||
117 | try { indicator.interrupt(); } catch (Exception e) { } |
||
118 | try { packetMonitor.interrupt(); } catch (Exception e) { } |
||
119 | } |
||
120 | |||
121 | private synchronized void createAndShowGUI () { |
||
122 | // init graphical elements
|
||
123 | panel = new JPanel(false); //set automatic double-buffering to false. we are doing it manually. |
||
124 | |||
125 | // Connection area
|
||
126 | 76 | gtress | txtMatrix = new JTextArea("- 9 3 - 1\n- - - 5 -\n4 - - - 2\n- - - - -\n1 - - 3 -"); |
127 | txtMatrix.setBorder(BorderFactory.createTitledBorder("Input Matrix")); |
||
128 | 32 | gtress | txtInfo = new JTextArea(); |
129 | txtInfo.setBorder(BorderFactory.createTitledBorder("Info")); |
||
130 | txtInfo.setEditable(false);
|
||
131 | btnGraph = new JButton("Run"); |
||
132 | txtHost = new JTextField("roboclub1.frc.ri.cmu.edu"); |
||
133 | txtHost.setBorder(BorderFactory.createTitledBorder("Host")); |
||
134 | txtPort = new JTextField("10123"); |
||
135 | txtPort.setBorder(BorderFactory.createTitledBorder("Port")); |
||
136 | btnConnect = new JButton("Connect"); |
||
137 | lblConnectionStatus = new JLabel("Status: Offline"); |
||
138 | panelConnect = new JPanel(); |
||
139 | panelConnect.setLayout(new GridLayout(6,1)); |
||
140 | panelConnect.add(lblConnectionStatus); |
||
141 | panelConnect.add(txtHost); |
||
142 | panelConnect.add(txtPort); |
||
143 | panelConnect.add(btnConnect); |
||
144 | panelConnect.add(txtInfo); |
||
145 | panelConnect.add(btnGraph); |
||
146 | panelServerInterface = new JPanel(); |
||
147 | panelServerInterface.setLayout(new GridLayout(2,1)); |
||
148 | panelServerInterface.add(panelConnect); |
||
149 | 76 | gtress | panelServerInterface.add(txtMatrix); |
150 | 32 | gtress | |
151 | // Status Elements
|
||
152 | lblTokenPasses = new JLabel(); |
||
153 | lblBattery = new JLabel("???"); |
||
154 | panelStats = new JPanel(); |
||
155 | panelStats.setLayout(new GridLayout(4,2)); |
||
156 | panelStats.add(new JLabel("Token Passes / sec ")); |
||
157 | panelStats.add(lblTokenPasses); |
||
158 | panelStats.add(new JLabel("Battery ")); |
||
159 | panelStats.add(lblBattery); |
||
160 | panelStats.add(new JLabel("Token Passes / sec ")); |
||
161 | panelStats.add(lblTokenPasses); |
||
162 | |||
163 | 72 | gtress | //TODO: add panelStats somewhere?
|
164 | 32 | gtress | |
165 | // Robot direction panel
|
||
166 | panelRobotDirection = new JPanel(); |
||
167 | btnF = new JButton("^"); |
||
168 | btnB = new JButton("v"); |
||
169 | btnL = new JButton("<"); |
||
170 | btnR = new JButton(">"); |
||
171 | btnActivate = new JButton("o"); |
||
172 | panelRobotDirection = new JPanel(); |
||
173 | panelRobotDirection.setLayout(new GridLayout(3,3)); |
||
174 | panelRobotDirection.add(new JLabel("")); |
||
175 | panelRobotDirection.add(btnF); |
||
176 | panelRobotDirection.add(new JLabel("")); |
||
177 | panelRobotDirection.add(btnL); |
||
178 | panelRobotDirection.add(btnActivate); |
||
179 | panelRobotDirection.add(btnR); |
||
180 | panelRobotDirection.add(new JLabel("")); |
||
181 | panelRobotDirection.add(btnB); |
||
182 | panelRobotDirection.add(new JLabel("")); |
||
183 | |||
184 | // Robot Control and Commands
|
||
185 | panelRobotCommands = new JPanel(); |
||
186 | panelRobotCommands.setLayout(new FlowLayout()); |
||
187 | 67 | gtress | cmbRobotNum = new JComboBox(); |
188 | panelRobotCommands.add(cmbRobotNum); |
||
189 | 32 | gtress | panelRobotCommands.add(new JLabel("Commands go here")); |
190 | panelRobotControl = new JPanel(); |
||
191 | panelRobotControl.setLayout(new GridLayout(2,1)); |
||
192 | panelRobotControl.add(panelRobotDirection); |
||
193 | panelRobotControl.add(panelRobotCommands); |
||
194 | |||
195 | // Task Manager
|
||
196 | panelTaskManager = new JPanel(); |
||
197 | panelTaskManager.setLayout(new BorderLayout()); |
||
198 | taskListModel = new DefaultListModel(); |
||
199 | taskListModel.addElement("Map the Environment");
|
||
200 | taskListModel.addElement("Clean Up Chemical Spill");
|
||
201 | taskListModel.addElement("Grow Plants");
|
||
202 | taskListModel.addElement("Save the Cheerleader");
|
||
203 | taskListModel.addElement("Save the World");
|
||
204 | taskList = new JList(taskListModel); |
||
205 | taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||
206 | taskList.setSelectedIndex(0);
|
||
207 | spTaskManager = new JScrollPane(taskList); |
||
208 | panelTaskManagerControls = new JPanel(); |
||
209 | panelTaskManagerControls.setLayout(new GridLayout(1,3)); |
||
210 | panelTaskManagerControlsPriority = new JPanel(); |
||
211 | panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2)); |
||
212 | btnAddTask = new JButton("Add..."); |
||
213 | btnRemoveTask = new JButton("Remove"); |
||
214 | btnMoveTaskUp = new JButton("^"); |
||
215 | btnMoveTaskDown = new JButton("v"); |
||
216 | panelTaskManagerControlsPriority.add(btnMoveTaskUp); |
||
217 | panelTaskManagerControlsPriority.add(btnMoveTaskDown); |
||
218 | panelTaskManagerControls.add(btnAddTask); |
||
219 | panelTaskManagerControls.add(btnRemoveTask); |
||
220 | panelTaskManagerControls.add(panelTaskManagerControlsPriority); |
||
221 | panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
|
||
222 | panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
|
||
223 | panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH); |
||
224 | |||
225 | // Message log
|
||
226 | log = new JTextArea(); |
||
227 | spLog = new JScrollPane(log, |
||
228 | ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
|
||
229 | ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||
230 | spLog.setBorder(BorderFactory.createTitledBorder("Log")); |
||
231 | spLog.setPreferredSize(new Dimension(0, 150)); |
||
232 | log.setEditable(false);
|
||
233 | |||
234 | // Main control mechanism
|
||
235 | panelControl = new JPanel(); |
||
236 | panelControl.setLayout(new GridLayout(1,1)); |
||
237 | tabPaneControl = new JTabbedPane(JTabbedPane.TOP); |
||
238 | tabPaneControl.setPreferredSize(new Dimension(300, 0)); |
||
239 | tabPaneControl.addTab("Connection", panelServerInterface);
|
||
240 | tabPaneControl.addTab("Robots", panelRobotControl);
|
||
241 | tabPaneControl.addTab("Tasks", panelTaskManager);
|
||
242 | panelControl.add(tabPaneControl); |
||
243 | |||
244 | // Set up elements in the south
|
||
245 | panelSouth = new JPanel(); |
||
246 | panelSouth.setLayout(new GridLayout(1,2)); |
||
247 | panelSouth.add(spLog); |
||
248 | |||
249 | this.getContentPane().setLayout(new BorderLayout()); |
||
250 | this.getContentPane().add(panel, BorderLayout.CENTER); |
||
251 | this.getContentPane().add(panelSouth, BorderLayout.SOUTH); |
||
252 | this.getContentPane().add(panelControl, BorderLayout.EAST); |
||
253 | this.setVisible(true); |
||
254 | |||
255 | 72 | gtress | /* Add all listeners here */
|
256 | // Task Management
|
||
257 | 32 | gtress | btnAddTask.addActionListener(this);
|
258 | btnRemoveTask.addActionListener(this);
|
||
259 | btnMoveTaskUp.addActionListener(this);
|
||
260 | btnMoveTaskDown.addActionListener(this);
|
||
261 | 72 | gtress | // Robot Control
|
262 | btnF.addActionListener(this);
|
||
263 | btnB.addActionListener(this);
|
||
264 | btnL.addActionListener(this);
|
||
265 | btnR.addActionListener(this);
|
||
266 | btnActivate.addActionListener(this);
|
||
267 | // Other
|
||
268 | 32 | gtress | btnGraph.addActionListener(this);
|
269 | btnConnect.addActionListener(this);
|
||
270 | panel.addMouseListener(this);
|
||
271 | |||
272 | |||
273 | // Get the graphics configuration of the screen to create a buffer
|
||
274 | gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||
275 | .getDefaultScreenDevice().getDefaultConfiguration(); |
||
276 | image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE); |
||
277 | canvas = image.createGraphics(); |
||
278 | canvas.setStroke(new BasicStroke(2)); //set pen width |
||
279 | |||
280 | // Calculate center of canvas
|
||
281 | cx = image.getWidth() / 2;
|
||
282 | cy = image.getHeight() / 2;
|
||
283 | |||
284 | botFont = new Font("Arial", Font.PLAIN, 30); |
||
285 | tokenLoc = 0;
|
||
286 | numBots = 0;
|
||
287 | selectedBot = 0;
|
||
288 | |||
289 | // Set up dependent threads
|
||
290 | indicator = new SelectionIndicator(canvas);
|
||
291 | indicator.setRadius(RADIUS+3, 15); //a tad more than the bot radius |
||
292 | packetMonitor = new PacketMonitor();
|
||
293 | 76 | gtress | simulator = new Simulator();
|
294 | 35 | gtress | |
295 | 76 | gtress | csi = new ColonetServerInterface(log, txtMatrix);
|
296 | 32 | gtress | |
297 | } |
||
298 | |||
299 | 35 | gtress | public synchronized void paint (Graphics g) { |
300 | 32 | gtress | /* First, redraw the graphical components in the applet.
|
301 | This paint method overrides the built-in paint of the
|
||
302 | JApplet, and we don't want to deal with redrawing the
|
||
303 | components manually. Fuck that shit. */
|
||
304 | super.paint(g);
|
||
305 | |||
306 | // Place the buffered image on the screen, inside the panel
|
||
307 | panel.getGraphics().drawImage(image, 0, 0, Color.WHITE, this); |
||
308 | |||
309 | } |
||
310 | |||
311 | 35 | gtress | public synchronized void update (Graphics g) { |
312 | 32 | gtress | paint(g); |
313 | } |
||
314 | |||
315 | 35 | gtress | public void actionPerformed (ActionEvent e) { |
316 | 32 | gtress | Object source = e.getSource();
|
317 | if (source == btnGraph) {
|
||
318 | btnGraph.setEnabled(false);
|
||
319 | lblConnectionStatus.setText("Simulating");
|
||
320 | |||
321 | //Start dependent threads
|
||
322 | drawThread = new Thread(this, "drawThread"); |
||
323 | drawThread.start(); |
||
324 | indicator.start(); |
||
325 | packetMonitor.start(); |
||
326 | 76 | gtress | simulator.start(); |
327 | 32 | gtress | } else if (source == btnConnect) { |
328 | doSocket(); |
||
329 | 76 | gtress | dataUpdater = new DataUpdater();
|
330 | dataUpdater.start(); |
||
331 | 32 | gtress | } else if (source == btnRemoveTask) { |
332 | try {
|
||
333 | taskListModel.remove(taskList.getSelectedIndex()); |
||
334 | } catch (ArrayIndexOutOfBoundsException ex) { |
||
335 | } |
||
336 | 72 | gtress | |
337 | // Robot controls
|
||
338 | } else if (source == btnF) { |
||
339 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", ColonetServerInterface.GLOBAL_DEST);
|
||
340 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", ColonetServerInterface.GLOBAL_DEST);
|
||
341 | } else if (source == btnB) { |
||
342 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", ColonetServerInterface.GLOBAL_DEST);
|
||
343 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", ColonetServerInterface.GLOBAL_DEST);
|
||
344 | } else if (source == btnL) { |
||
345 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", ColonetServerInterface.GLOBAL_DEST);
|
||
346 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", ColonetServerInterface.GLOBAL_DEST);
|
||
347 | } else if (source == btnR) { |
||
348 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", ColonetServerInterface.GLOBAL_DEST);
|
||
349 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", ColonetServerInterface.GLOBAL_DEST);
|
||
350 | } else if (source == btnActivate) { |
||
351 | csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 0", ColonetServerInterface.GLOBAL_DEST);
|
||
352 | csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 0", ColonetServerInterface.GLOBAL_DEST);
|
||
353 | 32 | gtress | } |
354 | } |
||
355 | |||
356 | 35 | gtress | private void randomize () { |
357 | 32 | gtress | Random r = new Random(); |
358 | StringBuilder s = new StringBuilder(); |
||
359 | |||
360 | int count = r.nextInt(8) + 1; |
||
361 | 35 | gtress | for (int i = 0; i < count; i++) { |
362 | for (int j = 0; j < count; j++) { |
||
363 | 32 | gtress | if (r.nextBoolean())
|
364 | s.append("" + (r.nextInt(16) + 1)); |
||
365 | else
|
||
366 | s.append("-");
|
||
367 | if (j != count-1) |
||
368 | s.append(" ");
|
||
369 | } |
||
370 | if (i != count-1) s.append("\n"); |
||
371 | } |
||
372 | |||
373 | 76 | gtress | txtMatrix.setText(s.toString()); |
374 | 32 | gtress | } |
375 | |||
376 | 35 | gtress | private void doSocket () { |
377 | csi.connect(txtHost.getText(), txtPort.getText()); |
||
378 | 32 | gtress | } |
379 | |||
380 | 35 | gtress | public void drawRobot (int id, int x, int y) { |
381 | 32 | gtress | //save the bot in memory, so we can tell if we click on it later
|
382 | botRect[id] = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS); |
||
383 | |||
384 | //draw the bot on the canvas
|
||
385 | canvas.setColor(Color.BLACK);
|
||
386 | canvas.drawOval(x-RADIUS, y-RADIUS, RADIUS*2, RADIUS*2); |
||
387 | |||
388 | //draw the label
|
||
389 | canvas.setFont(botFont); |
||
390 | canvas.drawString("" + id, x-10, y+10); |
||
391 | } |
||
392 | |||
393 | 35 | gtress | public void drawConnection (int start, int end, int radius, Color color) { |
394 | 32 | gtress | final int ARROW_LENGTH = 18; |
395 | |||
396 | double angle = 2.0 * Math.PI / numBots; |
||
397 | int startx, starty, endx, endy;
|
||
398 | startx = cx - (int)(radius * Math.cos(start * angle)); |
||
399 | starty = cy - (int)(radius * Math.sin(start * angle)); |
||
400 | endx = cx - (int)(radius * Math.cos(end * angle)); |
||
401 | endy = cy - (int)(radius * Math.sin(end * angle)); |
||
402 | canvas.setColor(color); |
||
403 | canvas.drawLine(startx, starty, endx, endy); |
||
404 | |||
405 | //create arrow
|
||
406 | if (color.equals(Color.BLACK)) return; |
||
407 | int big_dy = starty - endy;
|
||
408 | int big_dx = endx - startx;
|
||
409 | double theta = 0; |
||
410 | if (big_dx == 0 && starty > endy) //pointing up |
||
411 | theta = Math.PI/2; |
||
412 | else if (big_dx == 0 && starty < endy) //pointing down |
||
413 | theta = 3*Math.PI/2; |
||
414 | else if (big_dy == 0 && startx > endx) //pointing left |
||
415 | theta = Math.PI;
|
||
416 | else if (big_dy == 0 && startx < endx) //pointing right |
||
417 | theta = 0;
|
||
418 | else
|
||
419 | theta = Math.atan(1.0 * big_dy / big_dx); |
||
420 | |||
421 | //create ploygon
|
||
422 | Polygon poly = new Polygon(); |
||
423 | int dx_arrow = Math.abs((int)(ARROW_LENGTH * Math.cos(theta))); |
||
424 | int dy_arrow = Math.abs((int)(ARROW_LENGTH * Math.sin(theta))); |
||
425 | int dy_half = (int)(ARROW_LENGTH/2 * Math.cos(theta)); |
||
426 | int dx_half = (int)(ARROW_LENGTH/2 * Math.sin(theta)); |
||
427 | int rx = (big_dx > 0) ? endx - dx_arrow : endx + dx_arrow; |
||
428 | int ry = (big_dy > 0) ? endy + dy_arrow : endy - dy_arrow; |
||
429 | poly.addPoint(endx, endy); |
||
430 | poly.addPoint(rx - dx_half, ry - dy_half); |
||
431 | poly.addPoint(rx + dx_half, ry + dy_half); |
||
432 | canvas.fillPolygon(poly); |
||
433 | } |
||
434 | |||
435 | 35 | gtress | public void run () { |
436 | while (true) { |
||
437 | 32 | gtress | step(); |
438 | repaint(); |
||
439 | 35 | gtress | try {
|
440 | Thread.sleep(90); |
||
441 | } catch (InterruptedException e) { |
||
442 | return;
|
||
443 | } |
||
444 | 32 | gtress | } |
445 | } |
||
446 | |||
447 | 35 | gtress | public void step () { |
448 | 32 | gtress | final int DIAMETER = image.getWidth() - 2*BUFFER; |
449 | final int BIGRADIUS = DIAMETER / 2; |
||
450 | final int TOKENRADIUS = 40; |
||
451 | boolean valid;
|
||
452 | |||
453 | // clear image
|
||
454 | canvas.setColor(Color.WHITE);
|
||
455 | canvas.fillRect(0, 0, image.getWidth(), image.getHeight()); |
||
456 | |||
457 | // parse the matrix, to see what robots exist
|
||
458 | 76 | gtress | String [] rows = txtMatrix.getText().split("\n"); |
459 | 32 | gtress | numBots = rows.length; |
460 | String [][] entries = new String[numBots][numBots]; |
||
461 | valid = true;
|
||
462 | 35 | gtress | for (int i = 0; i < numBots; i++) { |
463 | 32 | gtress | entries[i] = rows[i].split(" ");
|
464 | if (entries[i].length != rows.length) valid = false; |
||
465 | } |
||
466 | |||
467 | 35 | gtress | if (valid) {
|
468 | 38 | gtress | this.showStatus("Running"); |
469 | 32 | gtress | |
470 | // draw robots and find which one is seleced
|
||
471 | double angle = 2.0 * Math.PI / numBots; |
||
472 | canvas.setColor(Color.BLACK);
|
||
473 | botRect = new Rectangle[numBots]; |
||
474 | int x, y;
|
||
475 | if (selectedBot >= numBots) selectedBot = 0; |
||
476 | 35 | gtress | for (int i = 0; i < numBots; i++) { |
477 | 32 | gtress | x = cx - (int)(BIGRADIUS * Math.cos(i * angle)); |
478 | y = cy - (int)(BIGRADIUS * Math.sin(i * angle)); |
||
479 | drawRobot(i, x, y); |
||
480 | if (i == selectedBot) indicator.setCenter(x, y);
|
||
481 | } |
||
482 | |||
483 | // draw token marker
|
||
484 | int tokenx, tokeny;
|
||
485 | int tokenNum = tokenLoc;
|
||
486 | tokenx = cx - (int)(BIGRADIUS * Math.cos(tokenNum * angle)); |
||
487 | tokeny = cy - (int)(BIGRADIUS * Math.sin(tokenNum * angle)); |
||
488 | canvas.setColor(Color.RED);
|
||
489 | canvas.drawOval(tokenx-TOKENRADIUS, tokeny-TOKENRADIUS, 2*TOKENRADIUS, 2*TOKENRADIUS); |
||
490 | |||
491 | // create an inner circle along which the connections are made.
|
||
492 | // let the diameter of this circle be 2*RADIUS less than the outerDiameter.
|
||
493 | // see what connections exist
|
||
494 | 35 | gtress | for (int row = 0; row < numBots; row++) { |
495 | for(int col = 0; col < numBots; col++) { |
||
496 | if (!entries[row][col].equals("-") && entries[col][row].equals("-") && row != col) { |
||
497 | //TODO: Make a standard gray
|
||
498 | 32 | gtress | drawConnection(row, col, BIGRADIUS-RADIUS, new Color(200,200,200)); |
499 | 38 | gtress | } else if (!entries[row][col].equals("-") && ! entries[col][row].equals("-") && row != col) { |
500 | 32 | gtress | drawConnection(row, col, BIGRADIUS-RADIUS, Color.BLACK);
|
501 | } |
||
502 | } |
||
503 | } |
||
504 | |||
505 | // draw the selection indicator
|
||
506 | indicator.draw(); |
||
507 | |||
508 | 35 | gtress | } else {// if matrix is not valid |
509 | 32 | gtress | this.showStatus("Error: Invalid matrix"); |
510 | } |
||
511 | |||
512 | } |
||
513 | |||
514 | /* At this point, moveToken is only called by the simulator.
|
||
515 | * In the future, it can be rewritten to account for non-standard
|
||
516 | * token passing or deleted if the information can be retrieved
|
||
517 | * directly from the Colonet server instead.
|
||
518 | */
|
||
519 | 35 | gtress | public void moveToken () { |
520 | try {
|
||
521 | tokenLoc = (tokenLoc+1)%numBots;
|
||
522 | } catch (ArithmeticException e) { // in case numRobots is zero |
||
523 | } |
||
524 | 32 | gtress | |
525 | packetMonitor.addTokenPass(); |
||
526 | } |
||
527 | |||
528 | //
|
||
529 | // MouseEvent methods
|
||
530 | //
|
||
531 | public void mouseExited(MouseEvent e) {} |
||
532 | public void mouseEntered(MouseEvent e) {} |
||
533 | public void mouseReleased(MouseEvent e) {} |
||
534 | public void mouseClicked(MouseEvent e) {} |
||
535 | 35 | gtress | public void mousePressed(MouseEvent e) { |
536 | 32 | gtress | try {
|
537 | 35 | gtress | for (int i = 0; i < numBots; i++) { |
538 | 32 | gtress | if (botRect[i].contains(e.getPoint()))
|
539 | selectedBot = i; |
||
540 | } |
||
541 | } catch (Exception ex) { |
||
542 | System.out.println(e);
|
||
543 | } |
||
544 | |||
545 | } |
||
546 | |||
547 | /*
|
||
548 | * SelectionIndicator thread.
|
||
549 | * Graphical representation of the selection marker
|
||
550 | *
|
||
551 | * step() and draw() are synchronized methods. step() is private and
|
||
552 | * used to update the position of the crosshairs. draw() is called
|
||
553 | * externally and should only run if all calculations in step() have
|
||
554 | * been completed.
|
||
555 | */
|
||
556 | 35 | gtress | private class SelectionIndicator extends Thread { |
557 | 32 | gtress | |
558 | final int INDICATOR_DELAY = 100; |
||
559 | final double DTHETA = 0.3; //larger values make the marker rotate faster |
||
560 | Graphics2D g; //canvas to draw on |
||
561 | boolean running;
|
||
562 | |||
563 | int sx, sy; //center |
||
564 | int r, dr; //radius and width of marker |
||
565 | double theta; //current angle |
||
566 | |||
567 | volatile Polygon poly1, poly2, poly3, poly4; |
||
568 | |||
569 | int px1, py1;
|
||
570 | int rx1, ry1;
|
||
571 | int px2, py2;
|
||
572 | int rx2, ry2;
|
||
573 | int px3, py3;
|
||
574 | int rx3, ry3;
|
||
575 | int px4, py4;
|
||
576 | int rx4, ry4;
|
||
577 | |||
578 | int steps;
|
||
579 | |||
580 | 35 | gtress | public SelectionIndicator (Graphics2D g) { |
581 | 32 | gtress | super("SelectionIndicator"); |
582 | this.g = g;
|
||
583 | running = false;
|
||
584 | steps = 0;
|
||
585 | |||
586 | theta = 0;
|
||
587 | rx1 = 0; ry1 = 0; |
||
588 | px1 = 0; py1 = 0; |
||
589 | rx2 = 0; ry2 = 0; |
||
590 | px2 = 0; py2 = 0; |
||
591 | rx3 = 0; ry3 = 0; |
||
592 | px3 = 0; py3 = 0; |
||
593 | rx4 = 0; ry4 = 0; |
||
594 | px4 = 0; py4 = 0; |
||
595 | } |
||
596 | |||
597 | 35 | gtress | public synchronized void setCenter (int sx, int sy) { |
598 | 32 | gtress | if (sx == this.sx && sy == this.sy) return; |
599 | this.sx = sx;
|
||
600 | this.sy = sy;
|
||
601 | steps = 0;
|
||
602 | } |
||
603 | |||
604 | 35 | gtress | public synchronized void setRadius (int r, int dr) { |
605 | 32 | gtress | this.r = r;
|
606 | this.dr = dr;
|
||
607 | steps = 0;
|
||
608 | } |
||
609 | |||
610 | 35 | gtress | public void run () { |
611 | 32 | gtress | running = true;
|
612 | 35 | gtress | while (running) {
|
613 | 32 | gtress | step(); |
614 | 35 | gtress | try {
|
615 | Thread.sleep(INDICATOR_DELAY);
|
||
616 | } catch (InterruptedException e) { |
||
617 | running = false;
|
||
618 | return;
|
||
619 | } |
||
620 | 32 | gtress | } |
621 | } |
||
622 | |||
623 | 35 | gtress | private synchronized void step () { |
624 | 32 | gtress | Polygon poly1_new = new Polygon(); |
625 | Polygon poly2_new = new Polygon(); |
||
626 | Polygon poly3_new = new Polygon(); |
||
627 | Polygon poly4_new = new Polygon(); |
||
628 | |||
629 | //the step
|
||
630 | theta = (theta + DTHETA/Math.PI) % (Math.PI); |
||
631 | |||
632 | //the calculation
|
||
633 | //let p be the point of the pointy thing toward the center
|
||
634 | //let r be the point at the opposite side
|
||
635 | |||
636 | //recalculate radius, if it will look cool, lolz
|
||
637 | int newr = r;
|
||
638 | 38 | gtress | if (steps < 100) |
639 | newr = (int)( r + 200/(steps+1) ); |
||
640 | 32 | gtress | |
641 | //precompute values for dx and dy
|
||
642 | int dx_inner = (int)(newr * Math.cos(theta)); |
||
643 | int dy_inner = (int)(newr * Math.sin(theta)); |
||
644 | int dx_outer = (int)((newr+dr) * Math.cos(theta)); |
||
645 | int dy_outer = (int)((newr+dr) * Math.sin(theta)); |
||
646 | |||
647 | //calculate polygon constants
|
||
648 | int dy_poly = (int)(dr/2 * Math.cos(theta)); |
||
649 | int dx_poly = (int)(dr/2 * Math.sin(theta)); |
||
650 | |||
651 | //determine critical points
|
||
652 | 35 | gtress | //kansas city shuffle!
|
653 | 32 | gtress | px1 = sx + dx_inner; |
654 | py1 = sy - dy_inner; |
||
655 | rx1 = sx + dx_outer; |
||
656 | ry1 = sy - dy_outer; |
||
657 | px2 = sx - dx_inner; |
||
658 | py2 = sy + dy_inner; |
||
659 | rx2 = sx - dx_outer; |
||
660 | ry2 = sy + dy_outer; |
||
661 | px3 = sx - dy_inner; |
||
662 | py3 = sy - dx_inner; |
||
663 | rx3 = sx - dy_outer; |
||
664 | ry3 = sy - dx_outer; |
||
665 | px4 = sx + dy_inner; |
||
666 | py4 = sy + dx_inner; |
||
667 | rx4 = sx + dy_outer; |
||
668 | ry4 = sy + dx_outer; |
||
669 | |||
670 | //create polygons
|
||
671 | poly1_new.addPoint(px1, py1); |
||
672 | poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly); |
||
673 | poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly); |
||
674 | poly2_new.addPoint(px2, py2); |
||
675 | poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly); |
||
676 | poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly); |
||
677 | poly3_new.addPoint(px3, py3); |
||
678 | poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly); |
||
679 | poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly); |
||
680 | poly4_new.addPoint(px4, py4); |
||
681 | poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly); |
||
682 | poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly); |
||
683 | |||
684 | //reassign updated polygons
|
||
685 | poly1 = poly1_new; |
||
686 | poly2 = poly2_new; |
||
687 | poly3 = poly3_new; |
||
688 | poly4 = poly4_new; |
||
689 | |||
690 | if (steps < 300) steps++; |
||
691 | } |
||
692 | |||
693 | 35 | gtress | public synchronized void draw () { |
694 | 32 | gtress | if (!running) return; |
695 | g.setColor(Color.GRAY);
|
||
696 | //draw polygons
|
||
697 | g.fillPolygon(poly1); |
||
698 | g.fillPolygon(poly2); |
||
699 | g.fillPolygon(poly3); |
||
700 | g.fillPolygon(poly4); |
||
701 | } |
||
702 | |||
703 | } |
||
704 | |||
705 | /*
|
||
706 | * Simulator thread.
|
||
707 | *
|
||
708 | */
|
||
709 | 35 | gtress | private class Simulator extends Thread { |
710 | 32 | gtress | final int SIMULATOR_DELAY = 300; |
711 | boolean running;
|
||
712 | |||
713 | 35 | gtress | public Simulator () {
|
714 | 32 | gtress | super("Simulator"); |
715 | running = false;
|
||
716 | } |
||
717 | |||
718 | 35 | gtress | public void run () { |
719 | 32 | gtress | running = true;
|
720 | 35 | gtress | while (running) {
|
721 | 32 | gtress | step(); |
722 | 35 | gtress | try {
|
723 | Thread.sleep(SIMULATOR_DELAY);
|
||
724 | } catch (InterruptedException e) { |
||
725 | running = false;
|
||
726 | return;
|
||
727 | } |
||
728 | 32 | gtress | } |
729 | } |
||
730 | |||
731 | 35 | gtress | private void step () { |
732 | 32 | gtress | // simulate passing the token
|
733 | moveToken(); |
||
734 | } |
||
735 | |||
736 | } |
||
737 | |||
738 | /*
|
||
739 | * PacketMonitor thread.
|
||
740 | *
|
||
741 | * Currently, this counts the rate of token passes but will eventually
|
||
742 | * be modified to keep more important statistics.
|
||
743 | */
|
||
744 | 35 | gtress | private class PacketMonitor extends Thread { |
745 | 32 | gtress | final int PACKETMONITOR_DELAY = 1000; |
746 | |||
747 | boolean running;
|
||
748 | int tokenPasses;
|
||
749 | |||
750 | 35 | gtress | public PacketMonitor () {
|
751 | 32 | gtress | super("PacketMonitor"); |
752 | running = false;
|
||
753 | tokenPasses = 0;
|
||
754 | } |
||
755 | |||
756 | 35 | gtress | public void run () { |
757 | 32 | gtress | running = true;
|
758 | 35 | gtress | while (running) {
|
759 | 32 | gtress | displayTokenPasses(); |
760 | 35 | gtress | try {
|
761 | Thread.sleep(PACKETMONITOR_DELAY);
|
||
762 | } catch (InterruptedException e) { |
||
763 | running = false;
|
||
764 | return;
|
||
765 | } |
||
766 | 32 | gtress | } |
767 | } |
||
768 | |||
769 | 35 | gtress | public synchronized void addTokenPass () { |
770 | 32 | gtress | tokenPasses++; |
771 | } |
||
772 | |||
773 | 35 | gtress | public synchronized void displayTokenPasses () { |
774 | 32 | gtress | lblTokenPasses.setText("" + tokenPasses);
|
775 | tokenPasses = 0;
|
||
776 | } |
||
777 | |||
778 | } |
||
779 | |||
780 | /*
|
||
781 | 67 | gtress | * DataUpdater thread.
|
782 | 76 | gtress | * The purpose of this thread is to request data from the server at regular intervals.
|
783 | 32 | gtress | *
|
784 | */
|
||
785 | 67 | gtress | class DataUpdater extends Thread { |
786 | 76 | gtress | final int DATAUPDATER_DELAY = 1100; |
787 | 32 | gtress | |
788 | 67 | gtress | public DataUpdater () {
|
789 | super("Colonet DataUpdater"); |
||
790 | 32 | gtress | } |
791 | |||
792 | 35 | gtress | public void run () { |
793 | 32 | gtress | String line;
|
794 | 35 | gtress | while (true) { |
795 | 32 | gtress | try {
|
796 | 76 | gtress | //request more data
|
797 | if (csi.isReady())
|
||
798 | csi.sendRequest(ColonetServerInterface.REQUEST_BOM_MATRIX, "");
|
||
799 | 67 | gtress | Thread.sleep(DATAUPDATER_DELAY);
|
800 | 32 | gtress | } catch (InterruptedException e) { |
801 | return;
|
||
802 | 39 | gtress | } |
803 | 32 | gtress | } |
804 | } |
||
805 | |||
806 | } |
||
807 | |||
808 | } |