mirror of https://github.com/iotcat/wIoT
parent
b2bd5ed46c
commit
afc71749ab
20 changed files with 362 additions and 660 deletions
@ -0,0 +1,38 @@ |
||||
const heartbeat = require(__dirname + '/modules/heartbeat.js')(); |
||||
const app = require('express')(); |
||||
|
||||
app.listen(8081); |
||||
|
||||
|
||||
app.get('/', async function (req, res) { |
||||
|
||||
if(!req.query.hasOwnProperty('id') || !req.query.hasOwnProperty('fid') || !req.query.hasOwnProperty('body')){ |
||||
res.send('Illegal params'); |
||||
return; |
||||
} |
||||
console.log(req.query.body) |
||||
let msg = await heartbeat.push(req.query.id, req.query.fid, JSON.parse(new Buffer(req.query.body, 'base64').toString())[0]); |
||||
res.send(msg); |
||||
}) |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
;(async () => { |
||||
//console.log('aaaaaaaaaaaaaaa: ', await heartbeat.push('test1', 'info', ''))
|
||||
//heartbeat.push('test1', 'test', 'test1', (r)=>{
|
||||
//console.log(r)
|
||||
//})
|
||||
|
||||
//heartbeat.push('test1', 'exec', 'pwm.setup(2, 100, 512);pwm.start(2);local n = 0; local timer = tmr.create(); timer:register(10, tmr.ALARM_AUTO, function() pwm.setduty(2, n%1024); n = n + 1; end);timer:start();');
|
||||
|
||||
|
||||
//console.log(await heartbeat.push('test1', 'exec', 'return adc.read(0)'))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})() |
@ -1,38 +1,5 @@ |
||||
const heartbeat = require(__dirname + '/modules/heartbeat.js')(); |
||||
const app = require('express')(); |
||||
const nodetable = require(__dirname + '/modules/nodetable.js')(); |
||||
|
||||
app.listen(8081); |
||||
|
||||
|
||||
app.get('/', async function (req, res) { |
||||
|
||||
if(!req.query.hasOwnProperty('id') || !req.query.hasOwnProperty('fid') || !req.query.hasOwnProperty('body')){ |
||||
res.send('Illegal params'); |
||||
return; |
||||
} |
||||
console.log(req.query.body) |
||||
let msg = await heartbeat.push(req.query.id, req.query.fid, JSON.parse(new Buffer(req.query.body, 'base64').toString())[0]); |
||||
res.send(msg); |
||||
}) |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
;(async () => { |
||||
//console.log('aaaaaaaaaaaaaaa: ', await heartbeat.push('test1', 'info', ''))
|
||||
//heartbeat.push('test1', 'test', 'test1', (r)=>{
|
||||
//console.log(r)
|
||||
//})
|
||||
|
||||
//heartbeat.push('test1', 'exec', 'pwm.setup(2, 100, 512);pwm.start(2);local n = 0; local timer = tmr.create(); timer:register(10, tmr.ALARM_AUTO, function() pwm.setduty(2, n%1024); n = n + 1; end);timer:start();');
|
||||
|
||||
|
||||
//console.log(await heartbeat.push('test1', 'exec', 'return adc.read(0)'))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})() |
@ -0,0 +1,173 @@ |
||||
module.exports = (host = '0.0.0.0', port = 4444) => { |
||||
|
||||
let cbArr = { |
||||
income: [], |
||||
outcome: [], |
||||
forward: [], |
||||
error: [] |
||||
}; |
||||
|
||||
|
||||
let o = { |
||||
income: cb => { //cb(nid, info{funcID, error}, data)
|
||||
cbArr.income.push(cb); |
||||
}, |
||||
forward: cb => { //cb(nid, info{funcID, error}, data)
|
||||
cbArr.forward.push(cb); |
||||
}, |
||||
outgo: (nid, data) => { |
||||
if(!nidtable.hasOwnProperty(nid) || !nidtable[nid].socket){ |
||||
flow.error('[OUTGOING]', '<nid lookup failure>', data.to+'<--'+data.from, data.name, data.body); |
||||
return false; |
||||
} |
||||
nidtable[nid].socket.write(JSON.stringify(data)); |
||||
flow.log('[OUTGOING]', data.from + '-->' + data.to, data.name, data.body); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
let nidtable = {} |
||||
|
||||
|
||||
|
||||
function isJson(str) { |
||||
try { |
||||
if (typeof JSON.parse(str) == "object") { |
||||
return true; |
||||
} |
||||
} catch(e) { |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
const LOG_PATH = __dirname + '/../data/log/iptable/'; |
||||
|
||||
|
||||
const net = require('net'); |
||||
const log4js = require('log4js'); |
||||
|
||||
|
||||
log4js.configure({ |
||||
appenders: { |
||||
flow: {type: 'file', filename: LOG_PATH + 'flow.log'}, |
||||
access: {type: 'file', filename: LOG_PATH + 'access.log'}, |
||||
console: { type: 'console' } |
||||
}, |
||||
categories: { |
||||
flow: {appenders: ['flow', 'console'], level: 'trace' }, |
||||
access: { appenders: ['access', 'console'], level: 'trace' }, |
||||
default: { appenders: ['access', 'console'], level: 'trace' } |
||||
} |
||||
}); |
||||
|
||||
|
||||
const flow = log4js.getLogger('flow'); |
||||
const access = log4js.getLogger('access'); |
||||
|
||||
|
||||
const server = net.createServer((socket) => { |
||||
access.trace('Unspecified', socket.remoteAddress+':'+socket.remotePort, 'New TCP request.'); |
||||
let nid = null; |
||||
socket.on('data', (data) => { |
||||
data = data.toString(); |
||||
if(!isJson(data)){ |
||||
access.error(nid, socket.remoteAddress+':'+socket.remotePort, 'Data is not JSON! Data::'+data); |
||||
return; |
||||
} |
||||
data = JSON.parse(data); |
||||
|
||||
if(data.hasOwnProperty('nid') && data.hasOwnProperty('funcID')){ |
||||
nid = data.nid; |
||||
if(!nidtable.hasOwnProperty(nid)){ |
||||
nidtable[nid] = {}; |
||||
} |
||||
nidtable[nid].funcID = data.funcID; |
||||
nidtable[nid].ip = socket.remoteAddress; |
||||
nidtable[nid].ip = socket.remotePort; |
||||
nidtable[nid].socket = socket; |
||||
if(data.hasOwnProperty('error') && data.error){ |
||||
nidtable[nid].error = data.error; |
||||
} |
||||
access.info(nid, socket.remoteAddress+':'+socket.remotePort, 'New TCP connection.'); |
||||
return; |
||||
} |
||||
|
||||
if(!nid){ |
||||
access.error(nid, socket.remoteAddress+':'+socket.remotePort, 'No nid is specified. Data::', data); |
||||
return; |
||||
} |
||||
|
||||
if(data.hasOwnProperty('uptime') && data.hasOwnProperty('heap') && data.hasOwnProperty('spiff')){ |
||||
nidtable[nid].LastUpTime = new Date().valueOf() - data.uptime*1000; |
||||
nidtable[nid].heap = data.heap; |
||||
nidtable[nid].spiff = data.spiff; |
||||
access.info(nid, socket.remoteAddress+':'+socket.remotePort, '[HEARTBEAT]', data); |
||||
return; |
||||
} |
||||
|
||||
if(!data.hasOwnProperty('from') || !data.hasOwnProperty('to') || !data.hasOwnProperty('name') || !data.hasOwnProperty('body')){ |
||||
access.error(nid, socket.remoteAddress+':'+socket.remotePort, 'Illegal package format. Data::', data); |
||||
return; |
||||
} |
||||
|
||||
|
||||
if(data.to != 'director'){ |
||||
if(nidtable.hasOwnProperty(data.to)){ |
||||
nidtable[data.to].socket.write(JSON.stringify(data)); |
||||
flow.log('[FORWARD]', data.to+'<--'+data.from, data.name, data.body); |
||||
}else{ |
||||
flow.error('[FORWARD]', '<nid lookup failure>', data.to+'<--'+data.from, data.name, data.body); |
||||
} |
||||
cbArr.forward.forEach(cb => { |
||||
cb(nid, nidtable[nid], data); |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
|
||||
if(data.to == 'director'){ |
||||
cbArr.income.forEach(cb => { |
||||
cb(nid, nidtable[nid], data); |
||||
}); |
||||
flow.log('[INCOMING]', data.to+'<--'+data.from, data.name, data.body); |
||||
return; |
||||
} |
||||
|
||||
|
||||
|
||||
}); |
||||
|
||||
socket.on('close', function(err){ |
||||
if(nid){ |
||||
nidtable[nid].socket = null; |
||||
nidtable[nid].ip = null; |
||||
nidtable[nid].port = null; |
||||
} |
||||
}) |
||||
}).on('error', (err) => { |
||||
|
||||
throw err; |
||||
}); |
||||
|
||||
// Grab an arbitrary unused port.
|
||||
server.listen({ |
||||
host: host, |
||||
port: port, |
||||
exclusive: true |
||||
}, () => { |
||||
access.info('TCP Server Begin at ', host + ':' + port); |
||||
}); |
||||
|
||||
|
||||
|
||||
|
||||
return o; |
||||
|
||||
|
||||
|
||||
} |
@ -0,0 +1,12 @@ |
||||
{ |
||||
"name": "wiot-director", |
||||
"version": "0.0.1", |
||||
"description": "Director for wIoT systems.", |
||||
"main": "index.js", |
||||
"repository": "https://wiot.js.org", |
||||
"author": "iotcat", |
||||
"license": "MIT", |
||||
"dependencies": { |
||||
"log4js": "^6.3.0" |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. |
||||
# yarn lockfile v1 |
||||
|
||||
|
||||
date-format@^2.1.0: |
||||
version "2.1.0" |
||||
resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" |
||||
integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== |
||||
|
||||
date-format@^3.0.0: |
||||
version "3.0.0" |
||||
resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" |
||||
integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== |
||||
|
||||
debug@^4.1.1: |
||||
version "4.3.1" |
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" |
||||
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== |
||||
dependencies: |
||||
ms "2.1.2" |
||||
|
||||
flatted@^2.0.1: |
||||
version "2.0.2" |
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" |
||||
integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== |
||||
|
||||
fs-extra@^8.1.0: |
||||
version "8.1.0" |
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" |
||||
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== |
||||
dependencies: |
||||
graceful-fs "^4.2.0" |
||||
jsonfile "^4.0.0" |
||||
universalify "^0.1.0" |
||||
|
||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0: |
||||
version "4.2.6" |
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" |
||||
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== |
||||
|
||||
jsonfile@^4.0.0: |
||||
version "4.0.0" |
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" |
||||
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= |
||||
optionalDependencies: |
||||
graceful-fs "^4.1.6" |
||||
|
||||
log4js@^6.3.0: |
||||
version "6.3.0" |
||||
resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb" |
||||
integrity sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw== |
||||
dependencies: |
||||
date-format "^3.0.0" |
||||
debug "^4.1.1" |
||||
flatted "^2.0.1" |
||||
rfdc "^1.1.4" |
||||
streamroller "^2.2.4" |
||||
|
||||
ms@2.1.2: |
||||
version "2.1.2" |
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" |
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== |
||||
|
||||
rfdc@^1.1.4: |
||||
version "1.2.0" |
||||
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.2.0.tgz#9e9894258f48f284b43c3143c68070a4f373b949" |
||||
integrity sha512-ijLyszTMmUrXvjSooucVQwimGUk84eRcmCuLV8Xghe3UO85mjUtRAHRyoMM6XtyqbECaXuBWx18La3523sXINA== |
||||
|
||||
streamroller@^2.2.4: |
||||
version "2.2.4" |
||||
resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.4.tgz#c198ced42db94086a6193608187ce80a5f2b0e53" |
||||
integrity sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ== |
||||
dependencies: |
||||
date-format "^2.1.0" |
||||
debug "^4.1.1" |
||||
fs-extra "^8.1.0" |
||||
|
||||
universalify@^0.1.0: |
||||
version "0.1.2" |
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" |
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== |
Binary file not shown.
Binary file not shown.
@ -1,87 +0,0 @@ |
||||
-- |
||||
-- File: _init.lua |
||||
--[[ |
||||
This is a template for the LFS equivalent of the SPIFFS init.lua. |
||||
It is a good idea to such an _init.lua module to your LFS and do most of the LFS |
||||
module related initialisaion in this. This example uses standard Lua features to |
||||
simplify the LFS API. |
||||
For Lua 5.1, the first section adds a 'LFS' table to _G and uses the __index |
||||
metamethod to resolve functions in the LFS, so you can execute the main |
||||
function of module 'fred' by executing LFS.fred(params), etc. |
||||
It also implements some standard readonly properties: |
||||
LFS._time The Unix Timestamp when the luac.cross was executed. This can be |
||||
used as a version identifier. |
||||
LFS._config This returns a table of useful configuration parameters, hence |
||||
print (("0x%6x"):format(LFS._config.lfs_base)) |
||||
gives you the parameter to use in the luac.cross -a option. |
||||
LFS._list This returns a table of the LFS modules, hence |
||||
print(table.concat(LFS._list,'\n')) |
||||
gives you a single column listing of all modules in the LFS. |
||||
For Lua 5.3 LFS table is populated by the LFS implementation in C so this part |
||||
of the code is skipped. |
||||
---------------------------------------------------------------------------------]] |
||||
|
||||
local lfsindex = node.LFS and node.LFS.get or node.flashindex |
||||
local G=_ENV or getfenv() |
||||
local lfs_t |
||||
if _VERSION == 'Lua 5.1' then |
||||
lfs_t = { |
||||
__index = function(_, name) |
||||
local fn_ut, ba, ma, size, modules = lfsindex(name) |
||||
if not ba then |
||||
return fn_ut |
||||
elseif name == '_time' then |
||||
return fn_ut |
||||
elseif name == '_config' then |
||||
local fs_ma, fs_size = file.fscfg() |
||||
return {lfs_base = ba, lfs_mapped = ma, lfs_size = size, |
||||
fs_mapped = fs_ma, fs_size = fs_size} |
||||
elseif name == '_list' then |
||||
return modules |
||||
else |
||||
return nil |
||||
end |
||||
end, |
||||
|
||||
__newindex = function(_, name, value) -- luacheck: no unused |
||||
error("LFS is readonly. Invalid write to LFS." .. name, 2) |
||||
end, |
||||
} |
||||
|
||||
setmetatable(lfs_t,lfs_t) |
||||
G.module = nil -- disable Lua 5.0 style modules to save RAM |
||||
package.seeall = nil |
||||
else |
||||
lfs_t = node.LFS |
||||
end |
||||
G.LFS = lfs_t |
||||
|
||||
--[[------------------------------------------------------------------------------- |
||||
The second section adds the LFS to the require searchlist, so that you can |
||||
require a Lua module 'jean' in the LFS by simply doing require "jean". However |
||||
note that this is at the search entry following the FS searcher, so if you also |
||||
have jean.lc or jean.lua in SPIFFS, then this SPIFFS version will get loaded into |
||||
RAM instead of using. (Useful, for development). |
||||
See docs/en/lfs.md and the 'loaders' array in app/lua/loadlib.c for more details. |
||||
---------------------------------------------------------------------------------]] |
||||
|
||||
package.loaders[3] = function(module) -- loader_flash |
||||
return lfs_t[module] |
||||
end |
||||
|
||||
--[[---------------------------------------------------------------------------- |
||||
These replace the builtins loadfile & dofile with ones which preferentially |
||||
load from the filesystem and fall back to LFS. Flipping the search order |
||||
is an exercise left to the reader.- |
||||
------------------------------------------------------------------------------]] |
||||
|
||||
local lf = loadfile |
||||
G.loadfile = function(n) |
||||
if file.exists(n) then return lf(n) end |
||||
local mod = n:match("(.*)%.l[uc]a?$") |
||||
local fn = mod and lfsindex(mod) |
||||
return (fn or error (("Cannot find '%s' in FS or LFS"):format(n))) and fn |
||||
end |
||||
|
||||
-- Lua's dofile (luaB_dofile) reaches directly for luaL_loadfile; shim instead |
||||
G.dofile = function(n) return assert(loadfile(n))() end |
@ -1,37 +0,0 @@ |
||||
-- |
||||
-- File: LFS_dummy_strings.lua |
||||
--[[ |
||||
luac.cross -f generates a ROM string table which is part of the compiled LFS |
||||
image. This table includes all strings referenced in the loaded modules. |
||||
If you want to preload other string constants, then one way to achieve this is |
||||
to include a dummy module in the LFS that references the strings that you want |
||||
to load. You never need to call this module; it's inclusion in the LFS image is |
||||
enough to add the strings to the ROM table. Your application can use any strings |
||||
in the ROM table without incuring any RAM or Lua Garbage Collector (LGC) |
||||
overhead. |
||||
The local preload example is a useful starting point. However, if you call the |
||||
following code in your application during testing, then this will provide a |
||||
listing of the current RAM string table. |
||||
do |
||||
local a=debug.getstrings'RAM' |
||||
for i =1, #a do a[i] = ('%q'):format(a[i]) end |
||||
print ('local preload='..table.concat(a,',')) |
||||
end |
||||
This will exclude any strings already in the ROM table, so the output is the list |
||||
of putative strings that you should consider adding to LFS ROM table. |
||||
--------------------------------------------------------------------------------- |
||||
]]-- |
||||
|
||||
-- luacheck: ignore |
||||
local preload = "?.lc;?.lua", "/\n;\n?\n!\n-", "@init.lua", "_G", "_LOADED", |
||||
"_LOADLIB", "__add", "__call", "__concat", "__div", "__eq", "__gc", "__index", |
||||
"__le", "__len", "__lt", "__mod", "__mode", "__mul", "__newindex", "__pow", |
||||
"__sub", "__tostring", "__unm", "collectgarbage", "cpath", "debug", "file", |
||||
"file.obj", "file.vol", "flash", "getstrings", "index", "ipairs", "list", "loaded", |
||||
"loader", "loaders", "loadlib", "module", "net.tcpserver", "net.tcpsocket", |
||||
"net.udpsocket", "newproxy", "package", "pairs", "path", "preload", "reload", |
||||
"require", "seeall", "wdclr", "not enough memory", "sjson.decoder","sjson.encoder", |
||||
"tmr.timer", "table", "string", "number", "function", "director", "collect", "__getInfo", "__setNS", "__checkNS", |
||||
"__setFunc", "__restart", "default", "from", "to", "body", "name", "online", "func", "nid", |
||||
"connection", "disconnection", "receive", "set", "get", "del", "list", "clear", |
||||
'config.json', '__system/flag', 'func.json', '__data/', '__system/swap/' |
@ -1,488 +0,0 @@ |
||||
-- WIOT Lua Firmware on NodeMCU |
||||
__main = coroutine.create(function(__run) |
||||
--Packages Used: file, sjson, encoder, timer, node, wifi, net, gpio, uart |
||||
|
||||
--Global Constant |
||||
----------------- |
||||
local CONFIG_PATH = 'config.json'; |
||||
local FLAG_PATH = '__system/flag'; |
||||
local FUNC_PATH = 'func.json'; |
||||
local DB_PREFIX = '__data/'; |
||||
local SWAP_PREFIX = '__system/swap/'; |
||||
local SIGNAL_LED = 0; |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('Global Constant', node.heap()) |
||||
|
||||
|
||||
--Global Methods (in RAM) |
||||
---------------- |
||||
-- Package Methods |
||||
local pack = { |
||||
encode = function(obj) |
||||
local status, json = pcall(sjson.encode, {obj}); |
||||
if status then |
||||
return string.sub(json, 2, -2); |
||||
else |
||||
return ''; |
||||
end |
||||
end, |
||||
decode = function(str) |
||||
if type(str) ~= 'string' then |
||||
return nil |
||||
end; |
||||
local status, obj = pcall(sjson.decode, '['..str..']'); |
||||
if status then |
||||
return obj[1]; |
||||
else |
||||
return nil; |
||||
end |
||||
end |
||||
} |
||||
-- File Object Operation |
||||
local fs = { |
||||
read = function(f)--f:filename |
||||
return pack.decode(file.getcontents(f)); |
||||
end, |
||||
write = function(f, obj) |
||||
local res = pack.encode(obj); |
||||
if res == '' then |
||||
return false; |
||||
else |
||||
while(file.getcontents(f) ~= res) do |
||||
file.putcontents(f, res); |
||||
end |
||||
return true; |
||||
end |
||||
end |
||||
} |
||||
--split string |
||||
local stringSplit = function(str, reps) |
||||
local resultStrList = {} |
||||
string.gsub(str,'[^'..reps..']+', function (w) |
||||
table.insert(resultStrList,w); |
||||
end); |
||||
return resultStrList; |
||||
end |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('Global Methods (in RAM)', node.heap()) |
||||
|
||||
|
||||
-- Signal LED Begin |
||||
------------------- |
||||
--Mode Init |
||||
gpio.mode(SIGNAL_LED, gpio.OUTPUT); |
||||
local signal_timer = tmr.create(); |
||||
--Control Methods |
||||
local setSignalInterval = function(interval) |
||||
signal_timer:alarm(interval, tmr.ALARM_AUTO, function() |
||||
if gpio.read(SIGNAL_LED) == gpio.HIGH then |
||||
gpio.write(SIGNAL_LED, gpio.LOW); |
||||
else |
||||
gpio.write(SIGNAL_LED, gpio.HIGH); |
||||
end |
||||
end); |
||||
end |
||||
--SIGNAL: Start |
||||
setSignalInterval(200); |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('Signal LED Begin', node.heap()) |
||||
|
||||
|
||||
--Load Config (Assume config file is |
||||
-- exist and good) |
||||
------------- |
||||
local config = fs.read(CONFIG_PATH); |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('Load Config', node.heap()) |
||||
|
||||
--Check Flag (Assume flag is at FLAG_PATH in SPIFF) |
||||
--(flag represent the number of startup failures before) |
||||
------------ |
||||
--Load Flag |
||||
local flag = file.getcontents(FLAG_PATH); |
||||
if flag == nil then |
||||
flag = 0; |
||||
else |
||||
flag = tonumber(flag) + 1; |
||||
end |
||||
--Update Flag Record in SPIFF |
||||
file.putcontents(FLAG_PATH, tostring(flag)); |
||||
--Failures > 2 Times |
||||
if flag > 2 then |
||||
--remove func file to run in DEFAULT mode |
||||
file.remove(FUNC_PATH); |
||||
end |
||||
--Release Resource |
||||
flag = nil; |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('Check Flag', node.heap()) |
||||
|
||||
|
||||
--WiFi Setup |
||||
------------ |
||||
--Set Mode to STA |
||||
wifi.setmode(wifi.STATION); |
||||
--Set HostName to WIOT-<nid> |
||||
wifi.sta.sethostname('WIOT-'..config.nid); |
||||
--Configure the WiFi |
||||
wifi.sta.config(config.wifi); |
||||
--Connect to AP |
||||
tmr.create():alarm(1000, tmr.ALARM_AUTO, function(timer) |
||||
--print('Setting WiFi'); |
||||
if wifi.sta.getip() ~= nil then --When WiFi is Ready |
||||
--print('WiFi OK'); |
||||
--Release Timer Resources |
||||
timer:unregister(); |
||||
--Resume Main Process |
||||
coroutine.resume(__main); |
||||
end |
||||
end); |
||||
--Suspend Main Process Until WiFi is ready |
||||
coroutine.yield(); |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('WiFi Setup', node.heap()) |
||||
|
||||
|
||||
--SIGNAL: WiFi OK |
||||
setSignalInterval(600); |
||||
|
||||
|
||||
--SWAP Setup (SWAP is a part of space in FLASH |
||||
-- specifically storage some low-frequency |
||||
-- usage data and methods) |
||||
-------------- |
||||
-- SWAP Method |
||||
local swap = {} |
||||
function swap:set(key, val, prefix) |
||||
return fs.write(prefix..encoder.toBase64(key), val); |
||||
end |
||||
function swap:get(key, prefix) |
||||
return fs.read(prefix..encoder.toBase64(key)); |
||||
end |
||||
function swap:del(key, prefix) |
||||
file.remove(prefix..encoder.toBase64(key)); |
||||
end |
||||
function swap:list(prefix) |
||||
local fileList = file.list(prefix..'.'); |
||||
local keys = {}; |
||||
for k, v in pairs(fileList) do |
||||
table.insert(keys, encoder.fromBase64(string.sub(k, #prefix + 1))); |
||||
end |
||||
return keys; |
||||
end |
||||
function swap:clear(prefix) |
||||
for index, key in pairs(self:list(prefix)) do |
||||
self:del(key, prefix); |
||||
end |
||||
end |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('SWAP Setup', node.heap()) |
||||
|
||||
|
||||
--Name Service (NS) Setup |
||||
-------------------- |
||||
local ns = setmetatable({}, { |
||||
__index = function(table, key) |
||||
local res = swap:get(key, SWAP_PREFIX..'NS/'); |
||||
if res == nil or res.port == nil or res.ip == nil then |
||||
return nil; |
||||
else |
||||
return res; |
||||
end |
||||
end, |
||||
__newindex = function(table, key, val) |
||||
swap:set(key, val, SWAP_PREFIX..'NS/'); |
||||
end |
||||
}); |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('NS Setup', node.heap()) |
||||
|
||||
|
||||
--MSG Register Init |
||||
------------------- |
||||
--MSG Register Clear |
||||
swap:clear(SWAP_PREFIX..'M/'); |
||||
--MSG register (in SWAP) |
||||
local msgReg = setmetatable({}, { |
||||
__index = function(table, key) |
||||
local res = swap:get(key, SWAP_PREFIX..'M/'); |
||||
if res == nil then |
||||
return nil; |
||||
else |
||||
return loadstring(encoder.fromBase64(res)); |
||||
end |
||||
end, |
||||
__newindex = function(table, key, val) |
||||
if type(val) ~= 'function' then |
||||
swap:del(key, SWAP_PREFIX..'M/'); |
||||
else |
||||
swap:set(key, encoder.toBase64(string.dump(val)), SWAP_PREFIX..'M/'); |
||||
end |
||||
end |
||||
}); |
||||
--MSG Reg Method |
||||
msgReg_run = function(data) |
||||
--decode data |
||||
local data = pack.decode(data); |
||||
--check data |
||||
if type(data) ~= 'table' or type(data.from) ~= 'string' or type(data.name) ~= 'string' or data.to ~= config.nid then |
||||
return nil; |
||||
end; |
||||
--Search mached methods in MSG register |
||||
local method = msgReg[data.name]; |
||||
if type(method) == 'function' then |
||||
print(pcall(method, data.from, data.body)); |
||||
end |
||||
end |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('MSG Register Init', node.heap()) |
||||
|
||||
|
||||
--UDP Startup |
||||
------------- |
||||
--Start UDP Service |
||||
local udpd = net.createUDPSocket(); |
||||
udpd:listen(config.msg.port); |
||||
udpd:on('receive', function(socket, data, port, ip) |
||||
msgReg_run(data); |
||||
end); |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('UDP Startup', node.heap()) |
||||
|
||||
--TCP Startup |
||||
------------- |
||||
--Start TCP Service |
||||
local tcpd = net.createConnection(net.TCP, 0); |
||||
local socket = nil; |
||||
tcpd:on('connection', function(sck, data) |
||||
socket = sck; |
||||
--SIGNAL: TCP OK, delete timer object |
||||
setSignalInterval = nil; |
||||
signal_timer:unregister(); |
||||
signal_timer = nil; |
||||
gpio.write(SIGNAL_LED, gpio.HIGH); |
||||
end); |
||||
tcpd:on('disconnection', function(sck, data) |
||||
socket = nil; |
||||
end); |
||||
tcpd:on('receive', function(sck, data) |
||||
msgReg_run(data); |
||||
end); |
||||
--connect to director |
||||
tmr.create():alarm(3000, tmr.ALARM_AUTO, function() |
||||
if socket == nil then |
||||
tcpd:connect(config.director.port, config.director.ip); |
||||
end |
||||
end) |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('TCP Startup', node.heap()) |
||||
|
||||
|
||||
--MSG Setup |
||||
-------------- |
||||
-- MSG Methods |
||||
local msg = { |
||||
send = function(to, name, body, proxy) |
||||
local port_ip = ns[to]; |
||||
local package = pack.encode({ |
||||
from = config.nid, |
||||
to = to, |
||||
name = name, |
||||
body = body |
||||
}); |
||||
if port_ip == nil or proxy == true then |
||||
if socket ~= nil then |
||||
socket:send(package); |
||||
return ture; |
||||
else |
||||
return false; |
||||
end |
||||
end |
||||
udpd:send(port_ip.port, port_ip.ip, package); |
||||
return true; |
||||
end, |
||||
onSend = function(name, method) |
||||
msgReg[name] = method; |
||||
end |
||||
} |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('MSG Setup', node.heap()) |
||||
|
||||
|
||||
--DB Setup |
||||
-------------- |
||||
--DB Methods |
||||
local db = { |
||||
set = function(key, val) |
||||
return swap:set(key, val, DB_PREFIX); |
||||
end, |
||||
get = function(key) |
||||
return swap:get(key, DB_PREFIX); |
||||
end, |
||||
del = function(key, prefix) |
||||
swap:del(key, DB_PREFIX); |
||||
end, |
||||
list = function(prefix) |
||||
return swap:list(DB_PREFIX); |
||||
end, |
||||
clear = function() |
||||
swap:clear(DB_PREFIX); |
||||
end |
||||
} |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('DB Setup', node.heap()) |
||||
|
||||
|
||||
--Heartbeat Startup |
||||
------------------- |
||||
local tcpd_heartbeat_timer = tmr.create():alarm(config.director.HeartbeatInterval, tmr.ALARM_AUTO, function() |
||||
if socket then |
||||
socket:send(config.nid..':'..tostring(tmr.time())); |
||||
end |
||||
end); |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('Heartbeat Startup', node.heap()) |
||||
|
||||
|
||||
--System APIs |
||||
------------- |
||||
--getInfo API |
||||
rawset(msgReg, '__getInfo', function(from, body) |
||||
if from == 'director' then |
||||
msg.send('director', '__getInfo', { |
||||
remainHeap = node.heap(), |
||||
remainFS = file.fsinfo(), |
||||
msgPort = config.msg.port, |
||||
HeartbeatInterval = config.director.HeartbeatInterval, |
||||
ns = swap:list(SWAP_PREFIX..'NS/'), |
||||
funcID = func.id |
||||
}, true); |
||||
end |
||||
end); |
||||
--setNS API |
||||
rawset(msgReg, '__setNS', function(from, body) |
||||
if from == 'director' and type(body) == 'table' and type(body.nid) == 'string' then |
||||
if type(body.port) == 'number' and type(body.ip) == 'string' then |
||||
ns[body.nid] = { |
||||
port = body.port, |
||||
ip = body.ip |
||||
} |
||||
else |
||||
ns[body.nid] = nil; |
||||
end |
||||
end |
||||
end); |
||||
--checkNS API |
||||
rawset(msgReg, '__checkNS', function(from, body) |
||||
if type(body) == 'string' then |
||||
if from == 'director' then |
||||
msg.send(body, '__checkNS', config.nid); |
||||
else |
||||
msg.send('director', '__checkNS', body, true); |
||||
end |
||||
end |
||||
end); |
||||
--setFunc API |
||||
rawset(msgReg, '__setFunc', function(from, body) |
||||
if from == 'director' then |
||||
if type(body.func) == 'table' and type(body.func.id) == 'string' and type(body.func.online) == 'string' then |
||||
fs.write(FUNC_PATH, body.func); |
||||
if type(body.ns) == 'table' then |
||||
swap:clear(SWAP_PREFIX..'NS/'); |
||||
for k, v in pairs(body.ns) do |
||||
ns[k] = v; |
||||
end |
||||
end |
||||
node.restart(); |
||||
end |
||||
end |
||||
end); |
||||
--restart API |
||||
msgReg['__restart'] = function(from, body) |
||||
if from == 'director' then |
||||
node.restart(); |
||||
end |
||||
end |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('System APIs', node.heap()) |
||||
|
||||
|
||||
--FUNC Startup |
||||
-------------- |
||||
--Load Func to RAM |
||||
local func = fs.read(FUNC_PATH); |
||||
--Default Mode |
||||
if type(func) ~= 'table' or type(func.id) ~= 'string' or type(func.online) ~= 'string' then |
||||
func = { |
||||
id = 'default', |
||||
online = '' |
||||
} |
||||
end |
||||
--warp running |
||||
--print(func.id) |
||||
local status, errmsg = __run(func.online, db, msg); |
||||
--print(status, errmsg) |
||||
if status then |
||||
tmr.create():alarm(10000, tmr.ALARM_SINGLE, function() |
||||
file.remove(FLAG_PATH); |
||||
end); |
||||
else |
||||
node.restart(); |
||||
end |
||||
status, errmsg = nil, nil; |
||||
|
||||
--For Debug Purpose |
||||
--collectgarbage("collect") |
||||
--print('FUNC Startup', node.heap()) |
||||
|
||||
|
||||
--SIGNAL: FUNC OK |
||||
setSignalInterval(1000); |
||||
|
||||
end); |
||||
|
||||
--Boot __main env with a sandbox for user program running |
||||
coroutine.resume(__main, function(func, db, msg) |
||||
local status, res = pcall(loadstring, 'return function(db, msg) '..func..' end'); |
||||
if not status then |
||||
return status, res; |
||||
else |
||||
status, res = pcall(res); |
||||
if not status then |
||||
return status, res; |
||||
else |
||||
return pcall(res, db, msg); |
||||
end |
||||
end |
||||
return |
||||
end); |
Loading…
Reference in new issue