Hosting Tutorial 4 (C#) - Creating Service Instances Dynamically
To run DSS services you can either use the DssHost.exe tool or create your own application to host a DSS node. In the Hosting Tutorials we provide samples of such applications. You can use the DssEnvironment.dll assembly and the DssEnvironment static class to initialize the DSS runtime within an application of your choice or a .NET Windows Service.
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\HostingTutorials\Tutorial4\CSharp
This tutorial teaches you how to:
- Creating an Instance of a Service
- Dropping a Service
Prerequisites
This tutorial uses the services written in Service Tutorial 1 (C#) - Creating a Service and Service Tutorial 2 (C#) - Updating State.
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.
Running the Program
The Visual Studio solution for this sample is called HostingTutorial4.sln and can be found in the sample location mentioned above. To run it in the debugger, open the solution file in Microsoft Visual Studio. Alternatively, create a new C# Console Application project in Microsoft Visual Studio and replace the contents of the source file with the code below. The program needs a reference to the proxy assembly of ServiceTutorial1.
To run the tutorial directly, open a DSS Command Prompt and enter the command:
bin\HostingTutorial4.exe
When you run the application, it displays a message in the console window that asks you to open a web browser and browse to https://localhost:50000/console/output (the Debug and Trace Messages page) to see the DSS node running and verify that ServiceTutorial1 has started. Remain on this page so that you can refresh it while the program is running.
Next the program shuts down ServiceTutorial1 and then again attempts to get its state. This is expected to fail. Lastly, it waits for a short period of time before shutting down the DSS node.
Creating an Instance of a Service
As with the previous hosting tutorials, a DssEnvironment is created initially. The comments show a method for running the executable from a different folder than the one where RDS is installed. After launching a DssEnvironment, the program starts an iterator to perform run most of the code.
using System;
using System.Collections.Generic;
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core;
using Microsoft.Dss.Hosting;
using Microsoft.Dss.ServiceModel.Dssp;
using ds = Microsoft.Dss.Services.Directory;
using st1 = RoboticsServiceTutorial1.Proxy;
using Microsoft.Dss.Services.Constructor;
using W3C.Soap;
namespace HostingTutorial4
{
class Program
{
// Used to set exit code
static int _status = 1;
/// <summary>
/// Main entry to the program
/// </summary>
/// <returns>0 if program succeeds, otherwise a value that is not 0</returns>
static int Main()
{
try
{
// Uncomment the lines below if you want to run the Hosting executable
// in a different directory from the MRDS root directory.
// Note that you must set the root directory and the location of
// the security settings file as appropriate for your installation.
//DssRuntimeConfiguration dssRuntimeConfig = new DssRuntimeConfiguration();
//dssRuntimeConfig.HostRootDir = @"C:\Microsoft Robotics Dev Studio 2008";
//dssRuntimeConfig.PublicHttpPort = 50000;
//dssRuntimeConfig.PublicTcpPort = 50001;
//dssRuntimeConfig.SecuritySettings = @"C:\Microsoft Robotics Dev Studio 2008\store\SecuritySettings.xml";
//DssEnvironment.Initialize(dssRuntimeConfig, null);
// Initialize DSS without any manifests
DssEnvironment.Initialize(50000, 50001);
// Spawn RunTask
Arbiter.Activate(DssEnvironment.TaskQueue, Arbiter.FromIteratorHandler(RunTask));
// Wait here until DSS Environment has been shut down
DssEnvironment.WaitForShutdown();
// Exit program
return _status;
}
catch
{
return -1;
}
}
The code demonstrates hosting a DSS node and creating an instance of a service to query. This sample is similar to Hosting Tutorial 2, except the DSS node is started with no manifest and instead the program attempts to create a new instance of the ServiceTutorial1 service by calling the DssEnvironment.CreateService method. It then sends a Get request to ServiceTutorial1 service to retrieve its state and logs the value of the Member property of ServiceTutorial1State object. If any request fails during this process the application will log an error, shut down the DSS node and exit.
/// <summary>
/// The runtask function uses iterators to mimic sequential programming even though
/// it is actually running asynchronously.
/// </summary>
/// <returns></returns>
static IEnumerator<ITask> RunTask()
{
// This is for debugging ONLY
// You should not normally use Console.WriteLine.
ConsoleColor save = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Open a web browser and view https://localhost:50000/console/output");
Console.ForegroundColor = save;
// Create service tutorial 1 instance locally
PortSet<CreateResponse,Fault> createResultPort = DssEnvironment.CreateService(st1.Contract.Identifier);
// Uncomment lines below to create service on some remote node that is already running
// createResultPort = CreateServiceRemotely(
// new Uri("https://machinename:port"),
// new ServiceInfoType(st1.Contract.Identifier));
// wait for success or failure
yield return createResultPort.Choice();
// retrieve success result. If null, a fault was returned
CreateResponse success = createResultPort;
if (success == null)
{
// Request failed.
DssEnvironment.LogError("Could not find Service Tutorial 1");
_status = 1;
yield break;
}
// Create Request succeeded.
DssEnvironment.LogInfo("Created Service Tutorial 1 service: " + success.Service);
// create forwarder port to service instance
var serviceTutorial1Port =
DssEnvironment.ServiceForwarder<st1.ServiceTutorial1Operations>(new System.Uri(success.Service));
// Issue Get request to service instance
var getResultPort = serviceTutorial1Port.Get();
// Wait for either success or fault
yield return getResultPort.Choice();
// Check if we have success response
st1.ServiceTutorial1State getResponse = getResultPort;
if (getResponse != null)
{
// GET Request succeeded
DssEnvironment.LogInfo("Service Tutorial1 state: " + getResponse.Member);
}
else
{
// GET Request failed
// Retrieve the Fault from the port
Fault f = getResultPort;
DssEnvironment.LogError("Could not retrieve state: " + f.ToException().Message);
_status = 2;
}
Dropping a Service
After getting the state of ServiceTutorial1, the program then sends a Drop message to ServiceTutorial1. This is a deliberate shutdown of ServiceTutorial1 so that it is no longer running and does not respond to messages. Next the code does another Get, but this time with a TimeSpan set on the request so that it will time out if there is no response.
When the Get fails, the program logs an error message. It then waits for a little while so that you have time to refresh the browser window and view the log output on the Debug and Trace Messages page. Finally, it shuts down the DSS node and exits.
// Now drop Service Tutorial 1
var dropResultPort = serviceTutorial1Port.DsspDefaultDrop();
yield return dropResultPort.Choice();
DefaultDropResponseType dropResponse = dropResultPort;
if (dropResponse != null)
{
// Drop was successful
DssEnvironment.LogInfo("Service Tutorial1 dropped");
}
else
{
// Drop Request failed
Fault f = dropResultPort;
DssEnvironment.LogError("Could not drop service: " + f.ToException().Message);
_status = 3;
}
// NOTE: Inside a service you should use LogInfo not Console.WriteLine
Console.WriteLine("Issuing a Get with Timeout");
// Try a Get again, but this time there is no service
// This uses a more explicit approach to sending a message so
// that the TimeSpan can be set
st1.Get getRequest = new st1.Get();
getRequest.TimeSpan = TimeSpan.FromMilliseconds(5000);
serviceTutorial1Port.Post(getRequest);
// Wait for either success or fault
yield return getRequest.ResponsePort.Choice();
// Check if we have success response
getResponse = getRequest.ResponsePort;
if (getResponse != null)
{
// GET Request succeeded
DssEnvironment.LogInfo("Service Tutorial1 state: " + getResponse.Member);
// Set exit code to 0 for success
_status = 0;
}
else
{
// GET Request failed
// Retrieve the Fault from the Response port
Fault f = getRequest.ResponsePort;
// Convert Fault to a suitable message and log it
DssEnvironment.LogError("Could not retrieve state: " + f.ToException().Message);
// The exit status should indicate an error, but in this example
// an error is actually the expected result!
_status = 0;
}
// Shut down runtime
DssEnvironment.LogInfo("Run task completed - sleeping for a while");
// This is for debugging ONLY
// You should not normally use Console.WriteLine.
save = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Refresh Debug and Trace Messages now in your web browser");
Console.ForegroundColor = save;
// Wait a little while
yield return Wait(5);
DssEnvironment.Shutdown();
}
The last section of the code contains some helper methods that are used in the main body of the code.
/// <summary>
/// Helper that creates a service remotely. The DsspServiceBase.CreateService method has similar functionality
/// </summary>
/// <param name="serviceInfo"></param>
/// <param name="remoteHostAndPort">a uri of the form https://hostname:port</param>
/// <returns></returns>
static PortSet<CreateResponse,W3C.Soap.Fault> CreateServiceRemotely(Uri remoteHostAndPort, ServiceInfoType serviceInfo)
{
var remoteConstructorPort = DssEnvironment.ServiceForwarder<ConstructorPort>(new Uri(remoteHostAndPort + ServicePaths.Constructor));
var create = new Create(serviceInfo);
remoteConstructorPort.Post(create);
return create.ResponsePort;
}
/// <summary>
/// Wait - Helper that creates a timeout port, queues a timer and returns a receiver on the port
/// </summary>
/// <param name="seconds">Timeout period in seconds</param>
/// <returns></returns>
static Receiver<DateTime> Wait(int seconds)
{
Port<DateTime> p = new Port<DateTime>();
DssEnvironment.TaskQueue.EnqueueTimer(TimeSpan.FromSeconds(seconds), p);
return Arbiter.Receive(false, p, delegate(DateTime t) { });
}
Summary
This tutorial covered:
- Creating an Instance of a Service
- Dropping a Service
See Also |
DSS User Guide: DSS Application Configuration File
© 2010 Microsoft Corporation. All Rights Reserved.