controlpanel/lua/main.tl

158 lines
3.9 KiB
Plaintext

local json = require("json")
local fb = require("framebuffer")
local ringbuffer = require("ringbuffer")
local UUID <const> = "8b9faf9f-9470-4a50-b405-0af5f0152550"
local ENDPOINT <const> = "ws://localhost:8000/monitoring/computer/" .. UUID .. "/ws"
print("[MAIN] Init")
local enum SocketState
"reset"
"connecting" -- currently unused
"ok"
end
local record Socket
state: SocketState
ws: http.Websocket
end
local socket: Socket = {
state = "reset",
ws = nil,
}
local function send(message: string)
-- "message" needs to be valid JSON
-- otherwise the server will not accept it
if socket.state ~= "ok" then return end
local r = { pcall(socket.ws.send, message) }
if r[1] == false then
if (r[2] as string):sub(-11) == "closed file" then
socket.state = "reset"
elseif (r[2] as string):sub(-9) == "too large" then
-- TODO handle
-- the connection stays open though
end
end
end
-- Set up framebuffer capture and statusline
print("[MAIN] Setup framebuffer")
local orig_native = term.native
local buffer = fb.wrap(orig_native())
term.native = function(): term.Redirect
return buffer.target
end
local width, height = term.getSize()
local top_line = window.create(buffer.target, 1, 1, width, 1)
local main_view = window.create(buffer.target, 1, 2, width, height - 1)
term.redirect(main_view as term.Redirect)
local function set_bar(text: string, fg: string | nil, bg: string | nil)
fg = fg or ("9"):rep(text:len())
bg = bg or ("f"):rep(text:len())
top_line.clear()
top_line.setCursorPos(1,1)
top_line.blit(text, fg, bg)
main_view.restoreCursor()
end
-- Create tasks
local ws_task = coroutine.create(function()
while true do
if socket.state == "reset" then
set_bar("[WS] RST", "78870111")
local r = http.websocket(ENDPOINT)
if r ~= false then
socket.ws = r as http.Websocket
set_bar("[WS] OK\x03", "78870DD5")
socket.state = "ok"
else
set_bar("[WS] ERR", "78870EEE")
end
end
repeat
sleep(1)
until socket.state ~= "ok"
end
end)
local report_task = coroutine.create(function()
local last_report = -1.0
while true do
local now = os.clock()
if now - last_report >= 0.05 then
local message = json.encode({
screen = buffer.serialize()
})
send(message)
last_report = now
end
sleep(0) -- until next gametick
end
end)
local shell_task = coroutine.create(function()
shell.run("shell")
end)
-- basically parallel.waitForAny
local record Task
coro: thread
filter: string | nil
end
local tasks: {Task} = {
{coro = shell_task}, -- pid 1
{coro = ws_task},
{coro = report_task},
}
local event_queue = ringbuffer.new(64)
event_queue:push({n = 0})
local shell_running = true
while shell_running do
local e: table
if not event_queue:is_empty() then
e = event_queue:pop() as table
else
e = table.pack(os.pullEventRaw())
end
for pid = 1, #tasks do
local task = tasks[pid]
if task.filter == nil or task.filter == e[1] or e[1] == "terminate" then
local ok, param = coroutine.resume(task.coro, table.unpack(e as {any}))
if not ok then
term.redirect(orig_native())
term.clear()
term.setCursorPos(1,1)
print("OMEGABIG OOF")
print(("pid %d"):format(pid))
error(param, 0)
else
task.filter = param as string
end
if pid == 1 and coroutine.status(task.coro) == "dead" then
shell_running = false
end
end
end
end
term.native = orig_native
term.redirect(term.native())
term.clear()
term.setCursorPos(1,1)