How to cope with IoT Hub enrichment restrictions

As seen in my previous post, The IoT Hub routing feature supports message enrichment, both for IoT devices and IoT edge modules.

Using the routing message enrichments, each incoming message gets extra user properties based on either static values, device twin tags, or device twin desired properties.

Unfortunately, only ten enrichments can be added:

If you want to pass more values, this will not work for you.

It would be great if nested JSON properties would count as one.

Again, unfortunately, only simple types (string, decimal, boolean, date/time, etc.) are supported so this excludes nested JSON (complex types).

Below, a viable solution to overcome both restrictions, using Azure Stream Analytics, is presented.

Let’s see how this works out.

Doorgaan met het lezen van “How to cope with IoT Hub enrichment restrictions”

Decoding and parsing JSON using NewtonSoft

So you are working with JSON, you get a JSON message and you want to retrieve some values from it. How?

Let’s suppose you get something like (this is an example take from here):

"{\"port\":1,\"counter\":39,\"payload_raw\":\"KAA=\",\"payload_fields\":{\"errorCode\":0,\"numberOfCycles\":40},\"metadata\":{\"time\":\"2017-01-15T22:55:04.204820257Z\",\"frequency\":868.1,\"modulation\":\"LORA\",\"data_rate\":\"SF7BW125\",\"coding_rate\":\"4/5\",\"gateways\":[{\"gtw_id\":\"eui-b827ebffffc19ca8\",\"timestamp\":4113032806,\"time\":\"1754-08-30T22:43:41.128654848Z\",\"channel\":0,\"rssi\":-90,\"snr\":8,\"latitude\":51.46018,\"longitude\":5.61902,\"altitude\":10}]}}"

The backslashes, the escape characters, are only for C# to know which double quotes are part of the message. It’s not necessary but you can remove them and then you have:

{"port":1,"counter":39,"payload_raw":"KAA=","payload_fields":{"errorCode":0,"numberOfCycles":40},"metadata":{"time":"2017-01-15T22:55:04.204820257Z","frequency":868.1,"modulation":"LORA","data_rate":"SF7BW125","coding_rate":"4/5","gateways":[{"gtw_id":"eui-b827ebffffc19ca8","timestamp":4113032806,"time":"1754-08-30T22:43:41.128654848Z","channel":0,"rssi":-90,"snr":8,"latitude":51.46018,"longitude":5.61902,"altitude":10}]}}

Well, it’s always good to check the message is proper JSON. You can check it with a ‘tool’ like http://jsonlint.com/

jsonlint

This is a valid message, if we format the structure, it looks a lot better:

{
  "port": 1,
  "counter": 504,
  "payload_raw": "+QA=",
  "payload_fields": {
    "errorCode": 0,
    "numberOfCycles": 249
  },
  "metadata": {
    "time": "2017-01-10T23:31:06.087189682Z",
    "frequency": 868.1,
    "modulation": "LORA",
    "data_rate": "SF7BW125",
    "coding_rate": "4/5",
    "gateways": [{
      "gtw_id": "eui-b827ebffffc19ca8",
      "gtw_trusted": true,
      "timestamp": 3771642998,
      "time": "1754-08-30T22:43:41.128654848Z",
      "channel": 0,
      "rssi": -80,
      "snr": 9,
      "latitude": 51.46018,
      "longitude": 5.61902,
      "altitude": 10}]
  }
}

There are several ways to get values from this JSON message.

Doorgaan met het lezen van “Decoding and parsing JSON using NewtonSoft”

jsTreeview in ASP.NET MVC gevuld met Json action

Op dit moment heeft jQuery UI nog geen treeview control. Als alternatief zijn er enkele andere aanbieders van Treeview controls. In het verleden heb ik al eens bericht over de Telerik controls. Deze kan ik zonder meer aanbevelen maar helaas zijn de gratis extensies alleen voor open source projecten toe te passen.

Een andere treeview is de jsTreeview. Deze is heel aardig (ondersteunt ajax, de jQuery themes en bv. ook checkboxes). Helaas is de deocumentatie nogal fragmentarisch. Daarom geef ik hier een opstap hoe jsTreeview is toe te passen in ASP.NET MVC. We maken hierbij gebruik van Json actions om de kinderen op te vragen vanuit de controller.

Stap 1: Bestanden plaatsen

Haal eerst de laatste versie op vanaf http://www.jstree.com/ . Dit is de versie pre 1.0 fix 2.

Plaats in het ASP.NET MVC4 project de jquery.jstree.js in de \Scripts map.

Plaats d.gif (voor ie6 gebruik), d.png, styles.css en throbber.gif in \Scripts\themes\treeview map.

Stap 2: bundles aanpassen

Vul de bundles aan in de BundleConfig.cs:

bundles.Add(new StyleBundle("~/Scripts/themes/treeview").Include(
                         "~/Scripts/themes/treeview/style.css"));
bundles.Add(new ScriptBundle("~/bundles/jqueryplugins").Include(
                        [skip other plugins],
                        "~/Scripts/jquery.jstree.js"));

En deze bundles moeten op de _Layout.cshtml aangeroepen worden:

…
 @Styles.Render("~/Scripts/themes/treeview")
…
</head>
<body>
…
 @Scripts.Render("~/bundles/jquery", "~/bundles/jqueryui", "~/bundles/jqueryplugins")
…

Stap 3: HTML placeholder

Plaats een placeholder op de _Layout.cshtml

<div id="treeviewEdit"> Tree view placeholder</div>

Stap 4: JavaScript

Plaats daarna het volgende  script onder aan de _Layout.cshtml pagina  om te worden uitgevoerd als de pagina laadt (dit moet dus in een javascript blok):

$(function () {
    $("#treeviewEdit").jstree({
        "json_data": {
            "ajax": {
                "url": "/Home/FillEditTree",
                "data": function (n) {
                    return { id: n.attr ? n.attr("id") : 0 };
                }
            }
        },
        "themes": {
            dots: false,
            icons: true
        },
        "plugins": ["themes", "json_data"]

    });
});

Deze jQuery functie zal op de treeview div losgelaten worden om het treeview tekenen te forceren.

Stap 5: de (view) modellen

We praten straks in Json formaat met de treeview, maar we willen dit in typed classes samenstellen. Voeg dus de volgende viewmodellen toe:

jstreeview1

De code wordt dan:

public class JsTreeModel
{
    public JsTreeData data { get; set; }

    public JsTreeAttribute attr { get; set; }

    // "open" or "closed" or null (not available for ajax)
    public string state { get; set; }

    public List<JsTreeModel> children { get; set; }
}
public class JsTreeAttribute
{
    public string id { get; set; }
}
public class JsTreeData
{
    public string title { get; set; }

    public JsTreeDataAttribute attr { get; set; }

    // "folder"  or "/" for no icon thus file
    public string icon { get; set; }
}
public class JsTreeDataAttribute
{
    public string href { get; set; }
}

Laatste stap 6: Controller action

Voeg de volgende action toe aan de controller. Deze geeft ter demonstratie als antwoord altijd de opgevraagde node met daaronder twee kinderen. De eerste keer wordt node 0 opgevraagd. het eerste kind heeft zelf ook kinderen (niet meegestuurd in deze aanroep) en de tweede heeft geen kinderen:

public ActionResult FillEditTree(int id)
{
    // just to show some nodes
    var rootId = id + 2;

    var children = new List<JsTreeModel>();

    // first child containing one or more children
    children.Add(
        new JsTreeModel
            {
                attr = new JsTreeAttribute { id = (rootId + 1).ToString() },
                data = new JsTreeData
                        {
                            title = "Child " + (rootId + 1).ToString(),
                            attr =
                                new JsTreeDataAttribute
                                    {
                                        href =
                                            @"\home\edit\"
                                            + (rootId + 1).ToString()
                                    }
                        },
                state = "closed",
            });

    // second child containing NO children
    children.Add(
        new JsTreeModel
            {
                attr = new JsTreeAttribute { id = (rootId + 2).ToString() },
                data = new JsTreeData { icon = "/", title = "Child " + (rootId + 2).ToString(), },
            });

    // de ‘root’
    var requestedSubTree = new List<JsTreeModel>();

    requestedSubTree.Add(
            new JsTreeModel()
            {
                attr = new JsTreeAttribute { id = rootId.ToString() },
                state = "open",
                data =
                    new JsTreeData
                        {
                            title = "Title " + rootId.ToString(),
                            attr =
                                new JsTreeDataAttribute { href = @"\home\edit\23" }
                        },
                children = children
            });

    return Json(requestedSubTree, JsonRequestBehavior.AllowGet);
}

Dit geeft het volgende plaatje (na enkele keren kinderen opvragen):

jstreeview2

Er zijn een aantal zaken die opvallen:

  • De ‘root’ moet standaard de state geopend (“open”) krijgen
  • Je moet dus onderzoeken of de getoonde kinderen op hun beurt ook kinderen hebben
    • De kinderen moeten standaard de state te openen (“closed”) krijgen als deze op hun beurt kinderen hebben.
    • Een kind zonder kinderen moet geen state krijgen (dus null). Dan komt er ook geen ajax call mogelijkheid.

Bonus stap 7: jQuery Thema

Als laatste in het rijtej plugins kan “themeroller” toevoegen worden.  Deze kan je toepassen als alternatief voor thema:

$(function () {
    $("#treeviewEdit").jstree({
        [skip],
        "themes": {
            dots: false,
            icons: true
        },
        "plugins": ["json_data", "themeroller"]
    });
});

Dit geeft een aardige uitbreiding op het standaard aanwezige thema:

jstreeview3

Bonus stap 8: cookies

Je kunt ook plugin “cookies” toepassen. Dan wordt bij een F5 de selectie bewaard en niet opnieuw opgevraagd. Maar de laatste kan ook op de server in de sessie onthouden worden en bij de initiële aanroep (node id is 0) toegepast kunnen worden.

Conclusie 

Deze plugin is heel aardig om voor weinig geld en snel een treeview toe te passen in ASP.NET MVC.

Seeing anything you like? Google translate does not work out? Drop me a note and I will translate this post.