Saturday, October 15, 2016

MQTT - Paho AWS IOT Websockets example using Typescript, Javascript and C# and DotNet Core:


Sources:
https://eclipse.org/paho/clients/js/

Using the mqttSample, written in Javascript.
https://github.com/awslabs/aws-iot-examples

My GitHub Repository containing the code
https://github.com/julfeng/MqttTestApp

The location where the typescript conversion was kept below:




I'm writing an app to display device data as a stream to graph on a dashboard. I looked for an example for typescript and there wasn't any to be found as of yet. So, I converted the MQTT sample above (Item #2) to serve my purposes. I certainly hope someone besides me finds this useful.

Some of the problems I had while converting the app to typescript:
  1. The main component would not redraw when new list items were added to the log. to solve this problem, I added ApplicationRef and called the tick method.
  2. I used a javascript library for mqtt. In order to use this in Javascript you must declare a variable with the same name as the library object you are using as any. Typescript will magically recognize this variable as the imported type.
  3. To use the mqtt libraries, you must put script tags in your home page to load them.
  4. callbacks in the javascript library don't recognize the "this" pointer of the typescript class. You must call bind(this) after each callback method implemented in your class that is being called by the javascript library.


1. The main Component for the app is listed below:

import { Component, OnInit, OnDestroy, ApplicationRef } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import {LogService} from '../messages/logService';
import {RcvMessage} from '../messages/rcvMessage';
import {ConnectOptions} from '../iOT/connectOptions';
import {ClientControllerCache} from '../iOT/clientControllerCache';
import {ClientController} from '../iOT/clientController';
import {MQTTClient} from '../iOT/mqttClient';
@Component({
    moduleId: module.id,
    templateUrl: 'aws.component.html'
})
export class AWSComponent implements OnInit, OnDestroy {
    pageTitle: string = 'AWS Test App';
    endpoint: string;
    regionName: string;
    clientId: string;
    accessKey: string;
    secretKey: string;
    clients: ClientControllerCache;
    logs: LogService;
    connectOptions: ConnectOptions;
    constructor(private route: ActivatedRoute,
        private router: Router,
        private appRef: ApplicationRef) {
        
        
        this.endpoint = 'a327xxxxxxxxxxxxxx.iot.us-east-1.amazonaws.com';//substitute your endpoint you made in the AWS Console.
        this.regionName = 'us-east-1';
        this.accessKey = '';
        this.secretKey = '';
        this.clientId = 'AnyClientName';

    }

    ngOnInit(): void {
        this.logs = new LogService();
        this.logs.onTick = () => { this.appRef.tick() };
        //this.logs.log('hello');
        this.connectOptions = new ConnectOptions(this.endpoint, this.regionName, this.accessKey, this.secretKey, this.clientId);
        this.clients = new ClientControllerCache(this.logs);
    }

    ngOnDestroy() {

    }

    subscribe(clientController: ClientController): void {
        if (clientController)
            clientController.subscribe();
    }
    
    createClient(): void {
        var client = this.clients.getClient(this.connectOptions);
        if (!client.connected) {
            client.connect();
            
        }
        //this.appRef.tick();
        //alert('client created' + this.clientId);
    }

    removeClient(clientCtr) : void{
        this.clients.removeClient(clientCtr);
    }

    clearLog(): void {
        this.logs.clear();
    }

    onBack(): void {
        this.router.navigate(['/home']);
    }
}

2 and 3. Using a javascript library in typescript:

import {ConnectOptions} from './connectOptions';
import {SigV4Utils} from './sigV4Utils';
declare var Paho: any; // Magic
declare var moment: any;
export class MQTTClient {
...
}

Also in the /Views/Home/Index.html (the main page) I have script tags for the javascript libraries.
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
    <script src="node_modules/core-js/client/shim.min.js"></script>
    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/reflect-metadata/Reflect.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>
    <script src="~/lib/moment/min/moment.min.js"></script>
    <script src="~/lib/paho-mqtt-js/mqttws31.js"></script>
    <script src="~/lib/cryptojslib/rollups/sha256.js"></script>
    <script src="~/lib/cryptojslib/rollups/hmac-sha256.js"></script>

4. Callbacks used in the javascript that are defined in typescript class:
import {LogService} from '../messages/logService';
import {MQTTClient } from './mqttClient';
import {RcvMessage} from '../messages/rcvMessage';
export class ClientController {
    client: MQTTClient;
    topicName: string;
    message: string;
    logService: LogService;
    msgs: RcvMessage[];
    id: string;
    constructor(client: MQTTClient, logs: LogService) {
        this.client = client;
        this.msgs = [];

        this.topicName = 'DashDemoUnit/Data';
        this.logService = logs;

        this.client.on('connectionLost', this.onConnectionLost.bind(this));
        this.client.on('messageArrived', this.onMessageArrived.bind(this));
        this.client.on('connected', this.onConnected.bind(this));
        this.client.on('subscribeFailed', this.onSubscribeFailed.bind(this));
        this.client.on('subscribeSucess', this.onSubscribeSuccess.bind(this));
        this.client.on('publishFailed', this.onPublishFailed.bind(this));
....
    }
Conclusion: Please feel free to browse the app on git hub and play with it. Navigate to the tab AWS Test and try it out. you will need an account on AWS with the IOT setup.

Tuesday, March 15, 2016



Had a busy week this week so far, my fun angel co-pilot flight to Central Illinois was cancelled due to weather, but, instead, I rehearsed for Easter on Sunday for a few hours, I play violin in the orchestra. 

Went to JJ Keller yesterday for Technology Days, where the whole team of developers come and meet to discuss current new technologies, and self-improvement tips. We had Raj, from Microsoft, come and explain some of Azure, cloud stuff (which was very interesting to me). It was exhilarating to talk to him some about the new DocumentDb storage for JSON data, (the Microsoft version of MongoDb).

Wednesday, November 4, 2015

MVC 6 and Dependency Injection on object with Parameters:

Sometimes things aren't obvious like this, which I spent a good portion of the day on.

In Startup.cs in method ConfigureServices(), 


   
            services.AddMvc();
            

            // Uncomment the following line to add Web API services which makes it easier to port Web API 2 controllers.
            // You will also need to add the Microsoft.AspNet.Mvc.WebApiCompatShim package to the 'dependencies' section of project.json.
            // services.AddWebApiConventions();

            // Register application services.
            services.AddTransient();
            services.AddTransient();
#if DNX451
           services.AddSingleton(provider => {
                var endPt = Configuration["Data:DocumentDbConnection:EndpointUrl"];
                var authKey = Configuration["Data:DocumentDbConnection:RWAuthKey"];
                var dbName = Configuration["Data:JobApp:DatabaseName"];
                var collName = Configuration["Data:JobApp:CollectionName"];
                return new JobAppDb(authKey, endPt, dbName, collName);
            }); 
#endif

Sunday, November 1, 2015

11/2015

After Struggling a bit to find documentation on how to create a Repository for DocumentDb in Azure for MVC 6, I decided to publish what I created last night. I hope someone finds this helpful.

-Jonathan Ulfeng

Basic DocumentDb class for Azure, MVC 6:

1. Create a Base Class for the document repository:

 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Threading.Tasks;  
 using Newtonsoft.Json;  
 using JobApp33.Util;  
 #if DNX451  
 using Microsoft.Azure.Documents.Client;  
 using Microsoft.Azure.Documents.Linq;  
 using Microsoft.Azure.Documents;  
 using JobApp33.Models;  
 #endif  
 namespace JobApp33.Db  
 {  
   public abstract class DocumentDb  
   {  
     protected string DatabaseName { get; set; }  
     protected string CollectionName { get; set; }  
     protected string EndpointUrl { get; set; }  
     protected string AuthorizationKey { get; set; }  
     protected string CollectionLink => string.Format("dbs/{0}/colls/{1}", DatabaseName, CollectionName);  
     protected string DatabaseLink => string.Format("dbs/{0}", DatabaseName);  
     public DocumentDb()  
     {  
     }  
 #if DNX451  
     protected DocumentClient _client;  
     protected DocumentClient Client  
     {  
       get  
       {  
         if (_client == null)  
           _client = GetClient();  
         return _client;  
       }  
     }  
     protected Database _database;  
     protected Database Database  
     {  
       get  
       {  
         if (_database == null)  
         {  
           _database = ReadOrCreateDatabase();  
         }  
         return _database;  
       }  
     }  
     protected DocumentCollection _collection;  
     protected DocumentCollection Collection  
     {  
       get  
       {  
         if (_collection == null)  
           _collection = ReadOrCreateCollection();  
         return _collection;  
       }  
     }  
     protected DocumentCollection ReadOrCreateCollection()  
     {  
       DocumentCollection col = Client.CreateDocumentCollectionQuery(Database.SelfLink)  
                  .Where(c => c.Id == CollectionName)  
                  .AsEnumerable()  
                  .FirstOrDefault();  
       if (col == null)  
       {  
         col = Client.CreateDocumentCollectionAsync(DatabaseLink, new DocumentCollection { Id = CollectionName }).Result;  
       }  
       return col;  
     }  
     protected Database ReadOrCreateDatabase()  
     {  
       Database db = Client.CreateDatabaseQuery()  
               .Where(d => d.Id == DatabaseName)  
               .AsEnumerable()  
               .FirstOrDefault();  
       if (db == null)  
       {  
         db = Client.CreateDatabaseAsync(new Database { Id = DatabaseName }).Result;  
       }  
       return db;  
     }  
     //private async Task<DocumentClient> GetClientAsync() => new DocumentClient(new Uri(config.EndpointUrl), config.AuthorizationKey);  
     protected DocumentClient GetClient() => new DocumentClient(new Uri(EndpointUrl), AuthorizationKey);  
     #region "CRUD Methods"  
     public async Task<Document> CreateItemAsync<T>(T item) where T : class  
     {  
       return await Client.CreateDocumentAsync(Collection.SelfLink, item);  
     }  
     public async Task<Document> UpsertItemAsync<T>(T item) where T : class  
     {  
       return await Client.UpsertDocumentAsync(Collection.SelfLink, item);  
     }  
     public async Task DeleteItemAsync<T>(string id) where T : class  
     {  
       foreach (Document document in Client.CreateDocumentQuery(  
           CollectionLink,  
           new SqlQuerySpec(  
             string.Format("SELECT * FROM {0} col WHERE col.id = @id", CollectionName),  
             new SqlParameterCollection(new[] { new SqlParameter { Name = "@id", Value = id } }))))  
         {   
           // Optionally, cast to CrawlResult using a dynamic cast  
           var result = (T)(dynamic)document;  
           await Client.DeleteDocumentAsync(document.SelfLink);  
         }  
     }  
     protected async Task<T> GetDocument<T>(string id) where T : class  
     {  
       // Query the documents using DocumentDB SQL.  
       IQueryable<Document> jobApps = from f in Client.CreateDocumentQuery(CollectionLink) where f.Id == id select f;  
       Document d = jobApps.AsEnumerable().FirstOrDefault();  
       T deserializedJobApp = JsonConvert.DeserializeObject<T>(d.ToString());  
       return deserializedJobApp;  
     }  
     #endregion  
 #endif  
   }  
 }  

2. Override that class to handle your poco Objects:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JobApp33.Util;
using Microsoft.Framework.OptionsModel;
using JobApp33.Models;

namespace JobApp33.Db
{

    public class JobAppDb : DocumentDb
    {
        public JobAppDb(IOptions settings) : base()
        {
            this.AuthorizationKey = settings.Options.AuthorizationKey;
            this.EndpointUrl = settings.Options.EndpointUrl;
            this.DatabaseName = settings.Options.JobAppDatabase;
            this.CollectionName = settings.Options.JobAppCollection;
        }
#if DNX451
        public JobApp GetJobApp(string id)
        {
            JobApp jobApp = null;
            Task.Run(async () =>
            {
                jobApp = await this.GetDocument(id);
            }).GetAwaiter().GetResult();
            return jobApp;
        }

        public Microsoft.Azure.Documents.Document UpsertJobApp(JobApp item)
        {
            Microsoft.Azure.Documents.Document doc = null;
            if (item != null)
            { 
                Task.Run(async () =>
                {
                    doc = await this.UpsertItemAsync(item);
                }).GetAwaiter().GetResult();
            }
            return doc;
        }

        public void DeleteJobApp(string id)
        {
            if (!string.IsNullOrEmpty(id))
            {
                Task.Run(async () =>
                {
                    await this.DeleteItemAsync(id);
                }).GetAwaiter().GetResult();
            }

        }

#endif
    }
}

Wednesday, May 13, 2015

C# Using JSON in Web.Config (or any config) file for static object store

This is a short article on how to access static data using JSON in a config file. Hope you find it useful in your project.

I'm going to show you how I use static data to store things like commands, or anything that remains unchanged. This project is based on the Automotive CAN Bus, where there are many messages you can query, thousands, actually. By adding the ones you're interested in, into a static file, you can chose which messages you want to display to the user.

1. Create Your Static JSON Data in your added config file, this one is called CanReceive.Config.








2. Add reference to the added config file to your main config file. Here, Under configSections, section add value for canReceive and then add a section called canReceive with the filename of your configSource.




3. Create a POCO class to represent your JSON Data.


4. Create a Static Config Class to read the data from the config file as a JSON String. CanReceive is the one we are interested in here.
















5. Create a Dictionary Class to store your serialized JSON objects and an ID to get them.




















5. Use your dictionary in Code by calling the methods:

A. CanReceiveSingleton.Instance.GetMapByIdx(0);



There it is, I find this useful in a number of scenarios where I need a way to access a list of data for further processing.

Jonathan