Project - Locate your gadget over IP

I just posted Locate your gadget over IP on Codeshare. Feel free to discuss and make suggestions here.

This project demonstrates how to use the discovery features of DPWS to find your gadget in an IP network. Instead of using static IP addresses, this simple project allows you to use DHCP for your gadget and find which address the gadget is using from a Windows application via WCF.

DPWS is seen by many as complicated and difficult to use. Discovery is a built in feature to DPWS that can be used without generating services and service proxies. All that is required is some boilerplate code.

To achieve discovery we will use a service definition that has no methods.

GADGET
On the gadget side the service definition is a simple class derived from DpwsHostedService. The serviceTypeName can be changed for each gadget you have so they can be located uniquely. The service will be found by matching ServiceNameSpace and ServiceTypeName.

    class DPWSHostService : DpwsHostedService
    {
        private String serviceTypeName = "IMyCustomGadget";

        public DPWSHostService(ProtocolVersion v)
            : base(v)
        {
            // Add ServiceNamespace. Set ServiceID and ServiceTypeName
            ServiceNamespace = new WsXmlNamespace("h", "http://schemas.example.org/" + serviceTypeName);
            ServiceID = "urn:uuid:3cb0d1ba-cc3a-46ce-b416-212ac2419b51";
            ServiceTypeName = serviceTypeName;
        }
    }

Hosting and starting the DPWS service consist of three steps which are the same however you name your gadget. These should be done once the network is up and running e.g. in a NetworkUp event. The uploaded project wraps up all of the DPWS functionality into a single class (DPWSServer) that can be called like this:


    public partial class Program
    {
        private DPWSServer server = new DPWSServer();
...
        void ethernet_J11D_NetworkUp(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state)
        {
            server.StartDPWS();
        }

For those who want to know how it all works here is a breakdown of the steps:

  1. Initialize the DPWS device and bindings

ProtocolVersion version = new ProtocolVersion11();
Guid g = Guid.NewGuid();
string urn = "urn:uuid:" + g.ToString();

Device.Initialize(new WS2007HttpBinding(new HttpTransportBindingConfig(urn, 8084)), version);

  1. Set Metadata. The values can be changed without affecting discovery
// Set device information
Device.ThisModel.Manufacturer = "Microsoft Corporation";
Device.ThisModel.ManufacturerUrl = "http://www.microsoft.com/";
Device.ThisModel.ModelName = "Gadget Test Device";
Device.ThisModel.ModelNumber = "1.0";
Device.ThisModel.ModelUrl = "http://www.microsoft.com/";
Device.ThisModel.PresentationUrl = "http://www.microsoft.com/";

Device.ThisDevice.FriendlyName = "MyGadget";
Device.ThisDevice.FirmwareVersion = "alpha";
Device.ThisDevice.SerialNumber = "12345678";

  1. Start the service. DPWSHostService is the service defined above.
// Add a Host service type
Device.Host = new DPWSHostService(version);

// Set this device property if you want to ignore this clients request
Device.IgnoreLocalClientRequest = false;

ServerBindingContext sbContext = new ServerBindingContext(version);
// Start the device
Device.Start(sbContext);

WINDOWS
On the Windows side the first step is a matching service. Note the Namespace and Name must match the ServiceNamespace and ServiceTypeName on the gadget.


    [System.ServiceModel.ServiceContractAttribute(Namespace = "http://schemas.example.org/IMyCustomGadget", Name = "IMyCustomGadget")]
    public interface IGadgetService
    {
    }

Again the uploaded project contains a class that takes care of finding your gadget (Locator.cs). The gadget will broadcast a hello message which the locator will catch and fire the OnHelloEvent. You can also use the FindService method too directly probe for an already running device. For both methods you will get an EndpointDiscoveryMetadata object. ListenUris[0].Host will give you the gadget address.


private Locator _locator = new Locator(typeof(IGadgetService));
//register an event handler that will be called when the device starts up and send a hello
_locator.OnHelloEvent += _locator_OnHelloEvent;
//start listening for events
_locator.ListenForAnnouncements();
...

//Initiate a probe for the device
var metaData = _locator.FindService();
if (metadata != null)
{
...

For those who want to understand how it works…

Subscribing to Hello broadcasts is done by creating an Announcement Service and starting it up.


// Create an AnnouncementService instance
AnnouncementService announcementService = new AnnouncementService();

// Subscribe the announcement events
announcementService.OnlineAnnouncementReceived += announcementService_OnlineAnnouncementReceived;
// announcementService.OfflineAnnouncementReceived += OnOfflineEvent;

// Create ServiceHost for the AnnouncementService
announcementServiceHost = new ServiceHost(announcementService);

// Listen for the announcements sent over UDP multicast
announcementServiceHost.AddServiceEndpoint(new UdpAnnouncementEndpoint());
announcementServiceHost.Open();

The announcement service picks up all hello messages so we need to match it to our gadget:


private void announcementService_OnlineAnnouncementReceived(object sender, AnnouncementEventArgs e)
    {

        FindCriteria DeviceFindCriteria = new FindCriteria(typeof(IGadgetService));
        var metaData = e.EndpointDiscoveryMetadata;

        if (DeviceFindCriteria.IsMatch(metaData))
        {
            // This is our device host
...

Direct probing uses a discovery client to broadcast a probe and catch responses


var endPoint = new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscovery11);

                //http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/c08c55c6-784a-4896-abfa-ea5299a03cfa
                //http://support.microsoft.com/kb/2777305
                endPoint.Behaviors.Add(new WorkaroundBehavior());

                DiscoveryClient discoveryClient = new DiscoveryClient(endPoint);

                Collection<EndpointDiscoveryMetadata> services = discoveryClient.Find(new FindCriteria(typeof(IGadgetService))).Endpoints;

                discoveryClient.Close();

                if (services.Count != 0)
                {
                    return services[0];
                }
                else
                {
                    return null;
                }

Nick

6 Likes

Very good!

Nice, good breakdown & explanation!

For me, examples are not fully working. I get the announcement, when my Spider reboots, but probing from sample application always returns „Not found…

Yes, all my devices and PCs use DHCP.

Simon, were you using the debugger or did you deploy and run?

I usually run the code from visual studio in debug mode but when I rebooted the device and ran without the debugger attached I saw the same behavior.

I tried removing debug statements and also doing a release build but no difference. Has me stumped right now; I will have to do some digging.

I came across this thread: https://www.ghielectronics.com/community/forum/topic?id=5743&page=2 but am not sure how this applies to a gadgeteer program.

I’ve tried both ways. No luck…

@ Simon can you tell if there is data being sent to the gadgeteer with a sniffer?

On the broader issue of not running without a debugger, I added a timer blinking an led and it works fine so I dont think it is thread blocking. I will have to find time to build the DPWS source and litter it with screen traces.

The reason for my problems was that I had Visual Studio running. Even with a release build it seems that the spider detects VS and that causes it to hang on Thread start.

Stopping VS and the system runs fine :slight_smile:

1 Like