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/*/target.txt
|
||||
files/*/settings.txt
|
||||
files/*/engaged.txt
|
||||
files/*/scan.txt
|
||||
files/*/pid_gains.txt
|
||||
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.)
|
||||
|
||||
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
|
||||
|
||||
|
||||
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)
|
||||
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)
|
||||
# rolle-spesifikt: cockpit-koden hoerer ikke hjemme paa autopilot-PCen
|
||||
declare -A ROLE_EXCLUDES=(
|
||||
|
||||
@@ -97,6 +97,20 @@ local function profileComplete()
|
||||
return loadCalib().signMeasured == true
|
||||
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.
|
||||
local SETTINGS_FILE = "settings.txt"
|
||||
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
|
||||
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 target = loadTarget() or (launchAlt + cfg.takeoffOffset)
|
||||
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 == "stop" then stopReq = true 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)
|
||||
local function serve(secs)
|
||||
local timer = os.startTimer(secs)
|
||||
@@ -823,7 +844,10 @@ local function runFly()
|
||||
})
|
||||
|
||||
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
|
||||
|
||||
@@ -898,7 +922,21 @@ local function awaitCmd(secs)
|
||||
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
|
||||
-- re-proev modem hver runde: fanger et modem som plugges til mens vi staar i standby
|
||||
local hasModem = link.open()
|
||||
@@ -939,6 +977,11 @@ end
|
||||
-- Foerste boot uten komplett profil -> tilby wizarden. Timeout -> standby, slik at en ubemannet
|
||||
-- reboot (chunk-reload) aldri blir staaende og vente paa tastetrykk.
|
||||
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
|
||||
term.clear(); term.setCursorPos(1, 1)
|
||||
print("==== AIRSHIP AUTOPILOT ====")
|
||||
@@ -959,7 +1002,7 @@ local function runBoot()
|
||||
end
|
||||
end
|
||||
end
|
||||
runStandby()
|
||||
runStandby(true) -- true = auto-resume flukt hvis vi fløy da chunken unloadet
|
||||
end
|
||||
|
||||
-- ============================ PROFIL / MENY ============================
|
||||
@@ -1015,7 +1058,7 @@ local function menu()
|
||||
term.clear(); term.setCursorPos(1, 1); showProfile()
|
||||
print("\nR = reset SHIP PROFILE (new ship), Enter = back"); local a = read():lower()
|
||||
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
|
||||
end
|
||||
print("Ship profile reset -- setup wizard on next boot."); print("[Enter]"); read()
|
||||
@@ -1060,20 +1103,24 @@ local function runMode(mode, a2, a3)
|
||||
end)
|
||||
-- opprydding: aktive driv-modus -> hold hover (aldri etterlat drivende uten beskjed);
|
||||
-- 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
|
||||
allOff()
|
||||
elseif mode == "signtest" or mode == "setup" then
|
||||
-- Ctrl+T midt i sign-testen: luftbaaren -> hold hover (ALDRI frittfall); paa bakken -> alt av
|
||||
if signtestAirborne then
|
||||
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear)
|
||||
else
|
||||
allOff()
|
||||
end
|
||||
elseif mode == "cal" or mode == "fly" or mode == "observe" or mode == "standby" or mode == "boot" then
|
||||
if fs.exists(CALIB_FILE) then
|
||||
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
|
||||
if signtestAirborne then holdHoverOut() else allOff() end
|
||||
elseif mode == "cal" or mode == "fly" or mode == "observe" then
|
||||
holdHoverOut() -- disse fl...yr aktivt -> hold hover ved avslutning
|
||||
elseif mode == "boot" or mode == "standby" then
|
||||
-- Ctrl+T ut av standby: hold hover KUN hvis vi faktisk fløy (engasjert). Et parkert skip
|
||||
-- paa bakken skal IKKE begynne aa varme brennere og lette av seg selv.
|
||||
if isEngaged() then holdHoverOut() else allOff()
|
||||
end
|
||||
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" }
|
||||
|
||||
-- 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;
|
||||
-- videre oppdateringer ryddes automatisk via installasjons-loggen .installed_<role>)
|
||||
|
||||
Reference in New Issue
Block a user