From 7884eec118c540acdb0a4c772b7daabc0308e288 Mon Sep 17 00:00:00 2001 From: Brendan Date: Thu, 26 Feb 2015 14:09:39 -0700 Subject: [PATCH] adds draft version 2 Groundstation --- GroundStation/v2/ConnectionPanel.java | 245 ++++++++++++++++++++++++++ GroundStation/v2/Display3d.java | 96 ++++++++++ GroundStation/v2/EStopWindow.java | 89 ++++++++++ GroundStation/v2/GroundStation.java | 93 ++++++++++ GroundStation/v2/MessageContent.java | 70 ++++++++ GroundStation/v2/MotorTestPanel.java | 189 ++++++++++++++++++++ GroundStation/v2/VerticalSlider.java | 194 ++++++++++++++++++++ GroundStation/v2/stopSign.png | Bin 0 -> 34840 bytes 8 files changed, 976 insertions(+) create mode 100644 GroundStation/v2/ConnectionPanel.java create mode 100644 GroundStation/v2/Display3d.java create mode 100644 GroundStation/v2/EStopWindow.java create mode 100644 GroundStation/v2/GroundStation.java create mode 100644 GroundStation/v2/MessageContent.java create mode 100644 GroundStation/v2/MotorTestPanel.java create mode 100644 GroundStation/v2/VerticalSlider.java create mode 100644 GroundStation/v2/stopSign.png diff --git a/GroundStation/v2/ConnectionPanel.java b/GroundStation/v2/ConnectionPanel.java new file mode 100644 index 0000000..0b04c8f --- /dev/null +++ b/GroundStation/v2/ConnectionPanel.java @@ -0,0 +1,245 @@ +import java.util.ArrayList; +import java.util.Scanner; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; + +// communication +import java.lang.*; +import java.io.*; +import java.net.*; + +/** + * + */ +class ConnectionPanel extends JPanel implements ActionListener { + /** list to hold subscribed actionlisteners */ + private ArrayList listeners = new ArrayList(); + + /** TCP connection options */ + private JPanel tcpOptions; + /** label for address field */ + private JLabel hostAddrLabel; + /** host address field */ + private JTextField hostAddr; + /** label for port field */ + private JLabel hostPortLabel; + /** port field */ + private JTextField hostPort; + /** connect button */ + private JButton connectButton; + + /** holds outgoing message info */ + private MessageContent contentOut; + /** holds incomming message info */ + private MessageContent contentIn; + + /** timer to cause repeating sends */ + private Timer tcpTimer; + /** tcp connection socket */ + private Socket tcpSocket; + /** scanner for incomming tcp messages */ + public BufferedReader in; + /** printwriter for outgoing tcp messages */ + public PrintWriter out; + + /** + * no-args constructor + */ + public ConnectionPanel() { + setLayout( new FlowLayout() ); + + tcpOptions = new JPanel(); + tcpOptions.setLayout( new FlowLayout() ); + + hostAddrLabel = new JLabel( "Host Address: " ); + hostAddr = new JTextField( "192.168.42.1" ); + hostAddr.setColumns( 10 ); + hostPortLabel = new JLabel( "Port: " ); + hostPort = new JTextField( "51717" ); + hostPort.setColumns( 6 ); + + tcpOptions.add( hostAddrLabel ); + tcpOptions.add( hostAddr ); + tcpOptions.add( hostPortLabel ); + tcpOptions.add( hostPort ); + add( tcpOptions ); + + connectButton = new JButton( "Connect" ); + connectButton.addActionListener( this ); + add( connectButton ); + } + + /** + * @param outgoing - MessageContent object containing options for + */ + public ConnectionPanel( MessageContent outgoing, MessageContent incomming ) { + this(); + contentOut = outgoing; + contentIn = incomming; + } + + /** + * connect to tcp socket bound to port at host addres addr + * @param addr - host address + * @param port - port of socket at host + */ + public void connectTcp( String addr, int port ) { + try { + tcpSocket = new Socket( addr, port ); + in = new BufferedReader( new InputStreamReader( tcpSocket.getInputStream() ) ); + out = new PrintWriter( tcpSocket.getOutputStream(), true ); //autoflushing + tcpTimer = new Timer( 100, this ); // .1 seconds + out.println( "PROTOCOL_2" ); // identify protocol to be used with tcp + tcpTimer.start(); + setGui( true ); + fireActionPerformed( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, "CONNECT" ) ); + } + catch( Exception ex ) { + System.out.println( "Error Connecting to TCP server: " + ex ); + } + } + + /** + * disconnects from tcp server + * @param reconnect - tells server whether to listen for new connections. If false, server will shut down + */ + public void disconnectTcp( boolean reconnect ) { + out.print( "DISCONNECT" ); + if( reconnect ) out.print( "_R" ); + out.println(); + + try { + in.close(); + out.close(); + tcpSocket.close(); + setGui( false ); + fireActionPerformed( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, "DISCONNECT" ) ); + } + catch( IOException ex ) { + System.out.println( ex ); + } + } + + /** + * + */ + private void sendTCP() { + out.print( "H " ); // Heartbeat / begin message indicator + if( contentOut.getMotorsEnabled() ) out.print( "E " ); // enable motors + if( contentOut.motorTesting ) { + for( int i = 0; i < contentOut.motorValues.length; i++ ) + out.print( "M" + i + "_" + contentOut.motorValues[i] + " " ); + } + if( contentOut.controls ) { + for( int i = 0; i < contentOut.controlValues.length; i++ ) + out.print( "C" + i + "_" + contentOut.controlValues[i] + " " ); + } + out.println(); + } + + /** + * + */ + private void readTCP() { + try { + if( in.ready() ) { + String messageIn = in.readLine(); + Scanner temp = new Scanner( messageIn ); + while( temp.hasNext() ) { + String next = temp.next(); + if( next.indexOf( "H" ) == 0 ) System.out.print( "\nH" ); + System.out.print( next ); + if( next.indexOf( "M" ) > 0 ) { + Scanner motorScan = new Scanner( next ); + + } + } + } + contentIn.orientation = true; + contentIn.fireActionPerformed( new ActionEvent(contentIn, ActionEvent.ACTION_PERFORMED, "" ) ); + } + catch( IOException ex ) {} + } + + /** + * @param enabled - true to + */ + private void setGui( Boolean connected ) { + if( connected ) connectButton.setText( "disconnect" ); + else connectButton.setText( "connect" ); + hostAddr.setEnabled( !connected ); + hostPort.setEnabled( !connected ); + } + + /** + * sets everything in the panel enabled/disabled + * @param state - desired enable status (true = enabled, false = disabled) + */ + public void setEnabled( boolean state ) { + hostAddr.setEnabled( state ); + hostPort.setEnabled( state ); + connectButton.setEnabled( state ); + } + + /////////////////////////////////////// + + /** + * sends message over tcp + */ + public void actionPerformed( ActionEvent evt ) { + if( evt.getSource() == connectButton ) { + if( evt.getActionCommand().equals( "disconnect" ) ) { + disconnectTcp( false ); + } + else { + connectTcp( hostAddr.getText(), Integer.parseInt( hostPort.getText() ) ); + } + } + else { + sendTCP(); + readTCP(); + } + } + + //////////////////////////////////////// + + /** + * adds actionlistener to subscribd listener list + * @param al - actionlistener to add + */ + public void addActionListener( ActionListener al ) { + listeners.add( al ); + } + + /** + * removes actionlistener from subscribed listener list + * @param al - actionlistener to remove + */ + public void removeActionListener( ActionListener al ) { + listeners.remove( al ); + } + + /** + * sends an actionevent to subscribed listeners + * @param ae - actionevent to use as parameter to all subscribed listeners + */ + public void fireActionPerformed( ActionEvent ae ) { + for( ActionListener al : listeners ) { + al.actionPerformed( ae ); + } + } + + /** + * + */ + public void fireActionPerformed() { + fireActionPerformed( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, "" ) ); + } +} + + + + diff --git a/GroundStation/v2/Display3d.java b/GroundStation/v2/Display3d.java new file mode 100644 index 0000000..e9f6dc9 --- /dev/null +++ b/GroundStation/v2/Display3d.java @@ -0,0 +1,96 @@ +import javax.swing.*; +import javax.swing.event.*; +import java.awt.*; +import java.awt.event.*; + +/** + * + */ +class Display3d extends JPanel implements ActionListener, ChangeListener { + + private JSlider slider; + private MessageContent source = new MessageContent(); + private Font normalFont = new Font( "Normal", Font.PLAIN, 15 ); + + /** + * no-args constructor + */ + public Display3d() { + setBackground( Color.DARK_GRAY ); + setPreferredSize( new Dimension( 500, 100 ) ); + } + + /** + * + */ + public Display3d( MessageContent orientationSource ) { + source = orientationSource; + source.addActionListener( this ); + + setBackground( Color.DARK_GRAY ); + setPreferredSize( new Dimension( 0, 100 ) ); + + setLayout( new BorderLayout() ); + slider = new JSlider(); + slider.setOrientation( JSlider.VERTICAL ); + slider.addChangeListener( this ); + add( slider, BorderLayout.EAST ); + } + + /** + * overrides super.paintComponent + */ + public void paintComponent( Graphics g ) { + super.paintComponent( g ); + + int centerX = getWidth() / 2; + int centerY = getHeight() / 2; + + slider.setEnabled( source.orientation ); + + if( source.orientation ) { + + // yaw axis + g.setColor( Color.BLUE ); + //g.drawLine( centerX, centerY, , ); + + g.setColor( Color.RED ); + //g.drawLine( centerX, centerY, X, Y ); + + g.setColor( Color.GREEN ); + //g.drawLine( centerX, centerY, X, Y ); + + g.setColor( Color.BLACK ); + g.setFont( normalFont ); + g.drawString( "w = " + source.orientValues[ 0 ], 10, 35 ); + g.drawString( "x = " + source.orientValues[ 1 ], 10, 60 ); + g.drawString( "y = " + source.orientValues[ 2 ], 10, 85 ); + g.drawString( "z = " + source.orientValues[ 3 ], 10, 110 ); + } + else { + g.setColor( Color.RED ); + g.setFont( new Font( "Alert", Font.BOLD, 30 ) ); + g.drawString( "NO ORIENTATION DATA", centerX - 180, centerY + 15 ); + } + } + + /** + * + */ + public void actionPerformed( ActionEvent evt ) { + repaint(); + } + + /** + * + */ + public void stateChanged( ChangeEvent evt ) { + repaint(); + } +} + + + + + + diff --git a/GroundStation/v2/EStopWindow.java b/GroundStation/v2/EStopWindow.java new file mode 100644 index 0000000..3dbf817 --- /dev/null +++ b/GroundStation/v2/EStopWindow.java @@ -0,0 +1,89 @@ +import java.util.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; + +/** + * + */ +class EStopWindow extends JFrame implements ActionListener { + /** list to hold subscribed actionlisteners */ + private ArrayList listeners = new ArrayList(); + + /** stop sign image */ + //private Image sign = new Image( "./stopSign.png" ); + + /** Emergency stop button */ + private JButton disableButton; + + /** + * + */ + public EStopWindow() { + super("Emergency Stop"); + setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE ); + setAlwaysOnTop( true ); + setLocation( 600, 100 ); + + //disableButton = new JButton( "DISABLE" ); + disableButton = new JButton( new ImageIcon( (new ImageIcon( "./stopSign.png" )).getImage().getScaledInstance( 200, 200, Image.SCALE_DEFAULT) ) ); + disableButton.addActionListener( this ); + add( disableButton ); + + setEnabled( true ); + pack(); + setVisible( true ); + } + + /** + * + */ + public void actionPerformed( ActionEvent evt ) { + if( evt.getSource() == disableButton ) { + fireActionPerformed(); + } + } + + ///////////////////////////////////// + + /** + * + */ + public void setEnabled( boolean enable ) { + disableButton.setEnabled( enable ); + } + + /** + * adds actionlistener to subscribd listener list + * @param al - actionlistener to add + */ + public void addActionListener( ActionListener al ) { + listeners.add( al ); + } + + /** + * removes actionlistener from subscribed listener list + * @param al - actionlistener to remove + */ + public void removeActionListener( ActionListener al ) { + listeners.remove( al ); + } + + /** + * sends an actionevent to subscribed listeners + * @param ae - actionevent to use as parameter to all subscribed listeners + */ + public void fireActionPerformed( ActionEvent ae ) { + for( ActionListener al : listeners ) { + al.actionPerformed( ae ); + } + } + + /** + * + */ + public void fireActionPerformed() { + fireActionPerformed( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, "CLICKED" ) ); + } +} \ No newline at end of file diff --git a/GroundStation/v2/GroundStation.java b/GroundStation/v2/GroundStation.java new file mode 100644 index 0000000..7d60718 --- /dev/null +++ b/GroundStation/v2/GroundStation.java @@ -0,0 +1,93 @@ +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; + +/** + * + */ +class GroundStation implements ActionListener { + + /** main window to hold everything */ + private JFrame mainWindow; + /** window to hold emergency stop button */ + private EStopWindow eStopWindow; + + /** connection options section of window */ + private ConnectionPanel connectPanel; + + /** tabbed pane to hold stuff */ + private JTabbedPane tabbedPane; + /** motor testing panel */ + private MotorTestPanel motorTestPanel; + /** */ + private Display3d orientationPanel; + /** outgoing message content options */ + public MessageContent sendContent = new MessageContent(); + /** incoming message content */ + public MessageContent receiveContent = new MessageContent(); + + public GroundStation() { + mainWindow = new JFrame( "PiCopter Ground Station" ); + mainWindow.setResizable( true ); + mainWindow.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); + mainWindow.getContentPane().setLayout( new BoxLayout( mainWindow.getContentPane(), BoxLayout.PAGE_AXIS ) ); + + connectPanel = new ConnectionPanel( sendContent, receiveContent ); + connectPanel.setEnabled( true ); + mainWindow.getContentPane().add( connectPanel ); + + tabbedPane = new JTabbedPane(); + mainWindow.getContentPane().add( tabbedPane ); + + orientationPanel = new Display3d( receiveContent ); + tabbedPane.add( orientationPanel, "Orientation" ); + + motorTestPanel = new MotorTestPanel( sendContent ); + motorTestPanel.setEnabled( false ); + tabbedPane.add( motorTestPanel, "Motor Test" ); + + connectPanel.addActionListener( this ); + sendContent.addActionListener( this ); + + mainWindow.pack(); + mainWindow.setVisible( true ); + } + + public void disableMotors() { + connectPanel.setEnabled( true ); + eStopWindow.dispose(); + } + + public void enableMotors() { + connectPanel.setEnabled( false ); + eStopWindow = new EStopWindow(); + eStopWindow.addActionListener( this ); + } + + public void actionPerformed( ActionEvent evt ) { + if( evt.getSource() == eStopWindow ) { + sendContent.setMotorsEnabled( false ); + } + if( evt.getSource() == connectPanel ) { + if( evt.getActionCommand().equals( "CONNECT" ) ) { + mainWindow.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE ); + motorTestPanel.setEnabled( true ); + } + else if( evt.getActionCommand().equals( "DISCONNECT" ) ) { + mainWindow.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); + motorTestPanel.setEnabled( false ); + } + } + else if( evt.getActionCommand().equals( "ENABLE_MOTORS" ) ) { + enableMotors(); + } + else if( evt.getActionCommand().equals( "DISABLE_MOTORS" ) ) { + disableMotors(); + } + } + + public static void main( String[] args ) { + GroundStation station = new GroundStation(); + } +} \ No newline at end of file diff --git a/GroundStation/v2/MessageContent.java b/GroundStation/v2/MessageContent.java new file mode 100644 index 0000000..62ca4cd --- /dev/null +++ b/GroundStation/v2/MessageContent.java @@ -0,0 +1,70 @@ +import java.util.*; +import java.awt.event.*; + +/** + * + */ +class MessageContent { + /** list to hold subscribed actionlisteners */ + private ArrayList listeners = new ArrayList(); + + private boolean motorsEnabled = false; + + public boolean motorTesting = false; + public int[] motorValues = { 0, 0, 0, 0 }; + + public boolean controls = false; + public int[] controlValues = { 0, 0, 0, 0, 0, 0 }; + + public boolean orientation = false; + public double[] orientValues = { 1, 0, 0, 0 }; // w, x, y, z + + /** + * sets motorsEnabled variable and triggers fireActionPerformed + * @param enable - boolean value to set motorsEnabled to + */ + public void setMotorsEnabled( Boolean enable ) { + motorsEnabled = enable; + if( enable ) { + fireActionPerformed( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, "ENABLE_MOTORS" ) ); + } + else { + fireActionPerformed( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, "DISABLE_MOTORS" ) ); + } + } + + /** + * @return motorsEnabled status + */ + public Boolean getMotorsEnabled() { + return motorsEnabled; + } + + ///////////////////////////////////////////// + + /** + * adds actionlistener to subscribd listener list + * @param al - actionlistener to add + */ + public void addActionListener( ActionListener al ) { + listeners.add( al ); + } + + /** + * removes actionlistener from subscribed listener list + * @param al - actionlistener to remove + */ + public void removeActionListener( ActionListener al ) { + listeners.remove( al ); + } + + /** + * sends an actionevent to subscribed listeners + * @param ae - actionevent to use as parameter to all subscribed listeners + */ + public void fireActionPerformed( ActionEvent ae ) { + for( ActionListener al : listeners ) { + al.actionPerformed( ae ); + } + } +} \ No newline at end of file diff --git a/GroundStation/v2/MotorTestPanel.java b/GroundStation/v2/MotorTestPanel.java new file mode 100644 index 0000000..2b1af87 --- /dev/null +++ b/GroundStation/v2/MotorTestPanel.java @@ -0,0 +1,189 @@ +import java.util.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; + +/** + * + */ +class MotorTestPanel extends JPanel implements ActionListener { + + /** checkBox to enable/disable motor testing */ + private JCheckBox checkBox; + /** panel to hold sliders */ + private JPanel sliderPanel; + + /** slider for motor 1 */ + public VerticalSlider slider0; + /** slider for motor 2 */ + public VerticalSlider slider1; + /** slider for motor 3 */ + public VerticalSlider slider2; + /** slider for motor 4 */ + public VerticalSlider slider3; + /** slider for all motors */ + private VerticalSlider masterSlider; + + /** */ + public MessageContent controlOutput = new MessageContent(); + + /** list to hold subscribed actionlisteners */ + private ArrayList listeners; + + /** + * + */ + public MotorTestPanel() { + listeners = new ArrayList(); + + this.setLayout( new BorderLayout() ); + checkBox = new JCheckBox( "Enable Motor Testing" ); + checkBox.addActionListener( this ); + this.add( checkBox, BorderLayout.NORTH ); + + sliderPanel = new JPanel( new FlowLayout() ); + slider0 = new VerticalSlider( "Motor 0" ); + slider1 = new VerticalSlider( "Motor 1" ); + slider2 = new VerticalSlider( "Motor 2" ); + slider3 = new VerticalSlider( "Motor 3" ); + masterSlider = new VerticalSlider( "Master" ); + + slider0.addActionListener( this ); + slider1.addActionListener( this ); + slider2.addActionListener( this ); + slider3.addActionListener( this ); + masterSlider.addActionListener( this ); + + sliderPanel.add( slider0 ); + sliderPanel.add( slider1 ); + sliderPanel.add( slider2 ); + sliderPanel.add( slider3 ); + sliderPanel.add( masterSlider ); + + this.add( sliderPanel, BorderLayout.CENTER ); + + setTestStatus( false ); + } + + /** + * + */ + public MotorTestPanel( MessageContent controls ) { + this(); + controlOutput = controls; + controlOutput.addActionListener( this ); + } + + /** + * + */ + public void setEnabled( boolean state ) { + if( !state ) { + setTestStatus( false ); + } + checkBox.setEnabled( state ); + } + + /** + * + */ + public void setTestStatus( boolean enable ) { + checkBox.setSelected( enable ); + controlOutput.setMotorsEnabled( enable ); + controlOutput.motorTesting = enable; + if( !enable ) { + masterSlider.setValue( 0 ); + masterSlider.fireActionPerformed(); + } + + slider0.setEnabled( enable ); + slider1.setEnabled( enable ); + slider2.setEnabled( enable ); + slider3.setEnabled( enable ); + masterSlider.setEnabled( enable ); + } + + /** + * @return true if testing is enabled + */ + public boolean getTestStatus() { + return checkBox.isSelected(); + } + + /** + * + */ + public int[] getValues() { + int[] values = {slider0.getValue(), + slider1.getValue(), + slider2.getValue(), + slider3.getValue() }; + return values; + } + + /** + * + */ + public void actionPerformed( ActionEvent evt ) { + if( evt.getSource() == controlOutput ) { + if( evt.getActionCommand().equals( "DISABLE_MOTORS" ) && getTestStatus() ) { + setTestStatus( false ); + } + return; + } + if( evt.getSource() == checkBox ) { + setTestStatus( checkBox.isSelected() ); + return; + } + if( evt.getSource() == masterSlider ) { + slider0.setValue( Integer.parseInt( evt.getActionCommand() ) ); + slider1.setValue( Integer.parseInt( evt.getActionCommand() ) ); + slider2.setValue( Integer.parseInt( evt.getActionCommand() ) ); + slider3.setValue( Integer.parseInt( evt.getActionCommand() ) ); + } + fireActionPerformed(); + } + + //////////////////////////////////////////// + + /** + * adds actionlistener to subscribd listener list + * @param al - actionlistener to add + */ + public void addActionListener( ActionListener al ) { + listeners.add( al ); + } + + /** + * removes actionlistener from subscribed listener list + * @param al - actionlistener to remove + */ + public void removeActionListener( ActionListener al ) { + listeners.remove( al ); + } + + /** + * sends an actionevent to subscribed listeners + * @param ae - actionevent to use as parameter to all subscribed listeners + */ + public void fireActionPerformed( ActionEvent ae ) { + //controlOutput.motorValues = { slider0.getValue(), slider1.getValue(), slider2.getValue(), slider3.getValue() }; + controlOutput.motorValues[0] = slider0.getValue(); + controlOutput.motorValues[1] = slider1.getValue(); + controlOutput.motorValues[2] = slider2.getValue(); + controlOutput.motorValues[3] = slider3.getValue(); + + + for( ActionListener al : listeners ) { + al.actionPerformed( ae ); + } + } + + /** + * + */ + public void fireActionPerformed() { + fireActionPerformed( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, "" ) ); + } +} \ No newline at end of file diff --git a/GroundStation/v2/VerticalSlider.java b/GroundStation/v2/VerticalSlider.java new file mode 100644 index 0000000..3939a68 --- /dev/null +++ b/GroundStation/v2/VerticalSlider.java @@ -0,0 +1,194 @@ +import java.util.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; + +class VerticalSlider extends JPanel implements ActionListener, ChangeListener { + + /** up button */ + private JButton up; + /** slider bar */ + private JSlider slider; + /** down button */ + private JButton down; + /** value field */ + private JTextField value; + /** list to hold subscribed actionlisteners */ + private ArrayList listeners; + /** set to false for use fo setValue */ + private boolean internalTrigger = true; + + //////////////////////// + ///// CONSTRUCTORS ///// + //////////////////////// + + /** + * no-args constructor + */ + public VerticalSlider() { + setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) ); + setLabel( "" ); + + + up = new JButton( "\u25B2" ); + slider = new JSlider(); + slider.setOrientation( JSlider.VERTICAL ); + slider.setValue( 0 ); + down = new JButton( "\u25BC" ); + + value = new JTextField( "0" ); + + up.addActionListener( this ); + down.addActionListener( this ); + value.addActionListener( this ); + + slider.addChangeListener( this ); + + this.add( up ); + this.add( slider ); + this.add( down ); + this.add( value ); + + listeners = new ArrayList(); + } + + /** + * sets label of slider on construction + * @param newLabel - label to put on slider + */ + public VerticalSlider( String newLabel ) { + this(); + setLabel( newLabel ); + } + + /** + * sets slider extremes + * also sets slider value to minimum + * @param sliderMin - minimum extreme value of slider + * @param sliderMax - maximum extreme value of slider + */ + public VerticalSlider( int sliderMin, int sliderMax ) { + this(); + slider.setMinimum( sliderMin ); + slider.setMaximum( sliderMax ); + setValue( sliderMin ); + } + + /** + * sets label of slider and sets slider extremes + * also sets slider value to minimum + * @param newLabel - label to put on slider + * @param sliderMin - minimum extreme value of slider + * @param sliderMax - maximum extreme value of slider + */ + public VerticalSlider( String newLabel, int sliderMin, int sliderMax ) { + this( sliderMin, sliderMax ); + setLabel( newLabel ); + } + + /////////////////// + ///// METHODS ///// + /////////////////// + + /** + * @return the value of the slider + */ + public int getValue() { + return slider.getValue(); + } + + /** + * @param newVal - value to set slider to + */ + public void setValue( int newVal ) { + internalTrigger = false; + slider.setValue( newVal ); // will trigger changeListener and update everything else + internalTrigger = true; + } + + /** + * changes the slider's label + * @param newLabel - the new name to put on slider + */ + public void setLabel( String newLabel ) { + setBorder( BorderFactory.createTitledBorder( newLabel ) ); + } + + /** + * implementation of actionlistener interface + */ + public void actionPerformed( ActionEvent evt ) { + if( evt.getSource() == up ) { + slider.setValue( slider.getValue() + 1 ); + } + else if( evt.getSource() == down ) { + slider.setValue( slider.getValue() - 1 ); + } + else if( evt.getSource() == value ) { + slider.setValue( Integer.parseInt( value.getText() ) ); + } + } + + /** + * run whenever slider changes position + * implementation of changelistener interface + */ + public void stateChanged( ChangeEvent evt ) { + if( evt.getSource() == slider ) { + value.setText( ""+ slider.getValue() ); + if( internalTrigger ) { + fireActionPerformed(); + } + } + } + + /** + * + */ + public void setEnabled( boolean enable ) { + up.setEnabled( enable ); + slider.setEnabled( enable ); + down.setEnabled( enable ); + value.setEnabled( enable ); + } + + /** + * adds actionlistener to subscribd listener list + * @param al - actionlistener to add + */ + public void addActionListener( ActionListener al ) { + listeners.add( al ); + } + + /** + * removes actionlistener from subscribed listener list + * @param al - actionlistener to remove + */ + public void removeActionListener( ActionListener al ) { + listeners.remove( al ); + } + + /** + * sends an actionevent to subscribed listeners + * @param ae - actionevent to use as parameter to all subscribed listeners + */ + public void fireActionPerformed( ActionEvent ae ) { + for( ActionListener al : listeners ) { + al.actionPerformed( ae ); + } + } + + /** + * + */ + public void fireActionPerformed() { + fireActionPerformed( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, ""+ slider.getValue() ) ); + } +} + + + + + + diff --git a/GroundStation/v2/stopSign.png b/GroundStation/v2/stopSign.png new file mode 100644 index 0000000000000000000000000000000000000000..d23a8860d7df9af9905a895486bf5e289d35401b GIT binary patch literal 34840 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 AssI20 literal 0 HcmV?d00001