Build a Core Service

The next step in building a hardware interface is to design a core service. This service is also commonly referred to as the brick service. This is a Decentralized Software Services (DSS) service that serves to represent the hardware—or, in this case, the ARobot. The core service is responsible for communicating with the ARobot through a serial port. You can connect the ARobot to the development machine through either a wired or a wireless connection.

Note

Note

The ARobot does not come packaged with wireless capabilities. To communicate with the ARobot wirelessly, you will have to install a wireless receiver on the ARobot.

To create the core service, create a new DSS service using the Visual Studio template provided with MSRS. You can name the service ARobot. By default, this creates two source files: ARobot.cs and ARobotTypes.cs. The ARobotTypes.cs code file is where you add code concerning the robot’s state and list the DSS operations that can be performed.

Add Code to the Contract Class

The ARobot controller board comes with red and green LED lights, whiskers, and a speaker. Each of these components is represented through state variables. There is also a state variable used to hold the COM port number. The COM port number is assigned when you connect the ARobot to your computer using a serial cable.

Additionally, we need state variables to represent the configuration parameters used to drive the robot. These are variables representing direction, speed, and distance. For these variables to be returned with the robot’s state, they must be declared in the ARobotTypes contract class and include the DataMember attribute above each variable declaration. For example, you can use the following code to represent the state for the ARobot:

[DataContract()]
public class ARobotState
{

    private bool _connected;
    private int _comPort;
    private DriveConfig _driveConfig;
    private Whiskers _whiskers;
    private Speaker _speaker;
    private LEDs _leds;

    [DataMember]
    [Description("Indicates whether the ARobot is connected.")]
    public bool Connected
    {
        get { return this._connected; }
        set { this._connected = value; }
    }

    [DataMember]
    [Description("Indicates COM Port that the ARobot is connected to.")]
    public int ComPort
    {
       get { return this._comPort; }
       set { this._comPort = value; }
    }

    [Description("Drive Configuration")]
    [DataMember]
    public DriveConfig DriveConfig
    {
        get { return this._driveConfig; }
        set { this._driveConfig = value; }
    }

    [Description("Whisker State")]
    [DataMember]
    public Whiskers Whiskers
    {
        get { return this._whiskers; }
        set { this._whiskers = value; }
    }

    [Description("Speaker Configuration")]
    [DataMember]
    public Speaker Speaker
    {
        get { return this._speaker; }
        set { this._speaker = value; }
    }

    [Description("LED State")]
    [DataMember]
    public LEDs LEDs
    {
        get { return this._leds; }
        set { this._leds = value; }
    }
}

The user-defined types referenced in the state variable declarations (i.e. DriveConfig, Whiskers, Speaker, and LEDs) are defined beneath the state declarations. These class definitions must also use the DataMember attribute, along with the DataContract attribute. For example, the class definition used to define the DriveConfig type is shown as follows:

[Description("The ARobots Drive configuration parameters.")]
[DataContract]
public class DriveConfig
{
    private int _motorSpeed;
    private int _distance;
    private string _direction;

    [DataMember]
    [Description("Indicates the motor speed used by the ARobot.")]
    public int MotorSpeed
    {
        get { return this._motorSpeed; }
        set { this._motorSpeed = value; }
    }

    [Description("Indicates the direction that the ARobot moves towards.")]
    [DataMember]
    public string Direction
    {
       get { return this._direction; }
       set { this._direction = value; }
    }
    [Description("Indicates the distance in half-inches ARobot drives")]
    [DataMember]
    public int Distance
    {
        get { return this._distance; }
        set { this._distance = value; }
    }
    public DriveConfig()
    {
    }

    public DriveConfig(int motorSpeed, string direction, int distance)
    {
        MotorSpeed = motorSpeed;
        Direction = direction;
        Distance = distance;
    }
}

You can see that the ARobot uses three input parameters to drive the robot: MotorSpeed, Direction, and Distance. These variables are set with values that are passed on to the ARobot when a request is made to drive the robot. The following value ranges can be used for these variables:

  • MotorSpeed. Use a value from 0 to 15, in which 0 indicates no movement and 15 is the maximum speed.

  • Direction. Use a string-based value that represents one of the following: F=Forwards, B=Backwards, R=Right, L=Left, S=Stop.

  • Distance. Use an integer-based value that represents the distance the ARobot will travel. You can use an approximate value of 16 to represent 12 inches or 1 foot. If you wanted the robot to move 2 feet, then you would use a distance of 32.

Modify the Portset

Based on the interface program created in an earlier section, the ARobot can accept commands to drive the robot, turn on and off the lights, activate the speaker, and receive sensor information from the whiskers. These functions need to be represented as DSS operations. You define them in the ARobotTypes class and add them to the PortSet using the following code:

[ServicePort()]
public class ARobotOperations : PortSet<DsspDefaultLookup,
                           DsspDefaultDrop,
                           Get,
                           Replace,
                           Subscribe,
                           UpdateWhiskers,
                           PlaySpeaker,
                           SetLEDs,
                           DriveRobot>
{
}

As you learned in earlier chapters, the DsspDefaultLoopkup, DsspDefaultDrop, and Get operations are standard DSS operations added to the code when the template is created. The Replace operation is added to allow for a change to the state. The Subscribe operation allows us to receive notifications whenever the whiskers state has changed. The remaining operations correspond to the functions that can be performed on the ARobot.

Add Code to the Implementation Class

The implementation class, which in this case is named ARobot, is where you add code used to send commands to the ARobot onboard interface. To do this, you first need to add code that declares the subscription manager service as a partner. This is needed when we implement the code associated with the subscribe operation. The code to declare the subscription service partner is as follows:

   [Partner(dssp.Partners.SubscriptionManagerString,
      Contract = submgr.Contract.Identifier,
      CreationPolicy = PartnerCreationPolicy.CreateAlways)]
submgr.SubscriptionManagerPort _subMgrPort = new
   submgr.SubscriptionManagerPort();

You also need to add a declaration for the Initial State partner. This allows us to retrieve state values from an XML-based file, which in this case is a file named ARobot.config.xml. The code for this declaration should appear as follows:

[InitialStatePartner(Optional = true, ServiceUri =
         "samples/config/arobot.config.xml")]
private ARobotState _state = new ARobotState();

Note

Note

By default, MSRS searches for the Initial State partner in the store directory beneath the MSRS installation folder. If you want to move the config file to a different directory (such as the samples/config directory), you need to include a portion of the path in the Initial State partner declaration.

The ARobot.config.xml file contains values for each state variable included in the contract class. The ARobot.config.xml file should look like the following:

<?xml version="1.0" encoding="utf-8"?>
<ARobotState xmlns:s=http://www.w3.org/2003/05/soap-envelope
  xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
  xmlns:d="http://schemas.microsoft.com/xw/2004/10/dssp.html"
  xmlns="http://custsolutions.net/2007/10/arobot.html">
<Connected>false</Connected>
<ComPort>4</ComPort>
<DriveConfig>
<Direction>F</Direction>
<MotorSpeed>3</MotorSpeed>
<Distance>0</Distance>
</DriveConfig>
<Whiskers>
<WhiskerLeft>false</WhiskerLeft>
<WhiskerRight>false</WhiskerRight>
</Whiskers>
<Speaker>
<frequency>50</frequency>
<Duration>250</Duration>
</Speaker>
<LEDs>
<RedLED>false</RedLED>
<GreenLED>false</GreenLED>
</LEDs>
</ARobotState>

Modify the Start Method

The next thing to do is add code to the Start method. The Start method is created through the template when the service is created. You need to add code to the Start method so that it looks like the following:

protected override void Start()
{
    base.Start();

    ConnectToARobot();

    //add custom handlers to interleave
    Activate(
     Arbiter.Interleave(
      new TeardownReceiverGroup(),
      new ExclusiveReceiverGroup(
        Arbiter.ReceiveWithIterator<SetLEDs>(true, _mainPort,
                SetLEDsHandler),
        Arbiter.ReceiveWithIterator<UpdateWhiskers>(true, _mainPort,
                UpdateWhiskersHandler),
        Arbiter.ReceiveWithIterator<DriveRobot>(true, _mainPort,
                SetDriveConfigHandler),
        Arbiter.ReceiveWithIterator<PlaySpeaker>(true, _mainPort,
                PlaySpeakerHandler)
         ),
      new ConcurrentReceiverGroup()
    ));
}

Connect to the ARobot

The ConnectARobot method sets the value for the Connected state variable by calling the Connect function. The Connected state variable indicates whether MSRS has successfully connected to the ARobot. The code for this method looks like the following:

private void ConnectToARobot()
{
    //Connect to the ARobot and establish connection which will
    //register a receive handler to get the whisker data coming
    //back from the robot
    string errorMessage;
   _state.Connected = Connect(_state.ComPort, out errorMessage);
    if (!_state.Connected && !string.IsNullOrEmpty(errorMessage))
    {
        LogError(LogGroups.Activation, errorMessage);
        return;
    }
}

Before you can run the ARobot services, you need to connect your development machine to the ARobot using either a serial cable or a wireless connection. You get the number for the port assignment at the time you connect your machine to the robot. After you get this port assignment number, change the value for the state variable named ComPort. This value is retrieved from the ARobot.config.xml file. Open the file with a text editor and change the associated value.

The Connect function first checks to see that we are using a valid COM port number. This number is determined at the point a serial connection to the ARobot is established on your development machine; therefore, it varies with each machine. The code for the Connect function is shown as follows:

public bool Connect(int serialPort, out string errorMessage)
{
      //Make sure we have a valid COM port number
      if (serialPort <= 0)
      {
          errorMessage = "The ARobot serial port is not configured!";
          return false;
      }

      //Check to see if we are already connected
      if (connected)
      {
          Close();
      }

      //Open a connection
      try
      {
          string ComPort = "COM" + serialPort.ToString();
          _serialPort = new SerialPort(ComPort, _baudRate);
          _serialPort.Parity = Parity.None;
          _serialPort.DataBits = 8;
          _serialPort.StopBits = StopBits.One;
          _serialPort.ReadTimeout = 2000; //in ms
          _serialPort.WriteTimeout = 2000; //in ms
          _serialPort.ReceivedBytesThreshold = 1;
          _serialPort.DataReceived += new
               SerialDataReceivedEventHandler(_serialPort_DataReceived);
          _serialPort.Open();
          _serialPort.DiscardInBuffer();
          errorMessage = string.Empty;
          connected = true;
          return true;
      }
      catch (Exception ex)
      {
          errorMessage = string.Format("Error connecting ARobot to
                 COM{0}:1}", serialPort, ex.Message);
          return false;
      }
}

Create Message Handlers

The ARobot service uses four message handlers to handle the associated DSS operations listed in the PortSet. For example, there is a message handler named UpdateWhiskersHandler that is called every time the whisker state variables are updated. The code for this handler is shown as follows:

public virtual IEnumerator<ITask> UpdateWhiskersHandler(UpdateWhiskers update)
{
     //Value the state variables
     _state.Whiskers.WhiskerLeft = update.Body.WhiskerLeft;
     _state.Whiskers.WhiskerRight = update.Body.WhiskerRight;

     // Send Notifications to subscribers
     SendNotification<UpdateWhiskers>(_subMgrPort, new
            UpdateWhiskers(_state.Whiskers));

     //Post the update operation
     update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
     yield break;
}

Before updating the value of the whisker state variables, a notification is sent to the subscription port, which indicates that the whisker state has changed. All subscribers to the service are then notified of the change. The remaining handlers are used to process updates associated with the robot’s speaker, LEDs, and drive configuration. Each of these handlers invokes a method that sends commands to the ARobot’s serial port. For example, the SetDriveConfigHandler is called whenever the DriveRobot operation is called. This handler calls another method named DriveRobot and passes to it variables representing the distance, direction, and motor speed. The SetDriveConfigHandler method is shown as follows:

public virtual IEnumerator<ITask> SetDriveConfigHandler(DriveRobot update)
{
     //Make sure we are connected
     if (!_state.Connected)
     {
          LogError("trying to drive the robot, but not connected");
          update.ResponsePort.Post(new Fault());
          yield break;
     }

     //Value the state variables
     _state.DriveConfig.Distance = update.Body.Distance;
     _state.DriveConfig.Direction = update.Body.Direction;
     _state.DriveConfig.MotorSpeed = update.Body.MotorSpeed;

     //Send command to the ARobot to set the configuration and drive robot
     DriveRobot(update.Body.Distance, update.Body.Direction,
         update.Body.MotorSpeed);

     //Post the update operation
     update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
     yield break;
}

The DriveRobot method is used to send the drive configuration information to the ARobot through a byte array. The following code shows what the DriveRobot method looks like:

public void DriveRobot(int inches, string direction, int speed)
{

     byte[] packet = new byte[_packetSize];
     packet[0] = _driveRobot;
     //Approximate distance in half inches
     packet[1] = Convert.ToByte(inches);
     //Direction should be a S=Stop,B=Backwards,F=Forwards,R=Right,L=Left
     packet[2] = (byte)Convert.ToChar(direction);
  //Motor Speed is set with value ranging from 0 to 15
     packet[3] = Convert.ToByte(speed);
     SendData(ref packet);
}

The byte array is necessary because this is what the BS2 accepts through the serial port. Depending on what type of robot you are designing an interface for, the method of interfacing with that robot may vary. For the ARobot interface, the first byte in the array contains a unique number that represents a command for the robot. In the case of the DriveRobot command, this number is 20. Why 20? The number was selected when the interface was designed, and it is used by the onboard interface (introduced in the last section) to determine which subroutine is executed. Constants located at the top of the ARobot class file store the values for these command assignments. If for some reason the numbers assigned to these commands are changed in the onboard interface program, you must also change the associated constant value in the ARobot class.

The last step is to add the remaining handlers, which represent updates to the speaker and LEDs. After this is done, you can compile the ARobot project by clicking Build and then clicking Build Solution from the menu bar. In this chapter, we only cover how to create the drive and bumper services. The LED and speaker services, which are included with the services on the companion Web site, function much the same as the drive service.

Tip

Tip

If you encounter strange errors when trying to build the proxy project for your service, try compiling manually using the msbuild command. This is done by going to the command prompt for MSRS and typing something similar to the following: msbuild "samplesmyservicemyservice.csproj."

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.239.105