Visualize Azure IoT Edge device routes as a flowchart in Asp.Net MVC

If you look at the routes page in Azure IoT Edge configuration wizard, what do you prefer?

The current notation:

Or do you prefer a flow chart like this:

The routes in Azure IoT edge are a clever solution to describe how messages from one module are sent to another. But the JSON notation can become less readable once you add more (up to twenty) modules. That could end up eg. nineteen routes or more!

Just as an experiment I was thinking about how the ease the experience using a graphical interface.

I prefer the second solution, probably just like you.

So let’s look at how you can create the same experience with your routes of your IoT Edge device.

Doorgaan met het lezen van “Visualize Azure IoT Edge device routes as a flowchart in Asp.Net MVC”

Create your own local Azure IoT Edge dashboard

Earlier this year, when Azure IoT Edge was still in Public Preview, I wrote a couple of blogs about Visualizing Azure IoT Edge using local dashboard.

Back then, I had to do some magic with both a C# IoT Edge module, a custom NodeJS docker container, and a Docker network to get it running.

Since then, a lot has changed. Microsoft already released a ton of new features. a And there is still more to come regarding the Azure IoT platform.

But that awkward local dashboard solution was nagging me. A few months ago, Microsoft introduced a NodeJS module as a first-class citizen for IoT Edge modules.

So it was time to pick up the gauntlet and use NodeJS for this awesome local IoT Edge dashboard:

#tldr;  If you like to dig into the code, zip it, clone it, extend it or even make a pull request, I made this project open source. If you only want to use it the easy-going way, pull it from docker eg. ‘svelde/localdashboard:1.0.1-amd64’.

At this moment, only Linux containers are supported. It is tested both on Windows and Ubuntu as host OS.

Interested in this module? Let’s see how you can use it.

Doorgaan met het lezen van “Create your own local Azure IoT Edge dashboard”

Visualizing Azure IoT Edge using local dashboard

In my last series of blogs, we first looked at how to deploy a non-IoT Edge module using Azure IoT Edge.

For this example, I used a NodeJS website running SocketIO. It was possible to access this website with a default SocketIO chat application.

After that, we looked at how to add some charts in the HTML page offered by the NodeJS server.

Let’s see how we can combine this all into one solution. Let’s build a local for raw Azure IoT Edge telemetry.

Doorgaan met het lezen van “Visualizing Azure IoT Edge using local dashboard”

Show telemetry in NodeJS using SocketIO and HighCharts

In my previous blog, I showed you how to host NodeJS in a Docker Image.

Today we will learn how we show telemetry in NodeJS. The message will arrive as a string on an HTML page using SocketIO and we will put it on a chart from HighCharts.

This is a great example of how we can represent raw data in something useful, something end user will understand.

We will extend our previous example. In that example, we were leaning on NodeJS and we have the Express web framework running to show an HTML page. We added SocketIO so users of the index.html can exchange messages.

But what if the incoming message is

{
  "machine":{
    "temperature":21.810507137356563
  },
  "timeCreated":"2018-01-06T23:55:56.1058921Z"
}

a JSON message on a single line? And it is shown as a string?

Nice, but this is only good for nerds like me.

What if we could represent it as:

a chart?

This a much better solution, isn’t it?

Doorgaan met het lezen van “Show telemetry in NodeJS using SocketIO and HighCharts”

Deploying a NodeJS server with SocketIO to Docker using Azure IoT Edge

The current Azure IoT Edge public preview uses Docker to deploy logic from the cloud into local gateways. It’s currently featuring:

  • C# modules written in .Net standard
  • Python modules
  • Azure Function built on your machine
  • Azure Stream Analytics jobs built and deployed in the cloud
  • Azure Machine Learning

We can expect Microsoft will support other types of modules soon as they have proven with other recent projects. An Azure Cognitive Services module is a good example, it’s put in every IoT Edge presentation.

The IoT Edge portal makes it possible to deploy modules which are available in private or public image repositories.

Could it be possible to build and deploy images to the gateway which are not specifically designed for IoT Edge?

It turns out, it is possible.

Let’s deploy a NodeJS server which serves SocketIO.

Doorgaan met het lezen van “Deploying a NodeJS server with SocketIO to Docker using Azure IoT Edge”

User Defined Function in Stream Analytics

Azure Stream Analytics provides a great solution for temporal queries of streams of data. The query language is pretty simple, especially if you have a background in SQL queries.

The list of built-in functions is a long list, ranging from aggregation, analytics, geospatial, records, scalars to the recently introduced anomaly detection.

But what if you want to write your own functions?

Stream Analytics supports three types of custom functions:

  1. user-defined functions (UDF) written in Javascript
  2. user-defined aggregates (UDA) written in Javascript
  3. Machine learning endpoint disguised as functions

In this blog, I will show how easy it is to write and use your own custom logic in a Stream Analytics job. We will look at the user-defined functions.

Doorgaan met het lezen van “User Defined Function in Stream Analytics”

Using Node.js to access Azure IoT Hub

By now the Cloud strategy of Microsoft must be very clear. It’s not about Windows, it’s not about Office, it’s not about Microsoft programming languages even. Microsoft is opening up towards all devices, operating systems, programming platforms, etc. Everybody is welcome in the cloud.

Although this is going on for quite some time, it is still a surprise for quite a few non-Microsoft developers. So today I decided to program against the Azure IoT Hub using another language, just to check out their experience.

Microsoft supports multiple programming languages, there are multiple SDK’s available:

If your favorite language is not listed here, but it talks MQTT, AMQP or HTTP, chances a big you can build your own SDK.

Today I picked up Node.js because I know a bit of javascript 🙂 Let’s check out what Javascript developers have to do to connect to an Azure IoT Hub.

Doorgaan met het lezen van “Using Node.js to access Azure IoT Hub”

Introduction to Tessel combined with Azure EventHub

Last month, during the Azure Bootcamp, I was introduced in programming the Tessel.

This device is a fairly cheap IoT programming board and for me, the most remarkable feature was that it was running on JavaScript.

WP_20150425_004

Yes, everybody with the basic understanding of JavaScript can start programming the Tessel.

My main goal for  that day was putting some effort in Azure EventHubs but first I did the ‘hello world’ of the Tessel called Blinky.js

// Import the interface to Tessel hardware
var tessel = require('tessel');
// Set the led pins as outputs with initial states
// Truthy initial state sets the pin high
// Falsy sets it low.
var led1 = tessel.led[0].output(1);
var led2 = tessel.led[1].output(0);
setInterval(function () {
  console.log("I'm blinking! (Press CTRL + C to stop)");
  // Toggle the led states
  led1.toggle();
  led2.toggle();
}, 100);

It was not that intuitive to get this working (I had to install node.js etc) but the convenience of JavaScript is very nice. So just follow the scripted examples.

Then I tried out and combined two other hands-on labs:

These HOLs helped me to explore how I can send messages to the EventHub. And I was surprised how easy it was to send JSON data into the EventHub. And now I can imagine how the EventHub is just a VERY BIG bucket in which Azure MessageBus messages are off-loaded. And I can image how the data can be investigated by StreamInsight for a couple of days before the data gets steel and is deleted.

So finally, just for fun, I came up with this continuous probing and sending:

var https = require('https');
var crypto = require('crypto');
var climatelib = require('climate-si7020');
var tessel = require('tessel');
var climate = climatelib.use(tessel.port['A']);

climate.on('ready', function () {
  console.log('Connected to si7005');
});

climate.on('error', function(err) {
  console.log('error connecting module', err);
});

// Event Hubs parameters
console.log('----------------------------------------------------------------------');
console.log('Please ensure that you have modified the values for: namespace, hubname, partitionKey, eventHubAccessKeyName');
console.log('Please ensure that you have created a Shared Access Signature Token and you are using it in the code')
console.log('----------------------------------------------------------------------');
console.log('');

var namespace = 'SvdvEh-ns';
var hubname ='svdveh';
var deviceName = 'mytessel';
var eventHubAccessKeyName = 'SendRights';
var createdSAS = 'SharedAccessSignature sr=[...]';

console.log('Namespace: ' + namespace);
console.log('hubname: ' + hubname);
console.log('publisher: ' + deviceName);
console.log('eventHubAccessKeyName: ' + eventHubAccessKeyName);
console.log('SAS Token: ' + createdSAS);
console.log('----------------------------------------------------------------------');
console.log('');

var SendItem = function(payloadToSend){
  // Send the request to the Event Hub
  var options = {
    hostname: namespace + '.servicebus.windows.net',
    port: 443,
    path: '/' + hubname + '/publishers/' + deviceName + '/messages',
    method: 'POST',
    headers: {
      'Authorization': createdSAS,
      'Content-Length': payloadToSend.length,
      'Content-Type': 'application/atom+xml;type=entry;charset=utf-8'
    }
  };

  var req = https.request(options, function(res) {
    console.log('----------------------------------------------------------------------');
    console.log("statusCode: ", res.statusCode);
    console.log('----------------------------------------------------------------------');
    console.log('');
    res.on('data', function(d) {
      process.stdout.write(d);
    });
  });

  req.on('error', function(e) {
    console.log('error');
    console.error(e);
  });

  req.write(payloadToSend);
  req.end();
}

setImmediate(function loop () {
  climate.readTemperature('f', function (err, temp) {
    climate.readHumidity(function (err, humid) {
      console.log('Degrees:', temp.toFixed(4) + 'F', 'Humidity:', humid.toFixed(4) + '%RH');
      var payload = '{\"Temperature\":\"'+temp.toFixed(4)+'\",\"Humidity\":\"'+humid.toFixed(4)+'\"}';
      SendItem(payload);
      setTimeout(loop, 5000);
    });
  });
});

This code was enough to see how the EventHub was filled with data. I had no time to check out StreamInsight. But it seems very easy to write some SQL-like code to get the right information out of the hubs.

The biggest problem was the administration of all the namespaces, keys, connection strings and secrets of both the MessageBus and the EventHub. Sometimes I had to cut/copy/paste parts of connection strings but that was not that intuitive.

And the Tessel? It was real fun to play with. And the complete humidity/temperature sensor was easy to plug in (had to fix the code because it was referencing another module type). But somehow I missed the spider web of wires and components and the accidental reboots of the RaspberryPi 🙂

A special mention goes to the free service bus explorer. It was very exciting to see messages coming in live. Very useful for debugging too.

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.

Unittests voor MVC4 model validaties

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

Dit is een vervolg op mijn eerdere blog: Custom client side validaties in ASP.NET MVC4

Als men vroeger (..) een MVC2 voorbeeld applicatie liet genereren dan kreeg men daar ook enkele custom validaties bij. Zo was er een validaties op het invoeren van tweemaal eenzelfde wachtwoord bij registratie. Dit voorbeeld werkte op class niveau om beide properties te vergelijken.

Een subtiele verbetering met de komst van MVC3 was dat dit opeens op property niveau ingesteld kon worden. Hierdoor was het mogelijk om de tekst van de validatie naast het tweede wachwoord veld te tonen.

Helaas wordt deze custom validatie niet meer gegenereerd maar is deze in het ASP.NET MVC framework als de CompareAttribute opgenomen. Gelukkig is ASP.NET MVC tegenwoordig open source en kan de broncode gedownload worden van http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/3007f73f8cb2 (adres kan veranderen).

Door hier eens in te duiken heb ik een vergelijkbare validatie gebouwd. Deze controleert ook twee velden en wel of deze in de juiste volgorde gevuld zijn. Het tweede veld mag niet gevuld worden als de eerste nog niet gevuld is.

valunit1

De client side code is als volgt:


[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CompareSequenceAttribute : ValidationAttribute, IClientValidatable
{
  private const string DefaultErrorMessage = "Fields {0} and {1} must be filled in the right sequence.";

  public CompareSequenceAttribute(string otherProperty)
    : base(DefaultErrorMessage)
  {
    if (otherProperty == null)
    {
      throw new ArgumentNullException("otherProperty");
    }

    OtherProperty = otherProperty;
  }

  public string OtherProperty { get; private set; }

  public string OtherPropertyDisplayName { get; internal set; }

  public override string FormatErrorMessage(string name)
  {
    return String.Format(CultureInfo.CurrentCulture, ErrorMessageString,
              name, OtherPropertyDisplayName ?? OtherProperty);
  }

  protected override ValidationResult IsValid(object value,
                         ValidationContext validationContext)
  {
    PropertyInfo otherPropertyInfo =
              validationContext.ObjectType.GetProperty(OtherProperty);

    if (otherPropertyInfo == null)
    {
      return new ValidationResult(String.Format(CultureInfo.CurrentCulture,
                     "Unknown {0} to compare with", OtherProperty));
    }

    object otherPropertyValue = otherPropertyInfo.GetValue(
                            validationContext.ObjectInstance, null);

    if (!string.IsNullOrWhiteSpace(value as string)
         && (string.IsNullOrWhiteSpace(otherPropertyValue as string)))
    {
      if (OtherPropertyDisplayName == null)
      {
        OtherPropertyDisplayName = ModelMetadataProviders.Current.
             GetMetadataForProperty(() => validationContext.ObjectInstance,
                  validationContext.ObjectType, OtherProperty).GetDisplayName();
      }

      return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }

    return null;
  }

  public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
                    ModelMetadata metadata, ControllerContext context)
  {
    if (metadata.ContainerType != null)
    {
      if (OtherPropertyDisplayName == null)
      {
        OtherPropertyDisplayName = ModelMetadataProviders.Current.
                 GetMetadataForProperty(() => metadata.Model,
                         metadata.ContainerType, OtherProperty).GetDisplayName();
      }
    }

    var rule = new ModelClientValidationRule()
    {
      ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
      ValidationType = "comparesequence",
    };

    rule.ValidationParameters.Add("other", OtherProperty);

    yield return rule;
  }
}

De bijbehorende JavaScript zier er dan zo uit:

// validation name must be equal to the ValidationType in the ModelClientValidationRule

$(function () {
  jQuery.validator.addMethod('compareSequence', function (value, element, param) {
    var other = param.other;
    var otherInput = $(element.form).find(":input[id='" + other + "']")[0];

    if (value != null
      && value.length > 0
      && (otherInput.value == null || otherInput.value == '')) {
        return false;
    }

    return true;
  });

  jQuery.validator.unobtrusive.adapters.add(
               'comparesequence', ['other'], function (options) {
    var params = {
      other: options.params.other
    };

    options.rules['compareSequence'] = params;

    if (options.message) {
      options.messages['compareSequence'] = options.message;
    }
  });
} (jQuery));

Maar kunnen we deze code ook unittesten? Ja, en best eenvoudig!

De client side script unittesten is hier dan niet mee ondersteund maar de server side validatie test zal ik tonen. Overigens, heeft iemand ervaring met JavaScript unittest frameworks?

Nu is het zo dat een Validatie class een publieke IsValid methode heeft.  Helaas is het simpel aanroepen van die IsValid() methode niet voldoende, Deze validatie heeft namelijk een ValidationContext nodig om bij de waarde van de andere property te komen en die context is lastig te injecteren.

En een viewmodel met een attribuut er op mocken is ook lastig. Dus ik heb maar voor stubs gekozen.De stub ziet er dan zo uit:



public class ViewModelToValidateCompareSequenceStub
{
  public string FirstPropertyName { get; set; }

  [CompareSequence("FirstPropertyName")]
  public string SecondPropertyName { get; set; }
}

En hiermee kan ik prima unittesten. Hier is een test waarbij de validatie goed gaat:

[TestMethod]
public void CompareSequenceFirstFilledSecondFilledReturnsTrue()
{
  //// ARRANGE

  var model = new ViewModelToValidateCompareSequenceStub {
        FirstPropertyName = "Bla", SecondPropertyName = "Bla" };

  //// ACT

  var actual = Validator.TryValidateObject(model,
            new ValidationContext(model, null, null), null, true);

  //// ASSERT

  Assert.IsTrue(actual);
}

En hier is een test waarbij het valideren fout gaat en dus een false teruggeeft:

[TestMethod]
public void CompareSequenceFirstNullSecondFilledReturnsFalse()
{
  //// ARRANGE

  var model = new ViewModelToValidateCompareSequenceStub {
                                   SecondPropertyName = "Bla" };
  var results = new List<ValidationResult>();

  //// ACT

  var actual = Validator.TryValidateObject(model,
         new ValidationContext(model, null, null), results, true);

  //// ASSERT

  Assert.IsFalse(actual);
  Assert.IsNotNull(results);
  Assert.AreEqual(1, results.Count);
  Assert.AreEqual(
    "Fields SecondPropertyName and FirstPropertyName must be filled in the right sequence.",
                             results[0].ErrorMessage);
}

Zoals te zien is, test ik ook op de specifieke foutmeldingen. Ik kan bv. ook een melding krijgen dat de opgegeven property niet bestaat. En er kunnen zelfs meerdere validaties gefaald hebben.

Conclusie

Het unittesten van validaties op viewmodellen is eenvoudig als men gebruik maakt van stubs. Enige nadeel is dat er geen controle is over de waarden die op de attributen ingesteld worden. Er moet dus voor andere varianten aparte stubs ingevuld worden.