Developing a Controller Using Design Patterns
The following is the source code that goes with the practical example of how to use software design patterns in modeling real-world embedded systems applications. It illustrates how to apply design patterns during the development of a garage door controller. Important benefits of state design patterns are a result: Software Modularity, Code Re-usability, Improved Maintainability, Lower Costs, Faster Time-to-Market. Read the full article: Developing a Controller Using Design Patterns.
Example Source Code
Below is the source code developed for this garage door opener design. The design was built and testing using version 3.2 of Java’s Eclipse IDE. A total of five (5) image files were used and placed inside a subdirectory called “Images”, located one level immediately below the location where the class files are generated. These image files are simply for demonstration purposes to show the garage door controller works.
The five (5) image files are:
GarageDoorClosed.jpg
GarageDoorOpeningOrClosing.jpg
GarageDoorOpen.jpg
GarageDoorOpener.jpg
GarageDoorSensor.jpg
GarageDoorControllerState.java:
package DesignPatterns.State;
import javax.swing.ImageIcon;
public interface GarageDoorControllerState {
public void pushButton( StateContext pStateContext );
public void motorStopped( StateContext pStateContext );
public boolean tripSensor( StateContext pStateContext );
public ImageIcon getImage( StateContext pStateContext );
}
Closed.java:
package DesignPatterns.State;
import javax.swing.ImageIcon;
public class Closed implements GarageDoorControllerState {
private static final String sImageFile = "GarageDoorClosed.jpg";
public void pushButton( StateContext pStateContext ) {
pStateContext.setState( new Opening() );
}
public void motorStopped( StateContext pStateContext ) { }
public boolean tripSensor( StateContext pStateContext ) { return( false ); }
public ImageIcon getImage( StateContext pStateContext ) {
return( new ImageIcon( Closed.class.getResource( "Images/" + sImageFile ) ) );
}
}
Opening.java:
package DesignPatterns.State;
import javax.swing.ImageIcon;
public class Opening implements GarageDoorControllerState {
private static final String sImageFile = "GarageDoorOpeningOrClosing.jpg";
public void pushButton( StateContext pStateContext ) {
pStateContext.setState( new Closing() );
}
public void motorStopped( StateContext pStateContext ) {
pStateContext.setState( new Open() );
}
public boolean tripSensor( StateContext pStateContext ) { return( false ); }
public ImageIcon getImage( StateContext pStateContext ) {
return( new ImageIcon( Opening.class.getResource( "Images/" + sImageFile ) ) );
}
}
Open.java:
package DesignPatterns.State;
import javax.swing.ImageIcon;
public class Open implements GarageDoorControllerState {
private static final String sImageFile = "GarageDoorOpen.jpg";
public void pushButton( StateContext pStateContext ) {
pStateContext.setState( new Closing() );
}
public void motorStopped( StateContext pStateContext ) { }
public boolean tripSensor( StateContext pStateContext ) { return( false ); }
public ImageIcon getImage( StateContext pStateContext ) {
return( new ImageIcon( Open.class.getResource( "Images/" + sImageFile ) ) );
}
}
Closing.java:
package DesignPatterns.State;
import javax.swing.ImageIcon;
public class Closing implements GarageDoorControllerState {
private static final String sImageFile = "GarageDoorOpeningOrClosing.jpg";
public void pushButton( StateContext pStateContext ) {
pStateContext.setState( new Opening() );
}
public void motorStopped( StateContext pStateContext ) {
pStateContext.setState( new Closed() );
}
public boolean tripSensor( StateContext pStateContext ) {
pStateContext.setState( new Opening() );
return( true );
}
public ImageIcon getImage( StateContext pStateContext ) {
return( new ImageIcon( Closing.class.getResource( "Images/" + sImageFile ) ) );
}
}
StateContext.java:
package DesignPatterns.State;
import javax.swing.ImageIcon;
public class StateContext {
private GarageDoorControllerState aState;
public StateContext () { aState = new Closed(); }
public void setState( GarageDoorControllerState pState ) {
// In C++, here is where we would do a delete[] aState to avoid memory leaks!
aState = pState;
}
public String getStateAsText() {
String lStateText = "";
if( aState instanceof Closed ) { lStateText = "Closed"; }
else if( aState instanceof Closing ) { lStateText = "Closing"; }
else if( aState instanceof Open ) { lStateText = "Open"; }
else if( aState instanceof Opening ) { lStateText = "Opening"; }
return( lStateText );
}
public void pushButton() { aState.pushButton( this ); }
public void motorStopped() { aState.motorStopped( this ); }
public boolean tripSensor() { return( aState.tripSensor( this ) ); }
public ImageIcon getImage() { return( aState.getImage( this ) ); }
}
StateTest.java:
package DesignPatterns.State;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class StateTest extends JPanel implements ActionListener {
private static final long serialVersionUID = 327686553613107226L;
private static final String sGarageDoorOpenerFile = "Images/GarageDoorOpener.jpg";
private static final String sGarageDoorSensorFile = "Images/GarageDoorSensor.jpg";
private static final int sGarageDoorOpenCloseTime = 10000;
private static final int sGarageDoorTimerInterval = 100;
private StateContext aStateContext;
private JButton aGarageDoorOpenerButton;
private JLabel aStateImage;
private JTextArea aStatus;
private JButton aSensor;
private Timer aGarageDoorTimer;
private int aTime = 0;
public StateTest() {
setLayout( new BorderLayout() );
aStateContext = new StateContext();
aGarageDoorTimer = new Timer( sGarageDoorTimerInterval, this );
// Make the bottom button for the garage door opener
aGarageDoorOpenerButton = new JButton( new ImageIcon(
StateTest.class.getResource( sGarageDoorOpenerFile ) ) );
aGarageDoorOpenerButton.addActionListener( this );
aSensor = new JButton( new ImageIcon( StateTest.class.getResource(
sGarageDoorSensorFile ) ) );
aSensor.addActionListener( this );
// Make Status area
aStatus = new JTextArea( 5, 5 );
aStatus.setEditable( false );
JScrollPane lStatusPane = new JScrollPane( aStatus );
// Make something for the center
JPanel lGarageDoorPanel = new JPanel( new BorderLayout() );
aStateImage = new JLabel( aStateContext.getImage() );
lGarageDoorPanel.add( aStateImage, BorderLayout.CENTER );
// Make Panel for the buttons on the RHS!
JPanel lSouthPanel = new JPanel( new BorderLayout() );
JPanel lButtonPanel = new JPanel( new BorderLayout() );
JPanel lTopButtonPanel = new JPanel( new FlowLayout() );
lTopButtonPanel.add( aGarageDoorOpenerButton );
JPanel lSensorButtonPanel = new JPanel( new FlowLayout() );
lSensorButtonPanel.add( aSensor );
lButtonPanel.add( lTopButtonPanel, BorderLayout.NORTH );
lButtonPanel.add( lSensorButtonPanel, BorderLayout.SOUTH );
lSouthPanel.add( lStatusPane, BorderLayout.CENTER );
lSouthPanel.setBorder( new TitledBorder( "Garage Door Status" ) );
// Add what we have!
add( lSouthPanel, BorderLayout.SOUTH );
add( lGarageDoorPanel, BorderLayout.WEST );
add( lButtonPanel, BorderLayout.CENTER );
aStatus.append( "The Garage Door is Currently: " +
aStateContext.getStateAsText() + "\n" );
}
public static void main( String[] pArguments ) {
JFrame lApplication = new JFrame( "Garage Door Opener" );
lApplication.add( new StateTest() );
lApplication.setSize( 1024, 768 );
lApplication.setVisible( true );
lApplication.setLocationRelativeTo( null );
lApplication.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
lApplication.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent pEvent ) {
System.exit( 0 );
} }); }
public void actionPerformed( ActionEvent pEvent ) {
if( pEvent.getSource() == aGarageDoorOpenerButton ) {
aTime = figureOutTime();
aStateContext.pushButton();
aGarageDoorTimer.restart();
updateView();
}
else if( pEvent.getSource() == aGarageDoorTimer ) {
aTime += sGarageDoorTimerInterval;
if( aTime >= sGarageDoorOpenCloseTime ) {
aStateContext.motorStopped();
aGarageDoorTimer.stop();
updateView();
}
}
else if( pEvent.getSource() == aSensor ) {
boolean lRetracted = aStateContext.tripSensor();
if( lRetracted == true ) {
aTime = figureOutTime();
aGarageDoorTimer.restart();
updateView();
}
}
}
private void updateView() {
aStateImage.setIcon( aStateContext.getImage() );
aStatus.append( "The Garage Door is Currently: " +
aStateContext.getStateAsText() + "\n" );
}
private int figureOutTime() {
int lTime = 0;
if( aGarageDoorTimer.isRunning() == true ) {
lTime = sGarageDoorOpenCloseTime - aTime;
}
return( lTime );
}
}
|
|
|
| EMBEDDED SYSTEMS NEWSLETTERS |
|
|
|
|
|