Service Tutorial 6 (C#) - Retrieving State and Displaying it Using an XML Transform
Glossary Item Box
Microsoft Robotics Developer Studio | Send feedback on this topic |
Service Tutorial 6 (C#) - Retrieving State and Displaying it Using an XML Transform
Writing an application using Decentralized Software Services (DSS) is a simple matter of orchestrating the input and output between a set of services. Services represent the interface to software or hardware and allow you to communicate between processes that perform specific functions.
This tutorial is provided in the C# language. You can find the project files for this tutorial at the following location under the Microsoft Robotics Developer Studio installation folder:
Samples\ServiceTutorials\Tutorial6\CSharp
This tutorial teaches you how to:
- Get the State of Another Service.
- Use an XSLT to Present Service State.
- Use XSLT as Embedded Resource.
- Use the DSS XSLT Template.
See Also:
- Appendix A: ServiceTutorial6.xslt
- Appendix B: Alternate pattern for request/response in DSSP iterator handlers
Prerequisites
This tutorial uses the service written in Service Tutorial 5 (C#) - Subscribing. The service you will create is referred to as ServiceTutorial6. Feel free to start a new project or keep working in the previous project, keeping in mind that some names may differ.
Hardware
This tutorial requires no special hardware.
Software
This tutorial is designed for use with Microsoft Visual C#. You can use:
- Microsoft Visual C# Express Edition
- Microsoft Visual Studio Standard, Professional, or Team Edition.
You will also need Microsoft Internet Explorer or another conventional web browser.
Step 1: Get the State of Another Service
In ServiceTutorial6Types.cs, add the properties Clock and InitialTicks to the ServiceTutorial6State class. These properties will be used to demonstrate how to get the state of another service.
private string _clock;
[DataMember]
public string Clock
{
get { return _clock; }
set { _clock = value; }
}
private int _initialTicks;
[DataMember]
public int InitialTicks
{
get { return _initialTicks; }
set { _initialTicks = value; }
}
In the file, ServiceTutorial6.cs, remove the line that calls _clockPort.Subscribe in the Start method and replace it with the following code. This will cause the iterator method OnStartup to run concurrently with the execution of the service.
SpawnIterator(OnStartup);
Now write the OnStartup method. The first thing that this method does is send a Get message to the ServiceTutorial4 partner service. To send this message, call the Get method on the _clockPort field. This constructs and sends a Get message to the service. The iterator then returns a Choice task using yield return, where the two choices available are the two possible response message types defined by the Get message of ServiceTutorial4. In general, the Get message of a service returns the state of that service or a Fault.
Here, the state of the service is stored in a local variable state in the success path and the fault is logged in the error path.
This function assumes that using W3C.Soap; was declared at the top of the file. |
private IEnumerator<ITask> OnStartup()
{
rst4.Get get;
yield return _clockPort.Get(GetRequestType.Instance, out get);
rst4.ServiceTutorial4State state = get.ResponsePort;
if (state == null)
{
LogError("Unable to Get state from ServiceTutorial4", (Fault)get.ResponsePort);
}
The above code uses a concise syntax for sending the request and handling the response. Instead of yield returning an Arbiter.Choice, here we are using an overload of the Get method that returns a Choice object. The received response is then assigned to the state variable. This is done by implicitly casting the ResponePort to the success response type (rst4.ServiceTutorial4State). If the ResponsePort is filled with a failure then the assigned state value will be null. See Appendix B for a version of this method that uses Arbiter.Choice, which may be more familiar. |
If the success path was followed, state will not be null. In that case, this code creates a new ServiceTutorial6State for the current state and populates the InitialTicks property with the current value of Ticks in the state of the ServiceTutorial4 service. The code also looks up the partner definition for the Clock partner and sets the Clock property to be the service address of the partner.
A replace message is then sent to the main port of this service to set the new state values.
else // if (state != null)
{
ServiceTutorial6State initState = new ServiceTutorial6State();
initState.InitialTicks = state.Ticks;
PartnerType partner = FindPartner("Clock");
if (partner != null)
{
initState.Clock = partner.Service;
}
Replace replace = new Replace();
replace.Body = initState;
_mainPort.Post(replace);
}
Having gotten the state of the ServiceTutorial4 service, the OnStartup method concludes by sending a Subscribe message to the ServiceTutorial4 service and logging a fault.
rst4.Subscribe subscribe;
yield return _clockPort.Subscribe(_clockNotify, out subscribe);
Fault fault = subscribe.ResponsePort;
if (fault != null)
{
LogError("Unable to subscribe to ServiceTutorial4", fault);
}
}
The above code uses a concise syntax for sending the request and handling the response for subscribe. Instead of yield returning an Arbiter.Choice, here we are using an overload of the Subscribe method that returns a Choice object. We only need to handle the failure case so by using an implicit cast we retrieve the fault message from the port. If the fault is null it means the subscription was successful because a Choice task is always handled on only one message. See Appendix B for a version of this method that uses Arbiter.Choice, which may be more familiar. |
Step 2: Use an XSLT to Present Service State
In Service Tutorial 1 (C#) - Creating a Service, you learned how to display the XML representation of a service state in a web browser. That state can be transformed to HTML (or text, or another XML representation) to better present it to the user. This process requires only three steps:
- Write an Extensible Stylesheet Language Transformations (XSLT) file describing how to transform the state - this is out of the scope of these tutorials, https://msdn.microsoft.comhas information on writing XSLTs and an example XSLT is presented in the appendix of this tutorial.
- Add the XSLT as an embedded resource of the service assembly.
- Provide a link to that XSLT in the serialized XML representation of the state.
First we’ll show you how to use the Mount Service to reference an XSLT stylesheet in the store\transforms sub-directory of the DSS installation directory.
In the file, ServiceTutorial6.cs, declare a string type called _transform and assign to it the relative path to the XSLT file through the Mount service.
string _transform = "/mountpoint/store/transforms/ServiceTutorial6.xslt";
The DssHost port number is not known until the DSS node is started, therefore, the URI for a transform referenced from the store must either be generated at runtime or specified as a relative path. |
In the method, HttpGetHandler, provide the path of the XSLT to the HttpResponseType constructor.
httpGet.ResponsePort.Post(new HttpResponseType(System.Net.HttpStatusCode.OK, _state, _transform));
Copy the ServiceTutorial6.xslt file to the store\transforms directory then build and run the service. You will see a page like the following.
Figure 1 - The XSLT stylesheet is applied to the state of the service when viewed in the browser.
When debugging the XSLT in Internet Explorer, always open the page in a new browser window or tab in order to see the changes. The caching in IE sometimes does not allow the page to refresh when there is an XSLT error even if you press Ctrl+F5. Opening the page in a new window always reloads the page. |
Step 3: Use XSLT as Embedded Resource
Copying the XSLT file to the store\transforms directory isn't always appropriate. It adds a level of complexity to deploying a service.
DSS supports loading resources embedded in an assembly. To embed the XSLT file, select it in the Solution Explorer in Microsoft Visual Studio. In the Properties window, change the Build Action field from Content to Embedded Resource. The resource is given a name that is a concatenation of the default project namespace and the name of the file. In this case, Robotics.ServiceTutorial6.ServiceTutorial6.xslt.
In the file, ServiceTutorial6.cs, add an EmbeddedResource attribute with the path to the transform.
[EmbeddedResource("Robotics.ServiceTutorial6.ServiceTutorial6.xslt")]
string _transform = null;
The value of the _transform field will be populated on the service start at runtime.
There is also an easier way to attach the embedded XSLT resource to the state in cases where you want to use the default Get and HttpGet handlers. In this case, instead of defining a null transform field and using the [EmbeddedResource] attribute you can do the following:
- Remove the _transform field and the EmbeddedResource attribute on top of it.
- Annotate the state field with the [ServiceState] attribute providing the embedded XSLT file to use:
// Declare the service state and also the XSLT Transform for displaying it
[ServiceState(StateTransform = "RoboticsServiceTutorial6.ServiceTutorial6.xslt")]
private ServiceTutorial6State _state = new ServiceTutorial6State();
- Remove the Get and HttpGet handlers.
This will tell the base class to implicitly handle the Get and HttpGet messages using the default handlers for both, using the specified XSL Transformation for HttpGet responses. Note that the Get and HttpGet operations should already be in the service's operations port for them to be handled implicitly.
To avoid namespace conflicts, the Service Tutorial 6 project installed with DSS has a different namespace than the one shown above. To view the default namespace in your project, open Project Properties then see Default namespace under the Application tab. |
Step 4: Use the DSS XSLT Template
You may have noticed that the transformed state view above does not have the common layout as in the other DSS services. DSS uses a common XSLT template for all DSS services to give them a consistent look. To learn how to apply this template on your services see the Tutorial for Using Default DSS XSLT Template.
This is an advanced step and to continue to the next tutorial you are not required to complete this step. However, when developing services for production it is highly recommended to follow the guidelines provided here. |
Summary
In this tutorial, you learned how to:
- Get the State of Another Service.
- Use an XSLT to Present Service State.
- Use XSLT as Embedded Resource.
- Use the DSS XSLT Template.
Appendix A: ServiceTutorial6.xslt
XML
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rst6="https://schemas.microsoft.com/2006/06/servicetutorial6.html">
<xsl:output method="html"/>
<xsl:template match="/rst6:ServiceTutorial6State">
<html>
<head>
<title>Service Tutorial 6</title>
<link rel="stylesheet" type="text/css" href="/resources/dss/Microsoft.Dss.Runtime.Home.Styles.Common.css" />
</head>
<body style="margin:10px">
<h1>Service Tutorial 6</h1>
<table border="1">
<tr class="odd">
<th colspan="2">Service State</th>
</tr>
<tr class="even">
<th>Clock:</th>
<td>
<xsl:value-of select="rst6:Clock"/>
</td>
</tr>
<tr class="odd">
<th>Initial Tick Count:</th>
<td>
<xsl:value-of select="rst6:InitialTicks"/>
</td>
</tr>
<tr class="even">
<th>Current Tick Count:</th>
<td>
<xsl:value-of select="rst6:TickCount"/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Appendix B: Alternate pattern for request/response in DSSP iterator handlers
In the OnStartup() method above it uses an alternate way to handle DSS request/responses in the iterator which is a little more concise and allows handling only those response messages that are of interest for us rather than having to define anonymous methods for all of the response types. The OnStartup() method above is equivalent to the following code. You can compare the two yield return parts to see the difference.
For more information on CCR Iterators see CCR Iterators.
private IEnumerator<ITask> OnStartup()
{
rst4.ServiceTutorial4State state = null;
yield return Arbiter.Choice(
_clockPort.Get(GetRequestType.Instance),
delegate(rst4.ServiceTutorial4State response)
{
state = response;
},
delegate(Fault fault)
{
LogError(null, "Unable to Get state from ServiceTutorial4", fault);
}
);
if (state != null)
{
ServiceTutorial6State initState = new ServiceTutorial6State();
initState.InitialTicks = state.Ticks;
PartnerType partner = FindPartner("Clock");
if (partner != null)
{
initState.Clock = partner.Service;
}
Replace replace = new Replace();
replace.Body = initState;
_mainPort.Post(replace);
}
yield return Arbiter.Choice(
_clockPort.Subscribe(_clockNotify),
delegate(SubscribeResponseType response) { },
delegate(Fault fault)
{
LogError(null, "Unable to subscribe to ServiceTutorial4", fault);
}
);
}
© 2012 Microsoft Corporation. All Rights Reserved.