Last active
October 11, 2019 16:21
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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. |
Sure,
I'll add it to the support ticket.
This re-write does have an issue when using ConnectionManager with MessageManager library. Adding a parameter to onConnect will cause an error when other libraries/classes onConnect or onTimeout handlers trigger with a parameter. See v3 file for a possible solution. It is worth noting that any library or class that have dependencies will have an issue if they register onConnect or onTimeout handlers. The above solution only deals with MessageManager.
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
This looks like a scope issue, however since the code examples above work for me with no errors I expect this may be an error cause by a different part of the code, likely the code that is making the calls to the library. Can you send me the code you are running that produces the error.