6 changed files with 140 additions and 393 deletions
@ -1,2 +0,0 @@ |
|||||
SSID="something" |
|
||||
PASSWORD="yourpassword" |
|
Binary file not shown.
Binary file not shown.
@ -1,11 +1,14 @@ |
|||||
nodemcu-tool reset |
nodemcu-tool reset |
||||
esptool.py erase_flash |
esptool.py --port /dev/ttyUSB0 erase_flash |
||||
esptool.py --port /dev/ttyUSB0 write_flash -fm qio 0x00000 firmware/nodemcu-release-19-modules-2021-11-10-19-19-06-float.bin |
esptool.py --port /dev/ttyUSB0 write_flash -fm qio 0x00000 firmware/firmware.bin |
||||
|
|
||||
nodemcu-tool upload *.lua libs/* |
CONN_DELAY=1000 |
||||
|
|
||||
|
nodemcu-tool --connection-delay $CONN_DELAY upload *.lua libs/* |
||||
|
#nodemcu-tool upload *.lua libs/* |
||||
|
|
||||
while [[ $? != 0 ]]; do |
while [[ $? != 0 ]]; do |
||||
nodemcu-tool upload *.lua libs/* |
nodemcu-tool --connection-delay $CONN_DELAY upload *.lua libs/* |
||||
done |
done |
||||
|
# |
||||
echo 'dofile("init.lua")' | nodemcu-tool terminal |
echo 'dofile("init.lua")' | nodemcu-tool --connection-delay $CONN_DELAY terminal |
||||
|
@ -1,293 +1,184 @@ |
|||||
-- Never change these unless the board changes |
-- Never change these unless the board changes |
||||
echo_pin = 2 |
trig_pin = 5 |
||||
trig_pin = 3 |
|
||||
light_pin = 5 |
|
||||
|
|
||||
use_sonar = false |
|
||||
light_on = false |
light_on = false |
||||
|
|
||||
if adc.force_init_mode(adc.INIT_ADC) |
function reset_light() |
||||
then |
gpio.write(trig_pin, gpio.LOW) |
||||
node.restart() |
tmr.delay(100000) |
||||
return -- don't bother continuing, the restart is scheduled |
gpio.write(trig_pin, gpio.HIGH) |
||||
|
tmr.delay(100000) |
||||
|
gpio.write(trig_pin, gpio.LOW) |
||||
end |
end |
||||
|
|
||||
print("System voltage (mV):", adc.readvdd33(0)) |
|
||||
|
|
||||
gpio.mode(light_pin, gpio.OUTPUT) |
|
||||
gpio.mode(echo_pin, gpio.INT) -- interrupt mode |
|
||||
gpio.mode(trig_pin, gpio.OUTPUT) |
|
||||
gpio.write(light_pin, gpio.HIGH) |
|
||||
|
|
||||
function toggle_light() |
function toggle_light() |
||||
if light_on then |
print("toggling") |
||||
gpio.write(light_pin, gpio.HIGH) |
reset_light() |
||||
else |
|
||||
gpio.write(light_pin, gpio.LOW) |
|
||||
end |
|
||||
light_on = not light_on |
light_on = not light_on |
||||
|
file.putcontents("./light_status", light_on and "on" or "off") |
||||
end |
end |
||||
|
|
||||
function turn_light_on() |
function turn_light_on() |
||||
gpio.write(light_pin, gpio.LOW) |
if not light_on then |
||||
|
toggle_light() |
||||
|
end |
||||
end |
end |
||||
|
|
||||
function turn_light_off() |
function turn_light_off() |
||||
gpio.write(light_pin, gpio.HIGH) |
if light_on then |
||||
end |
toggle_light() |
||||
|
|
||||
function tablelen(T) |
|
||||
local count = 0 |
|
||||
for _ in pairs(T) do count = count + 1 end |
|
||||
return count |
|
||||
end |
|
||||
|
|
||||
samples = {} |
|
||||
distance = {start_v=0.0, end_v=0.0} |
|
||||
|
|
||||
sample_rate = 4 -- number of samples it requires before triggering, recommended <= 5, >=3 |
|
||||
max_stderr = 0.5 -- margin of error allowed before triggering the light, recommended <= 1 |
|
||||
distance_max = 30 -- threshold for how far an object must be to trigger it in cm |
|
||||
distance_min = 10 -- minimum distance (below this amount don't trigger it), recommended >= 5, <= 20 |
|
||||
|
|
||||
-- Get the mean value of a table |
|
||||
function mean(t) |
|
||||
-- http://lua-users.org/wiki/SimpleStats |
|
||||
local sum = 0 |
|
||||
local count = 0 |
|
||||
|
|
||||
for k,v in pairs(t) do |
|
||||
if type(v) == 'number' then |
|
||||
sum = sum + v |
|
||||
count = count + 1 |
|
||||
end |
|
||||
end |
end |
||||
return (sum / count) |
|
||||
end |
end |
||||
|
|
||||
function stdev(t) |
light_status = file.getcontents("./light_status") |
||||
-- http://lua-users.org/wiki/SimpleStats |
if light_status == "on" then |
||||
local m |
turn_light_on() |
||||
local vm |
end |
||||
local sum = 0 |
print("Booted up") |
||||
local count = 0 |
|
||||
local result |
|
||||
|
|
||||
m = mean(t) |
|
||||
|
|
||||
for k, v in pairs(t) do |
|
||||
if type(v) == 'number' then |
|
||||
vm = v - m |
|
||||
sum = sum + (vm * vm) |
|
||||
count = count + 1 |
|
||||
end |
|
||||
end |
|
||||
|
|
||||
result = math.sqrt(sum / (count-1)) |
gpio.mode(trig_pin, gpio.OUTPUT) |
||||
|
gpio.write(trig_pin, gpio.LOW) |
||||
|
|
||||
return result |
function hex_to_char(x) |
||||
|
return string.char(tonumber(x, 16)) |
||||
end |
end |
||||
|
|
||||
function stderr(t) |
function get_time() |
||||
-- based on the definition of standard error |
local t = tmr.time() |
||||
return stdev(t) / math.sqrt(tablelen(t)) |
local hours = t/3600 |
||||
|
local seconds_leftover = t % 3600 |
||||
|
return tostring(hours) .. " hours, " .. tostring(minutes_leftover) |
||||
end |
end |
||||
|
|
||||
function measure(level, ts, evcount) |
function urldecode(url) |
||||
if level == 1 then |
if url == nil then |
||||
distance["start_v"] = ts |
return |
||||
else |
|
||||
-- check if there's a low voltage but no starting high voltage |
|
||||
if distance["start_v"] > 0 then |
|
||||
distance["end_v"] = ts |
|
||||
|
|
||||
if distance["start_v"] >= distance["end_v"] then |
|
||||
distance = {start_v=0.0, end_v=0.0} -- bad reading |
|
||||
return |
|
||||
end |
|
||||
|
|
||||
-- See https://www.adafruit.com/product/165 |
|
||||
local temperature = ((adc.read(0)*100)-50)/1000 |
|
||||
|
|
||||
-- See http://hyperphysics.phy-astr.gsu.edu/hbase/Sound/souspe.html |
|
||||
local speed_of_sound = (331.4 + 0.6*temperature)/10000 |
|
||||
|
|
||||
-- See https://randomnerdtutorials.com/esp8266-nodemcu-hc-sr04-ultrasonic-arduino/ |
|
||||
local d = ((distance["end_v"] - distance["start_v"]) * speed_of_sound) / 2.0 |
|
||||
|
|
||||
-- Only care about values between 10 and 50 |
|
||||
if math.floor(d) > distance_min and d < distance_max then |
|
||||
table.insert(samples, d) |
|
||||
local s = stderr(samples) |
|
||||
if s > max_stderr and tablelen(samples) == (sample_rate - 1) then -- start over if it would cause stderr to be too great |
|
||||
samples = {} |
|
||||
distance = {start_v=0.0, end_v=0.0} |
|
||||
end |
|
||||
|
|
||||
end |
|
||||
distance = {start_v=0, end_v=0} |
|
||||
end |
|
||||
end |
end |
||||
|
url = url:gsub("+", " ") |
||||
|
url = url:gsub("%%(%x%x)", hex_to_char) |
||||
|
return url |
||||
|
end |
||||
|
|
||||
local s_num = tablelen(samples) |
function get_info(group) |
||||
|
local info = node.info(group) |
||||
if s_num >= sample_rate then |
local result = "<table><thead><tr><th colspan='2'>" .. tostring(group) .. "</th></thead><tbody>" |
||||
local distance_mean = mean(samples) |
for key, value in pairs(info) do |
||||
local s = stderr(samples) |
result = result .. "<tr><td>" .. tostring(key) .. "</td><td>" .. tostring(value) .. "</td></tr>" |
||||
if s < max_stderr and distance_mean < distance_max and distance_mean > distance_min then |
|
||||
print("stder = " .. s) |
|
||||
print("distance = " .. distance_mean) |
|
||||
if not light_on then |
|
||||
toggle_light() |
|
||||
end |
|
||||
end |
|
||||
samples = {} |
|
||||
end |
end |
||||
end |
|
||||
|
|
||||
function trig() |
return result .. "</tbody></table>" |
||||
gpio.write(trig_pin, gpio.HIGH) |
|
||||
tmr.delay(11) |
|
||||
gpio.write(trig_pin, gpio.LOW) |
|
||||
end |
end |
||||
|
|
||||
-- load credentials, 'SSID' and 'PASSWORD' declared and initialize in there |
|
||||
dofile("credentials.lua") |
|
||||
|
|
||||
function startup() |
function startup() |
||||
|
sntp.sync( |
||||
|
nil, |
||||
|
function(sec, usec, server, info) |
||||
|
print('synced ntp ', sec, usec, server) |
||||
|
end, |
||||
|
function() |
||||
|
print('failed to sync ntp') |
||||
|
end, |
||||
|
1 -- auto-repeat sync |
||||
|
) |
||||
if file.open("init.lua") == nil then |
if file.open("init.lua") == nil then |
||||
print("init.lua deleted or renamed") |
print("init.lua deleted or renamed") |
||||
else |
else |
||||
print("Running") |
cron_error = nil |
||||
file.close("init.lua") |
file.close("init.lua") |
||||
|
|
||||
print("Starting up") |
print("Starting up") |
||||
sntp.sync(nil, |
|
||||
function(sec, usec, server, info) |
|
||||
print("sync'd") |
|
||||
|
|
||||
tm = rtctime.epoch2cal(rtctime.get()) |
|
||||
|
|
||||
print(tm["hour"]) |
|
||||
print(tm["min"]) |
|
||||
|
|
||||
if tm["hour"] >= 12 or tm["hour"] < 2 then |
|
||||
if tm["hour"] == 12 and tm["min"] < 30 then |
|
||||
return |
|
||||
end |
|
||||
turn_light_on() |
|
||||
end |
|
||||
|
|
||||
if tm["hour"] >= 2 and tm["hour"] < 12 then |
|
||||
turn_light_off() |
|
||||
end |
|
||||
|
|
||||
cron.schedule("0 02 * * *", function(e) -- 9 pm EST is 2 UTC |
require("httpserver").createServer(8080, function(req, res) |
||||
print("Turning light off") |
--print("+R", req.method, req.url, node.heap()) |
||||
turn_light_off() |
|
||||
end) |
|
||||
|
|
||||
cron.schedule("30 12 * * *", function(e) -- 7:30 am EST is 12:30 UTC |
req.ondata = function(self, chunk) |
||||
print("Turning light on") |
--print("+B", chunk and #chunk, node.heap()) |
||||
turn_light_on() |
|
||||
end) |
|
||||
|
|
||||
if use_sonar then |
|
||||
tmr.create():alarm(61, tmr.ALARM_AUTO, trig) |
|
||||
gpio.trig(echo_pin, "both", measure) |
|
||||
end |
|
||||
end, |
|
||||
nil, 1) |
|
||||
|
|
||||
require("httpserver").createServer(80, function(req, res) |
|
||||
print("+R", req.method, req.url, node.heap()) |
|
||||
|
|
||||
req.onheader = function(self, name, value) -- luacheck: ignore |
|
||||
print("+H", name, value) |
|
||||
end |
|
||||
-- setup handler of body, if any |
|
||||
req.ondata = function(self, chunk) -- luacheck: ignore |
|
||||
print("+B", chunk and #chunk, node.heap()) |
|
||||
print(req.url) |
print(req.url) |
||||
if not chunk then |
if chunk ~= nil then |
||||
-- reply |
|
||||
res:send(nil, 200) |
|
||||
res:send_header("Connection", "close") |
|
||||
if req.url == "/toggle" then |
if req.url == "/toggle" then |
||||
toggle_light() |
toggle_light() |
||||
elseif req.url == "/on" then |
elseif req.url == "/on" then |
||||
turn_light_on() |
turn_light_on() |
||||
elseif req.url == "/off" then |
elseif req.url == "/off" then |
||||
turn_light_off() |
turn_light_off() |
||||
elseif req.url == "/toggle_sonar" then |
elseif req.url == "/add_job" then |
||||
use_sonar = not use_sonar |
post_data = urldecode(chunk) |
||||
|
if string.len(post_data) > 6 then |
||||
|
local a, b = string.find(post_data, "=") |
||||
|
local cron_expression = post_data:sub(a+1) |
||||
|
|
||||
|
local ran, error_msg = pcall(cron.schedule, cron_expression, toggle_light) |
||||
|
print(error_msg) |
||||
|
else |
||||
|
ran = false |
||||
|
error_msg = "invalid" |
||||
|
end |
||||
|
|
||||
|
if not ran then |
||||
|
print("post_data = " .. post_data) |
||||
|
cron_error = error_msg |
||||
|
end |
||||
|
elseif req.url == "/reset_light" then |
||||
|
reset_light() |
||||
|
elseif req.url == "/clear_jobs" then |
||||
|
cron.reset() |
||||
|
elseif req.url == "/reboot" then |
||||
|
cron.reset() |
||||
|
node.restart() |
||||
|
end |
||||
|
end |
||||
|
if not chunk then |
||||
|
local toggle_status = light_on and "On" or "Off" |
||||
|
local color = light_on and "green" or "black" |
||||
|
-- reply |
||||
|
if req.url == "/" then |
||||
|
res:send(nil, 200) |
||||
|
res:send_header("Content-Type", "text/html") |
||||
|
res:send_header("Connection", "close") |
||||
|
res:send("<style>.button{text-decoration:underline;}.body{padding:0; margin:0;}.par{display:flex;flex-direction:row;}.a{margin: auto;width:50%;}.b{margin: auto;width:50%;}</style><html><body><div class='par'><div class='a'><span>Uptime: ".. tostring(tmr.time()) .. " seconds</span><form action='/add_job' method='post'><div class='button'><label for='name'>Enter a cron expression (UTC)</label><input type='text' name='cron' id='cron' required></div><div><input type='submit' value='Add cron expression'></div></form><br/><form action='/clear_jobs' method='post'><button style=\"color:black;\">Clear Jobs</button></form><br/><form action='/toggle' method='post'><button style=\"color:black;\">Toggle</button><span style=\"color:" .. color .. ";\"> ".. toggle_status .. "</span></form><br/><form action='/reset_light' method='post'><button style=\"color:black;\">Reset Light</button></form><br/><form action='/reboot' method='post'><button style=\"color:red;\">Reboot</button></form><br/></div><div class='b'>" .. get_info("hw") .. get_info("build_config") .. get_info("sw_version") .. "</div></div></body></html>") |
||||
|
res:send("\r\n") |
||||
|
elseif req.url == "/toggle" or req.url == "/reset_light" then |
||||
|
res:send(nil, 303) |
||||
|
res:send_header("Location", "/") |
||||
|
res:send_header("Connection", "close") |
||||
|
res:send("switching light\r\n") |
||||
|
res:send("\r\n") |
||||
|
elseif req.url == "/add_job" then |
||||
|
if cron_error then |
||||
|
res:send(nil, 400) |
||||
|
res:send_header("Content-Type", "text/html") |
||||
|
res:send_header("Connection", "close") |
||||
|
res:send(cron_error .. "\r\n") |
||||
|
res:send("\r\n") |
||||
|
cron_error = nil |
||||
|
else |
||||
|
res:send(nil, 303) |
||||
|
res:send_header("Location", "/") |
||||
|
res:send_header("Connection", "close") |
||||
|
res:send("\r\n") |
||||
|
end |
||||
|
else |
||||
|
res:send(nil, 303) |
||||
|
res:send_header("Location", "/") |
||||
|
res:send_header("Connection", "close") |
||||
|
res:send("\r\n") |
||||
end |
end |
||||
|
|
||||
res:send("The light is " .. (light_on and "on\n" or "off\n")) |
|
||||
res:finish() |
res:finish() |
||||
end |
end |
||||
end |
end |
||||
end) |
|
||||
|
|
||||
|
end) |
||||
end |
end |
||||
end |
end |
||||
|
|
||||
-- Define WiFi station event callbacks |
enduser_setup.start( |
||||
wifi_connect_event = function(T) |
function() |
||||
print("Connection to AP("..T.SSID..") established!") |
if wifi.sta.getip() ~= nil then |
||||
print("Waiting for IP address...") |
print("Connected to WiFi as:" .. wifi.sta.getip()) |
||||
if disconnect_ct ~= nil then disconnect_ct = nil end |
tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup) |
||||
end |
|
||||
|
|
||||
wifi_got_ip_event = function(T) |
|
||||
-- Note: Having an IP address does not mean there is internet access! |
|
||||
-- Internet connectivity can be determined with net.dns.resolve(). |
|
||||
print("Wifi connection is ready! IP address is: "..T.IP) |
|
||||
print("Startup will resume momentarily, you have 3 seconds to abort.") |
|
||||
print("Waiting...") |
|
||||
tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup) |
|
||||
mdns.register("smartlight", {hardware='NodeMCU'}) |
|
||||
end |
|
||||
|
|
||||
wifi_disconnect_event = function(T) |
|
||||
if T.reason == wifi.eventmon.reason.ASSOC_LEAVE then |
|
||||
--the station has disassociated from a previously connected AP |
|
||||
return |
|
||||
end |
|
||||
-- total_tries: how many times the station will attempt to connect to the AP. Should consider AP reboot duration. |
|
||||
local total_tries = 75 |
|
||||
print("\nWiFi connection to AP("..T.SSID..") has failed!") |
|
||||
|
|
||||
--There are many possible disconnect reasons, the following iterates through |
|
||||
--the list and returns the string corresponding to the disconnect reason. |
|
||||
for key,val in pairs(wifi.eventmon.reason) do |
|
||||
if val == T.reason then |
|
||||
print("Disconnect reason: "..val.."("..key..")") |
|
||||
break |
|
||||
end |
end |
||||
end |
end, |
||||
|
function(err, str) |
||||
if disconnect_ct == nil then |
print("enduser_setup: Err #" .. err .. ": " .. str) |
||||
disconnect_ct = 1 |
end, |
||||
else |
print |
||||
disconnect_ct = disconnect_ct + 1 |
) |
||||
end |
|
||||
if disconnect_ct < total_tries then |
|
||||
print("Retrying connection...(attempt "..(disconnect_ct+1).." of "..total_tries..")") |
|
||||
else |
|
||||
wifi.sta.disconnect() |
|
||||
print("Aborting connection to AP!") |
|
||||
disconnect_ct = nil |
|
||||
end |
|
||||
end |
|
||||
|
|
||||
-- Register WiFi Station event callbacks |
|
||||
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, wifi_connect_event) |
|
||||
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, wifi_got_ip_event) |
|
||||
wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, wifi_disconnect_event) |
|
||||
|
|
||||
print("Connecting to WiFi access point...") |
|
||||
wifi.setmode(wifi.STATION) |
|
||||
wifi.sta.config({ssid=SSID, pwd=PASSWORD}) |
|
||||
-- wifi.sta.connect() not necessary because config() uses auto-connect=true by default |
|
||||
|
@ -1,145 +0,0 @@ |
|||||
-- |
|
||||
-- Set verbose to 0 for quiet output (either the first assertion failure or |
|
||||
-- "All tests OK"), to 1 to see the events ("SEND", "SENT", "CHECK") without |
|
||||
-- the actual bytes, or to 2 to see the events with the bytes. |
|
||||
-- |
|
||||
local verbose = 0 |
|
||||
|
|
||||
local vprint = (verbose > 0) and print or function() end |
|
||||
|
|
||||
-- |
|
||||
-- Mock up enough of the nodemcu tmr structure, but pretend that nothing |
|
||||
-- happens between ticks. This won't exercise the optimistic corking logic, |
|
||||
-- but that's probably fine. |
|
||||
-- luacheck: push ignore |
|
||||
tmr = {} |
|
||||
tmr.ALARM_SINGLE = 0 |
|
||||
function tmr.create() |
|
||||
local r = {} |
|
||||
function r:alarm(_i, _t, cb) vprint("TMR") cb() end |
|
||||
return r |
|
||||
end |
|
||||
-- luacheck: pop |
|
||||
|
|
||||
-- |
|
||||
-- Mock up enough of the nodemcu net.socket type; have it log all the sends |
|
||||
-- into this "outs" array so that we can later check against it. |
|
||||
-- |
|
||||
local outs = {} |
|
||||
local fakesock = { |
|
||||
cb = nil, |
|
||||
on = function(this, _, cb) this.cb = cb end, |
|
||||
send = function(this, s) vprint("SEND", (verbose > 1) and s) table.insert(outs, s) end -- luacheck: no unused |
|
||||
} |
|
||||
local function sent() vprint("SENT") fakesock.cb() end |
|
||||
|
|
||||
-- And wrap a fifosock around this fake socket |
|
||||
local fsend = (require "fifosock").wrap(fakesock) |
|
||||
|
|
||||
-- Verify that the next unconsumed output is as indicated |
|
||||
local function fcheck(x) |
|
||||
vprint ("CHECK", (verbose > 1) and x) |
|
||||
assert (#outs > 0) |
|
||||
assert (x == outs[1]) |
|
||||
table.remove(outs, 1) |
|
||||
end |
|
||||
|
|
||||
-- Enqueue an empty function to prevent coalescing within the fifosock |
|
||||
local function nocoal() fsend(function() return nil end) end |
|
||||
|
|
||||
-- Send and check, for when the string should be sent exactly as is |
|
||||
local function fsendc(x) fsend(x) fcheck(x) end |
|
||||
|
|
||||
-- Check that there are no more outputs |
|
||||
local function fchecke() vprint("CHECKE") assert (#outs == 0) end |
|
||||
|
|
||||
-- |
|
||||
-- And now for the tests, which start easy and grow in complexity |
|
||||
-- |
|
||||
|
|
||||
fsendc("abracadabra none") |
|
||||
sent() ; fchecke() |
|
||||
|
|
||||
fsendc("abracadabra three") |
|
||||
fsend("short") |
|
||||
fsend("string") |
|
||||
fsend("build") |
|
||||
sent() ; fcheck("shortstringbuild") |
|
||||
sent() ; fchecke() |
|
||||
|
|
||||
-- Hit default FSMALLLIM while building up |
|
||||
fsendc("abracadabra lots small") |
|
||||
for i = 1, 32 do fsend("a") end -- luacheck: no unused |
|
||||
nocoal() |
|
||||
for i = 1, 4 do fsend("a") end -- luacheck: no unused |
|
||||
sent() ; fcheck(string.rep("a", 32)) |
|
||||
sent() ; fcheck(string.rep("a", 4)) |
|
||||
sent() ; fchecke() |
|
||||
|
|
||||
-- Hit string length while building up |
|
||||
fsendc("abracadabra overlong") |
|
||||
for i = 1, 10 do fsend(string.rep("a",32)) end -- luacheck: no unused |
|
||||
sent() ; fcheck(string.rep("a", 320)) |
|
||||
sent() ; fchecke() |
|
||||
|
|
||||
-- Hit neither before sending a big string |
|
||||
fsendc("abracadabra mid long") |
|
||||
for i = 1, 6 do fsend(string.rep("a",32)) end -- luacheck: no unused |
|
||||
fsend(string.rep("b", 256)) |
|
||||
nocoal() |
|
||||
for i = 1, 6 do fsend(string.rep("c",32)) end -- luacheck: no unused |
|
||||
sent() ; fcheck(string.rep("a", 192) .. string.rep("b", 256)) |
|
||||
sent() ; fcheck(string.rep("c", 192)) |
|
||||
sent() ; fchecke() |
|
||||
|
|
||||
-- send a huge string, verify that it coalesces |
|
||||
fsendc(string.rep("a",256) .. string.rep("b", 256) .. string.rep("c", 260)) |
|
||||
sent() ; fchecke() |
|
||||
|
|
||||
-- send a huge string, verify that it coalesces save for the short bit at the end |
|
||||
fsend(string.rep("a",256) .. string.rep("b", 256) .. string.rep("c", 256) .. string.rep("d",256)) |
|
||||
fsend("e") |
|
||||
fcheck(string.rep("a",256) .. string.rep("b", 256) .. string.rep("c", 256)) |
|
||||
sent() ; fcheck(string.rep("d",256) .. "e") |
|
||||
sent() ; fchecke() |
|
||||
|
|
||||
-- send enough that our 4x lookahead still leaves something in the queue |
|
||||
fsend(string.rep("a",512) .. string.rep("b", 512) .. string.rep("c", 512)) |
|
||||
fcheck(string.rep("a",512) .. string.rep("b", 512)) |
|
||||
sent() ; fcheck(string.rep("c",512)) |
|
||||
sent() ; fchecke() |
|
||||
|
|
||||
-- test a lazy generator |
|
||||
do |
|
||||
local ix = 0 |
|
||||
local function gen() vprint("GEN", ix); ix = ix + 1; return ("a" .. ix), ix < 3 and gen end |
|
||||
fsend(gen) |
|
||||
fsend("b") |
|
||||
fcheck("a1") |
|
||||
sent() ; fcheck("a2") |
|
||||
sent() ; fcheck("a3") |
|
||||
sent() ; fcheck("b") |
|
||||
sent() ; fchecke() |
|
||||
end |
|
||||
-- test a completion-like callback that does send text |
|
||||
do |
|
||||
local ix = 0 |
|
||||
local function gen() vprint("GEN"); ix = 1; return "efgh", nil end |
|
||||
fsend("abcd"); fsend(gen); fsend("ijkl") |
|
||||
assert (ix == 0) |
|
||||
fcheck("abcd"); assert (ix == 0) |
|
||||
sent() ; fcheck("efgh"); assert (ix == 1); ix = 0 |
|
||||
sent() ; fcheck("ijkl"); assert (ix == 0) |
|
||||
sent() ; fchecke() |
|
||||
end |
|
||||
-- and one that doesn't |
|
||||
do |
|
||||
local ix = 0 |
|
||||
local function gen() vprint("GEN"); ix = 1; return nil, nil end |
|
||||
fsend("abcd"); fsend(gen); fsend("ijkl") |
|
||||
assert (ix == 0) |
|
||||
fcheck("abcd"); assert (ix == 0) |
|
||||
sent() ; fcheck("ijkl"); assert (ix == 1); ix = 0 |
|
||||
sent() ; fchecke() ; assert (ix == 0) |
|
||||
end |
|
||||
print("All tests OK") |
|
Loading…
Reference in new issue