Skip to main content

Bullwinkle

Deprecated

This library is deprecated and will not be updated. Please update your code to use our revised and updated version of this library, MessageManager.

We recommend that you use MessageManager for all new projects because:

  • It is optimized for system timer utilization
  • It is optimized for traffic when sending, acknowledging and responding to messages
  • It is aware of its own and its partner’s connectivity status
  • It provides a richer and more flexible API to control the message workflow

Bullwinkle is an easy to use framework for asynchronous agent and device communication. The Bullwinkle library consists of two classes:

  • Bullwinkle — The core application, used to add/remove handlers and send messages.
    • Bullwinkle.send — Sends a message to the partner application.
    • Bullwinkle.on — Adds a message listener for the specified messageName.
      • message — The message table passed into .on handlers.
      • reply — A method passed into .on handlers that is used to reply to the message.
    • Bullwinkle.remove — Removes a message listener for the specified messageName.
  • Bullwinkle.Package — A packaged message with event handlers.
    • Package.onSuccess — Adds a handler that will be invoked if the message is successfully delivered and acknowledged.
    • Package.onReply — Adds a handler that will be invoked if the message is replied to.
    • Package.onFail — Adds an onFail handler that will be invoked if the send failed.
      • retry — A method passed into .onFail handlers that is used to retry sending the message.

You can view the library’s source code on GitHub. Click here to see information on the available versions of this library.

To add this library to your project, add #require "Bullwinkle.class.nut:2.3.2" to the top of your device and agent code.

Note You must #require and instantiate Bullwinkle in both the agent and device code.

Bullwinkle Usage

Constructor: Bullwinkle([options])

Calling the Bullwinkle constructor creates a new Bullwinkle application. An optional options table can be passed into the constructor to override default behaviors.

options

A table containing any of the following keys may be passed into the Bullwinkle constructor to modify the default behavior:

Key Data Type Default Value Description
messageTimeout Integer 10 Changes the default timeout required before a message is considered failed.
retryTimeout Integer 60 Changes the default timeout parameter passed to the retry method.
maxRetries Integer 0 Changes the default number of times the retry method will function. After this number the retry method will do nothing. If set to 0 there is no limit to the number of retries.
autoRetry Boolean false If set to true, Bullwinkle will automatically continue to retry sending a message until maxRetries has been reached when no onFail is supplied. Please note if maxRetries is set to 0, autoRetry will have no limit to the number of times it will retry.

Examples

// Initialize using default settings
bull <- Bullwinkle();
options <- { "messageTimeout": 5,   // If there is no response from a message in 5 seconds,
                                    // consider it failed
             "retryTimeout": 30,    // Calling package.retry() with no parameter will retry
                                    // in 30 seconds
             "maxRetries": 10,      // Limit to the number of retries to 10
             "autoRetry": true      // Automatically retry 10 times
           }
// Initialize using custom settings
bull <- Bullwinkle(options);

Bullwinkle Methods

send(messageName[, data])

Sends a named message to the partner’s Bullwinkle application, and returns a Bullwinkle.Package. The data parameter can be a basic Squirrel type (1, true, "A String") or more complex data structures such as an array or table, but it must be a serializable Squirrel value.

bull.send("setLights", true);   // Turn the lights on

The send() method returns a Bullwinkle.Package object that can be used to attach onFail, onSuccess and onReply handlers.

on(messageName, callback)

Adds a message listener (the callback) for the specified messageName. The callback method takes two parameters: message (the message) and reply (a method that can be called to reply to the message).

// Get a message, and do something with it
bull.on("setLights", function(message, reply) {
    led.write(message.data);
});

message

The message parameter is a table that contains some or all of the following keys:

Key Data Type Description
type Integer Bullwinkle message type
tries Integer Number of attempts made to deliver the message
name String Name of the message
id Integer ID of the message
ts Integer Timestamp when message was created
data Serializable Squirrel value data passed into the send method
retry Table A table containing ts the timestamp of the latest retry and sent a boolean
latency Float Seconds taken to deliver the message

reply(data)

The second parameter, reply, is a reference to a method that can be invoked to reply to the message caught by the .on handler. The reply method takes one parameter, data, representing the information we want to pass back to the partner. The data parameter can be can be any serializable Squirrel value.

// Get a message, and respond to it
bull.on("temp", function(message, reply) {
    // Read the temperature and humidity sensor
    local data = tempHumid.read();

    // Reply to the message
    reply(data);
});

remove(messageName)

The remove() method removes a message listener that was added with the .on method.

bull.remove("test");     // Don't listen for 'test' messages any more.

Bullwinkle.Package

A Bullwinkle.Package object represents a message that has been sent to the partner, and has event handlers attached to it. Bullwinkle.Package objects should never be manually constructed: the Bullwinkle.send() method returns a Bullwinkle.Package object.

onSuccess(callback)

The onSuccess() method adds an event listener (the callback) that will execute if the partner .on handler receives the message. The callback’s message parameter contains the successfully delivered message, including a tries count and a round-trip latency float in seconds.

bull.send("importantMessage")
    .onSuccess(function(message) {
        server.log("Done!");
    })
    .onFail(function(err, message, retry) {
        retry();
    });

onReply(callback)

The onReply() method adds an event listener (passed into the parameter callback) that will execute if the partner .on handler replies to the message with the reply() method. The callback takes a single parameter, message, which contains the message information, including a tries count and a round-trip latency float in seconds.

The following example demonstrates how to get real-time sensor information with Rocky and Bullwinkle:

// Agent Code
#require "Rocky.class.nut:1.3.0"
#require "Bullwinkle.class.nut:2.3.2"

app <- Rocky();
bull <- Bullwinkle();

app.get("/data", function(context) {
    bull.send("temp").onReply(function(message) {
        context.send(200, message.data);
    });
});
// Device Code
#require "Si702x.class.nut:1.0.0"
#require "Bullwinkle.class.nut:2.3.2"

bull <- Bullwinkle();

i2c <- hardware.i2c89;
i2c.configure(CLOCK_SPEED_400_KHZ);
tempHumid <- Si702x(i2c);

bull.on("temp", function(message, reply){
    local result = tempHumid.read();
    reply(result);
});

onFail(callback)

The onFail() method adds an event listener (passed into the parameter callback) that will execute if the partner application doesn’t have a handler for the specified message name, or if the partner fails to respond within a specified period of time (the messageTimeout). The callback method requires three parameters: err, message and retry.

The err parameter describes the error, and will either be Bullwinkle.NO_HANDLER (in the event the partner application does not have a handler for the specified message name), or Bullwinkle.NO_RESPONSE (in the event the partner application fails to respond in the specified timeout period).

The message parameter contains the failed message, including a tries count and a latency float in seconds.

The retry parameter is a method that can be invoked to retry sending the message in a specified period of time. This method must be called synchronously if it is to be called at all. If the retry() method is not called, the message will be expired.

bull.send("importantMessage")
    .onFail(function(err, message, retry) {
        // Try sending the message again in 60 seconds
        if (!retry(60)) {
            server.error("No more retry attempts are allowed");
        }
    }).onReply(function(message) {
        server.log("Done!");
    });

retry([timeout])

The retry() method is passed into onFail handler, and can be used to try sending the failed message again after the specified timeout has elapsed. If no timeout is specified, the retry message will use the default retryTimeout setting. If the maximum number of retries have been attempted then this function will return false and no more retries will be attempted, otherwise it will return true. See onFail for example usage.

Release History

The Electric Imp Dev Center documents the latest version of the library. For past versions, please see the Electric Imp public GitHub repos listed below.

Version Source Code Notes
1.0.0 GitHub Initial Release
2.0.0 GitHub Added server.error() message for unhandled NACKs; no API changes
2.0.1 GitHub Code improvements; bug fixes
2.2.0 GitHub Added Bullwinkle.Package onSuccess callback; tries and latency fields added to Bullwinkle.Package.on method callbacks’ message parameter; added message parameter to Bullwinkle.on callback function
2.2.1 GitHub Code improvements
2.3.0 GitHub Added auto retry setting to constructor’s options table
2.3.1 GitHub Improved timer handling
2.3.2 GitHub Added null check to onError handler

License

Bullwinkle is licensed under the MIT License.