Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save betzrhodes/49f6ac4b1792b7888c67998aee1350e0 to your computer and use it in GitHub Desktop.
Save betzrhodes/49f6ac4b1792b7888c67998aee1350e0 to your computer and use it in GitHub Desktop.
Connection Manager extension that bubbles server.connect connection reason up to connection handlers
#require "ConnectionManager.lib.nut:3.1.1"
class MyConnectionManager extends ConnectionManager {
// Attempts to connect. If the server is already connected, or the
// connection attempt was successful, run the onConnect handler, and
// any other onConnected tasks
function connect() {
// If we're connecting/disconnecting, try again in 0.5 seconds
if (_connecting) return false;
// If we're already connected: invoke the onConnectedFlow and return
if (_connected) {
_onConnectedFlow();
return true;
}
// Otherwise, try to connect...
// Set the _connecting flag at the start
_connecting = hardware.millis();
server.connect(function(result) {
// clear connecting flag when we're done trying to connect
_connecting = false;
if (result == SERVER_CONNECTED) {
// If it worked, run the onConnectedFlow
_connected = true;
_onConnectedFlow(result);
} else {
// Otherwise, restart the connection process
_onTimeoutFlow(result);
}
}.bindenv(this), _connectTimeout);
// Catch a race condition where server.connect() won't throw the callback if its already connected
if (server.isconnected()) {
_connecting = false;
_connected = true;
_onConnectedFlow();
}
return true;
}
// Runs whenever we connect or call connect()
function _onConnectedFlow(connReason = null) {
// Set the BlinkUp State
_setBlinkUpState();
while(_logs.len() > 0) {
local log = _logs.remove(0);
local d = date(log.ts);
local ts = format("%04d-%02d-%02d %02d:%02d:%02d", d.year, (d.month+1), d.day, d.hour, d.min, d.sec);
if (!log.error) {
server.log(ts + " " + log.log);
} else {
server.error(ts + " " + log.log);
}
}
// Run the global onConnected Handler if it exists
if (_onConnect != null) {
// Invoke all the callbacks in the loop
_invokeCallbacks(_onConnect, connReason);
}
_processQueue();
}
// Runs whenever a call to connect times out
function _onTimeoutFlow(connReason = null) {
// Set the BlinkUp State
_setBlinkUpState();
_connecting = false;
_connected = false;
if (_onTimeout != null) {
_invokeCallbacks(_onTimeout, connReason);
}
if (_retryOnTimeout) {
// We have a timeout trying to connect. We need to retry;
imp.wakeup(0, connect.bindenv(this));
}
}
}
// RUNTIME
// --------------------------------------------------------------
server.log("------------------------------------------------------------------");
server.log("Device Running...");
server.log(imp.getsoftwareversion());
server.log("------------------------------------------------------------------");
function getConnReasonDescription(reason) {
switch(reason) {
case NOT_CONNECTED:
return "Not Connected, unknown reason";
case NO_WIFI:
return "Failed to join WiFi or connect via Ethernet";
case NO_IP_ADDRESS:
return "Failed to get an IP address";
case NOT_RESOLVED:
return "The IP address of an Electric Imp server or proxy could not be resolved";
case NO_SERVER:
return "Failed to connect to the Electric Imp server";
case SERVER_CONNECTED:
return "The server is connected";
case NO_PROXY:
return "The imp cannot connect via saved proxy address and port";
case NOT_AUTHORISED:
return "The imp cannot connect because its proxy access credentials have been rejected";
}
return "";
}
// Settings for this test only, not recommended for an application
cm <- MyConnectionManager({
"stayConnected" : true,
"retryOnTimeout" : true,
"connectTimeout" : 10,
"blinkupBehavior" : CM_BLINK_ALWAYS
});
// Register On Connect Handler
cm.onConnect(function(reason = null) {
server.log("In on connect handler...");
if (reason != null) {
server.log("server.connect param: " + reason);
server.log("server.connect param description: " + getConnReasonDescription(reason));
}
}.bindenv(this))
cm.onTimeout(function(reason = null) {
cm.log("In on timeout handler...");
if (reason != null) {
cm.log("server.connect param: " + reason);
cm.log("server.connect param description: " + getConnReasonDescription(reason));
}
}.bindenv(this))
cm.onDisconnect(function(expected) {
cm.log("In on disconnected handler...");
cm.log("Disconnect was expected: " + expected);
}.bindenv(this))
// Trigger On Connect Handler (this should not pass a connection reason to the onConnect handler)
cm.connect();
// Disconnect
imp.wakeup(3, function() {
cm.disconnect();
cm.log("Disconnect called.");
}.bindenv(this))
// Re-connect
imp.wakeup(3, function() {
cm.log("Calling connect.");
cm.connect();
}.bindenv(this))
// Disconnect Wifi Here for at least the ammount of time set in the connectTimeout, then reconnect
// to see cm offline logs.
// PLEASE NOTE: If the device is offline for a long time cm logging can fill up and cause an out
// of memory error which will cause the device to reboot.
#require "ConnectionManager.lib.nut:3.1.1"
class MyConnectionManager extends ConnectionManager {
// Attempts to connect. If the server is already connected, or the
// connection attempt was successful, run the onConnect handler, and
// any other onConnected tasks
function connect() {
// If we're connecting/disconnecting, try again in 0.5 seconds
if (_connecting) return false;
// If we're already connected: invoke the onConnectedFlow and return
if (_connected) {
_onConnectedFlow();
return true;
}
// Otherwise, try to connect...
// Set the _connecting flag at the start
_connecting = hardware.millis();
server.connect(function(result) {
// clear connecting flag when we're done trying to connect
_connecting = false;
if (result == SERVER_CONNECTED) {
// If it worked, run the onConnectedFlow
_connected = true;
_onConnectedFlow(result);
} else {
// Otherwise, restart the connection process
_onTimeoutFlow(result);
}
}.bindenv(this), _connectTimeout);
// Catch a race condition where server.connect() won't throw the callback if its already connected
if (server.isconnected()) {
_connecting = false;
_connected = true;
_onConnectedFlow();
}
return true;
}
// Runs whenever we connect or call connect()
function _onConnectedFlow(connReason = null) {
// Set the BlinkUp State
_setBlinkUpState();
while(_logs.len() > 0) {
local log = _logs.remove(0);
local d = date(log.ts);
local ts = format("%04d-%02d-%02d %02d:%02d:%02d", d.year, (d.month+1), d.day, d.hour, d.min, d.sec);
if (!log.error) {
server.log(ts + " " + log.log);
} else {
server.error(ts + " " + log.log);
}
}
// Run the global onConnected Handler if it exists
if (_onConnect != null) {
// Invoke all the callbacks in the loop
_invokeCallbacks(_onConnect, connReason);
}
_processQueue();
}
// Runs whenever a call to connect times out
function _onTimeoutFlow(connReason = null) {
// Set the BlinkUp State
_setBlinkUpState();
_connecting = false;
_connected = false;
if (_onTimeout != null) {
_invokeCallbacks(_onTimeout, connReason);
}
if (_retryOnTimeout) {
// We have a timeout trying to connect. We need to retry;
imp.wakeup(0, connect.bindenv(this));
}
}
}
class App {
static CONNECT_TIMEOUT = 10;
cm = null;
constructor() {
// Settings for this test only, not recommended for an application
cm = MyConnectionManager({
"stayConnected" : true,
"retryOnTimeout" : true,
"connectTimeout" : CONNECT_TIMEOUT,
"blinkupBehavior" : CM_BLINK_ALWAYS
});
cm.onConnect(onConnected.bindenv(this));
cm.onTimeout(onConnTimeout.bindenv(this));
cm.onDisconnect(onDisconnected.bindenv(this));
}
function run() {
// Trigger On Connect Handler (this should not pass a connection reason to the onConnect handler)
cm.connect();
// Disconnect
imp.wakeup(3, function() {
cm.disconnect();
cm.log("Disconnect called.");
}.bindenv(this))
// Re-connect
imp.wakeup(3, function() {
cm.log("Calling connect.");
cm.connect();
// Give imp time to connect
imp.wakeup(5, function() {
// Disconnect Wifi Here for at least the ammount of time set in the connectTimeout
local msg = "Disconnect WiFi for at least " + CONNECT_TIMEOUT + " seconds.";
(cm.isConnected()) ? server.log(msg) : cm.log(msg);
}.bindenv(this))
}.bindenv(this))
}
function onConnTimeout(reason = null) {
cm.log("In on timeout handler...");
if (reason != null) {
cm.log("server.connect param: " + reason);
cm.log("server.connect param description: " + getConnReasonDescription(reason));
} else {
cm.log("no server.connect param given");
}
}
function onDisconnected(expected) {
cm.log("In on disconnected handler...");
cm.log("Disconnect was expected: " + expected);
}
function onConnected(reason = null) {
server.log("------------------------------------------------------------------");
server.log("In on connect handler...");
if (reason != null) {
server.log("server.connect param: " + reason);
server.log("server.connect param description: " + getConnReasonDescription(reason));
} else {
server.log("no server.connect param given");
}
server.log("------------------------------------------------------------------");
}
function getConnReasonDescription(reason) {
switch(reason) {
case NOT_CONNECTED:
return "Not Connected, unknown reason";
case NO_WIFI:
return "Failed to join WiFi or connect via Ethernet";
case NO_IP_ADDRESS:
return "Failed to get an IP address";
case NOT_RESOLVED:
return "The IP address of an Electric Imp server or proxy could not be resolved";
case NO_SERVER:
return "Failed to connect to the Electric Imp server";
case SERVER_CONNECTED:
return "The server is connected";
case NO_PROXY:
return "The imp cannot connect via saved proxy address and port";
case NOT_AUTHORISED:
return "The imp cannot connect because its proxy access credentials have been rejected";
}
return "";
}
}
// RUNTIME
// --------------------------------------------------------------
server.log("------------------------------------------------------------------");
server.log("Device Running...");
server.log(imp.getsoftwareversion());
server.log("------------------------------------------------------------------");
app <- App();
app.run();
#require "ConnectionManager.lib.nut:3.1.1"
#require "MessageManager.lib.nut:2.4.0"
class MyConnectionManager extends ConnectionManager {
// Attempts to connect. If the server is already connected, or the
// connection attempt was successful, run the onConnect handler, and
// any other onConnected tasks
function connect() {
// If we're connecting/disconnecting, try again in 0.5 seconds
if (_connecting) return false;
// If we're already connected: invoke the onConnectedFlow and return
if (_connected) {
_onConnectedFlow();
return true;
}
// Otherwise, try to connect...
// Set the _connecting flag at the start
_connecting = hardware.millis();
server.connect(function(result) {
// clear connecting flag when we're done trying to connect
_connecting = false;
if (result == SERVER_CONNECTED) {
// If it worked, run the onConnectedFlow
_connected = true;
_onConnectedFlow(result);
} else {
// Otherwise, restart the connection process
_onTimeoutFlow(result);
}
}.bindenv(this), _connectTimeout);
// Catch a race condition where server.connect() won't throw the callback if its already connected
if (server.isconnected()) {
_connecting = false;
_connected = true;
_onConnectedFlow();
}
return true;
}
// Runs whenever we connect or call connect()
function _onConnectedFlow(connReason = null) {
// Set the BlinkUp State
_setBlinkUpState();
while(_logs.len() > 0) {
local log = _logs.remove(0);
local d = date(log.ts);
local ts = format("%04d-%02d-%02d %02d:%02d:%02d", d.year, (d.month+1), d.day, d.hour, d.min, d.sec);
if (!log.error) {
server.log(ts + " " + log.log);
} else {
server.error(ts + " " + log.log);
}
}
// Run the global onConnected Handler if it exists
if (_onConnect != null) {
// Invoke all the callbacks in the loop
_invokeCallbacks(_onConnect, connReason);
}
_processQueue();
}
// Runs whenever a call to connect times out
function _onTimeoutFlow(connReason = null) {
// Set the BlinkUp State
_setBlinkUpState();
_connecting = false;
_connected = false;
if (_onTimeout != null) {
_invokeCallbacks(_onTimeout, connReason);
}
if (_retryOnTimeout) {
// We have a timeout trying to connect. We need to retry;
imp.wakeup(0, connect.bindenv(this));
}
}
function _invokeCallbacks(callbacks, arg = null) {
local cmCtx = this;
foreach (id, clbk in callbacks) {
local ctx = {
"arg" : arg,
"ctxClbk" : clbk
};
if (clbk && typeof clbk == "function") {
// TODO: we assume that null is not a valid value of the argument, it's null only if it's unset.
if (arg == null || (id == MM_CM_HANDLERS_ID && callbacks != cmCtx._onDisconnect) ) {
imp.wakeup(0, function() {ctxClbk();}.bindenv(ctx));
} else {
imp.wakeup(0, function() {ctxClbk(arg);}.bindenv(ctx));
}
}
}
}
}
// RUNTIME
// --------------------------------------------------------------
server.log("------------------------------------------------------------------");
server.log("Device Running...");
server.log(imp.getsoftwareversion());
server.log("------------------------------------------------------------------");
function getConnReasonDescription(reason) {
switch(reason) {
case NOT_CONNECTED:
return "Not Connected, unknown reason";
case NO_WIFI:
return "Failed to join WiFi or connect via Ethernet";
case NO_IP_ADDRESS:
return "Failed to get an IP address";
case NOT_RESOLVED:
return "The IP address of an Electric Imp server or proxy could not be resolved";
case NO_SERVER:
return "Failed to connect to the Electric Imp server";
case SERVER_CONNECTED:
return "The server is connected";
case NO_PROXY:
return "The imp cannot connect via saved proxy address and port";
case NOT_AUTHORISED:
return "The imp cannot connect because its proxy access credentials have been rejected";
}
return "";
}
// Settings for this test only, not recommended for an application
cm <- MyConnectionManager({
"stayConnected" : true,
"retryOnTimeout" : true,
"connectTimeout" : 10,
"blinkupBehavior" : CM_BLINK_ALWAYS
});
mm <- MessageManager({
"connectionManager" : cm
});
// Register On Connect Handler
cm.onConnect(function(reason = null) {
server.log("In on connect handler...");
if (reason != null) {
server.log("server.connect param: " + reason);
server.log("server.connect param description: " + getConnReasonDescription(reason));
}
}.bindenv(this))
cm.onTimeout(function(reason = null) {
cm.log("In on timeout handler...");
if (reason != null) {
cm.log("server.connect param: " + reason);
cm.log("server.connect param description: " + getConnReasonDescription(reason));
}
}.bindenv(this))
cm.onDisconnect(function(expected) {
cm.log("In on disconnected handler...");
cm.log("Disconnect was expected: " + expected);
}.bindenv(this))
// Trigger On Connect Handler (this should not pass a connection reason to the onConnect handler)
cm.connect();
// Disconnect
imp.wakeup(3, function() {
cm.disconnect();
cm.log("Disconnect called.");
}.bindenv(this))
// Re-connect
imp.wakeup(3, function() {
cm.log("Calling connect.");
cm.connect();
}.bindenv(this))
// Disconnect Wifi Here for at least the ammount of time set in the connectTimeout, then reconnect
// to see cm offline logs.
// PLEASE NOTE: If the device is offline for a long time cm logging can fill up and cause an out
// of memory error which will cause the device to reboot.
@GuillermoActus
Copy link

Great!, thank you for taking the time for a such nice explanation and feedback, I really appreciate it, this resolves our issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment