Skip to content

ragrus-nbl/MessageManager

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MessageManager

Build Status

MessageManager is framework for asynchronous bidirectional agent to device communication. The library is the successor to Bullwinkle.

The library uses ConnectionManager on the device side to receive notifications of connection and disconnection events, and to monitor connection status (ie. so that no attempt it made to send messages when the device is disconnected).

To add this library to your project, add #require "MessageManager.lib.nut:2.2.0" to the top of your agent and device code.

Note MessageManager is designed to run over reliable (ie. TCP/TLS) connections. Retries only occur in the case of dropped connections or lost packets, or if called manually from beforeSend() or beforeRetry().

API Overview

Details and Usage

MessageManager

Constructor: MessageManager([options])

Calling the MessageManager constructor creates a new MessageManager instance. An optional table can be passed into the constructor (as options) to override default behaviours. options can contain any of the following keys:

Key Data Type Default Value Description
debug Boolean false The flag that enables debug library mode, which turns on extended logging
retryInterval Integer 0 Changes the default timeout parameter passed to the retry method
messageTimeout Integer 10 Changes the default timeout required before a message is considered failed (to be acknowledged or replied to)
autoRetry Boolean false If set to true, MessageManager will automatically continue to retry sending a message until maxAutoRetries has been reached when no onFail() callback is supplied. Please note that if maxAutoRetries is set to 0, autoRetry will have no limit to the number of times it will retry
maxAutoRetries Integer 0 Changes the default number of automatic retries to be peformed by the library. After this number is reached the message will be dropped. Please note that the message will automatically be retried if there is no onFail() handler registered by the user
connectionManager ConnectionManager null Optional instance of the ConnectionManager library that helps MessageManager to track the connectivity status
nextIdGenerator Function null User-defined callback that generates the next message ID. The function has no parameters
onPartnerConnected Function null Sets the handler to be called when the partner is known to be connected. The handler’s signature is: handler(reply), where reply(data) is the callback to respond to the “connected” event
onConnectedReply Function null Sets the handler to be called when the partner responds to the connected status. The handler’s signature is: handler(response), where response is the response data
maxMessageRate Integer 10 Maximum message send rate, which defines the maximum number of messages the library allows to send per second. If application exceeds the limit, the onFail handler is called.
Note please don’t change the value unless absolutely necessary.
firstMessageId Integer 0 Initial value for the auto-incrementing message ID
Examples
// Initialize using default settings
local mm = MessageManager();
// Create ConnectionManager instance
local cm = ConnectionManager({
    "blinkupBehavior": ConnectionManager.BLINK_ALWAYS,
    "stayConnected": true
});
imp.setsendbuffersize(8096);

// MessageManager options
local options = {
    "debug": true,
    "retryInterval": 15,
    "messageTimeout": 2,
    "autoRetry": true,
    "maxAutoRetries": 10,
    "connectionManager": cm
};

local mm = MessageManager(options);

MessageManager.send(name[, data][, handlers][, timeout][, metadata])

Sends a named message to the partner side and returns the MessageManager.DataMessage object created. 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.

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

handlers is a table containing the message-local message event handlers:

Note Message-local handlers override the global ones. I.e. if a message-local handler is triggered, the global one, if set, is not executed.

Key Description Handler
onAck Acknowledgement handler MessageManager.DataMessage.onAck
onFail Failure handler MessageManager.DataMessage.onFail
onReply Reply handler MessageManager.DataMessage.onReply
onTimeout Timeout handler MessageManager.DataMessage.onTimeout

MessageManager.on(messageName, callback)

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

// Get a message, and do something with it
mm.on("lights", function(message, reply) {
    led.write(message.data);
    reply("Got it!");
});

MessageManager.beforeSend(callback)

Sets the callback which will be called before a message is sent. The callback has the following parameters:

Parameter Description
message An instance of DataMessage to be sent
enqueue A function with no parameters which appends the message to the retry queue for later processing
drop A function which disposes of the message. It takes two optional parameters: silently, which defaults to true and which governs whether the disposal takes place silently or through the onFail callbacks, and error which if silently is false, specifies the error message to be provided to the onFail callback

The enqueue and drop functions must be called synchronously, if they are called at all.

mm.beforeSend(
    function(msg, enqueue, drop) {
        if (runningOutOfMemory()) {
            drop();
        }

        if (needToPreserveMessageOrder() && previousMessagesFailed()) {
            enqueue();
        }
    }
)

MessageManager.beforeRetry(callback)

Sets the callback for retry operations. It will be called before the library attempts to re-send the message and has the following parameters:

Parameter Description
message An instance of DataMessage to be re-sent
skip A function with a single parameter, duration, which postpones the retry attempt and leaves the message in the retry queue for the specified amount of time. If duration is not specified, it defaults to the retryInterval provided for MessageManager constructor
drop A function which disposes of the message. It takes two optional parameters: silently, which defaults to true and which governs whether the disposal takes place silently or through the onFail callbacks, and error which if silently is false, specifies the error message to be provided to the onFail callback

The skip and drop functions must be called synchronously, if they are called at all.

mm.beforeRetry(
    function(msg, skip, drop) {
        if (runningOutOfMemory()) {
            drop();
        }

        if (needToWaitForSomeReasonBeforeRetry()) {
            skip(duration);
        }
    }
)

MessageManager.onFail(callback)

Sets the callack to be called when a message error occurs. The callback has the following parameters:

Parameter Description
message An instance of DataMessage that caused the error
reason The error description string
retry A function that can be invoked to retry sending the message in a specified period of time. This function must be called synchronously, if it is called at all. It takes one parameter, interval. If there is no interval parameter specified, the retryInterval value provided for MessageManager constructor is used. If the function is not called, the message will expire
mm.onFail(
    function(msg, error, retry) {
        // Always retry to send the message
        retry();
    }
)

MessageManager.onTimeout(callback)

Sets the callback to be called when a message timeout occurs. The callback has the following parameters:

Parameter Description
message An instance of DataMessage that caused the timeout
wait A function which resets the acknowledgement timeout for the message, which means the message will not raise a timeout error for the interval of time specified by the function’s interval parameter. This function must be called synchronously
fail A function which makes the message fall through the onFail callbacks

If neither wait nor fail are called, the message will expire.

mm.onTimeout(
    function(msg, wait, fail) {
        if (isStillValid(msg)) {
            wait(10);
        } else {
            // Fail otherwise
            fail();
        }
    }
);

MessageManager.onAck(callback)

Sets the callback to be called when the message’s receipt is acknowledged. The callback has the following parameters:

Parameter Description
message An instance of DataMessage that was acknowledged
mm.onAck(
    function(msg) {
        // Just log the ACK event
        server.log("ACK received for " + msg.payload.data);
    }
)

NOTE: a message is only acked if the receiving party has a handler for this message defined.

MessageManager.onReply(handler)

Sets the callback to be called when the message is replied to. The callback has the following parameters:

Parameter Description
message An instance of DataMessage that was replied to
response The response from the partner
mm.onReply(
    function(msg, response) {
        processResponseFor(msg.payload.data, response);
    }
)

NOTE: setting the callback doesn't necessarily imply that the other side must reply to the message. It just sets a handler, which is executed when/if the message is replied.

MessageManager.getPendingCount()

Returns the overall number of pending messages (either waiting for acknowledgement or waiting in the retry queue).

if (mm.getPendingCount() < SOME_MAX_PENDING_COUNT) {
    mm.send("temp", temp);
} else {
    // do something else
}

MessageManager.DataMessage

MessageManager.DataMessage instances are not intended to be created by users manually — they are always returned from the MessageManager.send() method.

MessageManager.DataMessage.onFail()

Sets a message-local version of the MessageManager.onFail() handler.

MessageManager.DataMessage.onTimeout()

Sets a message-local version of the MessageManager.onTimeout() handler.

MessageManager.DataMessage.onAck()

Sets a message-local version of the MessageManager.onAck() handler.

MessageManager.DataMessage.onReply()

Sets a message-local version of the MessageManager.onReply() handler.

Other Usage Examples

Integration with ConnectionManager

// Device code

#require "ConnectionManager.class.nut:1.0.2"
#require "MessageManager.lib.nut:2.2.0"

local cm = ConnectionManager({
    "blinkupBehavior": ConnectionManager.BLINK_ALWAYS,
    "stayConnected": true
});

// Set the recommended buffer size
// (see https://github.com/electricimp/ConnectionManager for details)
imp.setsendbuffersize(8096);

local config = {
    "messageTimeout": 2,
    "connectionManager": cm
};

local counter = 0;
local mm = MessageManager(config);

mm.onFail(
    function(msg, error, retry) {
        server.log("Error occurred: " + error);
        retry();
    }
);

mm.onReply(
    function(msg, response) {
        server.log("Response for " + msg.payload.data + " received: " + response);
    }
);

function sendData() {
    mm.send("name", counter++);
    imp.wakeup(1, sendData);
}

sendData();
// Agent code

#require "MessageManager.lib.nut:2.2.0"

local mm = MessageManager();

mm.on("name", function(data, reply) {
    server.log("message received: " + data);
    reply("Got it!");
});

License

MessageManager is licensed under the MIT License.

About

MessageManager Library

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Squirrel 100.0%