feat(routes): export/import validated routes — make the 100% DB permanent
The oracle-validated route DB (the realistic path to 100% under prose-only) lived only in the gitignored real.db, so a navdata rebuild would lose it. Now it is portable and version-controlled: - routes::export_validated -> TSV of every ifps_ok route (one per pair); import_seeds loads them back as reusable seeds (source=seed). - CLI `export-routes` / `import-routes`. - discover reuse now matches any ifps_ok route (not just source=ifps), so imported seeds are reused instantly (verified round-trip). - data/validated_routes.tsv: 65 validated European city-pair routes, built by bulk-seeding discover over ~85 common pairs (the DB grew ~11 -> 65). This is the PFPX-style route DB, committed: 65 pairs are now no-error & instant on any machine; `learn` + more bulk-seed grow it further. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -120,6 +120,24 @@ enum Command {
|
||||
#[arg(long)]
|
||||
force: bool,
|
||||
},
|
||||
/// Export all IFPS-validated routes to a portable TSV file (version-control it
|
||||
/// so the validated route DB survives a navdata rebuild).
|
||||
ExportRoutes {
|
||||
#[arg(long, default_value = "real.db")]
|
||||
db: PathBuf,
|
||||
/// Output TSV path.
|
||||
#[arg(long, default_value = "data/validated_routes.tsv")]
|
||||
out: PathBuf,
|
||||
},
|
||||
/// Import validated routes from a TSV file (as produced by `export-routes`)
|
||||
/// back into the DB as reusable seeds.
|
||||
ImportRoutes {
|
||||
#[arg(long, default_value = "real.db")]
|
||||
db: PathBuf,
|
||||
/// Input TSV path.
|
||||
#[arg(long, default_value = "data/validated_routes.tsv")]
|
||||
r#in: PathBuf,
|
||||
},
|
||||
/// Offline IFPS pre-check of a route string (best-effort, non-authoritative).
|
||||
IfpsCheck {
|
||||
/// Route string, e.g. "LFPG DCT PON UT300 ELCOB ... EGLL".
|
||||
@@ -210,6 +228,23 @@ fn main() -> anyhow::Result<()> {
|
||||
for e in &verdict.errors { println!(" {} {}", e.code, e.msg); }
|
||||
}
|
||||
}
|
||||
Command::ExportRoutes { db, out } => {
|
||||
use flightplanner_core::{db as database, routes};
|
||||
let conn = database::open(&db)?;
|
||||
let tsv = routes::export_validated(&conn)?;
|
||||
let n = tsv.lines().filter(|l| !l.trim().is_empty()).count();
|
||||
if let Some(parent) = out.parent() { std::fs::create_dir_all(parent).ok(); }
|
||||
std::fs::write(&out, &tsv)?;
|
||||
println!("exported {n} IFPS-validated route(s) → {}", out.display());
|
||||
println!(" commit this file so the validated route DB is permanent & shareable.");
|
||||
}
|
||||
Command::ImportRoutes { db, r#in } => {
|
||||
use flightplanner_core::{db as database, routes};
|
||||
let conn = database::open(&db)?;
|
||||
let tsv = std::fs::read_to_string(&r#in)?;
|
||||
let n = routes::import_seeds(&conn, &tsv)?;
|
||||
println!("imported {n} validated seed(s) from {} — discover will reuse them.", r#in.display());
|
||||
}
|
||||
Command::Route {
|
||||
from,
|
||||
to,
|
||||
|
||||
@@ -414,7 +414,9 @@ pub fn discover_route(
|
||||
// Reuse: instant return if our DB already holds an oracle-validated (no-error)
|
||||
// route for this pair — this is how the self-built IFPS route DB pays off.
|
||||
if let Ok(stored) = crate::routes::recent(&conn, &from, &to, 25) {
|
||||
if let Some(r) = stored.iter().find(|r| r.ifps_ok && r.source == "ifps") {
|
||||
// Any IFPS-validated route for this pair — whether discovered here
|
||||
// (source=ifps) or imported from an exported seed file (source=seed).
|
||||
if let Some(r) = stored.iter().find(|r| r.ifps_ok) {
|
||||
return Ok(routing::discover::DiscoverResult {
|
||||
accepted: true,
|
||||
route_string: r.route_string.clone(),
|
||||
|
||||
@@ -97,3 +97,61 @@ pub fn recent(conn: &Connection, dep: &str, dest: &str, limit: usize) -> Result<
|
||||
pub fn count(conn: &Connection) -> Result<i64> {
|
||||
Ok(conn.query_row("SELECT COUNT(*) FROM routes", [], |r| r.get(0))?)
|
||||
}
|
||||
|
||||
/// Export every IFPS-validated (`ifps_ok`) route as TSV lines
|
||||
/// (`dep⇥dest⇥fl⇥dist⇥via_airways⇥route_string`). This is the portable, version-
|
||||
/// controllable form of the route DB, so validated seeds survive a navdata rebuild
|
||||
/// (the `routes` table lives in the same SQLite file as the imported navdata).
|
||||
pub fn export_validated(conn: &Connection) -> Result<String> {
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT dep,dest,cruise_fl,dist_nm,via_airways,route_string FROM routes \
|
||||
WHERE ifps_ok=1 GROUP BY dep,dest ORDER BY dep,dest",
|
||||
)?;
|
||||
let rows = stmt.query_map([], |r| {
|
||||
Ok(format!(
|
||||
"{}\t{}\t{}\t{:.0}\t{}\t{}",
|
||||
r.get::<_, String>(0)?,
|
||||
r.get::<_, String>(1)?,
|
||||
r.get::<_, i32>(2)?,
|
||||
r.get::<_, f64>(3)?,
|
||||
r.get::<_, i32>(4)?,
|
||||
r.get::<_, String>(5)?,
|
||||
))
|
||||
})?;
|
||||
let mut out = String::new();
|
||||
for row in rows {
|
||||
out.push_str(&row?);
|
||||
out.push('\n');
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Import validated routes from TSV (as produced by [`export_validated`]),
|
||||
/// recording each as an `ifps_ok` seed (`source="seed"`). Returns the count.
|
||||
pub fn import_seeds(conn: &Connection, tsv: &str) -> Result<usize> {
|
||||
let mut n = 0;
|
||||
for line in tsv.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let f: Vec<&str> = line.splitn(6, '\t').collect();
|
||||
if f.len() < 6 {
|
||||
continue;
|
||||
}
|
||||
record(
|
||||
conn,
|
||||
f[0],
|
||||
f[1],
|
||||
f[2].parse().unwrap_or(0),
|
||||
f[5],
|
||||
f[3].parse().unwrap_or(0.0),
|
||||
f[4] == "1",
|
||||
true,
|
||||
&[],
|
||||
"seed",
|
||||
)?;
|
||||
n += 1;
|
||||
}
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
EBBR EBLG 60 61 1 EBBR DCT LNO DCT EBLG
|
||||
EBBR EBOS 360 67 1 EBBR DCT DENUT DCT EBOS
|
||||
EBBR EDDF 240 165 1 EBBR DCT SPI T180 PESOV T180 TOBOP T180 NIVNU T847 KULZO T847 IMCOM DCT EDDF
|
||||
EDDF EDDH 180 282 1 EDDF DCT MARUN Y152 ARPEG Z850 BADGO Z850 ABILU Z850 ADEMI Z850 HMM M170 DOMJO M170 OSNAD M170 EXPUH M170 ZEPHE M170 BASUM Y850 MUBZI M105 RIBSO DCT EDDH
|
||||
EDDF EDDS 240 101 1 EDDF DCT ANEKI Y163 NEKLO Y171 INKAM N850 KRH T128 BADSO DCT EDDS
|
||||
EDDF LFPG 190 249 1 EDDF DCT ULKIG Y180 RUDOT Y180 BITBU Y180 ASMOX Y180 NISIV Y180 DIK Y180 IDOSA Y180 TILVI Q763 TALUD Q763 RAPOR Z157 VEDUS DCT LFPG
|
||||
EDDH EDDW 100 62 1 EDDH DCT WSN L23 GIBMA DCT EDDW
|
||||
EDDL EDDK 140 81 1 EDDL DCT GMH Z841 GETNI T858 KOPAG DCT EDDK
|
||||
EDDM EDDF 240 165 1 EDDM DCT GIVMI Y101 ERNAS T161 GOLMO T161 REDNI T161 ASPAT T161 DEBHI DCT EDDF
|
||||
EDDM EDDN 360 74 1 EDDM DCT INPUD Y102 UPALA DCT EDDN
|
||||
EDDM LOWW 90 390 1 EDDM BIBA2E BIBAG L605 DOSEL L605 MIQ T105 ANKER Y104 AKINI Y104 MAMOR L132 RUDNO L132 AGNAV P733 LOMKI DCT OKL DCT VOZ M725 TABEM M725 OKF M725 LANUX LANU7W LOWW
|
||||
EGCC EGGD 240 131 1 EGCC DCT KUXEM P17 NOKIN N862 RISLA N862 KARNO N862 OGTAW N862 ALHUP N862 LUCSA N862 RUMKE N862 WEVBE DCT EGGD
|
||||
EGKK EGCC 360 169 1 EGKK DCT LAM UN57 WELIN T420 AKUPA T420 TIMPO T420 ELVOS DCT EGCC
|
||||
EGKK EGPF 240 410 1 EGKK DCT LAM L10 BPK N601 POTON N601 BEDFO N601 EBOTO N601 PIPIN N601 LESTA N601 EMBOR N601 XAPOS N601 BOLBI N601 ULSAX N601 POL N601 NELSA N601 RIBEL N601 ERGAB N601 SHAPP N601 ABEVI N601 INPIP N601 UTOGU N601 INREV N601 ESKDO N601 IPDOR N601 TLA N601 GRICE P600 ENUKU P600 EDONU P600 FINDO P600 PTH DCT EGPF
|
||||
EGLL EGCC 180 135 1 EGLL DCT UMLAT T418 WOBUN T418 WELIN T420 AKUPA T420 TIMPO T420 ELVOS DCT EGCC
|
||||
EGLL EGNX 190 93 1 EGLL DCT ULTIB T420 HEMEL DCT EGNX
|
||||
EGLL EGPH 250 292 1 EGLL DCT UMLAT T418 WOBUN T418 WELIN T420 AKUPA T420 TIMPO T420 ELVOS T420 TNT UN57 POL UN601 NELSA UN601 RIBEL N601 ERGAB N601 SHAPP N601 ABEVI N601 INPIP DCT EGPH
|
||||
EGNT EGLL 180 236 1 EGNT DCT GIRLI P18 TILNI P18 UVAVU P18 GASKO P18 ABKAT P18 IDKOL P18 LEHOC P18 POL P18 LISTO L612 HON DCT EGLL
|
||||
EGPH EGCC 250 194 1 EGPH DCT GOSAM P600 FENIK L612 LUSIV L612 ELBUS L612 NAVSO L612 BEBNI L612 DCS L612 ASCOM L612 LAKEY DCT EGCC
|
||||
EHAM EHEH 70 57 1 EHAM LOPI1Q LOPIK LOPI2E EHEH
|
||||
EHAM EHRD 70 536 1 EHAM DCT ANDIK M105 PABCO M105 COFCA M105 SUQIF M105 REVLA M105 OPWOT M105 MUBZI M105 RIBSO M105 OMWEG M105 HINHU M105 EXNEL M105 PIZID M105 WEZPU M105 AMRAK P999 ALS N872 DEMIR N872 IZZEX N872 WANZA N872 OGFON N872 AMCUB N872 KUBAT T200 BLUFA DCT EHRD
|
||||
EHAM LFPG 190 223 1 EHAM DCT WOODY N872 AMMOF N872 NIK N872 DENOX N872 CIV Y50 IDOKO Y50 MATIX DCT LFPG
|
||||
EKCH EDDH 280 152 1 EKCH DCT NEXEN T503 GIMRU T503 MICOS N850 NUSTA N850 NIGAD N850 BOGMU DCT EDDH
|
||||
EKCH ESSA 280 309 1 EKCH DCT KEMAX N851 NEXIL N851 ROXEN N851 DEKIK N851 VEPIP N851 BEDOS N851 NEFYN N851 MIKNA N851 PELUP Z226 NILUG DCT ESSA
|
||||
ENGM ENBR 130 176 1 ENGM ATLA3C ATLAP DCT AGEKU DCT PESUR PESU3N ENBR
|
||||
EPWA EPGD 360 164 1 EPWA DCT OLILA T270 MEBIV T270 RILAB T270 OSLOG DCT EPGD
|
||||
EPWA EPKK 360 134 1 EPWA DCT EVINA M66 NEGUV M66 KUKAM DCT EPKK
|
||||
ESGG ENGM 140 160 1 ESGG DCT SABAK L997 XENTA L997 REGMA L997 LUNIP DCT ENGM
|
||||
ESSA ESGG 280 226 1 ESSA DCT ARS N623 BEDLA N866 TABUT N866 DEPEX N866 LAPSI N866 MOXAM DCT ESGG
|
||||
LDZA LDSP 200 144 1 LDZA DCT NIVES Y137 RASTU Y137 PALEZ L862 DIGOT L862 NUPSO DCT LDSP
|
||||
LEBB LEMD 330 191 1 LEBB DCT AMTOS L14 NEA N864 NONTU DCT LEMD
|
||||
LEBL LEPA 190 109 1 LEBL DCT LARPA N859 TOLSO DCT LEPA
|
||||
LEBL LEZL 310 449 1 LEBL DCT LOTOS M985 TORDU M985 DIKUT M985 SOPET M985 VLC M985 SERRA M985 ASTRO M985 POBOS M985 XEBAR M985 YES N747 BLN DCT LEZL
|
||||
LEMD LEAL 300 198 1 LEMD DCT NANDO L150 MOTIL L150 SOSAV L150 ASTRO DCT LEAL
|
||||
LEMD LEBL 300 268 1 LEMD DCT PINAR N870 SEGRE N870 BRITO N870 PISUS N870 PONEN T600 CASPE DCT LEBL
|
||||
LEMD LEVC 300 154 1 LEMD DCT NANDO M871 MINGU M871 ABOSI DCT LEVC
|
||||
LEMG LEBL 260 422 1 LEMG DCT VIBAS M985 BAZAS M985 MAMIS M985 YES M985 XEBAR M985 POBOS M985 ASTRO M985 SERRA M985 VLC L34 CASIM N608 MATEX DCT LEBL
|
||||
LEPA LEMD 310 351 1 LEPA DCT DRAGO N863 PAPOS N863 UMURE N863 XIKEN N975 OSLAP N975 SALAS N975 RAMON N975 NEXAS N975 DISVU N975 ADUXO DCT LEMD
|
||||
LEVC LEBL 280 169 1 LEVC DCT SOPET N608 CASIM N608 MATEX DCT LEBL
|
||||
LEZL LEMD 320 215 1 LEZL DCT HIJ Z230 RUSEM Z230 SOTUK DCT LEMD
|
||||
LFBO LFPO 190 327 1 LFBO DCT LACOU A34 RUMEL A34 BUGUS A34 PERIG A34 MAVAC A34 LMG R10 BALAN R10 OLINO R10 SOPIL R10 AMB DCT LFPO
|
||||
LFLL LFMN 190 175 1 LFLL DCT ROMAM T43 LUKUM T43 BEPER T43 EVALA A3 RETNO A3 PERUS DCT LFMN
|
||||
LFML LFMN 110 101 1 LFML FJR6N MTG G701 LACAZ Q302 ABLAK ABLA8R LFMN
|
||||
LFMN LFPO 190 444 1 LFMN DCT LANKO G7 RIKPO G7 OB G7 POMEG G7 MTG Z16 AVN Z16 RARUS Z16 MTL A3 AMIKO A3 MURRO A3 BELEP A3 ONZON A3 BELVU A3 LESPI A3 FEDZI A3 OMANI A3 MOU DCT LFPO
|
||||
LFPG EDDF 230 270 1 LFPG DCT RANUX DCT VALEK DCT LIPNI UN858 LIMGO UN858 BETEX Z110 BITBU Z110 KULZO T847 IMCOM DCT EDDF
|
||||
LFPG EGLL 240 192 1 LFPG OPAL6A OPALE DCT KESAX DCT DIMAL DCT ALESO ALES1H EGLL
|
||||
LFPG LFBO 140 352 1 LFPG DCT MONOT R161 GIPEX R161 TRARE R161 LEMIN R161 RIVEK R161 CFA V12 SOMTI V12 XUPAL V12 GILUR V12 NASEP Z869 NARAK DCT LFBO
|
||||
LFPG LFMN 140 410 1 LFPG DCT MONOT R161 GIPEX R161 TRARE R31 MENOX R31 OLUGI R31 TIS R31 PIMAK R31 VEROT R31 AGREV R31 MTL A3 EVALA A3 RETNO A3 PERUS DCT LFMN
|
||||
LFPO LFBD 80 288 1 LFPO DCT OLZOM J18 MOTAL J18 VEROS G54 DEPOM G54 CTX G28 GUSON G28 LMG R10 NORON R10 VELIN DCT LFBD
|
||||
LFPO LFMN 190 393 1 LFPO DCT MONOT R161 GIPEX R161 TRARE R31 MENOX R31 OLUGI R31 TIS R31 PIMAK R31 VEROT R31 AGREV R31 MTL A3 EVALA A3 RETNO A3 PERUS DCT LFMN
|
||||
LFRN LFMN 190 514 1 LFRN DCT GODAN A25 NTS R66 BOBRI R66 LUGEN R66 ABSIE R66 BETOB R66 POI R66 ADABI R66 TUDRA R66 BALAN R66 GUSON R66 ETUKU R66 RISUN V18 ENESO V18 TIS R31 PIMAK R31 VEROT R31 AGREV R31 MTL A3 EVALA A3 RETNO A3 PERUS DCT LFMN
|
||||
LFRS LFST 80 405 1 LFRS DCT ANG H34 SABLE H34 TUNAX H34 LUMAN H34 KOVAK H20 DOMOD A3 GILUX A3 MOTAL R11 OSKIN R11 KUTAN R11 BALMU R11 RLP R11 EPL DCT LFST
|
||||
LICJ LIMC 190 491 1 LICJ DCT GIANO L12 RONAB L12 RUVIP L12 BETOT L12 POKAV L12 TINTO L12 VELAD L12 ELB M729 DOCME M729 NORNI M729 UPDIN M729 BETEN M729 SPEZI M729 IDONA Y69 MEBUR DCT LIMC
|
||||
LIMC LIRF 190 305 1 LIMC DCT LAGEN L50 ANAKI L50 IXITO L50 UNITA L50 TIDKA L50 KONER L50 EVROG L50 BAKRO L50 ELB L146 ELKAP DCT LIRF
|
||||
LIMC LIRN 190 424 1 LIMC DCT LAGEN L50 ANAKI L50 IXITO L50 UNITA L50 TIDKA L50 KONER L50 EVROG L50 BAKRO L50 ELB M729 PUDOC M729 GILIO M729 MEDAL M729 RAVAL M729 LUNAK M729 ELVIN M729 RIFFI M729 PNZ DCT LIRN
|
||||
LIME LIML 360 45 1 LIME SRN1V SRN SRN1U LIML
|
||||
LIME LIRF 190 296 1 LIME DCT LOGDI T293 LURUT M859 UVOKI M859 LUPOS L995 IPLUB L995 LOMED L995 KUGIX L995 IBRID L995 ILOWE L995 AMTEL Q411 UPONO Q411 RITEB DCT LIRF
|
||||
LIML LIRP 190 141 1 LIML DCT ITCAP M730 KALMO M730 MIVKI DCT LIRP
|
||||
LIPZ LIRF 190 226 1 LIPZ DCT CHI Y664 EKTUK Y664 GAVRA Y345 RITEB DCT LIRF
|
||||
LIRF LICJ 190 235 1 LIRF DCT KONUT Z923 RONAB L12 GIANO DCT LICJ
|
||||
LIRF LSZH 190 381 1 LIRF DCT SOVAN L995 AMTEL L995 ILOWE L995 IBRID L995 KUGIX L995 LOMED L995 IPLUB L995 LUPOS L995 RIPDU L995 IVLOX L995 OSBUL L995 MISPO L995 BEKAN L995 TZO L995 PEPAG L995 CANNE Z651 DETRI Z651 KELIP DCT LSZH
|
||||
LIRN LIRF 190 112 1 LIRN DCT SIPRO Z803 LAT DCT LIRF
|
||||
LJLJ LDZA 120 74 1 LJLJ LUPI2D LUPIX DCT LULUD LULU1A LDZA
|
||||
LSGG LFPO 180 224 1 LSGG DCT DIPIR T37 LUKID T37 IBABA T37 DJL A6 BALMU R11 KUTAN DCT LFPO
|
||||
LSGG LSZH 180 125 1 LSGG DCT SOSAL N871 TELNO N871 KORED N871 KONOL N871 BERSU DCT LSZH
|
||||
|
Reference in New Issue
Block a user