An useful key reader

Sometimes you want to interact with your Federate using the console keyboard.

I’ll show you how to implement an useful keyboard listener, so you can type keys ( or words ) to tell your Federate what to do.

In most cases a Federate will follow these steps:

public void runFederate() {
  createRtiAmbassador();
  connect();
  createFederation();
  joinFederation();
  publishAndSubscribe();
  
  while ( condition ) {
    ... 
    do the simulation
    ...
  }
  
  exitFromFederationAndQuit();
}

What I want is to send some keys inside the while loop so I can interact with the Federate. I’ll use the Observer pattern to read from console inside a Thread. This process will block the reader thread while our main simulation will still running. When the ENTER key is pressed, the reader thread is released and the key or word will be send to the observer ( our Federate ).

First we’ll need an Interface:

public interface IKeyReaderObserver {
  void notify( String key );
  void whenIdle();
}

Our Federate will implement this interface and these two methods. When the ENTER key is pressed, the notify() will be called with the key or word typed. Until nothing is done, the whenIdle() is called so you can put your main simulation there.

The Federate class will be something like this:

public class AircraftFederate implements IKeyReaderObserver {
  private RTIambassador rtiamb;
  private FederateAmbassador fedamb; 
  
  @Override
  public void notify( String key ) {
    if ( key.equals("n") ) {
      createAircraft();
    }
  }
  
  @Override
  public void whenIdle() {
    // Update only the position
    try {
      aircraftClass.updatePosition();
      rtiamb.evokeMultipleCallbacks(0.1, 0.3);
    } catch ( Exception e ) {
      //
    }
  }

  public void runFederate() throws Exception	{
    createRtiAmbassador();
    connect();
    createFederation();
    joinFederation();
    publishAndSubscribe();
    
    System.out.println("====== AIRCRAFT FEDERATE ======");
    System.out.println("Type:");
    System.out.println(" n + ENTER : New aircraft");
    System.out.println(" q + ENTER : Quit");

    KeyReader kr = new KeyReader( this, "q" );
    kr.readKeyUntilQuit();
    
    exitFromFederation();
  }

}

The KeyReader is our reader thread. The readUntilQuit() method will block the Federate flow until the key given in the constructor is pressed. I’m using String so you can use words too.

Note the constructor:

KeyReader kr = new KeyReader( this, "q" );

Where this is the AircraftFederate ( or any IKeyReaderObserver implementation ) and q is the key/word used to quit the reader thread.

Now, the best part: the KeyReader class.

public class KeyReader implements Runnable {
    private IKeyReaderObserver observer;
    private BufferedReader in ;
    private boolean quit = false;
    private String quitKey = "q";

    public KeyReader( IKeyReaderObserver observer, String quitKey ) {
        this.observer = observer;
        this.quitKey = quitKey;
    }
    
    public void readKeyUntilQuit() {
        in = new BufferedReader(new InputStreamReader( System.in ) );
        Thread t1=new Thread( this );
        t1.start();
         
        while(true) {
            try{
                Thread.sleep(1);       
            }catch(InterruptedException e){
                //
            }            
            if( quit == true) break;
            observer.whenIdle();
        }
    }

    @Override
    public void run() {
        String msg = null;
        while(true) {
            try {
                msg = in.readLine();
            } catch( IOException e ){
                e.printStackTrace();
            }
            if( msg.equals( quitKey ) ) {
                quit = true;
                break;
            } else {
                observer.notify( msg );
            }
        }
    }
    
    
}

The basic operation is: readUntilQuit()  will start a thread to block when reading from keyboard and enter in a loop until the global quit variable is true.  If quit still false, call the observer’s whenIdle().

When the blocked thread releases, it means the user sends some keys. If it is the quit key, stop all this madness and quit (set the global quit to true and exit from main thread loop). If not, pass it to the observer by calling notify().

Easy uh?

For some unknown reason I need to sleep a little:

try{
    Thread.sleep(1);       
}catch(InterruptedException e){
    //
}

because this line was not being reached:

if( quit == true) break;
observer.whenIdle();

Don’t ask me why. I think my simulation loop was too fast, but you may remove it to test.

Download the sources from my Github repository at:

https://github.com/icemagno/tankandaircraft

Add Comment