Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Задани на лабораторные работы. ПРК / Professional Microsoft Robotics Developer Studio

.pdf
Скачиваний:
126
Добавлен:
20.04.2015
Размер:
16.82 Mб
Скачать

www.it-ebooks.info

Chapter 4: Advanced Service Concepts

<xsl:value-of select=”svc:property2”/> </td>

</tr> </table>

</body> </html>

</xsl:template> </xsl:stylesheet>

The service contract identifier is listed as one of the xmlns (XML namespace) parameters at the top of the file using the alias svc so that you can easily refer to it throughout the rest of the file. In the <xsl:template> element, the match attribute specifies the ServiceState. This must be the actual class name of your service state. Remember that the XML file output by your service is a serialized version of the service state.

Be very careful with capitalization in the contract identifier and the names of the service state and the properties. XML is case sensitive.

Inside the template section is a complete HTML page. Property values are displayed from the state using the <xsl:value-of> element. All properties must be referred to using a full URI. This is why they are shown as svc:property1 and svc:property2. If you use nested structures within your state, then you cannot just use the “dot notation” that you use in C#. For example, the state for the TeleOperation service contains a class called Options, which in turn contains a number of other properties. To access Options.MotionSpeed you must write the following:

<xsl:value-of select=”svc:Options/svc:MotionSpeed”/>

Notice that a slash (/) replaces the dot (.) in the property name and that the contract identifier (svc:) is repeated. If you make a mistake in the syntax of the select attribute, the value quietly disappears. No errors are reported — the value is just missing from the web page.

In addition to formatting the layout of your state properties using XSLT, you can use a Cascading Style Sheet (CSS) file to control the formatting of the HTML tags, i.e., the appearance of the elements on the page.

It is beyond the scope of this book to explain how to write CSS code, so it is assumed that you already know how, or you can learn from a tutorial website on the Internet. The full definition of CSS is also available on the World Wide Web Consortium website, but it is quite complex to read.

A CSS file can be embedded in your service in exactly the same way as an XSLT file. (Add it to your project and then mark it as an embedded resource). Then you just need to know the appropriate URI. In the preceding example, the built-in MRDS Cascading Style Sheet is used for formatting. The full URI for this is in the href attribute of the <link> tag. This MRDS CSS file defines the odd and even classes that are used on the table rows to make them easier to read by changing the background highlighting.

Creating an XSLT file Using the MRDS Template

If you want to save yourself some time and promote page uniformity, instead of building an XSLT file from scratch as shown in the last section, you can always create one using the MRDS template. To use the MRDS template, follow these steps:

1. Copy the template from the C:\Resources\DSS folder (where you saved it earlier in the section “Setting Up Your Development Environment for XSLT”) into your project directory and give it an appropriate name. Click Project Add Existing Item to add it to your project.

193

www.it-ebooks.info

Part I: Robotics Developer Studio Fundamentals

Remember that once you have added the XSLT file to your project, you must change the properties so that the Build Action is set to Embedded Resource.

2. Edit the XSLT file. You need to insert your service’s contract identifier at the top of the file:

<?xml version=”1.0” encoding=”utf-8” ?> <xsl:stylesheet

version=”1.0”

xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” xmlns:soap=”http://www.w3.org/2003/05/soap-envelope” xmlns:dssp=”http://schemas.microsoft.com/xw/2004/10/dssp.html”

xmlns:svc=”http://www.promrds.com/contracts/2008/01/teleoperation.html”

>

For convenience, the alias for your service is svc. You can change it if you like, but it is easier to leave it alone.

3. In the first <xsl:template> section, change the Service Title, Name, and Description as appropriate for your service:

<xsl:template match=”/”>

<xsl:comment><!-- Service Header Info --></xsl:comment> <xsl:variable name=”title”>

TeleOperation Service

</xsl:variable>

<xsl:variable name=”serviceName”>

<img src=”/resources/teleoperation.y2008.m01/ProMRDS.Robotics.TeleOperation.Resources. icon_32x32.gif” align=”middle” /> TeleOperation

</xsl:variable>

<xsl:variable name=”description”>

Allows you to drive a Differential Drive robot using live video from a WebCam

on the robot

</xsl:variable>

4. Adding images to your page is also relatively easy. You can use embedded resources once again. The file format is not particularly important as long as it is one that the web browser understands. Note that BMP files are not understood by all web browsers. (This is a Windows format; and in any case it is not compressed, so the images can be quite large). Use the PNG, GIF, or JPG file format for your images.

Notice that the TeleOperation service displays an icon in the page header (in the preceding code) by adding an image to the serviceName variable. (Ignore the wrap-around in the URI).

In the MRDS template, you can either define CSS styles directly (between the <style> tags) or include a style sheet file. Look at the comments carefully. They indicate that the head parameter is placed in the HTML <head> section of the web page, and there is also a comment showing where to insert style definitions directly.

In the following example, an embedded style sheet file is used:

<!-- The contents of head param will be placed just before the </head> tag in html. -->

194

www.it-ebooks.info

Chapter 4: Advanced Service Concepts

<xsl:with-param name=”head”>

<link rel=”stylesheet” href=”/resources/teleoperation.y2008.m01/ProMRDS.Robotics

.TeleOperation.Resources.TeleOperation.css” type=”text/css” /> <style type=”text/css”>

/* Service-specific stylesheet goes here */ </style>

Using your own style sheet means that your page might not have the same appearance as other MRDS pages. Notice here that the CSS file is also an embedded resource in the Resources folder of the project. The standard MRDS style sheet is automatically included by the master page.

At the bottom of the first template is another <xsl:template> section. You need to change the match criterion from /svc:Template to use the correct name for your service state class. This is shown in the following example (ignore the <form> tag for now, as it is discussed later):

<xsl:template match=”/svc:TeleOperationState”>

<form name=”DssForm” method=”post” onsubmit=”return checkform(this);”> <div class=”Content”>

<table width=”100%” border=”0” cellpadding=”5” cellspacing=”5”> <tr>

<th>Host:</th> <td>

<xsl:value-of select=”svc:Host”/> </td>

</tr> <tr>

<th>Port:</th> <td>

<xsl:value-of select=”svc:Port”/> </td>

</tr> <tr>

<th>Connected:</th> <td>

<xsl:choose>

<xsl:when test=”svc:Connected = ‘true’”> Connection established

</xsl:when> <xsl:otherwise>

Not connected (<b>Tip:</b>

<span class=”greyText”>

Enter a Host name and a Port and click on Connect </span>)

</xsl:otherwise> </xsl:choose>

</td> </tr>

...

<tr>

<th colspan=”2”>Option Settings</th>

(continued)

195

www.it-ebooks.info

Part I: Robotics Developer Studio Fundamentals

(continued)

</tr> <tr>

<th>Main Window Start Position</th>

<td><xsl:value-of select=”svc:Options/svc:WindowStartX”/>, <xsl:value-of select=”svc:Options/svc:WindowStartY”/> (Saved automatically)

</td> </tr> <tr>

...

</table> </div> </form>

</xsl:template>

</xsl:stylesheet>

For this code, note the following:

Usually the data is displayed in a table with two columns — the left column consists of Table Headings (th) and the right column is Table Data (td).

Values are inserted into the page using the <xsl:value-of> element. Remember to use the correct syntax for nested structures in your state, such as for the WindowStartX property.

Notice the use of <xsl:choose> to display different text depending on the state of the Connected property. You can have several <xsl:when> elements as well as <xsl:otherwise>, so it is conceptually similar to a switch statement. Alternatively, you could also use <xsl:if>. However, there is no else part, so you would have to use two if statements.

The best way to learn how to create XSLT code is probably to look at existing files. Search the MRDS samples folder for XSLT files.

Displaying the State Using XSLT

You are not finished yet. To display your state using XSLT, you must create a handler for HttpGet requests and add this message type to your main operations port (if it is not there already). The handler specifies the XSLT file when it returns the state.

To refer to your embedded XSLT file in the HttpGet handler, you define a string variable at the top of the source file. It is usually called _transform, but it doesn’t have to be. You can use the [EmbeddedResource] attribute to construct the full URI for the XSLT file as shown here for the TeleOperation service:

///<summary>

///Embedded XSLT file for formatting State on a web page

///</summary> [EmbeddedResource(“ProMRDS.Robotics.TeleOperation.Resources.TeleOperation.xslt”)]

string _transform = null;

The appropriate prefix is added to the URI and the resulting string is assigned to _transform when you compile the code.

196

www.it-ebooks.info

Chapter 4: Advanced Service Concepts

The code for the HttpGet handler is quite simple:

///<summary>

///HttpGet Handler

///</summary>

///<param name=”get”></param>

///<returns></returns> [ServiceHandler(ServiceHandlerBehavior.Concurrent)]

public virtual IEnumerator<ITask> HttpGetHandler(HttpGet httpGet)

{

//Format the response using a transform httpGet.ResponsePort.Post(new HttpResponseType(

HttpStatusCode.OK, _state, _transform)

);

yield break;

}

The response posted back is created using _state and _transform.

That’s all there is to formatting your state information. However, if you make a mistake in the URI of your XSLT file in the [EmbeddedResource] attribute, you might see an error like the one shown in Figure 4-8.

Figure 4-8

197

www.it-ebooks.info

Part I: Robotics Developer Studio Fundamentals

You can check whether your embedded XSLT file is present in your assembly by entering the full URI into the address bar of the web browser. If it is not found, a blank page is displayed. In that case, you need to double-check your spelling, the path, and the placement of dots. The URI is not case sensitive, so that cannot be the problem.

Creating a Web Form for Data Input

Creating a Web Form is not much different from what you have already seen except that you wrap your HTML code in a <form> tag and put the property values into <input> tags. You also have to add a Submit button; otherwise, there is no way to post the Form! If you are familiar with web development, then you should not have any trouble:

1.

2.

Looking at the TeleOperation.xslt file again, there is a <form> tag at the top of the template:

<xsl:template match=”/svc:TeleOperationState”>

<form name=”DssForm” method=”post” onsubmit=”return checkform(this);”>

Notice that the method is post. You cannot (easily) use the get method because the HttpGet and HttpPost handlers are separate. The Form name is not important, and the onsubmit attribute is discussed in the next section.

In the main part of the HTML table, you can use <input> tags to get values from the user. The following example displays the current value of the DeadZoneX property in a textbox:

<tr>

<th>Dead Zone X</th> <td>

<input type=”text” name=”DeadZoneX” class=”TextBox”> <xsl:attribute name=”value”>

<xsl:value-of select=”svc:Options/svc:DeadZoneX”/> </xsl:attribute>

</input>

(Game Controller range is 1000, so typical value is 100) </td>

</tr>

Note that the XSL code sets the value attribute of the <input> tag to show the current value.

The dead zone is a region at the center of the joystick’s movement where the drive speed is set to zero. Most joysticks do not return exactly to the zero position when you release them. The small amount by which they are off-center causes very small set power commands to be sent to the robot’s wheels, but it is unlikely to move due to inertia. In some cases, this causes the motors to squeal, which can be quite annoying!

3. The last row in the table is a Submit button. The Form data is sent to the service (as a HttpPost request) when you click the button, which is labeled “Save”:

<tr> <td>

<input type=”submit” name=”Save” value=”Save” /> </td>

</tr>

198

www.it-ebooks.info

Chapter 4: Advanced Service Concepts

The Submit (Save) button is visible at the very bottom of Figure 4-9.

Figure 4-9

Notice in Figure 4-9 that the values above the Option Settings heading cannot be edited; they are for informational purposes only. The window positions cannot be edited either. They are obtained directly from the windows, so you just need to position the windows where you want them before you save the state.

All of the remaining properties have textboxes for entering new values. It is possible to make them rightaligned, but this has not been done. It is simply a matter of changing the TextBox style in the CSS file.

You can have more than one <form> tag on a web page, and you can have more than one Submit button in a Form (with different names). In that case, the code in your HttpPost handler needs to check the Form name and/or the Submit button name.

After you click Save, the data is sent in an HttpPost request to your handler. Before looking at the handler though, you need to add some support utilities. At the top of the service, add the following statement:

DsspHttpUtilitiesPort _httpUtilities;

199

www.it-ebooks.info

Part I: Robotics Developer Studio Fundamentals

This port can be used to request support services for processing HTTP forms. Do not be tempted to create a new port like this:

DsspHttpUtilitiesPort _httpUtilities = new DsspHttpUtilitiesPort();

Using new does not work. The new port must be created in the Start method:

// Needed for HttpPost

_httpUtilities = DsspHttpUtilitiesService.Create(Environment);

If you forget to create the HTTP utilities port and simply use new, you will fall into a common trap of MRDS — sending messages when there is nobody home! You can send as many messages as you like to a port, but if no service is listening on the other end then your requests will go unanswered. You will wait a very long time for the HTTP utilities to do their job!

The TeleOperation HttpPost handler begins like this:

///<summary>

///Http Post Handler for Web Form inputs

///</summary> [ServiceHandler(ServiceHandlerBehavior.Concurrent)]

public virtual IEnumerator<ITask> HttpPostHandler(HttpPost httpPost)

{

string ErrorMessage = String.Empty; Fault fault = null;

NameValueCollection parameters = new NameValueCollection();

// Use helper to read form data

ReadFormData readForm = new ReadFormData(httpPost.Body.Context); _httpUtilities.Post(readForm);

// Wait for result

yield return Arbiter.Choice( readForm.ResultPort, delegate(NameValueCollection col)

{

parameters = col;

}, delegate(Exception e)

{

fault = Fault.FromException(e);

LogError(null, “Error processing form data”, fault); ErrorMessage += e.Message;

}

);

if (fault != null)

{

httpPost.ResponsePort.Post(fault); yield break;

}

The first thing the handler does is send a ReadFormData request to the HTTP utilities. Because the utilities operate like a service, you have to wait for a response and be prepared to handle a Fault.

200

www.it-ebooks.info

Chapter 4: Advanced Service Concepts

There is an alternative to using these utilities: You can write your own code to extract the form parameters. The following code is not seriously proposed as a replacement, but it shows how

the parameters are obtained. The code performs no error handling, and it fails if the total amount of data exceeds 10K:

NameValueCollection parameters = new NameValueCollection();

// Extract the parameters from the Request data stream int length = 0;

byte[] data = new byte[10240];

length = httpPost.Body.Context.Request.InputStream.Read( data, 0, 10240);

StringBuilder sb = new StringBuilder(); for (int i = 0; i < length; i++)

sb.Append((char)data[i]);

string request = sb.ToString(); char[] ampersand = { ‘&’ }; char[] equals = { ‘=’ };

string[] pairs = request.Split(ampersand); for (int i = 0; i < pairs.Length; i++)

{

string[] namevalue = pairs[i].Split(equals); parameters.Add(namevalue[0], namevalue[1]);

}

Each of the parameters is checked in turn to see if it is valid. You must do this even if you use client-side validation in JavaScript because users can turn off JavaScript in their web browsers. For example, the following code confirms that the user entered a valid number for MotionSpeed:

double MotionSpeed = _state.Options.MotionSpeed; bool validValue = false;

if (!string.IsNullOrEmpty(parameters[“MotionSpeed”]))

{

try

{

MotionSpeed = double.Parse(parameters[“MotionSpeed”]); validValue = true;

}

catch (Exception e)

{

string msg = “Could not parse Motion Speed: “ + e.Message; LogError(msg);

ErrorMessage += msg;

}

}

if (validValue && MotionSpeed >= 10 && MotionSpeed <= 1000)

{

_state.Options.MotionSpeed = MotionSpeed;

}

201

www.it-ebooks.info

Part I: Robotics Developer Studio Fundamentals

The code also checks whether MotionSpeed is within a reasonable range, and finally assigns the new value to the property in the state. (Because it modifies the state, the HttpPost handler should be Exclusive. However, most implementations mark it as a Concurrent handler. Make sure that you mark it as Exclusive if it changes the state.)

Similar checks are performed for the rest of the properties. Notice that any error messages are appended to the ErrorMessage variable. Once all of the updates have completed, it is a simple matter to determine whether the ErrorMessage is empty and call the appropriate routine to post success or failure:

// Finally, process the result if (ErrorMessage == string.Empty)

{

HttpPostSuccess(httpPost);

}

else

{

HttpPostFailure(httpPost, ErrorMessage);

}

yield break;

}

The HttpPostSuccess routine does not require much comment. Just note that it saves the option settings back into public variables in the WinForms if they are active. In effect, it passes information from the main service to the WinForms. Then it saves the state to a config file:

///<summary>

///Send Http Post Success Response

///</summary>

private void HttpPostSuccess(HttpPost httpPost)

{

//Grab the current window location if (_driveControl != null)

{

_state.Options.WindowStartX = _driveControl.Location.X; _state.Options.WindowStartY = _driveControl.Location.Y;

}

if (_cameraForm != null)

{

_state.Options.WebCamStartX = _cameraForm.Location.X; _state.Options.WebCamStartY = _cameraForm.Location.Y;

}

//Update the Forms with the new option settings

//This is another implicit method of communication with WinForms if (_driveControl != null)

_driveControl.options = _state.Options; if (_cameraForm != null)

_cameraForm.options = _state.Options;

//Post a response

HttpResponseType rsp =

new HttpResponseType(HttpStatusCode.OK, _state, _transform);

202