Tutorial: Text Messages for Keyboard Input

From Tekkotsu Wiki

Jump to: navigation, search

Contents

Introduction

It's often useful for robot applications to receive text input from the user. For example, when debugging, you might want to insert a pause in your state machine so you can examine the local and world shape spaces, then use text input to tell the robot to proceed. Or you might want to test out a series of functions you've coded by entering a keyword to tell the robot which function to run next. The PilotDemo class, which you may be familiar with from exercises such as Lab: The Pilot and Localization, is an example of this style of keyword-dispatch programming.

If you were writing an ordinary Linux C or C++ program, you could simply accept input from stdin or cin, but this won't work on your robot, for several reasons. The main reason is that Tekkotsu uses stdin for its command line processor, so stdin is not available for user applications. A second reason is that some robots don't have keyboards, so you need to interact with them via some other mechanism, such as the ControllerGUI. And a third reason is that the simplest way to read from stdin or cin is to use a blocking read, i.e., a read that causes the program to wait until the user types a line of text and hits Enter. But if you did that in the Main process, the robot would freeze up and nothing else would work, including the ControllerGUI. (Tekkotsu's command line processor runs in a separate thread so that it can wait for input without holding up execution of your behavior.) This tutorial will explain how your own behaviors can receive and act on text input using text message events.

Sending Text Messages

There are two ways to send text messages to Tekkotsu. If you are running Tekkotsu in a shell, you can use the Tekkotsu command line processor's "msg" command to send a text message. The command line processor has a prompt that begins with "HAL-" followed by the robot model, e.g., HAL-Calliope or HAL-Calliope5KP. Here is an example of sending a text message this way:

HAL-Calliope> msg This is a text message.

The other way to send a text message is to use the ControllerGUI's "Send Input" text box in the upper right corner of the ControllerGUI window. This box has several different uses, but one of them is to issue commands to Tekkotsu. Such commands must begin with an exclamation point. So to send a text message using the ControllerGUI, you would type the following in the Send Input box and hit Enter:

!msg This is a text message.

Note: once you have given a !msg command, you can type subsequent text lines in the Send Input box without having to use the !msg prefix again. But if you're using the Tekkotsu command line (in a terminal window) you must always include the "msg".

Reacting To Text Messages

The easiest way to get a behavior to react to a text message is to include a =TM("string")=> transition in the state machine. The string argument is case sensitive and must match exactly. Examples:

listener: StateNode
listener =TM("left")=> turnLeft
listener =TM("right")=> turnRight
listener =TM=> invalidCommand

turnLeft: SpeechNode("Turning left") =N=> Turn(M_PI/2) =C=> listener
turnRight: SpeechNode("Turning right") =N=> Turn(-M_PI/2) =C=> listener
invalidCommand: SpeechNode("Invalid command") =N=> listener

Note that the three text message transitions above emanate from the listener node. This means they will only be active when listener is active, and when they fire, listener is deactivated and some other node,such as turnLeft, becomes active. If you type a text message when there is no text message transition listening for it, it will be ignored.

The second thing to note is that a =TM=> transition with no string argument will fire in response to any text message input, but only if no other text message transition fires first. So this type of transition can serve as an "otherwise" or default case.

If you want to pause your state machine so you can examine shape spaces or perform some other type of debugging activity, simply replace a =C=> or =N=> transition with =TM=> and type "msg" to the Tekkotsu command line processor when you want to resume execution.

Extracting The String From A Text Message Event

If you want to take text input that is more complex than a fixed set of keywords, you will need to use the wildcard =TM=> transition and access the text string to parse it yourself. Fortunately, this is easy to do.

Any time a state node is activated as a result of an event-triggered transition, the state node's event data member is set to a pointer to the triggering event. The type of event is const EventBase*. Text message events are of type TextMsgEvent, which is a child of EventBase<code>. They include a getText() method that returns the message text as a string. So to extract the string, simply recast the event as a text message event so you can call its getText() method. Here is a sample program to demonstrate the concept:

#include "Behaviors/StateMachine.h"

$nodeclass TextMessageDemo : StateNode {

  $nodeclass MessageHandler : StateNode : doStart {
    string const &theMessage = ((const TextMsgEvent*)event)->getText();
    cout << "I heard '" << theMessage << "'." << endl;
  }

  $setupmachine{
    startnode: StateNode =TM=> MessageHandler =N=> startnode
  }

}

REGISTER_BEHAVIOR(TextMessageDemo);

Advanced Topic: Your Own Event Listener

You don't have to rely on =TM=> transitions to set up a listener for text message events. You can set up your own listener in any state node by calling addListener to listen, and overriding the doEvent method to respond to events. This can be useful if you want to program complex behaviors in a single node instead of a collection of state nodes. Here is an example:

$nodeclass MyMessageListener : StateNode {

  virtual void doStart() {
    erouter->addListener(this, EventBase::textmsgEGID);
  }

  virtual void doEvent() {
    string const &theMessage = ((const TextMsgEvent*)event)->getText();
    cout << "I heard '" << theMessage << "'." << endl;
  }

}

For as long as the MyMessageListener node is active, it will receive text message events via its doEvent method and print out the message.