This article will focus on two parts of our application that provide some of the support functions that are needed across most application screens: the link manager, and the settings.

The Link manager

The link manager is created once in the application, and used by every view that needs it. It is in charge of handling direct communication with the controller board. In particular, it handles the “socket.io” connection to the server, and triggers javascript events whenever there is serial data incoming:

...
    this.processInput = function(data) {
        self.trigger('input', data);
        if (typeof data.ack != 'undefined' )
            self.trigger('ack', data.ack);
        if (typeof data.turnouts != 'undefined' ) {
            self.turnouts = data.turnouts;
            self.trigger('turnouts', data.turnouts);
        }
        self.lastInput = new Date().getTime();
    };
...

Besides the obvious advantage of centralising all communications in one location, the link manager also optimises communications: for instance, it caches some types of data that we know will never change on the controller, such as the number of accessories it supports. It also handles serial port management on the server side: opening, closing and status reporting. The server on its side sends only two kinds of messages: ‘serialEvent’, which is serial data, and ‘status’ which reports serial port connection status.

    // Initialization code:
    this.socket.on('serialEvent', this.processInput);
    this.socket.on('status', this.processStatus);
    // Initialize connexion status on the remote controller
    this.socket.emit('portstatus','');
    // Start a 3-seconds interval watchdog to listen for input:
    // if no input in the last 5 seconds, then request port status
    this.watchdog = setInterval(this.wdCall.bind(this), 5000);

The web socket itself is simply initialised at the beginning, it is so simple it is barely worth mentioning:

    this.socket = io.connect(); // (we connect on same host, we don't need a URL)

Socket.io then takes care of everything – heartbeat, establishing connection, and so on. Each time it gets a message, it triggers an event such as ‘serialEvent’ or ‘portstatus’, which is bound to a callback as shown above. And that’s it!

In order to provide a first layer of abstraction, the link manager also triggers higher level events, such as “ack” or “turnouts”, as well as calls to open ports, close ports and request status. The day the application supports multiple types of controllers, this will make it easier to implement those without touching the rest of the app.

Last, the link controller provides a “controllerCommand” object, which implements all the commands implemented in the hardware controller. Again, the day we want to support multiple controller types, we will just need to implement different types of controllerCommand and won’t need to change anything in the rest of the app. Since our current controller talks in JSON already, our job is very simple:

this.controllerCommand = {

        forward: function() {
            self.socket.emit('controllerCommand', '{"dir":"f"}');
        },
        backward: function() {
            self.socket.emit('controllerCommand', '{"dir":"b"}');
        },
        stop: function() {
            self.socket.emit('controllerCommand','{"dir":"s"}');
        },
        speed: function(val) {
            self.socket.emit('controllerCommand','{"speed":' + val + '}');
        },
        getPID: function(val) {
            self.socket.emit('controllerCommand','{"get": "pid"}');
        },
        setPID: function(kp,ki,kd,sample) {
            // The Arduino aJson library is sensitive to presence or
            // not of "." in floats...
            self.socket.emit('controllerCommand','{"pid": {"kp":'+
                             ((kp%1==0) ? kp+".0" : kp ) +',"ki":'+
                             ((ki%1==0) ? ki+".0" : ki ) +',"kd":'+
                             ((kd%1==0) ? kd+".0" : kd ) +',"sample":'+sample+'}}');
        },
...

So far, the application only manages one type of controller, so a bit of work will be required to support multiple types, but the bases are there already.

The settings object

The second topic in this article, is how we manage application state. The idea here is to make sure this state is preserved between sessions: if you run a train for a few minutes at some point, browse to another location and come back to the controller, you don’t want to have to reconfigure everything. And if you start a session on a browser, and then want to use your mobile phone, you also don’t want to have to reconfigure anything.

For this reason, I decided to create a “settings” object that is stored on the server. The use of Backbone and Mongoose on the backend makes this very simple.

Like I did for the link manager, a unique “settings” object is created at application startup, and passed to every view that needs it. Initialisation in main.js looks like this:

...
    initialize: function () {
        console.log("Initializing application");
        this.headerView = new HeaderView();
        $('.header').html(this.headerView.el);
        // Get our settings here, and
        // share them afterwards, rather than requesting it
        // everytime...
        this.settings = new Settings();
        // We need to be sure the settings are fetched before moving
        // further, so we add the Ajax option "async" below.
        this.settings.fetch({async:false});

        // Create our link manager: it is in charge of talking
        // to the server-side controller interface through a socket.io
        // web socket. It is passed to all views who need it.
        this.linkManager =  new linkManager();
    },
...

Then each view that needs either link manager or settings gets those in its options:

...
        locoList.fetch({success: function(){
            $("#content").html(new LocoListView({model: locoList, settings: self.settings, page: p}).el);
        }});
...

At this stage, the settings object only contains two entries: the currently selected layout, and the currently selected locomotive.

Conclusion

That’s it for this week’s – short – article on our train controller, next week I will move on to the definition of the user interface and what tools I used to make implementation as easy as possible. The article is now available.

Leave a Reply

Your email address will not be published. Required fields are marked *