Created
October 27, 2019 10:55
-
-
Save TerryE/721ffc10d2e397b5f25eec2a2c371d3a to your computer and use it in GitHub Desktop.
Rough cut coroutine based provisioning system
This file contains hidden or 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
local post, yield = node.task.post, coroutine.yield | |
local max_namelen = 31 | |
local request = '\z | |
%s %s%s.img HTTP/1.1\r\n\z | |
User-Agent: ESP8266 app (linux-gnu)\r\n\z | |
Accept: application/octet-stream\r\n\z | |
Accept-Encoding: identity\r\n\z | |
Host: %s\r\n\z | |
Connection: %s\r\n\r\n' | |
local host, dir, image, onDone | |
local cleanup, receiver | |
local function protected_receiver (soc, rec) -- Upval:post, next_rec, image | |
local status = tonumber(rec:match('HTTP/1.1%s+(%d+)') or '0') | |
assert(status == 200, "Image %s does not exist" % image) | |
local etag = rec:lower():match('\netag: *"([%d%-a-f]*)"\r') | |
local tailsize = max_namelen - #image - #'.img.' | |
local filename = image .. '.img.' .. (etag or ''):sub(-tailsize) | |
if etag and file.exists(filename) then | |
return "Image %s already downloaded" % image | |
end | |
soc:send(request % {'GET', dir, image, host,'Close'}) | |
soc, rec = yield() | |
local i = rec:find('\r\n\r\n',1,true) or 1 | |
local hdr = rec:sub(1,i+1):lower() | |
rec = rec:sub(i+4) | |
local size = tonumber(hdr:match('\ncontent%-length: *(%d+)\r')) or | |
error 'invalid size returned' | |
assert(etag == hdr:match('\netag: *"([%d%-a-f]*)"\r'), "Etag missmatch") | |
local f = file.open(filename, 'w+') | |
local j, nr = 0, 0 | |
repeat | |
f:write(rec) | |
j, nr = j + #rec, nr + 1 | |
if nr % 4 == 1 then -- hold and unhold socket to prevent overrun | |
soc:hold() | |
post(0, function() soc:unhold() end) | |
end | |
collectgarbage() | |
if j < size then soc, rec = yield() end | |
until j >= size | |
f:close() | |
if j ~= size then file.erase(filename) end | |
assert(j==size, "File size mismatch %s removed" % filename) | |
soc:on('receive', nil) | |
for n,l in pairs(file.list('^%s.img' % image)) do | |
if n ~= filename then file.remove(n) end | |
end | |
return "Image %s created as %s (%d bytes)" % {image, filename, size} | |
end | |
receiver = coroutine.wrap( | |
function(soc, rec) -- Upval: protected_receiver, post | |
local status, message = pcall(protected_receiver, soc, rec) | |
post(function() cleanup(status, message, soc) end) | |
end) | |
local function doRequest(sk,hostIP) | |
if not hostIP then return end | |
local con = net.createConnection(net.TCP,0) | |
con:connect(80,hostIP) | |
con:on("connection", function(sck) -- Upval: dir, iage, host | |
sck:send(request % {'HEAD', dir, image, host,'Keep-Alive'}) | |
sck:on("receive", receiver) | |
end) | |
end | |
local cleanTBD = true | |
cleanup = function (status, message, soc) -- Upval: onDone, post | |
if not cleanTBD then return end | |
cleanTBD = false | |
if soc and soc.close then soc:__gc() end | |
receiver=nil | |
collectgarbage() | |
if onDone and type(onDone)=="function" then | |
post(function() onDone(status, message) end) -- Upval onDone, status, message | |
else | |
print ("%s: %s" % {status and "Success" or "Failed", message}) | |
end | |
end | |
return function(h, d, i, f) | |
host, dir, image, onDone = h, d, i, f | |
net.dns.resolve(host, doRequest) | |
end |
This file contains hidden or 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
local ssid, pwd = 'passiveHouse','IN.TE.SPERANT.DOMINE' | |
local host, dir = '192.168.1.2','/test2/' | |
local images = 'api attrib big bitwise closure code constructs \z | |
coroutine db errors esp-files events files gc goto \z | |
literals locals main math nextvar pm sort stackbreak \z | |
strings tpack utf8 vararg verybig ' | |
local check | |
local post,yield = node.task.post, coroutine.yield | |
local httpload = dofile'HTTPloadfile.lua' | |
check = coroutine.wrap(function(t) | |
if wifi.sta.status() ~= wifi.STA_GOTIP and not wifi.sta.getip() then | |
wifi.setmode(wifi.STATION, false) | |
wifi.sta.config { ssid = ssid, pwd = pwd, save = false } | |
while wifi.sta.status() == wifi.STA_GOTIP do | |
uart.write(0,".") | |
yield() | |
end | |
end | |
print("NodeMCU connected", node.heap(), wifi.sta.getip()) | |
t:unregister() | |
for i in images:gmatch("([%w%-]+) ") do | |
print ("Starting download for %s" % i) | |
post(function() httpload(host, dir, i, check) end) | |
local status, message = yield() | |
if status then | |
print(message) | |
else | |
print("Load failed:", message) | |
end | |
end | |
end) | |
tmr.create():alarm(500, tmr.ALARM_AUTO,coroutine.wrap(check)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment