Atunci când pe un aeroport se construiește o pistă nouă sau într-o regiune se definește un spațiu aerian ori o rută de zbor noi, acestea intră în utilizare, nu arbitrar, ci urmând un calendar aeronautic. Calendar numit Aeronautical Information Regulation And Control, pe scurt AIRAC. De exemplu, în acest moment al scrierii acestui articol, adică 16 ianuarie 2021, următoarea dată AIRAC este 28 ianuarie 2021, apoi următoarea este 25 februarie 2021, adică e un ciclu de 28 de zile. Asta înseamnă că într-un an calendaristic au loc 13 cicluri AIRAC.
Motivul pentru care s-a introdus acest calendar este necesitatea unei sincronizări la nivel internațional între toate părțile implicate. Dacă ROMATSA aplică restricții pe o rută, e posibil ca o companie aeriană tocmai din Australia să fie interesată de acea schimbare pentru că respectiva companie survolează zilnic România. Mai mult, în trecut distribuția acestor informații se făcea pe suport de hârtie, prin urmare era nevoie de timp pentru prelucrare datelor. Cu toate că acum aceasta se face electronic, încă e nevoie de timp ca acei consumatori de date de acest tip să poată reacționa.
Toate aceste date despre unde se află un anumit aeroport și cum se numește sau care sunt caracteristicele pistei de rulare se numesc informații aeronautice. De asemenea, există un domeniu pe cât de complex, pe atât de fascinant numit Managementul Informațiilor Aeronautice (Aeronautical Information Management), care- în mod evident- are nevoie de software. În rândurile următoare voi încerca să detaliez problematica implicată de acest tip de software.
Existe câteva standarde folosite pentru encodarea acestor date, cele mai faimoase sunt ARINC-424, AIXM 4.5 și AIXM 5.1. Ultimele două folosesc XML, deci au în spate niște XSD-uri care deși sunt destul de stufoase, e relativ ușor pentru programatori să lucreze cu ele. Poate voi povesti despre ele într-un articol viitor. În schimb, ARINC-424 e un standard arhaic, conceput înainte de invenția XML-ului. Cu toate că e un format text, a fost optimizat pentru procesare de către hardware, nu e foarte human readable. O entitate e descrisă prin minim un record, adică o linie de text, iar altele prin mai multe recorduri, mai ales spațiile aeriene. Important de ținut minte că toate aceste standarde versionează entitățile după timp.
Mai jos e un fragment:
SEEUD CHU LR010920 DH
CHU N46105900E024491400 025002 376WGEDEALUL CIUHII 413001305
SEEUD CKG ZU011610VDLW N29444800E106391200 N29444800E106391200W0020013711 514WGEJIANGBEI (CHONGQING) 413021311
SEEUD CLJ LR011120VDHW N46480040E023471410 N46480040E023471410E0050016002 416WGECLUJ NAPOCA 413031311
SEEUD CMP EP011450VDH N52080000E016430900 N52080000E016430900E0050002952 527WGECZEMPIN 413041509
SEEUD CND LR011270VDHW N44174010E028284630 N44174010E028284630E0050003002 486WGECONSTANTA 413051002
SEEUD CNI LR011560 DH CNI N45425800E020542700 003002 297WGECENEI 413061205
SEEUD CRV LR011020VDHW N44190700E023552190 N44190700E023552190E0040006002 368WGECRAIOVA 413081003
ARINC-424 a fost conceput pentru a încărca cu date FMS-urile, adică niște calculatoare (Flight Management Systems) ce se găsesc în cockpiturile aeronavelor moderne.
Flight Management System - sursa foto: Wikipedia
Dar în același timp e folosit de companiile aeriene să-și alimenteze cu date bazele de date online sau diversele sisteme pentru calculul rutei optime de zbor între două aeroporturi.
Ultimele 4 caractere dintr-un record ARINC-424 reprezintă un câmp numit AIRAC Cycle ID. Acesta e numeric, de exemplu ID-ul 2101 înseamnă primul ciclu din anul 2021, iar ID-ul 2102 reprezintă al doilea ciclu din anul 2021. Concret, cycle id 2101 corespunde datei calendaristice 28 ianuarie 2021.
Cei care sunteți programatori cred că vă imaginați deja cum ar arăta implementarea unui parser pentru formatul ARINC-424. Nu e tocmai simplu și, bineînțeles, acest parser are nevoie de o formulă ce convertește cycle id-ul într-o dată calendaristică. Pe baza unei date de start considerată referință și ținând cont că e vorba de un ciclu format din 28 de zile se poate implementa un algoritm de calcul al datei calendaristice.
Cred că am făcut o introducere suficient de lungă, și acum să trecem la acțiune și revenim în prezent sau în context.
Recent, un client, o companie aeriană, îmi cere ajutorul în investigarea unei anomalii. Procesul lor, de sincronizare a datelor aeronautice a eșuat. Situația e tratată cu calm, pentru că s-au obișnuit să exerseze sincronizarea într-un environment de pre-producție și mai e suficient timp până la începutul următorului ciclu AIRAC.
Încep să studiez logurile și datele, și observ anomalia. 50 de entități de tip DesignatedPoint și 2 entități de tip RouteSegment, deși erau prezente în fișierul AIRAC-424 lipsesc la output. Se pierd undeva prin pipeline și mai sunt și efecte colaterale.
Mă apuc să citesc codul sursă și nimic nu se leagă, totul pare ilogic.
Într-un final, observ că AIRAC Cycle ID 2101 e convertit ca fiind 31 ianuarie 2020, dar acesta e data ciclului anterior, cu id-ul 2014! Adică atât 2014 cât și 2101 se converteau la aceeași dată calendaristică. Găsesc rapid metoda de conversie. Faceți cunoștință cu ea:
public String
transformAIRACDateToISO(String airacCycle) {
if (airacCycle != null && airacCycle.length() >= 4) {
int yearRef = 10;
int cycleRef = 1;
int year = Integer.parseInt(
airacCycle.substring(0, 2));
int cycle = Integer.parseInt(
airacCycle.substring(2, 4));
int cycles = (year - yearRef) * 13
+ cycle - cycleRef;
if (cycles < 0) {
cycles--;
}
return ISODateTimeFormat.dateTime()
.print(new DateTime(2010, 1, 14, 0, 0,
DateTimeZone.UTC)
.plusDays(cycles * 28));
}
return "2012-06-28T00:00:00.000Z";
}
Nu mă leg de valoarea implicită returnată, aceea e o altă poveste.
Codul acesta face parte dintr-o soluție software de anvergură ce e în producție de doi ani și jumătate, iar înainte a rulat în diverse teste de integrare cam încă doi ani jumate cât a durat dezvoltarea proiectului. Cum e posibil să dea rateuri tocmai acum?
Ziceam că un ciclu AIRAC are 28 de zile și că într-un an pot exista 13 cicluri AIRAC. Da, doar că odată la 23 de ani, ținând cont de anii bisecți, este un an în care sunt 14 cicluri AIRAC. Programatorului care a scris aceste linii de cod i-a scăpat acest detaliu. Tocmai anul 2020, anul lui COVID-19 este și anul cu 14 cicluri AIRAC. Mai mult, al 14-lea ciclu AIRAC din 2020 cade chiar în ajun de Anul Nou, când lumea are gânduri mai puțin operaționale.
Descoperirea mi se pare destul de filosofică din punct de vedere al testării. Acesta e un bug ce se reproduce odată la 23 ani. E o perioadă lungă, aproape cât o carieră de programator. E un bug de genul Y2K.
Sunt domenii, asigurări, aviație, etc. unde soluțiile software, spre deosebire de software-ul consumer oriented, au o viață mult mai lungă, reușind să ajungă la 30-40 de ani. Putem avea un test coverage de 100%, dar fără data coverage un bug de genul acesta nu poate fi prins decât din întâmplare, mai ales dacă programatorii sau testerii nu cunosc domeniul suficient de bine.
Din fericire, în acest caz problema a fost descoperită în pre-producție și nu a avut loc niciun incident.
Despre incidente în producție cu software pentru navigație aeriană am să vă povestesc în articole viitoare.
Today Software Magazine și Frequentis Romania vă provoacă să rezolvați bugul din problema descrisă în articol. Trimiteți implementarea voastră pe email la adresa contact@todaysoftmag.com și soluția cea mai elegantă va fi publicată în numărul următor, iar Frequentis Romania va premia primele trei cele mai bune soluții.
Pentru a vă ajuta, mai jos găsiți unit test:
@Test
public void testTransformAIRACtoISO() {
assertEquals("2020-12-31T00:00:00.000Z",
transformAIRACDateToISO("2014"));
assertEquals("2021-01-28T00:00:00.000Z",
transformAIRACDateToISO("2101"));
assertEquals("2022-12-29T00:00:00.000Z",
transformAIRACDateToISO("2213"));
assertEquals("2043-12-31T00:00:00.000Z",
transformAIRACDateToISO("4314"));
assertEquals("2044-01-28T00:00:00.000Z",
transformAIRACDateToISO("4401"));
}