Auto-resume flight across chunk unload/reload
When the ship's chunk unloads the CC computer powers off and its redstone outputs reset to 0, so on return the ship dropped and the pilot had to re-ENGAGE every time. Flight state now survives the reboot: - engaged.txt flag written while flying, cleared on pilot STOP. - On boot, if engaged + profile present: drive the hover baseline immediately (closes the fall gap), then runStandby(true) re-enters the flight loop automatically with the persisted target -- no manual ENGAGE. - runFly drives hover before its first sleep so no entry path leaves a 0-output gap. - Cleanup no longer lifts a PARKED ship: Ctrl+T out of standby holds hover only when engaged, else valves off. - engaged.txt registered as per-ship state (gitignore, installer keep/reset, menu reset, build excludes). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@ files/*/calib.txt
|
|||||||
files/*/sides.txt
|
files/*/sides.txt
|
||||||
files/*/target.txt
|
files/*/target.txt
|
||||||
files/*/settings.txt
|
files/*/settings.txt
|
||||||
|
files/*/engaged.txt
|
||||||
files/*/scan.txt
|
files/*/scan.txt
|
||||||
files/*/pid_gains.txt
|
files/*/pid_gains.txt
|
||||||
files/*/.installed_*
|
files/*/.installed_*
|
||||||
|
|||||||
21
README.md
21
README.md
@@ -63,7 +63,26 @@ Boot chain (autopilot): `startup.lua` → `ap boot` → `autopilot/main.lua`.
|
|||||||
Menu any time: `Ctrl+T`, then `ap`. (`stabilizeV2` remains as an alias.)
|
Menu any time: `Ctrl+T`, then `ap`. (`stabilizeV2` remains as an alias.)
|
||||||
|
|
||||||
Ship state lives on the computer only: `sides.txt`, `calib.txt`, `target.txt`,
|
Ship state lives on the computer only: `sides.txt`, `calib.txt`, `target.txt`,
|
||||||
`settings.txt`. Reset it via menu `p` → `R`, or when re-running the installer.
|
`settings.txt`, `engaged.txt`. Reset it via menu `p` → `R`, or when re-running
|
||||||
|
the installer.
|
||||||
|
|
||||||
|
## Surviving chunk unload / reload
|
||||||
|
|
||||||
|
When you fly away and the ship's chunk unloads, the CC computer powers off and
|
||||||
|
its redstone outputs reset to 0 — so on its own the ship would drop the moment
|
||||||
|
you return. The autopilot handles this:
|
||||||
|
|
||||||
|
- While flying it keeps an `engaged.txt` flag. On boot (`startup.lua` runs
|
||||||
|
automatically when the chunk reloads) it drives the hover baseline
|
||||||
|
**immediately**, then re-enters the flight loop on its own — no manual
|
||||||
|
ENGAGE, holding the same target it had.
|
||||||
|
- A pilot **STOP** clears the flag, so a ship you deliberately stopped stays
|
||||||
|
stopped across reloads. **HOLD**/**LAND** keep flying, so they resume.
|
||||||
|
|
||||||
|
There is still an unavoidable ~1–2 s gap while the computer boots; on this slow
|
||||||
|
ship that is harmless. If the computer comes back *off* after a reload (some
|
||||||
|
server setups), right-click it once — CC then runs `startup.lua` and the
|
||||||
|
auto-resume takes over.
|
||||||
|
|
||||||
## Updating the code
|
## Updating the code
|
||||||
|
|
||||||
|
|||||||
2
build.sh
2
build.sh
@@ -17,7 +17,7 @@ declare -A ROLE_SRC=(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# per-skip tilstand + utdaterte verktoey: shippes ALDRI (installeren rydder dem ogsaa)
|
# per-skip tilstand + utdaterte verktoey: shippes ALDRI (installeren rydder dem ogsaa)
|
||||||
EXCLUDES=(calib.txt sides.txt target.txt settings.txt scan.txt pid_gains.txt
|
EXCLUDES=(calib.txt sides.txt target.txt settings.txt engaged.txt scan.txt pid_gains.txt
|
||||||
stabilize.lua diag.lua probe.lua scan.lua manifest)
|
stabilize.lua diag.lua probe.lua scan.lua manifest)
|
||||||
# rolle-spesifikt: cockpit-koden hoerer ikke hjemme paa autopilot-PCen
|
# rolle-spesifikt: cockpit-koden hoerer ikke hjemme paa autopilot-PCen
|
||||||
declare -A ROLE_EXCLUDES=(
|
declare -A ROLE_EXCLUDES=(
|
||||||
|
|||||||
@@ -97,6 +97,20 @@ local function profileComplete()
|
|||||||
return loadCalib().signMeasured == true
|
return loadCalib().signMeasured == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ENGASJERT-flagg: finnes filen <=> autopiloten FLYR aktivt (etter ENGAGE/FLY, foer STOP).
|
||||||
|
-- Dette er kjernen i restart-overlevelsen: en chunk-unload slaar av datamaskinen, og reboot
|
||||||
|
-- nullstiller redstone -> skipet ville falle. Ligger flagget der ved boot, gjenopptar vi flukten
|
||||||
|
-- automatisk (og holder hover umiddelbart), i stedet for aa vente i standby paa manuell ENGAGE.
|
||||||
|
local ENGAGED_FILE = "engaged.txt"
|
||||||
|
local function setEngaged(on)
|
||||||
|
if on then
|
||||||
|
local f = fs.open(ENGAGED_FILE, "w"); f.write("1"); f.close()
|
||||||
|
elseif fs.exists(ENGAGED_FILE) then
|
||||||
|
fs.delete(ENGAGED_FILE)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function isEngaged() return fs.exists(ENGAGED_FILE) end
|
||||||
|
|
||||||
-- live brukerinnstillinger (justeres fra pilot-konsollen), persistert. Syntetiske noekler -> cfg.
|
-- live brukerinnstillinger (justeres fra pilot-konsollen), persistert. Syntetiske noekler -> cfg.
|
||||||
local SETTINGS_FILE = "settings.txt"
|
local SETTINGS_FILE = "settings.txt"
|
||||||
local function saveSettings(t) local f = fs.open(SETTINGS_FILE, "w"); f.write(textutils.serialise(t)); f.close() end
|
local function saveSettings(t) local f = fs.open(SETTINGS_FILE, "w"); f.write(textutils.serialise(t)); f.close() end
|
||||||
@@ -713,6 +727,11 @@ local function runFly()
|
|||||||
print(""); print("[Enter]"); read(); return
|
print(""); print("[Enter]"); read(); return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- driv hover FOERST -- lukker fall-gapet (spesielt paa auto-resume etter reboot der
|
||||||
|
-- redstone kom tilbake paa 0) foer loopen og status-broadcasten kommer i gang.
|
||||||
|
driveValves(calib.uHoverFront, calib.uHoverRear)
|
||||||
|
setEngaged(true) -- markér at vi FLYR -> overlever chunk-reload (auto-resume ved boot)
|
||||||
|
|
||||||
local launchAlt = sensors.readAltitude() or 0
|
local launchAlt = sensors.readAltitude() or 0
|
||||||
local target = loadTarget() or (launchAlt + cfg.takeoffOffset)
|
local target = loadTarget() or (launchAlt + cfg.takeoffOffset)
|
||||||
local est = Estimator.new(cfg)
|
local est = Estimator.new(cfg)
|
||||||
@@ -739,6 +758,8 @@ local function runFly()
|
|||||||
elseif msg.cmd == "set" then applySetting(msg.key, msg.value, true)
|
elseif msg.cmd == "set" then applySetting(msg.key, msg.value, true)
|
||||||
elseif msg.cmd == "stop" then stopReq = true end
|
elseif msg.cmd == "stop" then stopReq = true end
|
||||||
end
|
end
|
||||||
|
-- pilot-STOP er en eksplisitt "slutt aa fly" -> ryd engasjert-flagget saa en senere reload
|
||||||
|
-- IKKE gjenopptar av seg selv. (Ctrl+T beholder flagget: da vil du typisk fortsette senere.)
|
||||||
-- vent ~dt sekunder MEN behandle innkommende kommandoer underveis (ikke-blokkerende styring)
|
-- vent ~dt sekunder MEN behandle innkommende kommandoer underveis (ikke-blokkerende styring)
|
||||||
local function serve(secs)
|
local function serve(secs)
|
||||||
local timer = os.startTimer(secs)
|
local timer = os.startTimer(secs)
|
||||||
@@ -823,7 +844,10 @@ local function runFly()
|
|||||||
})
|
})
|
||||||
|
|
||||||
serve(cfg.dt) -- vent dt + behandle pilot-kommandoer (erstatter sleep)
|
serve(cfg.dt) -- vent dt + behandle pilot-kommandoer (erstatter sleep)
|
||||||
if stopReq then print("Pilot STOP -- holding hover, back to standby."); break end
|
if stopReq then
|
||||||
|
setEngaged(false) -- eksplisitt stopp -> ikke auto-resume etter neste reload
|
||||||
|
print("Pilot STOP -- holding hover, back to standby."); break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -898,7 +922,21 @@ local function awaitCmd(secs)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function runStandby()
|
local function runStandby(autoResume)
|
||||||
|
link.open()
|
||||||
|
-- AUTO-RESUME: fl...y da chunken unloadet -> gjenoppta UMIDDELBART (ingen fall, ingen manuell
|
||||||
|
-- ENGAGE). Bare paa boot-inngangen; manuell "standby" fra menyen gjor det ikke.
|
||||||
|
if autoResume and isEngaged() and fs.exists(CALIB_FILE) then
|
||||||
|
term.clear(); term.setCursorPos(1, 1)
|
||||||
|
print("==== RESUMING FLIGHT ====")
|
||||||
|
print("Autopilot was flying before the chunk")
|
||||||
|
print("reloaded -- re-engaging automatically.")
|
||||||
|
print("(Pilot STOP to stop; it will NOT resume")
|
||||||
|
print(" after a stop.)")
|
||||||
|
sleep(0.6)
|
||||||
|
runFly() -- blokkerer til STOP / Ctrl+T
|
||||||
|
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear)
|
||||||
|
end
|
||||||
while true do
|
while true do
|
||||||
-- re-proev modem hver runde: fanger et modem som plugges til mens vi staar i standby
|
-- re-proev modem hver runde: fanger et modem som plugges til mens vi staar i standby
|
||||||
local hasModem = link.open()
|
local hasModem = link.open()
|
||||||
@@ -939,6 +977,11 @@ end
|
|||||||
-- Foerste boot uten komplett profil -> tilby wizarden. Timeout -> standby, slik at en ubemannet
|
-- Foerste boot uten komplett profil -> tilby wizarden. Timeout -> standby, slik at en ubemannet
|
||||||
-- reboot (chunk-reload) aldri blir staaende og vente paa tastetrykk.
|
-- reboot (chunk-reload) aldri blir staaende og vente paa tastetrykk.
|
||||||
local function runBoot()
|
local function runBoot()
|
||||||
|
-- ALLER FOERST: fl...y vi da chunken unloadet? Driv hover UMIDDELBART saa skipet ikke rekker
|
||||||
|
-- aa falle mens vi tegner skjerm / venter. runStandby(true) gjenopptar selve reguleringen.
|
||||||
|
if isEngaged() and fs.exists(CALIB_FILE) then
|
||||||
|
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear)
|
||||||
|
end
|
||||||
if not profileComplete() then
|
if not profileComplete() then
|
||||||
term.clear(); term.setCursorPos(1, 1)
|
term.clear(); term.setCursorPos(1, 1)
|
||||||
print("==== AIRSHIP AUTOPILOT ====")
|
print("==== AIRSHIP AUTOPILOT ====")
|
||||||
@@ -959,7 +1002,7 @@ local function runBoot()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
runStandby()
|
runStandby(true) -- true = auto-resume flukt hvis vi fløy da chunken unloadet
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ============================ PROFIL / MENY ============================
|
-- ============================ PROFIL / MENY ============================
|
||||||
@@ -1015,7 +1058,7 @@ local function menu()
|
|||||||
term.clear(); term.setCursorPos(1, 1); showProfile()
|
term.clear(); term.setCursorPos(1, 1); showProfile()
|
||||||
print("\nR = reset SHIP PROFILE (new ship), Enter = back"); local a = read():lower()
|
print("\nR = reset SHIP PROFILE (new ship), Enter = back"); local a = read():lower()
|
||||||
if a == "r" then
|
if a == "r" then
|
||||||
for _, f in ipairs({ CALIB_FILE, SIDES_FILE, TARGET_FILE, SETTINGS_FILE }) do
|
for _, f in ipairs({ CALIB_FILE, SIDES_FILE, TARGET_FILE, SETTINGS_FILE, ENGAGED_FILE }) do
|
||||||
if fs.exists(f) then fs.delete(f) end
|
if fs.exists(f) then fs.delete(f) end
|
||||||
end
|
end
|
||||||
print("Ship profile reset -- setup wizard on next boot."); print("[Enter]"); read()
|
print("Ship profile reset -- setup wizard on next boot."); print("[Enter]"); read()
|
||||||
@@ -1060,20 +1103,24 @@ local function runMode(mode, a2, a3)
|
|||||||
end)
|
end)
|
||||||
-- opprydding: aktive driv-modus -> hold hover (aldri etterlat drivende uten beskjed);
|
-- opprydding: aktive driv-modus -> hold hover (aldri etterlat drivende uten beskjed);
|
||||||
-- monitor styrer ingenting (la utgangene staa).
|
-- monitor styrer ingenting (la utgangene staa).
|
||||||
|
local function holdHoverOut()
|
||||||
|
if fs.exists(CALIB_FILE) then
|
||||||
|
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear) -- hold, ikke null (aldri stup)
|
||||||
|
else
|
||||||
|
allOff()
|
||||||
|
end
|
||||||
|
end
|
||||||
if mode == "manual" or mode == "wiring" then
|
if mode == "manual" or mode == "wiring" then
|
||||||
allOff()
|
allOff()
|
||||||
elseif mode == "signtest" or mode == "setup" then
|
elseif mode == "signtest" or mode == "setup" then
|
||||||
-- Ctrl+T midt i sign-testen: luftbaaren -> hold hover (ALDRI frittfall); paa bakken -> alt av
|
-- Ctrl+T midt i sign-testen: luftbaaren -> hold hover (ALDRI frittfall); paa bakken -> alt av
|
||||||
if signtestAirborne then
|
if signtestAirborne then holdHoverOut() else allOff() end
|
||||||
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear)
|
elseif mode == "cal" or mode == "fly" or mode == "observe" then
|
||||||
else
|
holdHoverOut() -- disse fl...yr aktivt -> hold hover ved avslutning
|
||||||
allOff()
|
elseif mode == "boot" or mode == "standby" then
|
||||||
end
|
-- Ctrl+T ut av standby: hold hover KUN hvis vi faktisk fløy (engasjert). Et parkert skip
|
||||||
elseif mode == "cal" or mode == "fly" or mode == "observe" or mode == "standby" or mode == "boot" then
|
-- paa bakken skal IKKE begynne aa varme brennere og lette av seg selv.
|
||||||
if fs.exists(CALIB_FILE) then
|
if isEngaged() then holdHoverOut() else allOff()
|
||||||
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear) -- hold, ikke null (aldri stup)
|
|
||||||
else
|
|
||||||
allOff() -- aldri floeyet under vaar kontroll -> skipet staar paa bakken; ikke varm brennere
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not ok and err ~= "Terminated" then print("ERROR: " .. tostring(err)); print("[Enter]"); read() end
|
if not ok and err ~= "Terminated" then print("ERROR: " .. tostring(err)); print("[Enter]"); read() end
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ local BASE = "https://gt.zyon.no/Stian/airship-autopilot/raw/branch/main"
|
|||||||
local ROLES = { "autopilot", "cockpit" }
|
local ROLES = { "autopilot", "cockpit" }
|
||||||
|
|
||||||
-- per-skip tilstand: roeres aldri automatisk, men tilbys nullstilt ved install
|
-- per-skip tilstand: roeres aldri automatisk, men tilbys nullstilt ved install
|
||||||
local STATE_FILES = { "sides.txt", "calib.txt", "target.txt", "settings.txt" }
|
local STATE_FILES = { "sides.txt", "calib.txt", "target.txt", "settings.txt", "engaged.txt" }
|
||||||
|
|
||||||
-- kjente filer fra eldre versjoner som ikke lenger hoerer til rollen (engangs-rydding;
|
-- kjente filer fra eldre versjoner som ikke lenger hoerer til rollen (engangs-rydding;
|
||||||
-- videre oppdateringer ryddes automatisk via installasjons-loggen .installed_<role>)
|
-- videre oppdateringer ryddes automatisk via installasjons-loggen .installed_<role>)
|
||||||
|
|||||||
Reference in New Issue
Block a user