Using the AllJoyn ® Studio Extension

Sign in to queue

Description

The AllSeen Alliance created AllJoyn to empower the Internet of Things. Windows 10 has AllJoyn built natively into its platform, allowing developers to easily take advantage of AllJoyn to "IoT-enable" your Windows 10 apps. This article will outline the steps required to build apps for Windows 10 using the Universal Windows Platform (UWP) AllJoyn APIs and the Visual Studio 2015 AllJoyn® Studio Extension.

This blog post is a fulfillment of the promises made in the AllJoyn session presented at //build/ 2015:

AllJoyn: Building Universal Windows Apps that Discover, Connect, and Interact with Other Devices and Cloud Services Using AllJoyn

Understanding AllJoyn UWP App Development

Three major components form AllJoyn UWP apps:

  1. App layout and design (XAML or HTML) and class components (C#, JavaScript, C++, or VB).
  2. The AllJoyn core APIs: AllJoyn Standard Client API (C) and Windows.Devices.AllJoyn API (WinRT)  available in the Windows 10 SDK.
  3. One or more UWP Windows Runtime Components (the  generates this code from AllJoyn interfaces).

The following diagram shows the architecture of a typical AllJoyn UWP project:

 Generic Episode Image

AllJoyn-enabled UWP apps can be either producers  (implement and expose interfaces, typically a device ), consumers (use interfaces, typically apps), or both. Consumers and producers share the same starting steps, only diverging in the implementation details.

Creating an AllJoyn-enabled UWP app 

Follow these steps to develop AllJoyn-enabled UWP apps for Windows 10: (explained in detail later in this document)

  1. Prepare your build environment.
  2. Determine which AllJoyn interfaces will be used, and obtain or create necessary Introspection XML.
  3. Create an AllJoyn App project then select Introspection XML and desired Interfaces for code generation.
  4. Implement producer or ponsumer code in your app using APIs exposed from the generated UWP Windows Runtime Components.
  5. Build your UI.

Preparing your Build Environment

The Windows 10 build and related tools include all of the resources that you'll need to write AllJoyn-enabled UWP apps.

Here's what you'll need to do before you get started writing code:

Obtaining Introspection XML for AllJoyn Interfaces

There are three ways that you can obtain the AllJoyn interface  definitions that you will need to start your project:

  1. Extract the Introspection XML from AllJoyn Producers present on your network at runtime.
  2. Obtain the Introspection XML from documentation ; example: Lighting Service Framework (LSF) documentation from the AllSeen Alliance.
  3. Create your own Introspection XML that is compliant with the AllJoyn/D-Bus introspection format.

This blogpost covers the first two ways - AllJoyn® Studio natively supports querying the network for AllJoyn producers and extracting their XML as well as uploading Introspection XML files.  Learn how to create your own here.

At //build/ 2015, an AllJoyn-enabled toaster device was shown which will serve as the example for this post. This toaster exposes controls for starting and stopping the toasting sequence, setting the "darkness", and notifications when the toast is burnt.

Generic Episode Image

The AllJoyn toaster hardware sample in action

The toaster exposes the following XML:

<node name="/toaster">
  <interface name="org.alljoyn.example.Toaster">
    <annotation name="org.alljoyn.Bus.Secure" value="true"/>
    <description language="en">Example interface for controlling a toaster appliance</description>
    <description language="fr">Interface Exemple de commande d'un appareil de grille-pain</description>
    <property name="Version" type="q" access="read">
      <description>Interface version</description>
      <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
    </property>
    <signal name="ToastBurnt" sessioncast="true">
      <description language="en">Toast is burnt</description>
      <description language="fr">Toast est brûlé</description>
    </signal>
    <method name="StartToasting">
      <description language="en">Start toasting</description>
      <description language="fr">Lancer grillage</description>
    </method>
    <method name="StopToasting">
      <description language="en">Stop toasting</description>
      <description language="fr">Arrêtez de grillage</description>
    </method>
    <property name="DarknessLevel" type="y" access="readwrite">
      <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
      <description language="en">Toasting darkness level</description>
      <description language="fr">Grillage niveau de l'obscurité</description>
    </property>
  </interface>
</node>

Creating an AllJoyn Project

Create a Project the way you normally would: Click File->New->New Project to begin.

Generic Episode Image

Instead of navigating to a Windows Universal Template, select the "AllJoyn App" Template for your target language which was installed with the Extension.  Name your project and choose a file location to begin developing.

Generic Episode Image

Immediately after selecting an AllJoyn App Template, Visual Studio will ask you to select the AllJoyn Interfaces you would like to include in your Project. 

Generic Episode Image

Extracting interfaces from a device on the network

If you cannot find your AllJoyn device or interface on the network, follow this guide to troubleshoot. 

Generic Episode Image

To query your network for exposed interfaces, select the "Producers on the network" in the left-hand panel. This will find any AllJoyn producer on the network and list the interfaces they support.

Generic Episode Image

Select the interfaces you would like to inlude in your project.  In this case, we are only using the toaster interface, so we select just the "org.alljoyn.example.Toaster" interface.

Generic Episode Image

The "Actions" column describes the pending changes to the Project, displaying "Add" for newly-selected Interfaces. Here we have given the interface a friendly name of "ToasterLibrary", which triggers the "Rename" action to be applied.

 Generic Episode Image

Loading an XML from a File

Choose any number of Introspection XML files via the "Browse" button to see their contained Interface(s).

 Generic Episode Image

Navigate to and select the appropriate XML (here, we are using toaster.xml).

Generic Episode Image

Once AllJoyn® Studio loads the XML, it will parse the various Interfaces and the descriptions contained within, allowing you select which Interfaces you would like to support.

Generic Episode Image

The "Actions" column describes the pending changes to the Project, displaying "Add" for newly-selected Interfaces.

Generic Episode Image

Here we have chosen to include the "org.alljoyn.example.Toaster" Interface and have given it a friendly name of "ToasterLibrary", triggering the "Rename" action to be applied.

Generic Episode Image

Adding and Removing Interfaces

After completing these steps, the generated files are automatically added to a C++ Windows Runtime Component with the friendly name from above and added as a Reference to the application Project.  However, the Root Namespace is still the same "org.alljoyn.example.Toaster" as defined by the interface.  Any classes that access these components need to have a "using" clause for the component's root namespace.

Generic Episode ImageTIP: Build the generated components now in order to benefit from IntelliSense. 

After you have created your AllJoyn App solution, you can always go back and modify the interfaces you are using. From the main menu bar, click "AllJoyn->Add/Remove Interfaces..." to launch the Interface manager.

Generic Episode Image

From here, you may click "Browse..." to add more XML files, or de-select existing Interface(s). De-selecting an Interface updates its Action to "Remove", and clicking the Ok button removes the associated Windows Runtime Component from your solution.

 Generic Episode Image

Looking at the Solution Explorer, the Windows Runtime Component has been removed.

Generic Episode Image

Next Steps

When implementing AllJoyn functionality, always be sure to include the "Windows.Devices.AllJoyn" namespace as well as the namespace from the interface you want to use.

Generic Episode Image

Implementing an AllJoyn Consumer

The code generated for the interfaces contains a watcher and consumer class used to find and then control a producer. Implement the watcher by creating a new AllJoynBusAttachment, initialize the watcher with that AllJoynBusAttachment, register for the watcher finding a producer (the "Added" event), then start the watcher.

Generic Episode Image

The ToasterWatcher_Added event triggers whenever the watcher finds a producer.  Use this event to register a consumer. Note that Visual Studio will generate the necessary shell method through the Quick Actions.

Generic Episode Image

Generating the method produces the following shell code:

Generic Episode Image

Filling in the shell code with the correct logic enables registering the consumer.

Generic Episode Image

Note that this event triggers every time the watcher discovers a producer.  If you expect to find multiple producers, keep a data structure of the consumer as there will be one consumer for each producer.

Register events for the various signals the producer will emit – property changed signals are direct members of the consumer class, but other signals are members of the Signals class (just as before, use the Quick Actions to generate the shell code for these events).

Generic Episode Image

Use the consumer object to read and write properties as well as call methods.

Generic Episode Image

Implementing an AllJoyn Producer

Implementing the Service

Producers implement a service that expose their interfaces.  To implement a producer, first create a class for its service.

Generic Episode Image

Add the namespace for the interface to the "using" statements, then inherit the shell interface generated for the service and use the Quick Action to implement the class.

Generic Episode Image

From here, the Quick Action will fill in the necessary components.

Generic Episode Image

All the necessary parts of the code are ready to function, but you still need to implement the actual logic for each method and property call.

Generic Episode Image

Taking the SetDarknessLevelAsync as an example, we use a task to create a success result.  In case of failure, return ToasterSetDarknessLevelResult.CreateFailureResult().

Generic Episode Image

Implement the rest of the method and property calls to finish the service.

Implementing the Producer

Creating the producer is straightforward – create a new AllJoynBusAttachment, initialize a producer with that AllJoynBusAttachment, initialize the newly created service for the producer, then start the producer.

Generic Episode Image

Since the service contains the majority of the logic, the main functionality left to implement is to send the signals for property changes and discrete signals for non-state events.  Implement these as necessary through method calls.

Generic Episode Image

More Information

If you've completed all of the instructions in this document correctly, you are ready to start writing AllJoyn code in your app.

For reference, please look to the AllJoyn Universal Windows Apps samples on the Microsoft Sample GitHub for AllJoyn Producers and AllJoyn Consumers.

For a detailed walkthrough of how to create an AllJoyn app, please watch the AllJoyn session 623 from //build 2015:

"AllJoyn:  Building Windows apps that discover, connect and interact with other devices and cloud services using AllJoyn".

Note that AllJoyn communication between two UWP apps on the same machine is disabled by Windows policy unless they have enabled a loopback exception, such as when being directly deployed from Visual Studio.  For detailed instructions on enabling loopback exemption, see here.

In addition, here are some resources that will help you get up to speed with AllJoyn and AllJoyn support in Windows 10:

Happy Coding,

Brian

The Discussion

  • User profile image
    ppatierno

    Hi Brian,

    nice article ... let me to add other two useful links for XML introspection :

    https://wiki.allseenalliance.org/irb/extended_introspection_xml

    https://allseenalliance.org/schemas/introspect.xsd

    Thanks,

    Paolo.

  • User profile image
    Brian​DRockwell

    Thanks for adding those links, Paolo.  We also just put out a follow-up blogpost detailing exactly how to create your own XML.

  • User profile image
    Andreas​Dietrich

    HI brian,

     

    great article - it is the extension I've waited for for not creating the alljoyn project on my own.

    Have I overseen something?

    In the case the introspection xml is changed, the source for creating the interface dlls are not rebuild automatically, and I haven't seen a way to rerun the alljoyncodegenerator.exe to get new interface dlls.

    In case you want to create several consumer/producers you have to extend/modify the .xml file several time to meet the requirements.
    but e, currently, it seems that the project has to be rebuild  in case the xml receives a modification.

    Even using menu item alljoyn - add/remove interface and reselect the same interface, will not recreate the interface files.

    if you have more than a single <interface name="a.b.c"> in  the .xml you may switch to another and switch back to the original one to get the new  interface dlls.

    any idea to make this more simple?

    regards
    andy

  • User profile image
    Brian​DRockwell

    Thanks for the questions, Andy.  If you have modified your XML and would like to re-generate the code, currently you will need to use the Add/Remove interfaces menu to take the "Remove" action (as shown in the blogpost).  This will remove the generated code from the project; re-launch the Add/Remove interfaces menu to Browse for your modified XML file, which will generate code with your new changes.

    Keep in mind that you may use the Browse menu to add several XML files.  Say you have three interfaces that you would like to use: com.contoso.Foo, com.contoso.Bar, and com.contoso.Baz.  These may all be in the same XML file "interfaces.xml" or separate files (e.g., foo.xml, bar.xml, and baz.xml).  Later, you could add a fourth, com.contoso.Qux, by creating a qux.xml file and adding it through the Add/Remove interfaces menu or by adding it to the interfaces.xml file and using the Browse button to re-select the interfaces.xml file.

    This is true even for "child" interfaces in a hierarchy; e.g., com.contoso.Foo.Norf.  The definition for com.contoso.Foo.Norf can be in its own norf.xml file or combined with the foo.xml file.  Regardless, AllJoyn Studio will generate a separate C++ project for each interface that you Add to the project, allowing you to modify, add, and remove interfaces in isolation.

    As a final note, remember to build the C++ projects after code generation to enable IntelliSense to function correctly.

  • User profile image
    Andreas​Dietrich

    Hi Brian,

    thanks for answering on how to update the code if the introspection format .xml changes.

    Do you know whether there will be project settings in the future for creating command line based application which can be run in the background (e.g. controlled by a service instance),
    or as a service itself.
    I would be interested in C++ and C# command line based applications for controlling a lot of test machines from a central point.

    thanks in advance
    andy

  • User profile image
    oniono

     

    Hi Brian,

    I felt your article is pretty helpful and impressive to me but  faced an error "The path is not a legal form" while adding interfaces with toaster.xml suggested in this article.

    How can I figure out this problem to move on to the next step?

  • User profile image
    Michel

    Hello Brian,

    I have a problem with signals and multiple producers and while searching for an solution i found this article and hope you can help me.

    I have two producers (that have one signal exposed) and my "watcher_added" event is fired twice. I have wrapped the consumer object in a object "device" and in this object i register to the signal event. The problem is that the same signal event is triggered for both producers.

    I can't figure out what the problem is and can't find an example on the internet with multiple producers and signals.

    Do you have any thoughts or do you know of there is an example somewhere?

    Regards,
    Michel


  • User profile image
    Brian​DRockwell

    @AndreasDietrich: - we built AllJoyn Studio for the creation of Universal Windows Apps.  Depending on the need, in the future we may expand on our code generation solution to include Classic Windows Apps.  We appreciate the feedback about this use-case!

    @oniono: we have been unable to reproduce this locally.  Could you describe your setup? (Windows version, VS version, XML description, area path, etc)

    @Michel: This is likely due to our use of multipoint sessions.  However, each object on the AllJoyn Bus presents a unique identifier, so you can always determine exactly which producer emitted a signal.  For multiple producers, using a Dictionary for your consumers might be beneficial:

    Dictionary<string, ToasterConsumer> toasterDictionary;

    Here the Key will be equal to the unique identifier for the producer.

    Then in the "Added" event:

    ToasterConsumer toasterConsumer = toasterJoinSessionResult.Consumer;
    toasterConsumer.DarknessLevelChanged += ToasterConsumer_DarknessLevelChanged;
    toasterConsumer.Signals.ToastBurntReceived += Signals_ToastBurntReceived;
    toasterDictionary.Add(args.UniqueName, toasterConsumer);
    

    Finally, in the signal callback use the following to filter to the consumer you want to update/use.

    toasterDictionary[args.MessageInfo.SenderUniqueName]

  • User profile image
    MichaelFery

    @oniono: I had the same behavior even when I tried to load a well formatted xml file. The problem was with Visual Studio not capable of accessing folder or create files. I tried to launch Visual Studio as administrator and could create my Windows Runtime Component correctly (and then came back to the safe mode). Does it fix your problem ?

     

    Thanks a lot Brian for these extension and tutorial. It's really helpfull.

  • User profile image
    jasperhb

    @BrianDRockwell I read your response to @Michel but it's still no clear to me how to do the following:

     I have a multiple devices that each have multiple produceres. For instance multiple thermostats that each have multiple produceres such as:

    1. Battery level

    2. Setpoint

    3. Current Temperature

    ...

    Each thermostat also have a Name, a Manufactorer name, a Node Id etc.

    How do I recreate this data structure in an App? 

     AllJoyn Explorer seems to do this just fine, but I'm struggling to replicate. In fact, I can't even find the name and manufactorer for each node.

    Any hint will be valued. Thanks.

  • User profile image
    MathieuC

    Hi Brian,

    Thanks for your article. I also assisted to your session during the Allseen summit.
    I bought two Lifx light bulbs for testing purposes and used your article to create a simple Universal App to control both lights.
    Everything went fine to control the first light, but If I modify the code to manage several lights, I get an 0x9040 error in return of the JoinSessionAsync method meaning ER_BUS_OBJ_ALREADY_EXISTS when I try to establish the session to the second Light. Is there something I miss?

    Regards,
    Mathieu

  • User profile image
    MarkWH1963

    Thanks for the article.

    How do you deal with strings that can contain a null value? In our scenario a null value is important, however I can't "emit a property changed" or "get" a value that is null.

            public IAsyncOperation<MCPCGetGameInfoResult> GetGameInfoAsync(AllJoynMessageInfo info)
            {
                Task<MCPCGetGameInfoResult> task = new Task<MCPCGetGameInfoResult>(() =>
                {
                    var result = MCPCGetGameInfoResult.CreateSuccessResult(Source.GameInfo);
                    return result;
                });
     
                task.Start();
                return task.AsAsyncOperation();
            }
     
    Thanks
    Mark
  • User profile image
    Berney Villers

    Are there any code generation templates or utilities available to generate the introspection XML from a class?

    This introspection XML feels redundant. Ideally, we could markup our classes with attributes or generate the XML from classes.

  • User profile image
    bvillersjr

    For the particular product I'm working on, and I suspect any product I may ever want to create using this technology, I need the flexibility to distribute a group of services across one or more PC's and devices.

    Ideally I would architect one UI that pulls together all of these services and a common code base that ran on all the devices. This works nicely except in the case of the "one" in "one or more devices".

    As people just get started with our product line I need a service running on the local PC and the UI to manage it. From what I read, I'm not permitted to develop such a solution with the exception of testing in which case I can enable a loopback exception.

    Why is Windows enforcing this policy of disallowing multiple portions of a distributed app to exist on the same PC?

    Is there some method of architecting such a solution that I've overlooked?

     

Add Your 2 Cents