diff --git a/.gitignore b/.gitignore index 72f414d..928ec06 100644 --- a/.gitignore +++ b/.gitignore @@ -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_* diff --git a/README.md b/README.md index a3a8663..55b9422 100644 --- a/README.md +++ b/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 diff --git a/build.sh b/build.sh index 9cfb49f..c30d1af 100755 --- a/build.sh +++ b/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=( diff --git a/files/autopilot/autopilot/main.lua b/files/autopilot/autopilot/main.lua index d46e599..77ed106 100644 --- a/files/autopilot/autopilot/main.lua +++ b/files/autopilot/autopilot/main.lua @@ -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 diff --git a/install.lua b/install.lua index bfd300c..43cc817 100644 --- a/install.lua +++ b/install.lua @@ -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_)