Last active
May 12, 2022 13:14
-
-
Save ElectricImpSampleCode/a77000c6a33e61c2e402e7eff229ac4a to your computer and use it in GitHub Desktop.
Effective Internet <-> Agent <-> Device communications with the Rocky and MessageManager libraries
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
/* | |
* Load the libraries that we need: | |
* Rocky to serve the API, | |
* MessageManager to handle the agent-device interaction | |
*/ | |
#require "Rocky.agent.lib.nut:3.0.0" | |
#require "MessageManager.lib.nut:2.4.0" | |
/* | |
* Instantiate instances of Rocky and MessageManager | |
* NOTE Rocky is a singleton | |
*/ | |
api <- Rocky.init(); | |
mm <- MessageManager(); | |
/* | |
* This is the handler called in response to replies | |
* made to "request.reading" that the agent sends | |
*/ | |
function readingRequestReplyHandler(message, response) { | |
// Retrieve the stored Rocky context | |
local rockyContext = message.metadata; | |
// Assemble the table of data that we will return | |
// to the remote caller | |
local returnedData = {}; | |
if (typeof response == "array") { | |
returnedData.readings <- response; | |
} else { | |
returnedData.reading <- response; | |
} | |
// Use the Rocky context to respond with JSON | |
rockyContext.send(200, http.jsonencode(returnedData)); | |
} | |
/* | |
* Register a Rocky middleware function which we'll chain to | |
* all incoming requests with Rocky's 'use()' method. | |
* It also demonstrates some of the request info accessible | |
* via a Rocky context. | |
* NOTE Make sure you call the supplied 'next()' function to | |
* maintain the chain of calls | |
*/ | |
api.use(function(aRockyContext, next) { | |
server.log("API received a " + aRockyContext.req.method.toupper() + " request at " + time() + " from " + aRockyContext.getHeader("x-forwarded-for") + " at path " + aRockyContext.req.path.tolower()); | |
next();}); | |
/* | |
* Register a Rocky handler for incoming GET requests | |
* made to /readings. | |
* NOTE Rocky provides a lot of flexibility for defining | |
* endpoint paths, including regular expressions | |
*/ | |
api.get("/readings", function(theRockyContext) { | |
// Use a try...catch to trap JSON decode errors | |
try { | |
// Decode the incoming request body | |
local requestData = http.jsondecode(theRockyContext.req.rawbody); | |
// Check it's the right kind of request | |
if ("sensor" in requestData) { | |
// The request contains the key field, sensor, | |
// so use MessageManager to contact the device: | |
// We pass: | |
// 1. The message name | |
// 2. The data payload | |
// 3. A table of event handlers | |
// 4. A timeout | |
// 5. Message metadata | |
mm.send("request.reading", | |
requestData, | |
{"onReply": readingRequestReplyHandler}, | |
30, | |
theRockyContext | |
); | |
} else { | |
theRockyContext.send(500, "Request JSON lacks a \'sensor\' field"); | |
} | |
} catch (error) { | |
// The JSON decode failed so report the error | |
// NOTE We provide a single line of text, but this could | |
// be a complex JSON report - it's up to you | |
theRockyContext.send(500, "Bad data posted: " + error); | |
} | |
}); |
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
/* | |
* Load the libraries that we need: | |
* HTS221 to manage the board's thermal sensor | |
* MessageManager to handle the agent-device interaction | |
*/ | |
#require "HTS221.device.lib.nut:2.0.2" | |
#require "MessageManager.lib.nut:2.4.0" | |
/* | |
* Instantiate a MessageManager instance | |
*/ | |
local mm = MessageManager(); | |
/* | |
* Configure the I2C bus, then instantiate | |
* and configure the thermal sensor | |
*/ | |
hardware.i2cLM.configure(CLOCK_SPEED_400_KHZ); | |
sensor <- HTS221(hardware.i2cLM); | |
sensor.setMode(HTS221_MODE.ONE_SHOT); | |
/* | |
* Register a handler for incoming "request.reading" messages | |
* NOTE Here we're using an inline function definition | |
* cf. 'readingRequestReplyHandler()' in the agent code | |
*/ | |
mm.on( "request.reading", | |
function(message, reply) { | |
// Are we beging asked to read from the thermal sensor? | |
if (message.data.sensor == "temperature") { | |
// Get the number of readings requested, but use a default (1) if | |
// the 'readings' field was not included in the request | |
local numberOfReadings = "readings" in message.data ? message.data.readings : 1; | |
// Set up a store for multiple readings (may not be needed) | |
local readings = []; | |
// Get the required number of readings, pausing one second | |
// between each. | |
// NOTE Readings are received asynchronously through the callback | |
// passed into 'sensor.read()'. We bind the callback to the | |
// environment 'this' to ensure surrounding local variables, | |
// eg. 'numberOfReadings' are in the scope of the callback | |
for (local i = 0 ; i < numberOfReadings ; i ++) { | |
sensor.read(function(reading) { | |
if (!("error" in reading)) { | |
// No errors encountered while taking the reading so | |
// store the reading (if we want several) or return the | |
// single requested reading | |
if (numberOfReadings > 1) { | |
readings.append(reading.temperature); | |
if (readings.len() == numberOfReadings) { | |
// We've got all the requested readings so return them | |
reply(readings); | |
} | |
} else { | |
// Return the single reading requested | |
reply(reading.temperature); | |
} | |
} | |
}.bindenv(this)); | |
imp.sleep(1.0); | |
} | |
return; | |
} | |
// No other sensor type supported... yet | |
// For now, reply with an invalid reading | |
reply(999.99); | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment