Project

General

Profile

Revision 501

Added by Greg Tress about 16 years ago

Multithreaded the event handlers so the GUI is not unresponsive

View differences:

trunk/code/projects/colonet/client/Colonet.java
105 105
	volatile java.util.List <RobotIcon> robotIcons;  //contains boundary shapes around bots for click detection
106 106
	volatile int[] xbeeID;
107 107
	
108
	Colonet self = this;
108 109
	Thread paintThread;
109
	SelectionIndicator indicator;
110 110
	WebcamLoader webcamLoader;
111 111
	ColonetServerInterface csi;
112 112

  
......
142 142
	
143 143
	public void destroy () {
144 144
		try { paintThread.interrupt(); } catch (Exception e) { }
145
		try { indicator.interrupt(); } catch (Exception e) { }
146 145
	}
147 146

  
148 147
	private synchronized void createAndShowGUI () {
......
171 170
		
172 171
		// Connection area
173 172
		txtMatrix = new JTextArea();
174
		txtMatrix.setBorder(BorderFactory.createTitledBorder("Input Matrix"));
173
		txtMatrix.setBorder(BorderFactory.createTitledBorder("Info"));
175 174
		txtInfo = new JTextArea();
176 175
		txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
177 176
		txtInfo.setEditable(false);
......
343 342
		btnGetXBeeIDs.addActionListener(this);
344 343
		btnAssignID.addActionListener(this);
345 344
		panelWebcam.addMouseListener(this);	
346
				
347
		// Set up animation threads
348
		indicator = new SelectionIndicator(canvas);
349
		indicator.setRadius(RADIUS+3, 15);  //a tad more than the bot radius
350 345
	
351 346
	}
352 347
	
......
446 441
	    btnConnect.setEnabled(true);
447 442
	    lblConnectionStatus.setText("Status: Disconnected");
448 443
	    try { paintThread.interrupt(); } catch (Exception e) { }
449
		try { indicator.interrupt(); } catch (Exception e) { }
450 444
		
451 445
	}
452 446
	
......
549 543
	//
550 544
	// MouseListener methods
551 545
	//
546
	public void mousePressed(MouseEvent e) {
547
	    //Start a new Thread to handle the MouseEvent
548
	    SwingUtilities.invokeLater(new MouseHandler(e));
549
	}
552 550
	public void mouseExited(MouseEvent e) {
553 551
	}
554 552
	public void mouseEntered(MouseEvent e) {
......
557 555
	}
558 556
	public void mouseClicked(MouseEvent e) {
559 557
	}
560
	public void mousePressed(MouseEvent e) {
561
	
562
	    // If we are selecting a waypoint (destination) for a specific bot
563
	    if (setWaypoint && setWaypointID  >= 0) {
564
	        setWaypoint = false;
565
	        panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
566
	        if (selectedBot < 0)
567
	            return;
568
	        
569
	        RobotIcon r = robotIcons.get(selectedBot);
570
	        r.destx = e.getX();
571
	        r.desty = e.getY();
572
	        
573
	        if (csi != null)
574
	            csi.sendAbsoluteMove(r.id, r.destx, r.desty);
575
	        
576
	        return;
577
	    }
578
	    
579
	    // Right-click also means we are moving a robot
580
	    if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
581
	        if (selectedBot < 0)
582
	            return;
583
	        
584
	        RobotIcon r = robotIcons.get(selectedBot);
585
	        r.destx = e.getX();
586
	        r.desty = e.getY();
587
	        
588
	        if (csi != null)
589
	            csi.sendAbsoluteMove(r.id, r.destx, r.desty);
590
	        
591
	        return;
592
	    }
593
	    
594
	    // If we are setting all waypoints
595
	    if (setWaypoint) {
596
	        setWaypoint = false;
597
	        panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
598
	        for (int i = 0; i < robotIcons.size(); i++) {
599
	            RobotIcon r = robotIcons.get(i);
600
	            r.destx = e.getX();
601
	            r.desty = e.getY();
602
	        }
603
	        return;
604
	    }
605
	    
606
	    // Otherwise, we are selecting a bot, or doing nothing
607
		for (int i = 0; i < robotIcons.size(); i++) {
608
		    RobotIcon r = robotIcons.get(i);
609
		    if (r.contains(e.getX(), e.getY())) {
610
		        selectedBot = i;
611
		        lblSelected.setText(" " + r.id);
612
		    }
613
		}
614
		repaint();
615
	}
616 558
	public void mouseDragged(MouseEvent e) {
617 559
	}
618 560
	public void mouseMoved(MouseEvent e) {
......
650 592
	// ActionListener method
651 593
	//
652 594
	public void actionPerformed (ActionEvent e) {
653
		Object source = e.getSource();
654
		
655
		// General Actions
656
		if (source == btnConnect) {
657
			connect();
658
		} else if (source == btnGetXBeeIDs) {
659
			csi.sendXBeeIDRequest();
660
		} else if (source == btnAssignID) {
661
		    String message;
662
		    if (selectedBot < 0)
663
		        return;
664
		    int curID = robotIcons.get(selectedBot).id;
665
		    if (curID < 0)
666
		        message = "That robot is unidentified. Please specify its ID.";
667
		    else
668
		        message = "That robot has ID " + curID + ". You may reassign it now.";
669
		    String result = JOptionPane.showInputDialog(this, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
670
		    if (result == null)
671
		        return;
672
	        int newID = -1;
673
		    try {
674
		        newID = Integer.parseInt(result);
675
		    } catch (Exception ex) {
676
		        csi.warn("Invalid ID.");
677
		        return;
595
		// Start a new Thread to handle the ActionEvent
596
		SwingUtilities.invokeLater(new ActionHandler(e));
597
	}
598
	
599
	class MouseHandler extends Thread {
600
	    
601
	    MouseEvent e;
602
	    
603
	    public MouseHandler (MouseEvent event) {
604
	        super("MouseHandler");
605
	        this.e = event;
606
	    }
607
	
608
	    public void run () {
609
	        
610
	        // If we are selecting a waypoint (destination) for a specific bot
611
	        if (setWaypoint && setWaypointID  >= 0) {
612
	            setWaypoint = false;
613
	            panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
614
	            if (selectedBot < 0)
615
	                return;
616
	            
617
	            RobotIcon r = robotIcons.get(selectedBot);
618
	            r.destx = e.getX();
619
	            r.desty = e.getY();
620
	            
621
	            if (csi != null)
622
	                csi.sendAbsoluteMove(r.id, r.destx, r.desty);
623
	            
624
	            return;
625
	        }
626
	        
627
	        // Right-click also means we are moving a robot
628
	        if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
629
	            if (selectedBot < 0)
630
	                return;
631
	            
632
	            RobotIcon r = robotIcons.get(selectedBot);
633
	            r.destx = e.getX();
634
	            r.desty = e.getY();
635
	            
636
	            if (csi != null)
637
	                csi.sendAbsoluteMove(r.id, r.destx, r.desty);
638
	            
639
	            return;
640
	        }
641
	        
642
	        // If we are setting all waypoints
643
	        if (setWaypoint) {
644
	            setWaypoint = false;
645
	            panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
646
	            for (int i = 0; i < robotIcons.size(); i++) {
647
	                RobotIcon r = robotIcons.get(i);
648
	                r.destx = e.getX();
649
	                r.desty = e.getY();
650
	            }
651
	            return;
652
	        }
653
	        
654
	        // Otherwise, we are selecting a bot, or doing nothing
655
		    for (int i = 0; i < robotIcons.size(); i++) {
656
		        RobotIcon r = robotIcons.get(i);
657
		        if (r.contains(e.getX(), e.getY())) {
658
		            selectedBot = i;
659
		            lblSelected.setText(" " + r.id);
660
		        }
678 661
		    }
679
		    // Assign new ID and update display  
680
			csi.sendIDAssignment(curID, newID);
681
		    robotIcons.get(selectedBot).id = newID;
682
		    robotIcons.get(selectedBot).color = Color.GREEN;
683
		    lblSelected.setText(" " + newID);
684 662
		    
685
			
686
		}
687
		
688
		// Robot Movement Controls
689
		else if (source == btnF) {
690
			vectorController.setMaxForward();
691
			vectorController.sendToServer();
692
		} else if (source == btnB) {
693
			vectorController.setMaxReverse();
694
			vectorController.sendToServer();
695
		} else if (source == btnL) {
696
			vectorController.setMaxLeft();
697
			vectorController.sendToServer();
698
		} else if (source == btnR) {
699
			vectorController.setMaxRight();
700
			vectorController.sendToServer();
701
		} else if (source == btnActivate) {
702
			vectorController.setZero();
703
			vectorController.sendToServer();
704
		}
705
		// Robot Commands (non-movement)
706
		else if (source == btnCommand_MoveTo) {
707
		    if (selectedBot < 0)
708
		        return;
709
		    panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
710
		    setWaypoint = true;
711
		    setWaypointID = selectedBot;
712
		    		
713
		} else if (source == btnCommand_MoveAll) {
714
		    panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
715
		    setWaypoint = true;
716
		    setWaypointID = -1;
717
		    		
718
		} else if (source == btnCommand_StopTask) {
719
		
720
		} else if (source == btnCommand_ResumeTask) {
721
		
722
		} else if (source == btnCommand_ChargeNow) {
723
		
724
		} else if (source == btnCommand_StopCharging) {
725
		
726
		}
727
			
728
		// Queue Management
729
		else if (source == btnAddTask) {
730
			taskAddWindow.prompt();
731
		} else if (source == btnRemoveTask) {
732
			if (taskList.getSelectedIndex() >= 0);
733
				csi.sendQueueRemove(taskList.getSelectedIndex());
734
			csi.sendQueueUpdate();
735
		} else if (source == btnMoveTaskUp) {
736
			csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
737
			csi.sendQueueUpdate();
738
		} else if (source == btnMoveTaskDown) {
739
			csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
740
			csi.sendQueueUpdate();
741
		} else if (source == btnUpdateTasks) {
742
			csi.sendQueueUpdate();
743
		}
663
		    repaint();
664
	    }
744 665
	}
745 666
	
746
	/*
747
	*	SelectionIndicator thread.
748
	*	Graphical representation of the selection marker
749
	*
750
	*	step() and draw() are synchronized methods. step() is private and 
751
	*	used to update the position of the crosshairs. draw() is called 
752
	*	externally and should only run if all calculations in step() have
753
	*	been completed.
754
	*/
755
	private class SelectionIndicator extends Thread {
667
	class ActionHandler extends Thread {
756 668
	
757
		final int INDICATOR_DELAY = 180;
758
		final double DTHETA = 0.4;    //larger values make the marker rotate faster
759
		Graphics2D g;   //canvas to draw on
760
		boolean running;
669
	    ActionEvent e;
670
	
671
	    public ActionHandler (ActionEvent event) {
672
	        super("ActionHandler");
673
	        this.e = event;
674
	    }
675
	    
676
	    public void run () {
677
	        Object source = e.getSource();
761 678
		
762
		int sx, sy;		//center
763
		int r, dr;		//radius and width of marker
764
		double theta;   //current angle
765
		
766
		volatile Polygon poly1, poly2, poly3, poly4;
767
		
768
		int px1, py1;
769
		int rx1, ry1;
770
		int px2, py2;
771
		int rx2, ry2;
772
		int px3, py3;
773
		int rx3, ry3;
774
		int px4, py4;
775
		int rx4, ry4;
776
		
777
		int steps;
778
	
779
		public SelectionIndicator (Graphics2D g) {
780
			super("SelectionIndicator");
781
			this.g = g;
782
			running = false;
783
			steps = 0;
679
		    // General Actions
680
		    if (source == btnConnect) {
681
			    connect();
682
		    } else if (source == btnGetXBeeIDs) {
683
			    csi.sendXBeeIDRequest();
684
		    } else if (source == btnAssignID) {
685
		        String message;
686
		        if (selectedBot < 0)
687
		            return;
688
		        int curID = robotIcons.get(selectedBot).id;
689
		        if (curID < 0)
690
		            message = "That robot is unidentified. Please specify its ID.";
691
		        else
692
		            message = "That robot has ID " + curID + ". You may reassign it now.";
693
		        String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
694
		        if (result == null)
695
		            return;
696
	            int newID = -1;
697
		        try {
698
		            newID = Integer.parseInt(result);
699
		        } catch (Exception ex) {
700
		            csi.warn("Invalid ID.");
701
		            return;
702
		        }
703
		        // Assign new ID and update display  
704
			    csi.sendIDAssignment(curID, newID);
705
		        robotIcons.get(selectedBot).id = newID;
706
		        robotIcons.get(selectedBot).color = Color.GREEN;
707
		        lblSelected.setText(" " + newID);
708
		        
784 709
			
785
			theta = 0;
786
			rx1 = 0; ry1 = 0;
787
			px1 = 0; py1 = 0;
788
			rx2 = 0; ry2 = 0;
789
			px2 = 0; py2 = 0;
790
			rx3 = 0; ry3 = 0;
791
			px3 = 0; py3 = 0;
792
			rx4 = 0; ry4 = 0;
793
			px4 = 0; py4 = 0;
794
		}
710
		    }
795 711
		
796
		public synchronized void setCenter (int sx, int sy) {
797
			if (sx == this.sx && sy == this.sy) return;
798
			this.sx = sx;
799
			this.sy = sy;
800
			steps = 0;
801
		}
712
		    // Robot Movement Controls
713
		    else if (source == btnF) {
714
			    vectorController.setMaxForward();
715
			    vectorController.sendToServer();
716
		    } else if (source == btnB) {
717
			    vectorController.setMaxReverse();
718
			    vectorController.sendToServer();
719
		    } else if (source == btnL) {
720
			    vectorController.setMaxLeft();
721
			    vectorController.sendToServer();
722
		    } else if (source == btnR) {
723
			    vectorController.setMaxRight();
724
			    vectorController.sendToServer();
725
		    } else if (source == btnActivate) {
726
			    vectorController.setZero();
727
			    vectorController.sendToServer();
728
		    }
729
		    // Robot Commands (non-movement)
730
		    else if (source == btnCommand_MoveTo) {
731
		        if (selectedBot < 0)
732
		            return;
733
		        panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
734
		        setWaypoint = true;
735
		        setWaypointID = selectedBot;
736
		        		
737
		    } else if (source == btnCommand_MoveAll) {
738
		        panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
739
		        setWaypoint = true;
740
		        setWaypointID = -1;
741
		        		
742
		    } else if (source == btnCommand_StopTask) {
802 743
		
803
		public synchronized void setRadius (int r, int dr) {
804
			this.r = r;
805
			this.dr = dr;
806
			steps = 0;
807
		}
744
		    } else if (source == btnCommand_ResumeTask) {
808 745
		
809
		public void run () {
810
			running = true;
811
			while (running) {
812
				step();
813
				try { 
814
					Thread.sleep(INDICATOR_DELAY);
815
				} catch (InterruptedException e) {
816
					running = false;
817
					return;
818
				}
819
			}
820
		}
746
		    } else if (source == btnCommand_ChargeNow) {
821 747
		
822
		private synchronized void step () {
823
			Polygon poly1_new = new Polygon();
824
			Polygon poly2_new = new Polygon();
825
			Polygon poly3_new = new Polygon();
826
			Polygon poly4_new = new Polygon();
748
		    } else if (source == btnCommand_StopCharging) {
827 749
		
828
			//the step
829
			theta = (theta + DTHETA/Math.PI) % (Math.PI);
750
		    }
830 751
			
831
			//the calculation
832
			//let p be the point of the pointy thing toward the center
833
			//let r be the point at the opposite side
834
			
835
			//recalculate radius, if it will look cool, lolz
836
			int newr = r;
837
			if (steps < 100)
838
			newr = (int)( r + 200/(steps+1) );
839
			
840
			//precompute values for dx and dy
841
			int dx_inner = (int)(newr * Math.cos(theta));
842
			int dy_inner = (int)(newr * Math.sin(theta));
843
			int dx_outer = (int)((newr+dr) * Math.cos(theta));
844
			int dy_outer = (int)((newr+dr) * Math.sin(theta));
845
			
846
			//calculate polygon constants
847
			int dy_poly = (int)(dr/2 * Math.cos(theta));
848
			int dx_poly = (int)(dr/2 * Math.sin(theta));
849
			
850
			//determine critical points
851
			//kansas city shuffle!
852
			px1 = sx + dx_inner;
853
			py1 = sy - dy_inner;
854
			rx1 = sx + dx_outer;
855
			ry1 = sy - dy_outer;
856
			px2 = sx - dx_inner;
857
			py2 = sy + dy_inner;
858
			rx2 = sx - dx_outer;
859
			ry2 = sy + dy_outer;
860
			px3 = sx - dy_inner;
861
			py3 = sy - dx_inner;
862
			rx3 = sx - dy_outer;
863
			ry3 = sy - dx_outer;
864
			px4 = sx + dy_inner;
865
			py4 = sy + dx_inner;
866
			rx4 = sx + dy_outer;
867
			ry4 = sy + dx_outer;
868
			
869
			//create polygons
870
			poly1_new.addPoint(px1, py1);
871
			poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly);
872
			poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly);
873
			poly2_new.addPoint(px2, py2);
874
			poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly);
875
			poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly);
876
			poly3_new.addPoint(px3, py3);
877
			poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly);
878
			poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly);
879
			poly4_new.addPoint(px4, py4);
880
			poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly);
881
			poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly);
882
			
883
			//reassign updated polygons
884
			poly1 = poly1_new;
885
			poly2 = poly2_new;
886
			poly3 = poly3_new;
887
			poly4 = poly4_new;
888
		
889
			if (steps < 300) steps++;
890
		}
891
		
892
		public synchronized void draw () {
893
			if (!running) return;
894
			g.setColor(Color.GRAY);
895
			//draw polygons
896
			g.fillPolygon(poly1);
897
			g.fillPolygon(poly2);
898
			g.fillPolygon(poly3);
899
			g.fillPolygon(poly4);
900
		}
752
		    // Queue Management
753
		    else if (source == btnAddTask) {
754
			    taskAddWindow.prompt();
755
		    } else if (source == btnRemoveTask) {
756
			    if (taskList.getSelectedIndex() >= 0);
757
				    csi.sendQueueRemove(taskList.getSelectedIndex());
758
			    csi.sendQueueUpdate();
759
		    } else if (source == btnMoveTaskUp) {
760
			    csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
761
			    csi.sendQueueUpdate();
762
		    } else if (source == btnMoveTaskDown) {
763
			    csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
764
			    csi.sendQueueUpdate();
765
		    } else if (source == btnUpdateTasks) {
766
			    csi.sendQueueUpdate();
767
		    }
768
	    
769
	    }
901 770
	
902 771
	}
903 772
	
......
907 776
	*
908 777
	*/
909 778
	class DataUpdater extends Thread {
910
		final int DATAUPDATER_DELAY = 2200;
779
		final int DATAUPDATER_DELAY = 1200;
911 780
		
912 781
		public DataUpdater () {
913 782
			super("Colonet DataUpdater");
......
1051 920
	*/
1052 921
	class WebcamLoader extends Thread 
1053 922
	{
1054
		final int WEBCAMLOADER_DELAY = 300;
923
		final int WEBCAMLOADER_DELAY = 500;
1055 924
		final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg";
1056 925
				
1057 926
		URL imagePath;

Also available in: Unified diff