diff --git a/libraries/AP_Scripting/applets/net_webserver.lua b/libraries/AP_Scripting/applets/net_webserver.lua
index a1cac978c0205e..41e36e7aa01976 100644
--- a/libraries/AP_Scripting/applets/net_webserver.lua
+++ b/libraries/AP_Scripting/applets/net_webserver.lua
@@ -79,6 +79,9 @@ local HIDDEN_FOLDERS = { "@SYS", "@ROMFS", "@MISSION", "@PARAM" }
local CGI_BIN_PATH = "cgi-bin"
+local MNT_PREFIX = "/mnt"
+local MNT_PREFIX2 = MNT_PREFIX .. "/"
+
local MIME_TYPES = {
["apj"] = CONTENT_OCTET_STREAM,
["dat"] = CONTENT_OCTET_STREAM,
@@ -164,6 +167,75 @@ local MIME_TYPES = {
["7z"] = "application/x-7z-compressed",
}
+--[[
+ builtin dynamic pages
+--]]
+local DYNAMIC_PAGES = {
+
+["/"] = [[
+
+
+
+
+
+ ArduPilot
+
+
+
+ArduPilot Web Server
+
+
+
+Controller Status
+
+
+
+]],
+
+["@DYNAMIC/board_status.shtml"] = [[
+
+ Uptime | |
+ Arm Status | |
+ AHRS Location | |
+
GPS Location | |
+
+]]
+}
+
+--[[
+ builtin javascript library functions
+--]]
+JS_LIBRARY = {
+ ["dynamic_load"] = [[
+ function dynamic_load(div_id, uri, period_ms) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', uri);
+
+ xhr.setRequestHeader("Cache-Control", "no-cache, no-store, max-age=0");
+ xhr.setRequestHeader("Expires", "Tue, 01 Jan 1980 1:00:00 GMT");
+ xhr.setRequestHeader("Pragma", "no-cache");
+
+ xhr.onload = function () {
+ if (xhr.status === 200) {
+ var output = document.getElementById(div_id);
+ if (uri.endsWith('.shtml') || uri.endsWith('.html')) {
+ output.innerHTML = xhr.responseText;
+ } else {
+ output.textContent = xhr.responseText;
+ }
+ }
+ setTimeout(function() { dynamic_load(div_id,uri, period_ms); }, period_ms);
+ }
+ xhr.send();
+ }
+]]
+}
if not sock_listen:bind("0.0.0.0", WEB_BIND_PORT:get()) then
gcs:send_text(MAV_SEVERITY.ERROR, string.format("WebServer: failed to bind to TCP %u", WEB_BIND_PORT:get()))
@@ -175,6 +247,13 @@ if not sock_listen:listen(20) then
return
end
+function hms_uptime()
+ local s = (millis()/1000):toint()
+ local min = math.floor(s / 60) % 60
+ local hr = math.floor(s / 3600)
+ return string.format("%u hours %u minutes %u seconds", hr, min, s%60)
+end
+
--[[
split string by pattern
--]]
@@ -316,7 +395,22 @@ function substitute_vars(s, vars)
end))
return s
end
-
+
+--[[
+ lat or lon as a string, working around limited type in ftoa_engine
+--]]
+function latlon_str(ll)
+ local ipart = tonumber(string.match(tostring(ll*1.0e-7), '(.*[.]).*'))
+ local fpart = math.abs(ll - ipart*10000000)
+ return string.format("%d.%u", ipart, fpart, ipart*10000000, ll)
+end
+
+function location_string(loc)
+ return substitute_vars([[{lat} {lon} {alt}]],
+ { ["lat"] = latlon_str(loc:lat()),
+ ["lon"] = latlon_str(loc:lng()),
+ ["alt"] = string.format("%.1fm", loc:alt()*1.0e-2) })
+end
--[[
client class for open connections
@@ -468,9 +562,6 @@ local function Client(_sock, _idx)
]], {path=path})
for _,d in ipairs(dlist) do
local skip = d == "."
- if path == "/" and d == ".." then
- skip = true
- end
if not skip then
local fullpath = self.full_path(path, d)
local name = d
@@ -586,9 +677,14 @@ local function Client(_sock, _idx)
Using 'lstr' a return tostring(yourcode) is added to the code
automatically
--]]
- function self.send_processed_file()
+ function self.send_processed_file(dynamic_page)
sock:set_blocking(true)
- local contents = self.load_file()
+ local contents
+ if dynamic_page then
+ contents = file
+ else
+ contents = self.load_file()
+ end
while #contents > 0 do
local pat1 = "(.-)[<][?]lua[ \n](.-)[?][>](.*)"
local pat2 = "(.-)[<][?]lstr[ \n](.-)[?][>](.*)"
@@ -613,6 +709,9 @@ local function Client(_sock, _idx)
-- return a content type
function self.content_type(path)
+ if path == "/" then
+ return MIME_TYPES["html"]
+ end
local file, ext = string.match(path, '(.*[.])(.*)')
ext = string.lower(ext)
local ret = MIME_TYPES[ext]
@@ -628,16 +727,23 @@ local function Client(_sock, _idx)
path = string.sub(path, 2, #path)
end
DEBUG(string.format("%u: file_download(%s)", idx, path))
- file = io.open(path,"rb")
- if not file then
- DEBUG(string.format("%u: Failed to open '%s'", idx, path))
- return false
+ file = DYNAMIC_PAGES[path]
+ dynamic_page = file ~= nil
+ if not dynamic_page then
+ file = io.open(path,"rb")
+ if not file then
+ DEBUG(string.format("%u: Failed to open '%s'", idx, path))
+ return false
+ end
end
local vars = {["Content-Type"]=self.content_type(path)}
local cgi_processing = startswith(path, "/cgi-bin/") and endswith(path, ".lua")
local server_side_processing = endswith(path, ".shtml")
local stat = fs:stat(path)
- if not startswith(path, "@") and not server_side_processing and not cgi_processing and stat then
+ if not startswith(path, "@") and
+ not server_side_processing and
+ not cgi_processing and stat and
+ not dynamic_page then
local fsize = stat:size()
local mtime = stat:mtime()
vars["Content-Length"]= tostring(fsize)
@@ -656,9 +762,9 @@ local function Client(_sock, _idx)
end
end
self.send_header(200, "OK", vars)
- if server_side_processing then
+ if server_side_processing or dynamic_page then
DEBUG(string.format("%u: shtml processing %s", idx, path))
- run = self.send_processed_file
+ run = self.send_processed_file(dynamic_page)
elseif cgi_processing then
DEBUG(string.format("%u: CGI processing %s", idx, path))
run = self.send_cgi
@@ -715,7 +821,22 @@ local function Client(_sock, _idx)
end
end
- if isdirectory(path) and not endswith(path,"/") and header_vars['Host'] and not is_hidden_dir(path) then
+ if DYNAMIC_PAGES[path] ~= nil then
+ self.file_download(path)
+ return
+ end
+
+ if path == MNT_PREFIX then
+ path = "/"
+ end
+ if startswith(path, MNT_PREFIX2) then
+ path = string.sub(path,#MNT_PREFIX2,#path)
+ end
+
+ if isdirectory(path) and
+ not endswith(path,"/") and
+ header_vars['Host'] and
+ not is_hidden_dir(path) then
self.moved_permanently(path .. "/")
return
end
@@ -737,10 +858,15 @@ local function Client(_sock, _idx)
end
-- see if it is a directory
- if endswith(path,"/") or isdirectory(path) or is_hidden_dir(path) then
+ if (path == "/" or
+ DYNAMIC_PAGES[path] == nil) and
+ (endswith(path,"/") or
+ isdirectory(path) or
+ is_hidden_dir(path)) then
self.directory_list(path)
return
end
+
-- or a file
if self.file_download(path) then
return