2026-07-23 23:21:54 +02:00
|
|
|
-- install.lua -- ETT entry-point for hele autopiloten. Samme kommando paa begge PC-er:
|
2026-07-23 12:53:41 +02:00
|
|
|
--
|
2026-07-23 13:10:17 +02:00
|
|
|
-- wget run https://gt.zyon.no/Stian/airship-autopilot/raw/branch/main/install.lua
|
2026-07-23 23:21:54 +02:00
|
|
|
--
|
|
|
|
|
-- Installeren DETEKTERER selv hvilken PC den staar paa (flysensorer = autopilot,
|
|
|
|
|
-- monitor = cockpit), installerer riktig rolle, og gaar RETT videre i setup/oppstart --
|
|
|
|
|
-- ingen reboot, ingen rollenavn aa huske. Re-run samme kommando for aa oppdatere.
|
|
|
|
|
-- Eksplisitt rolle som argument overstyrer deteksjonen (avansert):
|
|
|
|
|
-- wget run .../install.lua autopilot | cockpit
|
2026-07-23 12:53:41 +02:00
|
|
|
--
|
|
|
|
|
-- Roles:
|
2026-07-23 23:21:54 +02:00
|
|
|
-- autopilot machine-room PC: full v3 observer autopilot (setup wizard / standby)
|
|
|
|
|
-- cockpit pilot-house PC: control console (monitor + modem)
|
|
|
|
|
--
|
|
|
|
|
-- Per-skip TILSTAND (sides/calib/target/settings) shippes ALDRI av installeren -- den maales
|
|
|
|
|
-- av setup-wizarden PAA skipet. Ved re-install kan profilen beholdes eller nullstilles.
|
|
|
|
|
-- Filer som er fjernet fra manifestet siden sist (eller kjente legacy-filer) ryddes bort.
|
2026-07-23 12:53:41 +02:00
|
|
|
|
2026-07-23 13:10:17 +02:00
|
|
|
local BASE = "https://gt.zyon.no/Stian/airship-autopilot/raw/branch/main"
|
2026-07-23 12:53:41 +02:00
|
|
|
local ROLES = { "autopilot", "cockpit" }
|
|
|
|
|
|
2026-07-23 23:21:54 +02:00
|
|
|
-- per-skip tilstand: roeres aldri automatisk, men tilbys nullstilt ved install
|
2026-07-25 17:55:15 +02:00
|
|
|
local STATE_FILES = { "sides.txt", "calib.txt", "target.txt", "settings.txt", "engaged.txt" }
|
2026-07-23 23:21:54 +02:00
|
|
|
|
|
|
|
|
-- kjente filer fra eldre versjoner som ikke lenger hoerer til rollen (engangs-rydding;
|
|
|
|
|
-- videre oppdateringer ryddes automatisk via installasjons-loggen .installed_<role>)
|
|
|
|
|
local LEGACY = {
|
|
|
|
|
autopilot = { "stabilize.lua", "diag.lua", "probe.lua", "scan.lua", "scan.txt",
|
|
|
|
|
"pid_gains.txt", "cockpit.lua", "pilot_startup.lua" },
|
|
|
|
|
cockpit = {},
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-23 12:53:41 +02:00
|
|
|
local args = { ... }
|
|
|
|
|
|
|
|
|
|
-- ---- helpers ---------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
local function die(msg)
|
|
|
|
|
term.setTextColor(colors.red)
|
|
|
|
|
print(msg)
|
|
|
|
|
term.setTextColor(colors.white)
|
|
|
|
|
error("", 0)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function get(url)
|
|
|
|
|
local h, err = http.get(url)
|
|
|
|
|
if not h then return nil, tostring(err) end
|
|
|
|
|
local body = h.readAll()
|
|
|
|
|
h.close()
|
|
|
|
|
return body
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function writeFile(path, data)
|
|
|
|
|
local dir = fs.getDir(path)
|
|
|
|
|
if dir ~= "" and not fs.exists(dir) then fs.makeDir(dir) end
|
|
|
|
|
local f = fs.open(path, "w")
|
|
|
|
|
f.write(data)
|
|
|
|
|
f.close()
|
|
|
|
|
end
|
|
|
|
|
|
2026-07-23 23:21:54 +02:00
|
|
|
local function inList(list, x)
|
|
|
|
|
for _, v in ipairs(list) do if v == x then return true end end
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
2026-07-23 12:53:41 +02:00
|
|
|
local function chooseRole()
|
|
|
|
|
print("Select role to install:")
|
2026-07-23 23:21:54 +02:00
|
|
|
print(" 1) autopilot (machine room -- has the")
|
|
|
|
|
print(" altitude + gimbal sensors)")
|
|
|
|
|
print(" 2) cockpit (pilot house -- has a")
|
|
|
|
|
print(" monitor + modem)")
|
2026-07-23 12:53:41 +02:00
|
|
|
write("> ")
|
|
|
|
|
local sel = read()
|
|
|
|
|
local n = tonumber(sel)
|
|
|
|
|
if n and ROLES[n] then return ROLES[n] end
|
|
|
|
|
for _, r in ipairs(ROLES) do if r == sel then return r end end
|
|
|
|
|
die("Unknown role: " .. tostring(sel))
|
|
|
|
|
end
|
|
|
|
|
|
2026-07-23 23:21:54 +02:00
|
|
|
-- gjett rollen ut fra hva som er koblet til: flysensorer er det sterkeste signalet
|
|
|
|
|
-- (maskinrommet kan ogsaa ha monitor), deretter monitor -> cockpit
|
|
|
|
|
local function detectRole()
|
|
|
|
|
if peripheral.find("altitude_sensor") or peripheral.find("gimbal_sensor") then
|
|
|
|
|
return "autopilot", "flight sensors attached"
|
|
|
|
|
end
|
|
|
|
|
if peripheral.find("monitor") then
|
|
|
|
|
return "cockpit", "monitor attached, no flight sensors"
|
|
|
|
|
end
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
|
2026-07-23 12:53:41 +02:00
|
|
|
-- ---- preflight -------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
if not http then
|
|
|
|
|
die("HTTP API is disabled on this server.\n" ..
|
|
|
|
|
"Enable it in the server's computercraft-server.toml:\n" ..
|
|
|
|
|
" [http] enabled = true then restart the server.")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local role = args[1]
|
2026-07-23 23:21:54 +02:00
|
|
|
if role then
|
2026-07-23 12:53:41 +02:00
|
|
|
local ok = false
|
|
|
|
|
for _, r in ipairs(ROLES) do if r == role then ok = true end end
|
|
|
|
|
if not ok then die("Unknown role '" .. role .. "'. Valid: " .. table.concat(ROLES, ", ")) end
|
2026-07-23 23:21:54 +02:00
|
|
|
else
|
|
|
|
|
local guess, why = detectRole()
|
|
|
|
|
if guess then
|
|
|
|
|
print("This looks like the " ..
|
|
|
|
|
(guess == "autopilot" and "MACHINE-ROOM" or "PILOT-HOUSE") .. " PC")
|
|
|
|
|
print(" (" .. why .. ")")
|
|
|
|
|
write("Install '" .. guess .. "'? [Y/n] ")
|
|
|
|
|
role = (read():lower() == "n") and chooseRole() or guess
|
|
|
|
|
else
|
|
|
|
|
print("Could not auto-detect this PC's role")
|
|
|
|
|
print("(no flight sensors or monitor attached).")
|
|
|
|
|
role = chooseRole()
|
|
|
|
|
end
|
2026-07-23 12:53:41 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- ---- fetch manifest --------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
print("Installing role: " .. role)
|
|
|
|
|
local manifestUrl = BASE .. "/files/" .. role .. "/manifest"
|
|
|
|
|
local manifest, mErr = get(manifestUrl)
|
|
|
|
|
if not manifest then die("Could not fetch manifest (" .. mErr .. ")\n" .. manifestUrl) end
|
|
|
|
|
|
|
|
|
|
local files = {}
|
|
|
|
|
for line in manifest:gmatch("[^\r\n]+") do
|
|
|
|
|
local p = line:match("^%s*(.-)%s*$")
|
|
|
|
|
if p ~= "" then files[#files + 1] = p end
|
|
|
|
|
end
|
|
|
|
|
if #files == 0 then die("Manifest is empty: " .. manifestUrl) end
|
|
|
|
|
|
|
|
|
|
-- ---- download --------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
local ok, fail = 0, {}
|
|
|
|
|
for i, rel in ipairs(files) do
|
|
|
|
|
term.write(("[%d/%d] %s "):format(i, #files, rel))
|
|
|
|
|
local data, err = get(BASE .. "/files/" .. role .. "/" .. rel)
|
|
|
|
|
if data then
|
|
|
|
|
writeFile(rel, data)
|
|
|
|
|
term.setTextColor(colors.green); print("ok"); term.setTextColor(colors.white)
|
|
|
|
|
ok = ok + 1
|
|
|
|
|
else
|
|
|
|
|
term.setTextColor(colors.red); print("FAIL"); term.setTextColor(colors.white)
|
|
|
|
|
fail[#fail + 1] = rel .. " (" .. tostring(err) .. ")"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
print(("Installed %d/%d files."):format(ok, #files))
|
|
|
|
|
if #fail > 0 then
|
|
|
|
|
term.setTextColor(colors.red)
|
|
|
|
|
print("Failed:")
|
|
|
|
|
for _, f in ipairs(fail) do print(" " .. f) end
|
|
|
|
|
term.setTextColor(colors.white)
|
|
|
|
|
die("Install incomplete -- re-run after fixing the errors above.")
|
|
|
|
|
end
|
|
|
|
|
|
2026-07-23 23:21:54 +02:00
|
|
|
-- ---- rydd: legacy + filer fjernet fra manifestet siden forrige install ------
|
|
|
|
|
|
|
|
|
|
local removed = 0
|
|
|
|
|
for _, f in ipairs(LEGACY[role] or {}) do
|
|
|
|
|
if fs.exists(f) then fs.delete(f); removed = removed + 1; print("removed legacy: " .. f) end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local RECORD = ".installed_" .. role
|
|
|
|
|
if fs.exists(RECORD) then
|
|
|
|
|
local rf = fs.open(RECORD, "r"); local prev = rf.readAll(); rf.close()
|
|
|
|
|
for line in (prev or ""):gmatch("[^\r\n]+") do
|
|
|
|
|
local p = line:match("^%s*(.-)%s*$")
|
|
|
|
|
if p ~= "" and not inList(files, p) and not inList(STATE_FILES, p) and fs.exists(p) then
|
|
|
|
|
fs.delete(p); removed = removed + 1; print("removed obsolete: " .. p)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
writeFile(RECORD, table.concat(files, "\n") .. "\n")
|
|
|
|
|
if removed > 0 then print(("Cleaned up %d old file(s)."):format(removed)) end
|
|
|
|
|
|
|
|
|
|
-- ---- skip-profil: behold eller nullstill (ETTER vellykket install, foer boot) ----
|
|
|
|
|
-- (kun autopilot-rollen har skip-tilstand; sletter aldri noe hvis nedlastingen feilet)
|
|
|
|
|
|
|
|
|
|
if role == "autopilot" then
|
|
|
|
|
local found = {}
|
|
|
|
|
for _, f in ipairs(STATE_FILES) do if fs.exists(f) then found[#found + 1] = f end end
|
|
|
|
|
if #found > 0 then
|
|
|
|
|
print("Existing ship profile found:")
|
|
|
|
|
print(" " .. table.concat(found, ", "))
|
|
|
|
|
write("Keep it? (n = new/rewired ship) [Y/n] ")
|
|
|
|
|
if read():lower() == "n" then
|
|
|
|
|
for _, f in ipairs(found) do fs.delete(f) end
|
|
|
|
|
print("Profile reset -- the setup wizard takes over.")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- ---- ferdig: gaa RETT videre (ingen reboot noedvendig) ----------------------
|
|
|
|
|
-- startup.lua er installert, saa alt auto-starter ved fremtidige reboots; naa
|
|
|
|
|
-- fortsetter vi direkte inn i samme flyt som boot ville gitt.
|
|
|
|
|
|
2026-07-23 12:53:41 +02:00
|
|
|
term.setTextColor(colors.green)
|
2026-07-23 23:21:54 +02:00
|
|
|
print("Done. (auto-starts on every reboot)")
|
2026-07-23 12:53:41 +02:00
|
|
|
term.setTextColor(colors.white)
|
2026-07-23 23:21:54 +02:00
|
|
|
print("")
|
|
|
|
|
if role == "autopilot" then
|
|
|
|
|
print("Continuing into setup/standby...")
|
|
|
|
|
sleep(1.0)
|
|
|
|
|
shell.run("ap", "boot")
|
|
|
|
|
else
|
|
|
|
|
print("Starting the pilot console...")
|
|
|
|
|
sleep(1.0)
|
|
|
|
|
shell.run("cockpit")
|
|
|
|
|
end
|