Upload
khangminh22
View
0
Download
0
Embed Size (px)
Citation preview
Övervakningssystem för tåg
Insamling och presentation av data via ett grafiskt gränssnitt
Monitoring system for trains
Gathering and presentation of data via a graphical interface
Karl Hansson
Ivan Toma
Fakulteten för hälsa, natur- och teknikvetenskap
Datavetenskap
C-uppsats 15 hp
Handledare: Lothar Fritsch
Examinerande lärare: Johan Eklund
Datum: 2019-01-16
iii
Sammanfattning
I dagsläget finns en del problem vid beslutsprocesser som trafikledningen inom tågtrafiken
dagligen får handskas med. Dessa beslutsprocesser kan effektiviseras genom ett förbättrat stöd
och underlag och tanken med projektet var att skapa detta stöd.
Detta projekt har resulterat i ett enkelt system som samlar in och presenterar data över tåg i ett
grafiskt gränssnitt i form av en karta. Systemet är utvecklat för att enkelt kunna få fler
funktioner applicerade i framtiden och var från början tänkt att bestå av högre utbud funktioner
än vad det har i dagsläget. Projektet har tagit form med hjälp av tågbolaget SJ med information
om de hinder de stöter på, och riktlinjer satta från vår handledare under projektet.
Vi kommer i denna rapporten att beskriva mer detaljerat hur projektet tagit form, vilka problem
vi fått handskas med och hur systemet är utvecklat.
v
Abstract
Currently there exists several problems with the decision-making processes that the traffic
management within train traffic daily must put up with. The decision processes can be made
more efficient by having a good support and structure, and the goal of this project was to create
this support.
This project has resulted in a simple system that collects and presents train data on a graphical
interface in the form of a map. The system is designed to be expandable with new features and
functionalities with relative ease and was initially supposed to include some of these extra
features. The project has taken shape with help from the train company SJ with information
about problems they’ve encountered, and guidelines set by our supervisor during the project
In this report we will describe how the project has taken shape, what problems we have dealt
with and how the system is developed.
vii
Tack
Vi vill tacka alla inblandade på Softcode som bidragit med stöd, expertis och även en mycket
trevlig arbetsplats. Vi vill även tacka Anders Åslund som genom Softcode har bidragit med
råd på vägen. Ett tack vill vi även ge till Christoffer Andersson som tog emot oss på SJ och
visade oss runt.
Vi vill även tacka vår handledare Lothar Fritsch för rådgivning, och Katarina Asplund för
tiden hon tog för att bidra med extra stöd.
ix
Innehållsförteckning
1 Inledning ............................................................................................................................ 1
1.1 Syfte ........................................................................................................................... 1
1.2 Mål ............................................................................................................................. 2
1.3 Uppdragsbeskrivning ................................................................................................. 2
1.4 Disposition ................................................................................................................. 4 Kapitel 2 – Bakgrund ............................................................................................................... 4 Kapitel 3 – Projektdesign ......................................................................................................... 4 Kapitel 4 – Implementation ...................................................................................................... 4 Kapitel 5 – Utvärdering ............................................................................................................ 4 Kapitel 6 – Slutsats .................................................................................................................. 4
2 Bakgrund ........................................................................................................................... 5
2.1 Språk .......................................................................................................................... 5 SQL .......................................................................................................................................... 5 C ............................................................................................................................................. 5 C++ .......................................................................................................................................... 5 Java .......................................................................................................................................... 6 C# ............................................................................................................................................. 6 PHP .......................................................................................................................................... 6 HTML ...................................................................................................................................... 7 JavaScript ................................................................................................................................. 7
2.2 Verktyg ...................................................................................................................... 8 Microsoft Azure ....................................................................................................................... 8 Apache Kafka ........................................................................................................................... 8 Kubernetes ............................................................................................................................... 9 Docker .................................................................................................................................... 10 Git .......................................................................................................................................... 10 MySQL Workbench ............................................................................................................... 11 Open Street Map .................................................................................................................... 11 Mapbox .................................................................................................................................. 11 Leaflet .................................................................................................................................... 11 Google Maps .......................................................................................................................... 12 Pubnub ................................................................................................................................... 12 XAMPP .................................................................................................................................. 13 NMEA GPRMC ..................................................................................................................... 13 AJAX ..................................................................................................................................... 14 JSON ...................................................................................................................................... 15
2.3 Reguljära uttryck ..................................................................................................... 17
2.4 Existerande lösningar ............................................................................................... 17 Utplacerade avläsningspunkter .............................................................................................. 18 Kommunikation via ombord personalens mobiler ................................................................. 19 GPS-enheter på tåg ................................................................................................................. 19
3 Projektdesign ................................................................................................................... 20
3.1 Projektets grundidé .................................................................................................. 20
3.2 Planering .................................................................................................................. 20 Möten med olika trafikledningar ............................................................................................ 20 Möten med handledare ........................................................................................................... 21
x
Vägledning på Softcode ......................................................................................................... 22
3.3 Motivering och mål kring projektändringarna ......................................................... 22
3.4 Användarfall ............................................................................................................ 23
3.5 Generell design ........................................................................................................ 23 Databas ................................................................................................................................... 24 Dataomvandlare ..................................................................................................................... 25 Grafiskt gränssnitt .................................................................................................................. 25
3.6 Val av språk ............................................................................................................. 25
3.7 Testning och validering av verktyg ......................................................................... 25
3.8 Design av databasen ................................................................................................ 26
3.9 Design av dataomvandlarna ..................................................................................... 27
3.10 Design av det grafiska gränssnittet .......................................................................... 28
4 Implementation ............................................................................................................... 29
4.1 Introduktion ............................................................................................................. 29 Mjukvara och programspråk som använts under implementationen ...................................... 29
4.2 Lokalisering av tågstationer och mötespunkter på järnvägen .................................. 30 Inhämtning av data och felhantering ...................................................................................... 30 Formatering av inhämtade data .............................................................................................. 32 Stationstabellens SQL-struktur och inmatning av data .......................................................... 33
4.3 Lokalisering av aktiva och inaktiva fordon ............................................................. 33 Inhämtning av data och felhantering ...................................................................................... 33 Formatering av inhämtade data .............................................................................................. 35 Tågtabellens SQL-struktur och inmatning av data ................................................................. 37
4.4 Databas och versionshantering ................................................................................ 38 Utvecklingsmiljö .................................................................................................................... 38 Produktionsmiljö .................................................................................................................... 38
4.5 Visualisering av data ................................................................................................ 39
5 Utvärdering ...................................................................................................................... 43
5.1 Resultat .................................................................................................................... 43
5.2 Problem .................................................................................................................... 44 GDPR-skyddade data och tillgång till API:er ........................................................................ 44 API:er som inte ville fungera ................................................................................................. 45
6 Slutsats ............................................................................................................................. 48
6.1 Helhetsbild av projektet ........................................................................................... 48
6.2 Vidareutveckling av systemet .................................................................................. 48
Referenser ............................................................................................................................... 50
Informationskällor ............................................................................................................. 50
Bildkällor ........................................................................................................................... 53
A Bilagor .............................................................................................................................. 54
A.1 Stationsdataomvandlaren ......................................................................................... 54
xi
A.2 Tågdataomvandlaren ................................................................................................ 59
A.3 Webbgränsnittet ....................................................................................................... 65 A.3.1 Index ...................................................................................................................................... 65 A.3.2 Trains ..................................................................................................................................... 72 A.3.3 Stations ................................................................................................................................... 73
xii
Figurförteckning
Figur 1.1 Översikt av projektet ........................................................................................... 3
Figur 2.1 SQL-exempel ....................................................................................................... 5
Figur 2.2 Exempel på PHP och HTML ............................................................................... 7
Figur 2.3 JSON-objekt ...................................................................................................... 15
Figur 2.4 JSON-array ........................................................................................................ 16
Figur 2.5 JSON-värde ....................................................................................................... 16
Figur 2.6 JSON-sträng ...................................................................................................... 16
Figur 2.7 JSON-nummer ................................................................................................... 17
Figur 2.8 RFID-tekniken ................................................................................................... 18
Figur 3.1 Visualisering av systemet .................................................................................. 22
Figur 3.2 Generell design av systemet .............................................................................. 24
Figur 3.3 Databasens tabeller ............................................................................................ 27
Figur 3.4 Första gränssnittsdesignen ................................................................................. 28
Figur 4.1 Frågan som skickas till Trafikverkets API ........................................................ 31
Figur 4.2 Utdrag ur svaret Trafikverkets API ................................................................... 31
Figur 4.3 Reguljärt uttryck för tolkning av Trafikverkets JSON-data .............................. 32
Figur 4.4 Utdrag ur stationstabellen .................................................................................. 33
Figur 4.5 Exempel på inhämtad data från Oxyfi ............................................................... 34
Figur 4.6 Reguljärt uttryck för tolkning av Oxyfi:s data .................................................. 34
Figur 4.7 Utdrag ur det reguljära uttrycket för tolkning av Oxyfi:s data .......................... 36
Figur 4.8 Utdrag ur tågtabellen ......................................................................................... 38
Figur 4.9 Utdrag ur JSON-strängen som kartan hämtar för att rita ut tågen ..................... 39
Figur 4.10 Utdrag ur JSON-strängen som kartan hämtar för att se tåg vid trafikplatser .. 39
Figur 4.11 Sveriges alla trafikplatser ................................................................................ 40
Figur 4.12 Värmlands (med omnejd) alla trafikplatser ..................................................... 41
Figur 5.1 Översikt över de aktiva tågen i Värmland ......................................................... 43
xiii
Tabellförteckning
Tabell 2.1 NMEA GPRMC ............................................................................................... 14
1
1 Inledning
I en allt mer digitaliserad värld förväntar sig konsumenter som leverantörer större, bättre och
smidigare systemlösningar. Någonting som dock inte hängt med ordentligt är Sveriges
järnvägar och kringliggande system. I dagsläget finns inte en komplett vy över var Sveriges alla
järnvägsoperatörer har sina fordon, både parkerade och resande, samt annan nyttig information
som kringliggande terräng. Detta projekt syftade till att bygga upp ett grundläggande system
som knyter ihop relevant (och tillgänglig) data för att sedan kunna användas till en lämplig
systemlösning, för att i sin tur kunna användas för att övertyga operatörer att se fördelarna och
användbarheten med ett sådant system.
1.1 Syfte
SJ, som idag är en marknadsledande tågoperatör, knyter samman Sverige och likaså
Skandinaviens huvudstäder Stockholm, Köpenhamn och Oslo. [1] Detta skapar idag
möjligheter för människor att kunna bo, arbeta och studera på platser och även kunna resa på
ett enkelt sätt. Med 1200 dagliga avgångar, 284 trafikerade stationer, cirka 4500 anställda och
en summa på ca 47,5 miljoner resenärer varje år, placeras en stor vikt på säkerhet och
framkomligheten för att resenärerna ska känna trygghet i att välja SJ som tågoperatör.
För att kunna garantera denna trygghet behöver trafikledningen på SJ diverse stöd för att
försäkra sig om att de hela tiden värnar om säkerheten för resenärer och även personal som
finns på tågen och även att framkomligheten är god så att resenärerna kommer fram enkelt och
i tid. Ett exempel på detta är att ha god vetskap om terrängen runt om ett tåg för att vid
evakuering kunna flytta resenärer och personal från ett tåg, på ett säkert och smidigt sätt. Ett
annat exempel är att kunna se när ett tåg står stilla på ett spår där det inte borde. I nuläget så tar
den beslutprocess och det arbetet för att lösa uppkomna problem inom de två exempel som
precis har givits, betydligt längre tid än vad som önskas. Det krävs att man letar inom flera olika
system och även förlitar sig på kommunikation mellan personalen på tåget och trafikledningen.
Inom vissa områden har man till och med inget underlag alls för att tackla problemen.
2
Att inte ha all data samlat på ett ställe och samtidigt inte ha en översikt över tågen i realtid är
det som tar tid och markant påverkar beslutsprocesserna. Syftet med detta projekt var att
underlätta och effektivisera dessa beslutsprocesser genom att skapa ett underlag i form av ett
system som kan knyta ihop den utspridda datan och ge en visuell överblick över tågens position.
Systemet kommer att fungera som ett demo för framtida implementationer.
1.2 Mål
Målet med detta projektet var att skapa ett system som kan fungera som underlag och upplysa
om de behov och möjligheter som finns för att utforma ett liknande system storskaligt. Tanken
var att samla in data i en databas, anpassat för att kunna användas till att bygga flertalet lager
med funktionalitet på en karta som kan användas till att underlätta beslutskrävande processer.
1.3 Uppdragsbeskrivning
Arbetet var tänkt att agera som underlag för framtida implementationer av ett liknande system.
Systemet skulle kunna använda sig av den insamlade datan för att presentera detta i ett grafiskt
gränssnitt. Datainsamling skulle ske från ett antal olika API:er. Datan som hämtas kunde
innehålla sådant som anses vara onödigt och även data som är trasigt. På lämpligt sätt skulle
den data som är nödvändig för det uttänkta systemet filtreras från resterande data och sedan
läggas in i en databas. Därefter skulle funktionalitet byggas för att kunna presentera ett antal
användarfall i ett grafiskt gränssnitt.
Figur 1.1 visar ett exempel på hur detta systemet var tänkt att se ut och användas. Användaren
var i detta fall tänkt att vara en trafiksamordnare eller annan ledningspersonal på trafikverket.
Data samlas in från ett antal API:er in i en omvandlare. Omvandlaren filtrerar och gör om datan
så den är i rätt format, och därefter lägger in datan i en databas. Från databasen hämtas data ut
till ett antal användarfall. Dessa användarfall skulle fungera som tilläggsprogram till ett grafiskt
gränssnitt, i detta fall fungera som lager på en karta med olika funktionaliteter.
3
För att ha förståelse för vilka användarfall som var av nytta och även vilken data som var av
värde att samla in så skulle följande göras:
• Möten med SJ och trafikledningen för att samla information över de beslutsprocesser
och problem som påverkar arbetet negativt.
• Studera befintliga lösningar som används för att underlätta dessa beslutsprocesser.
Utöver detta utvärderades och testas flera verktyg och tekniker som kunde användas vid
implementationen av systemet.
Figur 1.1 Översikt av projektet
4
1.4 Disposition
Kapitel 2 – Bakgrund
Kapitel 2 avser att introducera olika programmeringsspråk och verktyg som kommer att nämnas
i andra kapitel. De existerande lösningar som finns hos trafikledningen idag presenteras också.
Kapitel 3 – Projektdesign
I kapitel 3 ges en tydligare introduktion till projektet, hur projektet utformats och även de mål
som sattes för projektet. Här presenteras även test och utvärdering av olika verktyg och språk
för att ge en klar bild av varför projektet tog den form det gjorde.
Kapitel 4 – Implementation
Kapitel 4 presenterar systemet och dess komponenter. Här presenteras både systemets
inhämtning av data, filtrering av onödiga och nödvändiga data och det grafiska användar-
gränssnittet i detalj.
Kapitel 5 – Utvärdering
I kapitel 5 utvärderas projektet och systemet analyseras. Utvärderingen ska styrka de mål som
satts för projektet och ett helhetsresultat ges. I kapitlet presenteras även de problem och hinder
som uppstod under projektets gång och som påverkat slutprodukten.
Kapitel 6 – Slutsats
Kapitel 6 knyter samman projektet och ger en summering av hur arbetet har gått. Vidare ges
även förslag på hur systemet kan vidareutvecklas.
5
2 Bakgrund
Syftet med detta kapitel är att ge en kort introduktion till de olika språk som under projektets
gång har använts eller övervägts att användas. Vidare presenteras de verktyg som testades och
även en kort presentation av de lösningar som trafikledningen använder sig av idag. Även
reguljära uttryck presenteras kort, då det kan vara av användning för att förstå delar av
implementationen. I kapitel 3.6 står det om vilka av språken som valdes för projektet och i
kapitel 3.7 så står det om vilka verktygen som valdes.
2.1 Språk
SQL
SQL är ett programspråk som används för att hantera data i relationsdatabaser, och började
utvecklades av IBM på 1970-talet [2]. Exempel på SQL-kommando (Figur 2.1):
Hämta ut värdena namn och nummer från tabellen Kontakter där värdet favoriter är lika med
ja, sorterat efter värdet namn i bokstavsordning.
C
C är ett ALGOL-baserat (ALGOL, programspråk från 1958) imperativt programspråk [3].
Programspråket B, som C är baserat på, var inte plattformsoberoende, utan skrevs på PDP-7,
och hade endast en datatyp (word). C skapades därefter med kravet på att kunna fungera på
mainframe-, mini- och mikrodatorer. C släpptes för första gången 1970 och var skrivet i
assembler. C är idag ett av de mest populära programspråken och näst intill varje plattform har
C-kompilatorer.
C++
C++ är ett programspråk som stödjer objektorienterad programmering, generisk
programmering, dataabstraktion samt lågnivå hårdvarunära programmering [4]. Språket är
Figur 2.1 SQL-exempel
6
baserat på C, och omfattar stora delar av språket, men har också många semantiska skillnader.
C++ används mer och mer idag på områden där C traditionellt använts. C++ är idag, likt C, ett
av de mest populära språken och hittas ofta i produkter som datorspel, konsumentelektronik
och allt däremellan.
Java
Java är ett objektorienterat programspråk [5]. Språket skapades som ett alternativ till C och
C++, och dess syntax kommer därifrån. Javakod kompileras till Java-bytekod som senare kan
exekveras på alla enheter som stödjer Java, då bytekoden körs på virtuella Java-maskiner. Java
är, likt de två föregående språken, även det ett av de mest använda språken på marknaden.
C#
C# är ett objektorienterat programspråk [6]. Språket är i grunden baserat på C++, men har stora
liknelser med Java. C# är dock inte plattformsoberoende, då fria implementationer av språket
är ofullständiga och saknar vissa komponenter ur .NET-ramverket som C# är en del av. C#
använder, likt Java, bytekod som körs i en virtuell maskin. De olika bytekoderna är dock inte
kompatibla med varandra.
PHP
PHP är en förkortning för ”PHP: Hypertext Preprocessor” och är ett öppet generellt skriptspråk
som huvudsakligen är lämpat för webbutveckling och kan mycket smidigt integreras med
HTML [7]. PHP är väldigt enkelt för nybörjare samtidigt som det erbjuder avancerade
funktioner för utvecklare med mer erfarenhet. Fördelarna med PHP är att istället för att ha flera
rader med HTML kommandon så kan de ersättas med PHP. I Figur 2.2 ges exempel av kod
skriven i PHP. Koden är omsluten av instruktioner som avser start och slut av PHP. ’<?php’
anger starten av PHP-blocket och ’?>’ slutet. I exemplet så kommer ”Exempel på PHP” att
skrivas ut på skärmen.
7
HTML
HTML står för ”Hypertext Markup Language” och är ett sidbeskrivningsspråk för att skapa
webbsidor och webbapplikationer [8]. Ett sidbeskrivningsspråk är ett slags format för
dokument. Webbläsare får dessa HTML-dokument från en webbserver eller en lokal lagring.
Dessa dokument översätts till multimediasidor.
Grunden och byggstenarna i HTML-sidor är HTML-element. Dessa element är representerade
av taggar. Varje tagg beskriver olika delar av innehållet på en webbsida som tillexempel
”Header”, ”Paragraph” och ”Table”. Webbläsarna visar i sin tur inte dessa taggar utan använder
de för att rendera innehållet av webbsidorna. Taggarna är element som är omslutna av
vinkelparenteser och kommer i par med en start och en sluttagg.
JavaScript
JavaScript är ett skriptspråk som gör det möjligt att implementera mer komplexa funktioner på
en webbsida än HTML [9]. Med JavaScript kan man dynamiskt uppdatera innehållet på en
webbsida, kontrollera media, animera bilder, kontrollera fält innan data skickas vidare och en
mängd andra funktioner. Utöver detta så används JavaScript även i andra användningsområden
som till exempel: Tillämpningar på serversidan för att arbeta med olika databasanslutningar,
skicka data över nätet och liknande tillämpningar.
Figur 2.2 Exempel på PHP och HTML
8
2.2 Verktyg
Microsoft Azure
Microsoft Azure är en uppsättning med molntjänster från Microsoft skapta för att bygga och
hosta webbapplikationer [10]. Dessa görs via Microsofts egna datacenter. Bland dessa
molntjänster finns ”Azure SQL Database” som är en relationsdatabastjänst. ”Azure SQL
Database” har stöd för strukturer som JSON och XML och ska enligt Microsoft vara
högpresterande, tillförlitlig och en mycket säker databas [11]. Microsoft Azure utlovar även en
tillgänglighet på 99,99%. Tillgängligheten är väldigt viktig när man bygger ett realtidssystem
då man inte vill att det sker någon förlust av data. Bland alla funktioner finns alternativ som
kolumnlagringsindex för en enklare och effektiv analytisk analys, minnesintern OLTP för bättre
transaktionell bearbetning och elastiska pooler för att lösa problem som kan uppkomma för
företag med oförutsägbara användningsmönster. En elastisk pool innebär att prestandaresurser
allokeras till en enda pool istället för en databas. Detta gör att användarna inte behöver reglera
databasprestanda upp och ner efter behov.
Apache Kafka
Apache Kafka är en open-source-strömningsplattform [12]. Kafka är vanligtvis användbart
inom två väldigt breda användningsområden. Det ena är utveckling av datapipelines i realtid
som på ett säkert sätt hanterar och får data mellan olika system eller applikationer. Det andra är
utveckling av realtidsströmmande applikationer som omvandlar eller reagerar på olika
dataflöden.
För att förstå Kafka så behöver man ha vetskapen om att:
• Kafka körs på en eller flera servrar, som ett kluster, och kan ha ett span över multipla
datahallar.
• Kafka klustret lagrar flöden av poster i olika ämnen / kategorier.
• Varje post har en nyckel, ett värde och en angiven tidpunkt.
Därefter är det även viktigt att veta att Kafka består av fyra API:er:
• Producer API som tillåter en applikation att publicera en ström av olika register till en
eller flera antal Kafka kategorier,
• Consumer API som tillåter en applikation att prenumerera på en eller flera antal ämnen
och därefter bearbeta strömmen av poster som applikationen mottager.
9
• Streams API som tillåter en applikation att fungera likt en strömprocessor och
omvandlar en ingångström av poster från en eller flera ämnen till en utgångström för en
eller flera utgångsämnen.
• Connector API som gör det möjligt att bygga och köra återanvändbara producenter eller
prenumeranter som ansluter en eller flera Kafka ämnen till befintliga applikationer.
I kärnan av Kafka finns ämnen. Ett ämne är en kategori som poster publiceras till [13]. Ett ämne
kan ha inga, en eller flera prenumeranter på den data som skrivs till ämnet. Varje ämne blir
tilldelat en partitionerad logg. De partitionerade loggarna distribueras över Kafka-klustrets
olika servrar, där varje server hanterar data och förfrågningar för att läsa från loggarna. Sedan
kopieras partionerna till ett antal konfigurerbara servrar för felhantering.
Varje partion har en server som agerar som huvudserver och därefter noll, en eller flera servrar
som kan tänkas agera som ”följare”. Huvudservern hanterar partionernas alla läs och
skrivförfrågningar medan ”följarna” har i uppgift att hela tiden replikera huvudservern. Skulle
huvudservern misslyckas så kommer en av följarna att bli den nya huvudservern. Belastningen
är välbalanserad inom klustret just för att varje server fungerar som en huvudserver för några
av dess partitioner samtidigt som den är en ”följare” för andra servrar.
Kubernetes
Kubernetes är en portabel open-source-plattform som är till för hantering av tjänster och
arbetsbelastningar i containrar [14]. Kubernetes kan kortfattat ses som en mikroservice-
plattform eller en container-plattform och den tillhandahåller en container-centrerad
förvaltningsmiljö där den anpassar data, nätverk och lagring baserat på användarens
arbetsbelastningar. Målet med Kubernetes är att automatisera skalning, implementering, drift
och resurstilldelning av containrar över kluster. Kubernetes används främst tillsammans med
Docker (se delkapitel 2.2.4 för beskrivning av Docker).
Kubernetes centrala schemaläggningsenhet kallas för pod [15]. En pod består av en eller flera
containrar som kan dela resurser. Varje pod har en unik IP-adress inom klustret för att göra det
möjligt för applikationer att använda portar utan risk för att problem ska uppstå. Inom en pod
kan alla olika containrar referera till varandra på localhost men ingen av dessa containrar kan
direkt referera till en container inom en annan pod. Detta görs istället genom IP-adressen alla
podar blivit tilldelade.
10
Docker
Det Docker gör är att sätta upp containrar [16]. Detta görs baserat på virtualisering på
operativsystemnivå snarare än på hårdvarunivå. Detta innebär att containrarna är isolerade ifrån
varandra. De har sina egna filsystem och deras användning av datorresurser kan begränsas.
Samtidigt som de är enklare att sätta upp än virtuella maskiner så är de även bärbara över molnet
och operativsystemsfördelningar, då de inte är kopplade till det underliggande filsystemet och
infrastrukturen. Containrar gör det möjligt för en utvecklare att packa ner en applikation
tillsammans med alla de delar som den behöver [17]. Exempelvis så kan de bibliotek och
inställningar applikationen behöver, skickas med i en container. Detta gör att det går att leverera
allting som ett enda paket och försäkra sig om att applikationen kan köras på vilken annan
maskin som helst oavsett den nya maskinens inställningar.
Git
Git är ett versionshanteringssystem som är utformat för att hantera allt från små till stora projekt
[18]. Versionshantering har en rad med fördelar. En av de största fördelarna är att alla ändringar
av en kod lagras i en databas och hålls koll på. Detta gör det möjligt för utvecklare att jämföra
olika versioner med varandra och vid fel kunna gå tillbaka till en tidigare version av koden.
Det som även möjliggörs är att utvecklarna kan arbeta på samma kod utan att påverka
varandra [19]. För att ge en enkel överblick kan Git beskrivas med följande: En utvecklare
skriver kod, en annan ändrar på kod och den tredje tar bort kod och detta kan de göra samtidigt
utan att påverka varandra genom att befinna sig på olika brancher. En branch är en kopia av
koden där den enskilde utvecklaren kan arbeta med koden och därefter göra en merge, det vill
säga, slå ihop sina ändringar med huvudkoden och samtidigt kontrollera att inga konflikter
uppstår med ändringarna.
Ett annat exempel är att en utvecklare skapar en branch från version 1.5 av exempelprogrammet
”X” [20]. Syftet här är att förbereda publiceringen av version 1.6. Utvecklaren gör sina
ändringar och bestämmer sig för att göra en commit på dessa ändringar. En commit innebär att
ändringarna sparas till repositoryt och ett repository är ett förråd som lagrar metadata för bland
annat ändringar i koden. Utvecklaren bestämmer sig sedan för att hoppa till branchen för
version 1.3 av ”exempelprogrammet ”X. Här skapas en branch för att hantera en bugg som bara
11
påverkar version 1.3 av programmet. Buggen löses och branchen mergas tillbaka in i branchen
för version 1.3, detta utan att påverka ändringarna som skett i branchen för version 1.6.
MySQL Workbench
MySQL Workbench är ett verktyg som visuellt tillhandahåller datamodellering, SQL-
utveckling och annan databashantering [21]. MySQL Workbench underlättar databashantering
markant genom att möjliggöra hantering, generering modellering och design av databaser
visuellt. Här finns det även möjlighet för att kunna skapa, exekvera och optimera SQL-
kommandon samtidigt som färgmarkering av syntax, automatiskt slutförande, kortkommandon
och historik över tidigare SQL-kommandon förenklar arbetet med databaserna. Här kan
utvecklaren även visuellt strukturera sina databaser, sätta upp nya tabeller, redigera kolumner
och primärnycklar.
Open Street Map
Open Street Map är ett projekt framtaget och byggt av kartografer för att distribuera geografiska
data gratis [22]. Denna data kan i sin tur sedan användas för att skapa allt från vägkartor till
kartor för att presentera GPS-data till kartor över sevärdheter och en mängd andra
användningsområden. Open Street Maps data uppdateras och underhålls tack vare kartografer
men även bidragsgivare så som GIS-proffs, ingenjörer och frivilliga med hjälp av flygbilder,
GPS-enheter och lågteknologiska fältkartor.
Mapbox
Maxbox är en stor och växande leverantör av anpassningsbara kartor online för webbsidor och
mobilapplikationer [23]. Bland dessa finns giganter som Facebook och Snapchat. Mapbox
startades upp 2010 som en del av projektet Development Seed, där målet var att kunna erbjuda
anpassningsbara kartor till ej vinstgivande kunder. Mapbox använder sig av data hämtat från en
rad öppna datakällor, bland dessa finns Open Street Map. Det Mapbox också gör är att använda
data som kommer från användare på applikationer som använder sig av Mapbox för att med
hjälp av automatiserade funktioner upptäcka data som troligtvis saknas i Open Street Map.
Därefter rapporterar Mapbox-utvecklarna in detta till underhållarna av Open Street Map eller
själva hjälper till med att gå in och fixa problemen.
Leaflet
Leaflet är ett stort öppet JavaScript-bibliotek som används för att bygga applikationer för
webbkartläggning [24]. Leaflet stödjer de flesta mobilapplikationer och datorprogram med
12
HTML5 och CSS (se kapitel 4.1.1 för beskrivning av CSS). Leaflet är en av de mest populära
JavaScript-biblioteken för kartläggning och används av stora sidor som bland annat Pinterest.
Leaflet gör det möjligt för utvecklare med en bakgrund inom geografiska informationssystem
att enkelt och snabbt anpassa och visa ”tile maps” på publika servrar. ”Tile maps”-kartor som
presenteras i webbläsaren är byggda genom att limma ihop flera individuella bilder som bildar
en karta och är det mest populära sättet att visa och navigera i kartor på nätet. En bonus här är
möjligheterna att ladda in data från GeoJSON-filer för att kunna anpassa och skapa interaktiva
lager på kartan så som markörer, punkter som rör sig i realtid och klickbara områden med
popup-fönster.
Google Maps
Google Maps är en öppen webbkartläggningstjänst från Google som har allt från satellitbilder
till 360 graders panoramavy av gator till realtidsinformation av trafiksituationer [25]. Det
började som ett datorprogram utvecklat av ”Where 2 Technologies”, skrivet i C++. Programmet
köptes sedan upp av Google som vidareutvecklade programmet till en webbapplikation som är
byggt med en front av XML, JavaScript och Ajax. De kartor och satellitbilder som Google
Maps använder sig av täcker nästintill hela världen och det finns möjligheter för att zooma in
och ut utan problem. Flertalet av kartorna kommer ifrån olika leverantörer, bland annat Tele
Atlas och Transnavicom. Upptäcks fel i kartan av användarna kan det rapporteras in och i vissa
fall till och med rättas till direkt. Detta gör att kartorna håller sig uppdaterade och uppehåller
god kvalitet. Google Maps API som lanserades i juni 2005 gjorde det möjligt för utvecklare att
integrera Google Maps med sina webbsidor.
Pubnub
Pubnub är ett stort dataströmningsnätverk framtaget för utvecklare för att underlätta och ge stöd
för utveckling av realtidsapplikationer [26]. Med hjälp av PubNub kan man tillämpa en rad med
funktioner till sitt program så som till exempel:
• Strömma geografiska platsdata från valfri mängd av källor och anpassa denna efter
behov innan det presenteras på en karta.
• Lägga till dynamiska kartfunktioner.
• Skicka notifikationer till mobil- och dataplattformar.
• Aktivera SMS-baserade händelser baserat på olika event eller larm.
• Presentera aktioner, börs, poängställningar och liknande i realtid.
13
Utöver detta så har man tagit fram EON [27]. EON är ett projekt framställt för att stärka
utvecklingen av kartor med realtidsdata. EON är ett JavaScript-ramverk för grafer och
realtidskartor och fungerar på flera enheter. EON kopplar C3 diagram och Mapbox
kartfunktioner till Pubnubs dataströmningsnätverk och skapar tack vare detta ett enkelt sätt att
visualisera realtidsdata på grafer, diagram eller kartor.
XAMPP
XAMPP kan ses som en webbserverlösning som kommer i ett packet med flera
undersystem [28]. XAMPP består huvudsakligen av Apache HTTP-server, MariaDB-databas
och ett antal programtolkar för att exekvera instruktioner skrivna i PHP eller Perl. Då
majoriteten av faktiska webbservrar använder sig av samma komponenter som XAMPP
tillhandahåller så möjliggörs en enkel övergång av att sätta upp en lokal testserver till att vara
en live-server. Målet med XAMPP är att göra det enkelt för utvecklare att sätta upp en Apache
HTTP-webbserver och installeras med alla de funktioner som behövs där varje funktion är
konfigurerat för att kunna startas upp och köras direkt.
NMEA GPRMC
NMEA kan vara förvirrande då det är en förkortning för ”National Marine Electronics
Association” och är från början en förening som bildades 1957 av en grupp elektroniska
återförsäljare, vars syfte var att skapa en bättre kommunikation mellan dem och tillverkare [29].
Idag är NMEA ett standardiserat dataformat inom GPS-världen som stöds av alla GPS
tillverkare.
NMEA:s syfte är att ge användare möjlighet att själva blanda hårdvara och mjukvara. Ett
standardiserat dataformat gör det också möjligt för utvecklare att enklare skriva mjukvara för
en stort mängd av olika sorters GPS-mottagare.
Det man måste förstå om NMEA är att det inte bara finns ett enda NMEA-meddelande. Olika
NMEA meddelanden har olika funktioner och förmågor precis som GPS-mottagare har olika
kapaciteter. NMEA-data kan skickas över en stor mängd av olika kommunikationssätt, allt från
USB till WIFI.
$GPGGA är det grundläggande GPS NMEA meddelandet som innehåller GPS-koordinater.
Utöver detta finns det NMEA-meddelanden som innehåller ytterligare information som till
exempel $GPVTG som även inkluderar hastigheten över marken.
$GPRMC är ett alternativ till $GPGGA, och avser att täcka position, hastighet och tid.
14
Ett exempel på ett $GPRMC meddelande är:
$GPRMC, 111443.00, A, 4737.123,N, 2035.44,E, 60.4, 293.2, 241218, 002.2,W*6A
Meddelandet består av olika fält och betyder följande:
Tabell 2.1 NMEA GPRMC
AJAX
AJAX är en förkortning av ”Asynchronous JavaScript And XML” [30]. Och består kortfattat
av 7 steg:
1. En händelse sker på webbsidan. Tillexempel så laddas sidan in eller någon form an
knapptryckning sker på sidan.
2. JavaScript skapar ett XMLHttpRequest objekt.
3. XMLHttpRequest objektet skickar en förfrågan till en webbserver.
4. Servern tar emot och bearbetar förfrågan.
Del av meddelande Betydelse
$GPRMC Meddelandets ID. Alla NMEA meddelanden startar med ett $. GP säger
här att det är GPS positioner som meddelandet avser.
111443.00 UTC. Tidsstämpel för positionen. 111443.00 tolkas som 11:14 och 43.0
sekunder
A GPS-status. A = Active (Indikerar att en signal mottages och att det
fungerar som det ska). V = Void. (Indikerar att någonting kan vara fel
med den mottagna datan)
4737.123,N Anger latituden. Tolkas som 47 grader 37.123 minuter i norra hemisfären.
Ett S anges istället för N vid södra hemisfären.
2035.44,E Anger longituden. Tolkas som 20 grader 35.44 minuter i östra
hemisfären. Ett W anges istället för E vid västra hemisfären.
60.4 Anger hastigheten i knop
293.2 Spårningsvinkel i grader. Riktningen GPS:en rör sig i förhållande till norr
som är 0 grader.
241218 Datum. Tolkas som 24/12/18.
002.2,W Anger den magnetiska variationen.
*6A Kontrollsumma av datan. Börjar alltid med *.
15
5. Servern skickar därefter ett svar tillbaka till webbsidan.
6. Svaret hanteras och tolkas av JavaScript.
7. JavaScript utför rätt funktion. Till exempel sidan uppdateras eller en punkt på
webbsidan förflyttas.
AJAX använder sig alltså av ett XMLHttpRequest-objekt inbyggt i webbläsaren för att begära
data från en webbserver och JavaScript och HTML för att presentera eller använda sig av datan.
Att tänka på är att AJAX kan vara väldigt missledande. Namnet avser användning av XML för
överföring av data medan det samtidigt är vanligt att används sig av JSON eller vanlig text
istället för XML.
Fördelarna med AJAX är att utvecklare kan läsa data från en webbserver även efter att
webbsidan har laddats in, webbsidan kan uppdateras utan att en omladdning av webbsidan krävs
och data kan skickas till webbservern. Detta är stora fördelar vid implementering av webbsidor
som ska presentera realtidsdata.
JSON
JSON (JavaScript Object Notation) är ett datautbytesformat [31]. Lätt att läsa för användare,
och lätt att läsa och generera för datorer. JSON är ett textformat som är helt programspråks-
oberoende men använder konventioner från C-familjen (så som C, C++, C#, Java, JavaScript
etc).
JSON består av två strukturer: En samling av namn/värde-par (objekt), och en ordnad lista
(array). En JSON-sträng kan börja med både ett objekt eller en array. Ett objekt är, som redan
nämnt, en oordnad uppsättning av namn/värde-par och omsluts av ett klammerparentespar
”{ }”, namnet är en sträng som följs av ett kolon ”:” och sedan av värdet. Varje par separeras
med ett kommatecken ”,” ifall det finns fler än ett par i objektet (se Figur 2.3).
Figur 2.3 JSON-objekt [37]
16
En array är en ordnad uppsättning av endast värden och omsluts av ett hakparentespar ”[ ]”.
Värden separeras med ett kommatecken ”,” vid fler än ett värde (se Figur 2.4).
Ett värde kan vara en sträng, ett nummer, ”true”, ”false” eller ”null” (utan citationstecken), ett
objekt eller en array (se Figur 2.5). Objekt och arrayer kan alltså nästlas i varandra.
En sträng är en sekvens av noll eller fler Unicode-karaktärer, omslutna av citationstecken ” ”
(se Figur 2.6).
Figur 2.4 JSON-array [38]
Figur 2.6 JSON-sträng [40]
Figur 2.5 JSON-värde [39]
17
Ett nummer är uppbyggd som ett vanligt nummer, där en punkt ”.” används som decimaltecken
(se Figur 2.7).
2.3 Reguljära uttryck
Reguljära uttryck är en notation till att beskriva mängder av strängar [32], och ett uttryck består
av en sträng med särskilda syntaxregler. Ett reguljärt uttryck med endast ett tecken ”t” matchar
tecknet ”t” i order ”tryck”. Man kan bygga ihop uttryck med operatorer för att matcha
komplicerade mönster där strängar med olika innehåll kan matchas av samma uttryck.
Exempelvis så kan ”[0-9]” matcha en godtycklig siffra, medan ”[0-9]*” matchar allt från inga
till oändligt många godtyckliga nummer. ”Regulj(a|ä)r” matchar antingen ”Reguljar” eller
”Reguljär”, ”G(äv|ef)le” matchar antingen ”Gävle” eller ”Gefle”. En punkt ”.” används som
jokertecken och matchar alla typer av tecken. ”a{0-5}” matchar ”a”, ”aa”, ända till ”aaaaa”.
”.*” matchar inga till oändligt antal godtyckliga tecken. För att matcha en punkt måste ett
omvänt snedstreck placeras framför ”\.”.
Exempel: ”(Hej|Hallå), jag är 2[0-9] år gammal” matchar en sträng som börjar antingen med
”Hej” eller ”Hallå” och fortsätter med ”, jag är 2” följt av godtycklig siffra mellan 0 och 9, och
sedan ” år gammal”. ”2[0-9]” matchar alltså tal mellan 20 och 29.
2.4 Existerande lösningar
I nuläget har trafikledningen tillgång till en rad olika verktyg för att kunna följa tågen på ett
eller annat sätt. Vissa av verktygen är mer pålitliga än andra medan flertalet av lösningarna inte
täcker de behov eller tillför det stöd som trafikledningen behöver. Under ett studiebesök som
Figur 2.7 JSON-nummer [41]
18
gjordes hos SJ under projektets gång så presenterades tre sätt som används mest för att följa
tågen.
Utplacerade avläsningspunkter
Tack vare trafikverket kan man idag veta tågens position på järnvägen med hjälp av RFID-
teknik [33]. RFID som är en förkortning av ”Radio Frequency Identification”, är en teknik som
använder sig av trådlös informationsöverföring mellan en RFID-tagg och en RFID-läsare. RFID
har många användningsområden i dagens samhälle bland dessa går det att hitta busskort,
passering genom tullar, stöldskydd i butiker och passagesystem i byggnader. Inom tågtrafiken
finns ett stort nät av RFID-läsare där möjligheten att spåra och följa enskilda fordon ges. I
början på 2018 fanns det cirka 300 RFID-läsare runt om i landet och utbyggnad av dessa har
och pågår än idag.
Med RFID-tekniken så finns även möjligheten att koppla detektormätvärden och larm ihop med
fordonen (se Figur 2.8). Detta möjliggör att man får aktuell status och aktuella mätvärden för
varje individuellt fordon för att snabbt upptäcka fel och behov av service. Utöver RFID-läsarna
finns cirka 180 detektorer utplacerade. Data hämtas genom att RFID-taggar finns placerade på
varsin sida av ett fordon. När tågen passerar en RFID-läsare så registreras taggen och data
sparas i trafikverkets databas. Exempel på data är fordonets ID, vilken plats fordonet passerat,
fordonets hastighet, tidpunkt, skador på hjulen och inbromsningar. Denna information finns
tillgänglig som prenumerationsunderlag och används bland annat av SJ. För att få en bättre bild
av tågens position använder man sig av ett par algoritmer som räknar ut var tåget borde befinna
sig med tanke på hastighet och tidpunkt som tåget hade vid passering av en specifik RFID-
läsare. De problem som kan uppstå här är att man inte vet exakt var tågen befinner sig. Då varje
Figur 2.8 RFID-tekniken [42]
19
RFID-läsare kan befinna sig flera kilometer från varandra så går det inte att avgöra var på spåret
tåget är, hur terrängen runtom tåget ser ut och hur den faktiska tågsituationen är vilket i vissa
fall kan orsaka flera problem.
Kommunikation via ombord personalens mobiler
Personalen ombord på tågen har idag möjlighet att logga in genom en applikation på deras
mobiltelefoner och på så sätt dela med sig av GPS-data via den mobila enheten. Till skillnad
från tekniken att beräkna tågets position med hjälp av avläsningspunkter så är denna variant
mer tillförlitlig och täcker de områden som idag saknar avläsningspunkter. När personalen för
en specifik tågresa först befinner sig på tåget så ska de logga in via deras mobila enheter. När
de gör detta så börjar den mobila enheten att skicka GPS-data till de uppsatta databaserna.
Samtidigt som de loggar in så anger personalen även vilket tåg de befinner sig på, detta för att
kunna identifiera vilket tåg GPS-informationen avser. Därefter finns ett visuellt gränssnitt
uppsatt hos trafikledningen där personalen kan följa tågen. Ett problem som blir mycket tydligt
vid användningen av denna teknik är när tågen plötsligt inte befinner sig på tågrälsen utan
förflyttar sig åt annat håll. Orsaken till detta är att personalen kan glömma att stänga av
applikationen som skickar GPS-data och tar med sig mobilen hem, vilket även gör att tåget
följer med hem enligt det grafiska gränssnittet.
GPS-enheter på tåg
GPS-enheter finns på de modernare tågen idag som till exempel X40-tågen. Dessa skickar GPS-
data om det fordon de sitter på och datan lagras och presenteras i ett visuellt kartgränssnitt.
Detta är den absolut bästa lösningen och den lösning som man gärna ser appliceras på alla
fordon.
20
3 Projektdesign
I detta kapitel presenteras hur projektet har tagit form. En kort beskrivning av projektets
grundidé presenteras och därefter hur projektet formades och motivering till den nya
projektspecifikationen ges. Här presenteras också de val som gjorts kring de verktyg och språk
som använts för projektet och även hur systemets design var tänkt att se ut.
3.1 Projektets grundidé
Projektet påbörjades med en väldigt bristande förståelse om vad för sorts system som skulle
utvecklas och vad för möjligheter som fanns. Grundtanken var att det skulle utvecklas ett
system som skulle agera som underlag och stöd för att effektivisera beslutsprocesser av
operativa problem inom tågtrafiken. Systemet som skulle utvecklas var även tänkt att
effektivisera beslutsprocesserna genom att med hjälp av machine learning automatisera flödet
och påskynda besluten.
Under projektets första vecka hade följande tre frågeställningar utformats:
• Kan man med hjälp av en eller flera systemlösningar effektivisera och förenkla
beslutsprocessen för trafikledning inom tågtrafik?
• Kan man med denna lösning samla in data och statistik som på sikt kan vara användbart
för att effektivisera andra processer?
• Kan man med hjälp av denna/dessa lösningar, i framtiden, fatta beslut per automatik
genom att göra systemet självgående, t.ex. med hjälp av machine learning?
Dessvärre föll dessa bort rätt fort och nedan presenteras hur och varför projektet formades om.
3.2 Planering
Möten med olika trafikledningar
För att förstå vilka operativa problem som trafikledningen fick handskas med dagligen så
gjordes ett besök hos trafikledningen på SJ och Nobina redan första veckan. Målet med detta
var att lokalisera resurskrävande problem som skulle kunna automatiseras och lösas genom
någon typ av applikation. Besöken visade sig inte vara lika givande som önskat, då ingen tydlig
bild gavs av vad som kunde lösas som inte var internt inom trafikledningen. SJ:s personal
21
fokuserade på problem med existerande programvara, och besöket på Nobina resulterade inte i
något givande för det arbete som skulle genomföras. Resultatet var i grund och botten en rundtur
genom Nobina:s kontor, och en genomgång av vad de arbetar med.
Således fick ett nytt besök på SJ:s trafikledning göras i tredje veckan, där taktiken i
problemsökandet ändrades. En diskussion och genomgång av de problem som hade upptäckts
under första besöket som trafikledningen inte tänkte på genomfördes. Strategin var nu att istället
komma med förslag på funktionalitet som ansågs kunna hjälpa personalen och göra deras jobb
enklare. Detta i sin tur gjorde att personalen själva började komma med idéer och förslag på
vad de skulle vilja få stöd till. Det andra mötet gav mycket att arbeta med och funktionalitet
som till sist kunde anses som bra att ha var följande:
1. Realtidspositionering av tåg på karta, deras planerade körväg, samt planerade
passeringar av trafikplatser (där användaren kunde markera en trafikplats och få ut rätt
information).
2. Varningar för tåg som inte håller sitt planerade körschema och vid stillastående fordon
på plats där låga hastigheter inte ska uppstå.
3. Information om varje sträcka såsom hur många och vilka tåg som kör på vald plats.
4. Beräkning av sparad resetid vid ersättningstrafik.
Dessa funktioner fick senare omformuleras och begränsas på grund av de problem som
presenteras längre fram i kapitel 5.
Möten med handledare
I början av projektet hölls regelbunden kontakt med universitetets handledare, med möten
varannan vecka. Efter det andra mötet hos trafikledningen kunde en klarare bild av projektets
mål presenteras för handledaren som i sin tur informerade om vad för något han ville se i
projektet. Under veckan informerade handledaren om följande:
1. Ett datahanteringssystem som hämtar in data från olika källor och klistrar ihop det.
2. Användarfall i form av tilläggsprogram som använder sig av datan och presenterar den
i någon form för användaren.
22
Utöver detta så tillhandahöll handledaren även en visuell bild över hur systemet exempelvis
skulle kunna se ut (se Figur 3.1). Projektet och den omgjorda specifikationen tog form efter de
tolkningar som gjordes av handledarens riktlinjer för ett godkänt arbete.
Vägledning på Softcode
På Softcode skedde avstämning minst en gång varje vecka. Dessa möten bestod av diskussion
och vägledning från utvecklare på företagen och även hjälp med att få tillgång till servrar och
utrustning. Softcode hjälpte till med att få kontakt med SJ:s trafikledning och även besöket hos
Nobina. Softcode gjorde det även möjligt att komma i kontakt med externa parter som tidigare
arbetat med liknande system, detta för att hjälpa projektet att gå vidare. Under dessa möten
sattes det upp en rad olika användarfall. Även en del tips om olika verktyg och en del vägledning
i vilka verktyg som vore enklast att använda för projektets syfte kom från Softcode.
3.3 Motivering och mål kring projektändringarna
Trafikledningen har i nuläget fortfarande en del problem som går att lösa. Efter andra mötet hos
trafikledningen valdes det att inte arbeta med machine learning utan att istället utveckla ett
system för inhämtning och hantering av data som sedan presenteras visuellt. I kapitel 2
presenterades de existerande lösningar trafikledningen har idag. Tanken med projektet var att
Figur 3.1 Visualisering av systemet
23
utveckla ett program som tar emot tågdata från externa parter och sedan presenterar datan på
en karta i realtid. Tanken var att motivera trafikledningen till att införskaffa GPS-enheter på
alla fordon och att ge dem ett underlag till de problem de har idag. Systemet var tänkt att även
lösa följande problem som trafikledningen stöter på idag:
• Sätta prioritering på sena tåg, tåg som är ute och kör och tåg ska börja köra.
o Lösningsförslag: En visuell översikt över tågen i realtid skulle göra det möjligt
att enklare sätta prioriteringar på tågen då man kan se hur tågen förhåller sig till
varandra.
• Evakuering av personal och resenärer.
o Lösningsförslag: En visuell översikt över tågen i realtid skulle göra det möjligt
att se miljön runtom tågen och på så sätt enklare och snabbare planera in
evakuering och ersättningstrafik.
• Se mötesplatser och tågens förhållande till dessa.
o Lösningsförslag: En visuell översikt över tågen i realtid skulle göra det möjligt
att snabbare planera in ett nytt möte längs en sträcka.
3.4 Användarfall
De användarfall som sattes för projektet är:
• Följa tåg i realtid på en karta
• På lämpligt sätt se om ett tåg står stilla.
• Se tåg som ankommer, avgår eller har varit på en specifik trafikplats.
Dessa användarfall var även de mål som satts upp för att bedöma resultatet av systemet i slutet
av projektet. Projektet skulle komma använda sig av dessa användarfall som riktlinjer vid
utveckling av systemet och datainhämtningen.
3.5 Generell design
Huvudkomponenten i programmet skulle vara ett databassystem med kringliggande data-
omvandlare som förbereder inkommande data för att lagras i databasen. Ett grafiskt gränssnitt
som refererar till databasen för att få ut nödvändig information skulle också vara en del av
programmet (se Figur 3.2).
24
Databas
Databasen skulle kunna hantera både stationer och tåg. Stationerna skulle komma in via en
statisk lista, och behövde lagras med namn, ID, och koordinater. Tågen skulle komma in via en
konstant dataström och behövde lagra fordons-ID, annonserade/publika och tekniska/interna
nummer, hastigheter, koordinater och tidpunkter. Då tågens data konstant uppdaterades
behövde gamla data antingen slängas, skrivas över, eller sparas undan. Datan beslutades om att
sparas utifall man ville kunna gå tillbaka till en viss tidpunkt för att se var tåget vid en viss
tidpunkt.
Figur 3.2 Generell design av systemet
25
Dataomvandlare
För att stations- och tågdatan skulle kunna läggas in i databasen behövdes en omvandlare för
att kunna ta ut rätt och relevant information från den inkommande datan, då de olika källorna
som användes matade ut sin data på olika format. Dessutom behövdes en datauppladdare för
att faktiskt få in datan i databasen. Dessa två skulle kombineras så att omvandlaren även laddade
upp datan till databasen.
Då datan från stationerna inte kom från samma källa som datan från tågen (då de hade olika
attribut, och då stationsdatan inte behövde uppdateras lika frekvent som tågdatan) bestämdes
det att två olika omvandlare skulle köras separat.
Grafiskt gränssnitt
Förutom databassystemet skulle även ett grafiskt gränssnitt implementeras. Anledningen var
att ha någonting som använde sig av det underliggande systemet och kunde påvisa dess
funktionalitet, och samtidigt något att testa systemet med för att se så att den inkommande datan
inte hade bristande frekvens eller dåliga mätvärden som skulle kunna visa tåg på platser där de
inte skulle vara.
3.6 Val av språk
Språk som valdes för den grundläggande arkitekturen var C#, med tillägg för stöd av SQL. C#
valdes då det bedömdes vara det som var bäst lämpat för projektet och det systemet som var
tänkt att utvecklas. Webbgränssnittet skulle skrivas i PHP och HTML då kunskaperna inom
språken var goda. JavaScript skulle även användas för att rita ut kartan och dess innehåll.
Utöver detta testades och övervägdes C, C++ och Java. Dessa valdes dock bort väldigt fort. C
valdes bort på grund av språkets komplicerade kompilerings-, och felhanteringsprocess. I C++
saknades essentiella kunskaper om språkets syntax och funktioner. Java som är likt C# valdes
bort då kunskaperna ansågs vara bredare inom C#.
3.7 Testning och validering av verktyg
De verktyg som testades för kartbygget var Open Street Map, MapBox, Leaflet, Google Maps
och PubNub. Leaflet är i grund och botten byggt med delar från MapBox, och när MapBox
26
testades uppstod stora bekymmer som länge troddes kunde lösas. Då Leaflet dock skiljer sig en
del från MapBox på vissa håll men liknar på många andra så gjordes antaganden att samma
problem skulle uppstå även i Leaflet vilket gjorde att det valdes att inte lägga mer tid på det.
Valet stod sedan länge mellan MapBox och Google Maps. MapBox hade en rad användbara
funktioner som Google Maps inte hade, vilket gjorde att sökandet efter en lösning pågick
väldigt länge. PubNub upptäcktes sent in i projektet, i början på december, med stöd för både
MapBox och Google Maps. Efter att PubNub testats med MapBox, som kunde ha löst
problemet, visade det sig att PubNub:s kod för MapBox var utdaterad och kvar återstod
alternativet att använda Google Maps, trots att Google Maps inte erbjöd samma funktionalitet
som önskades (se kapitel 5.2.3 för mer djupgående beskrivning på problemet). Open Street Map
testades i början men eftersom att MapBox är byggt med hjälp av Open Street Map och
dessutom tillhandahöll mer funktionalitet så gjordes ingen djupgående testning med Open
Street Map.
Git valdes för versionshantering av koden och Docker och XAMPP för databashantering och
servrar. MySQL Workbench valdes för att visuellt presentera innehållet i databasen. Microsoft
Azure, Apache Kafka och Kubernetes valdes bort och detta på grund av verktygens komplexitet
och brist på kunskap om verktygen. Efter testning valdes det att fortsätta med Docker och
XAMPP då dessa var smidigare och snabbare att sätta upp, samt på grund av den tidspress som
projektet redan hade på sig i detta stadiet.
3.8 Design av databasen
Efter en undersökning på indatan från Trafikverket och Oxyfi (som är ett företag som bland
annat tillhandahåller realtidspositioneringsdata över tåg [34]), bestämdes databasens design, de
tabeller som skulle användas och hur tabellerna skulle vara uppbyggda. Databasen skulle bestå
av två tabeller. En för trafikplatserna, och en för tågen och deras positioner.
Trafikplatstabellen ”trainstations” skulle innehålla fem attribut, varav en primärnyckel. De
fem attributen skulle vara trafikplatsens namn ”AdvertisedLocationName”, trafikplatsens
ID/signatur ”LocationSignature” (som även var primärnyckel), trafikplatsens typ
”Prognosticated” (sant om station, falskt om mötespunkt), samt latitud ”Y” och longitud ”X”
(se Figur 3.3).
27
Tågtabellen ”trainlocations” skulle innehålla nio attribut, varav ett primärnyckelpar som
omfattade två av attributen. Tågets/fordonets ID-nummer ”Vehicle” och tidsstämpel för
avläsningen ”Time” (som tillsammans utgör primärnyckeln), annonserat/publikt nummer
”PublicNumber”, tekniskt/internt nummer ”InternalNumber”, status på tågets GPS-signal
”Active”, tågets latitud ”Y” och longitud ”X”, hastigheten ”Speed” samt riktningen tåget åker
i ”Angle” (som dock inte användes till något i denna lösning) (se Figur 3.3).
3.9 Design av dataomvandlarna
Dataomvandlarna skulle ha kontakt med varsin tabell i databasen samt varsin källa där indatan
kom från. Databasuppkopplingen skulle ske genom en MySQL-uppkoppling, och stations-
dataomvandlaren skulle skicka en förfrågan till Trafikverket om hämtning av stationsdata.
Detta skulle sedan tolkas och konverteras till SQL-vänlig kod som skrev till databasen. I
händelse av att kontakten bröts med databasen skulle omvandlaren stoppa uppladdningen och
upprepa uppkopplingsförsöket tills en återanslutning hittats och kommunikationen med
databasen kunde fortsätta. Tågdataomvandlaren skulle istället lyssna på Oxyfi:s dataström för
att kunna hämta tågdatan. För varje meddelande skulle detta tolkas och konverteras till SQL-
vänlig kod som sedan skrev till databasen. I händelse av att kontakten bröts skulle omvandlaren
kasta det uppladdade meddelandet och göra ett återuppkopplingsförsök. Detta skulle göras en
gång per meddelande då det handlar om realtidsdata och inte statiska data som för stationerna.
Figur 3.3 Databasens tabeller
28
3.10 Design av det grafiska gränssnittet
Det grafiska gränssnittet var ett komplement till databassystemet (databasen och omvandlarna)
och skulle presentera datan på ett snyggt men smidigt sätt. De första redan existerande kartor
som övervägdes att användas var MapBox eller Google Maps. MapBox valdes då dess
redigeringsmöjligheter för olika kartdesigner var mycket bättre än vad Google erbjöd.
Dessvärre gjordes ändringar i slutskedet av projektet och MapBox bytes bort mot Google Maps
på grund av problem med MapBox (se Kapitel 5.2.2).
Båda API:erna (MapBox och Google Maps), var gratis att använda. MapBox hade en
begränsning på hur ofta man kunde ladda in kartan, men begränsningen låg högt nog för att inte
skapa en oro för att behöva betala för överflödet av användning. Google Maps var gratis i ett år
för utvecklare.
För denna prototypversion valdes det att endast ha kartan på webbsidan utan någonting annat
(se Figur 3.4). Framtida möjligheter skulle kunna vara tabeller med tågens/valda tågs
information, så som hastighet, och förseningar.
Se även Figur 1.1 för hur systemet är tänkt att se ut och användas.
Figur 3.4 Första gränssnittsdesignen
29
4 Implementation
Hur implementationen genomfördes beskrivs utförligt i detta kapitel. Då flera olika mindre
program är delaktiga i hela processen, och då flera tidigare implementationer tvingats skrotas,
så delas dessa beskrivningar in i mindre delkapitel, där var och en genomgående beskriver en
huvudsaklig del i implementationen. Alla de problem som stöttes på under projektets gång tas
upp i kapitel 5.
4.1 Introduktion
Grundfunktionaliteten hos programmet var att lättvindigt kunna hämta in data från flera olika
källor och spara dem i en enda gemensam databas. Andra program skulle därefter kunna hämta
denna data för att enkelt kunna göra beräkningar på de tåg som datan avser och kunna skapa
grafiska lösningar för att förenkla lokalisering och informationshämtning för operatörer och
eventuellt resenärer. En sådan grafisk lösning är även inkluderad i detta program.
Källkod för stationsdataomvandlaren hittas i Bilagor A.1, tågdataomvandlaren hittas i Bilagor
A.2 och webbgränssnittet hittas i Bilagor A.3.
Mjukvara och programspråk som använts under implementationen
Då hela projektet bestod av mindre program som utförde olika typer av operationer har flertalet
olika språk använts.
1. De två programmen som kommunicerade med Trafikverket och Oxyfi skrevs båda i
C#.NET för att hämta in nödvändiga data, och använder SQL-kommandon för att
kommunicera med projektets databas.
2. Den grafiska klienten skrevs i HTML, PHP, SQL, JavaScript. Även lite CSS användes,
vilket kan ses som stilmallar för att beskriva hur HTML-element ska presenteras [35].
3. XAMPP Control Panel 3.2.2 användes för att kunna köra en lokal Apache server
tillsammans med en MariaDB-modul, där den inhämtade datan förvarades, när den
dedikerade databasen inte var tillgänglig.
4. Visual Studio Community 2017 användes som IDE (Integrated Development
Environment).
5. Git användes för versionshantering.
30
4.2 Lokalisering av tågstationer och mötespunkter på järnvägen
Inhämtning av data och felhantering
Namn, koordinater, signaturer och urskiljning av stationer och mötespunkter hämtades från
Trafikverket med deras egna API (se Figur 4.1), och lades in i en egen tabell i databasen. Denna
funktion kördes en gång då värdena den får in var statiska platser och behöver inte automatiskt
uppdateras. Skulle det dock hända att förändringar skedde genom exempelvis att en ny järnväg
lades till kunde man manuellt köra operationen igen.
När operationen sätts igång kan flera olika saker hända vid dataöverföringen då databasen kan
vara både tillgänglig eller otillgänglig
1. Operationen startas, men databasen är otillgänglig.
När operationen startas så etableras en anslutning till databasen. Skulle en sådan inte
hittas repeteras anslutningsförsöket utan automatiskt stopp tills att en anslutning hittats.
Skulle en anslutning aldrig hittas måste programmet stängas av manuellt, skulle beslutet
tvingas att fattas.
2. Operationen startas, och databasen är tillgänglig.
När operationen startas så etableras en anslutning till databasen. Operationen genomförs
och en eventuellt äldre variant av tabellen tas bort och ersätts med en ny version (skulle
tabellens struktur ha behövt ändras). Värdena laddas sedan upp i tabellen.
3. Operationen kör, men anslutningen till databasen avbryts mitt i dataöverföringen.
Skulle databasen plötsligt bli otillgänglig (till exempel vid strömavbrott, nätverksfel,
systemkrasch m.fl.) försöker programmet återansluta till databasen och dataöverföringen
pausas tills att en återanslutning kunnat genomföras. Dataöverföringen fortsätter sedan
som normalt från där den slutat fungera utan att hoppa över missade överföringar.
32
Formatering av inhämtade data
Datan som hämtades från Trafikverket (se Figur 4.1) var i ett JSON-format (se Figur 4.2) och
behövde först tolkas för att kunna extrahera värdena så de kunde skickas in till databasen. För
att tolka JSON-datan användes ett reguljärt uttryck (se Figur 4.3). Parenteserna i uttrycket är
grupper som sedan går att anropa för att få in just den data som finns mellan parenteserna.
Första gruppen som hämtas in är ”AdvertisedLocationName” och matchar bara om JSON-datan
innehåller just den textsträngen, följt av ”.{0,40}” vilket betyder att upp till och med 40
godtyckliga tecken grupperas in till ett ord eller mening. Detta är själva namnet på trafikplatsen
och kan innehålla mer än ett ord.
Grupp tre matchar ”Geometry” och visar att trafikplatsens koordinater kommer.
Matchningen måste även här vara exakt. Grupp fyra och fem är båda ”[0-9.]*” vilket matchar
godtyckligt antal tecken som är antingen siffror eller punkter. Detta motsvarar trafikplatsens
koordinater i longitud och latitud.
Grupp sex matchar ”LocationSignature” och måste vara en exakt matchning. Följt kommer
då trafikplatsens signatur. Exempelvis så har Stockholm Central signaturen ”Cst”, Karlstad C
har signaturen ”Ks”, och Göteborg C har signaturen ”G”. Detta är då matchningen i grupp sju
där signaturen kan vara ett till och med tjugo godtyckliga tecken, men vanligtvis är det inte mer
än fyra till fem tecken som mest. Signaturen är unik för varje trafikplats.
Grupp åtta matchar ”Prognosticated” och måste vara en exakt matchning. Grupp nio matchar
antingen ”true” eller ”false”. En prognostiserad trafikplats är en tågstation eller mötespunkt där
av- och påstigning antas vara tillåten. En ej prognostiserad trafikplats är en mötespunkt där på-
och avstigning vanligtvis inte tillåts enligt föregående antagande. Detta antagande har gjorts då,
vid kontroll av markörplacering på kartan, prognostiserade trafikplatser hamnat på tågstationer,
och ej prognostiserade trafikplatser hamnat på enbart mötespunkter.
Den skarpsynte kan se att det reguljära uttrycket använder apostrofer (’) medan JSON-strängen
som hämtas från Trafikverket använder citationstecken (”). Stationslokaliseraren formaterar om
JSON-strängen då det reguljära uttrycket blir lättare att hantera i koden.
Figur 4.3 Reguljärt uttryck för tolkning av Trafikverkets JSON-data
33
Stationstabellens SQL-struktur och inmatning av data
Stationstabellen är den tabell som har hand om alla trafikplatser, där deras namn, koordinater,
signaturer och trafikplatstyp sparas. Signaturen är alltid unik och används därför som
primärnyckel (flera rader kan inte ha samma värde i sin primärnyckel) i databasen.
Koordinaterna är av WGS84-format [36], där heltalen representeras av grader och decimaler.
Trafikplatstyperna är i det här fallet antingen är prognostiserade eller inte. Se Figur 4.4 för
utdrag från stationstabellen.
Stationslokaliserarens enda uppgift är att formatera den inhämtade JSON-datan, så att den
kan laddas upp till databasen. Uppladdningen sker med SQL-kommandon, där den eventuellt
äldre versionen av tabellen tas bort. Anledningen är att tabellens struktur kan ha uppdaterats.
Exempel på anledningar kan vara att Trafikverket helt byggt om sin egen struktur på sin data,
vilket eventuellt kan innebära att tabellens struktur också måste uppdateras.
4.3 Lokalisering av aktiva och inaktiva fordon
Inhämtning av data och felhantering
Fordonsnummer (statiskt nummer knutet till just det fordonet), annonserat nummer (det
nummer som resenärer ser), tekniskt nummer (internt nummer som trafikledningen ser, då ett
tåg kan ha olika tekniska nummer om det kör på fler än en sträcka), koordinater, tidpunkter och
hastighet hämtades genom Oxyfi och deras realtidspositionerings-API. Den inhämtade datan
lades in i en egen tabell i databasen. Denna funktion var byggd för att köras oavbrutet och
stannar vid manuell avstängning.
Figur 4.4 Utdrag ur stationstabellen
34
När operationen sätts igång kan flera olika saker hända vid dataöverföringen då databasen kan
vara både tillgänglig och otillgänglig.
1. Operationen startas, men databasen är otillgänglig.
När operationen startas så etableras en anslutning till databasen. Skulle en sådan inte
hittas repeteras anslutningsförsöket utan automagiskt stopp tills att en anslutning hittats.
Skulle en anslutning aldrig hittas måste programmet stängas av manuellt.
2. Operationen startas, och databasen är tillgänglig.
När operationens startas så etableras en anslutning till databasen. Operationen genomförs
och en eventuellt äldre variant av tabellen tas bort och ersätts med en ny version (skulle
tabellens struktur ha behövt andras). Värdena laddas sedan upp i tabellen.
3. Operationen körs, men anslutningen till databasen avbryts mitt i dataöverföringen.
Skulle databasen plötsligt bli otillgänglig (till exempel vid strömavbrott, nätverksfel,
systemkrasch m.fl.) försöker programmet återansluta till databasen och dataöverföringen
pausas tills att en återanslutning kunnat genomföras. Dataöverföringen fortsätter sedan
som normalt från där den slutat fungera utan att hoppa över missade överföringar.
Figur 4.5 Exempel på inhämtad data från Oxyfi
Figur 4.6 Reguljärt uttryck för tolkning av Oxyfi:s data
35
Formatering av inhämtade data
Datan som hämtades in är i ett NMEA GPRMC-format (se Figur 4.5) och behövde tolkas och
konverteras så att värdena kunde skickas till databasen. För att tolka NMEA GPRMC-datan
användes ett reguljärt uttryck (se Figur 4.6).
På första raden: de tre första grupperna som matchar ”[0-9]{2}” vilket är ett godtyckligt
nummer mellan 10 och 99. Dessa tillsammans bildar en tidstämpel, där den första matchningen
är timmar, andra är minuter, och tredje är sekunder. Den fjärde matchningen är ”[.0-9]*” och
matchar eventuella millisekunder, skulle sådana finnas.
På andra raden: först matchas antingen ett ”A”, som betyder ”OK”, eller ett ”V” för att
indikera en varning då något kan vara fel med positionssändningen. Sedan matchas ”[0-9.]*”
där värdet kommer vara tågets longitud, följt av en matchning av antingen ”N”, för nord, eller
”S” för syd. ”[0-9.]*” matchas återigen för tågets latitud och ”E” eller ”W” matchas för öst eller
väst.
På tredje raden: först matchas ”[0-9.]*” vilket blir tågets hastighet i knop, sedan matchas
”[0-9.]*” för tågets riktning i grader (0-360). Nästkommande tre matchningar, ”[0-9]{2}”,
matchar datumet där den första är året ”ÅÅ”, andra är månaden ”MM” och tredje är dagen
”DD”.
På fjärde raden: först matchas ”[0-9.]*” och antingen ”E” eller ”W”. Detta är den magnetiska
variationen, i vilken riktning, och hur gravt. Detta värde skickas dock inte in till databasen.
Detta följs av en matchning av ett till sex godtyckliga tecken, där checksumman finns. Denna
används dock inte.
Sedan fortsätter matchningen på fjärde raden men resterande matchning är nästlad. I Figur 4.7
har det reguljära uttrycket gjorts mer läsbart. Varje parentes indikerar en ny grupp, och varje
slutparentes indikerar slutet på en grupp. Enligt radnumreringen i Figur 4.7 matchas först ”[0-
9]+” mellan rad 1 och 3, vilket är tågets unika ID-nummer. Detta är unikt för varje tåg och
ändras inte mellan olika resor. Nästa grupp är mellan rad 3 och 30 och kan exempelvis kallas
för ”3-30”, där slutparentesen på rad 30 följs av ett frågetecken. I den här gruppen matchas först
en intern grupp ”[0-9]+”, tågets annonserade eller tekniska nummer, följt av ”public” eller
”internal”, vilket indikerar om det föregående numret är annonserat/publikt eller
tekniskt/internt, följt av en upprepning av gruppen ”3-30”. Denna gruppen ”8-29” fungerar
36
likadant som ”3-30” och har en egen nästlad grupp ”14-28”, som fungerar likadant som ”8-29”,
som i sig har en egen nästlad grupp ”20-27”. Denna sista nästlade grupp har dock ingen egen
nästlad grupp.
Det som händer i ”3-30” är att den matchar ett tågs annonserade/publika eller tekniska/interna
nummer. Anledningen till att den matchar flera grupper nästlade är att ett tåg kan ha olika
annonserade/publika och tekniska/interna nummer, och dessutom ha flera annonserade/publika
och tekniska/interna nummer då ett tåg kan köra på fler än en sträcka (därav flera
tekniska/interna nummer) eller vara försenat till sin nästa resa (därav flera annonserade/publika
nummer). Anledningen till att alla dessa nästlade grupper följs av ett frågetecken är att
grupperna inte behöver existera, detta skulle medföra att tåget inte alltid har ett
Figur 4.7 Utdrag ur det reguljära uttrycket för
tolkning av Oxyfi:s data
37
annonserat/publikt eller tekniskt/internt nummer i databasen, så inte alla fordon alltid har det.
De som inte har ett annonserat/publikt eller tekniskt/internt nummer får värdet 0 i databasen.
De flesta värdena som hämtades vid alla matchningar behövede inte konverteras, utan kunde
direkt läggas in i databasen. Däremot så behövde värdena för longitud, latitud och hastigheten
konverteras till rätt format och värden. Hastigheten var enklast att konvertera, vilket var angett
i knop och konverteras då till km/h. Longitud och latitud var däremot lite krångligare att
konvertera. Koordinaterna var angivna i ett format som liknar WGS84, men satte
decimaltecknet på ett annat ställe. Formatet använde grader, minuter och decimaler istället för
endast grader och decimaler. Graderna konkatenerades med minuterna och decimalerna, vilket
resulterade i en longitud som såg ut såhär: ”GGGMM.DDDD” där GGG är grader 000-180,
MM är minuter 00-59 och DDDD är decimaler 0000-9999, och en latitud med
”GGMM.DDDD”, där GG är grader 00-90. Koordinaterna är således stora tal på upp till 18 000,
med decimaler på upp till 9999.
För att det skulle matcha med koordinatsystemet som stationerna och Google Maps använde
behövde de konverteras till ett tal med värdena ”GG.DDDDDD” (eller ”GGG.DDDDDD”).
Detta gjordes med följande matematisk formel:
𝑎 = ⌊𝑏
100⌋ +
(𝑏
100 − ⌊𝑏
100⌋)
0,6
Värdet ”a” är den konverterade koordinaten på formen ”GG.DDDDD” och ”b” är den
inhämtade koordinaten på formen ”GGMM.DDDD”.
Tågtabellens SQL-struktur och inmatning av data
Tågtabellen (se Figur 4.8) är den tabell som har hand om alla aktiva och inaktiva fordon, där
deras annonserade/publika och tekniska/interna nummer, koordinater, tidpunkter och
hastigheter sparas. Fordonets unika nummer används som identifierare och är ett fyra till fem
tecken långt nummer som tillsammans med tidpunkten är primärnyckel i tabellen. Detta för att
kunna spara undan äldre data, utifall man vill kunna gå tillbaka och se var ett tåg varit vid en
viss tidpunkt.
Tåglokaliserarens enda uppgift är att formatera om NMEA GPRMC-datan så att den kan
laddas upp till databasen. Uppladdningen sker med SQL-kommandon, där den eventuellt äldre
versionen av tabellen tas bort vid uppstart. Detta då tabellens struktur kan ha uppdaterats, då
mer data kan behöva läggas in i tabellen vid nyare versioner.
38
4.4 Databas och versionshantering
Utvecklingsmiljö
Under utvecklingen och testning av systemen sattes en testmiljö upp på en av utvecklings-
datorerna. Detta gjordes med hjälp av XAMPP som gav tillgång till en Apache server och en
MariaDB-databas färdigkonfigurerad och redo att köras. Anledningen till att XAMPP valdes
att användas var för att hålla testdatan separerad ifrån den faktiska datan. Detta för att inte
riskera att lägga in trasiga data i databasen och för säkrare hantering av datan som användes
under utvecklingen. Dessutom underlättade detta utvecklingen då det möjliggjorde att alltid
kunna ha datan tillgängligt lokalt och därav blev frekvent testning av programmet smidigare
och enklare. En konfigurering som gjordes var att lägga till ett användarkonto med ett lösenord
för att kunna hämta och ladda upp data till MariaDB-databasen. Därefter skapades en ny databas
manuellt där stationslokaliseraren och tåglokaliseraren sedan skickade data till sina separerade
tabeller.
Produktionsmiljö
Produktionsmiljön bestod av Git för versionshanteringen av systemen. Detta gjorde det möjligt
att arbeta på samma kod samtidigt på olika brancher och testa dessa utan att påverka de delar
av systemet som var fullt fungerande. Med hjälp av Docker sattes det upp en MariaDB container
som stationslokaliseraren och tåglokaliseraren kunde skicka data till. Denna fanns på Softcode:s
server. En VPN tilldelades av Softcode för att kunna nå servern när utvecklingen skedde utanför
Figur 4.8 Utdrag ur tågtabellen
39
kontoret där nätverket inte var tillgänglig. För att visualisera innehållet i databasen så användes
MySQL Workbench.
4.5 Visualisering av data
Tågens unika ID-nummer, tidstämpel, koordinater, annonserat/publikt nummer och hastighet
hämtades in från databasen med SQL-kod och lades upp på en separat PHP-sida från den sida
där kartan visades (se Figur 4.9). Tågens unika identifieringsnummer, annonserat/publikt
nummer och hastighet för de tåg som var i närheten av en trafikplats lades även upp på en annan
Figur 4.9 Utdrag ur JSON-strängen som
kartan hämtar för att rita ut tågen Figur 4.10 Utdrag ur JSON-strängen
som kartan hämtar för att se tåg vid
trafikplatser
40
separat PHP-sida. Detta för att kunna urskilja på de tåg som väntar på möte, åker normalt på
väg, eller står stilla där det inte ska stå stilla (se Figur 4.10).
Figur 4.11 Sveriges alla trafikplatser
41
Gränssnittet för kartan ligger på en PHP-sida, där genereringen av kartan görs i JavaScript. För
att rita ut själva kartan användes Google Maps API, där trafikplatser och tåg sedan ritas ut. De
trafikpunkter som visas på kartan är begränsade till Värmland och ett fåtal tilliggande
trafikplatser (se Figur 4.12), så som till exempel Åmål. Begränsningen är gjord med hjälp av
koordinater och bildar en rektangel där trafikplatser inom begränsningarna visas. Detta på grund
av att de för nuvarande över 1 600 i Sverige existerande trafikplatser är väldigt prestanda-
krävande att rita ut (se Figur 4.11), då de datorer som använts inte klarat av att hantera så många
punkter på en gång utan att bli långsamma och oresponsiva. Stationer ritas ut som svarta prickar,
mötespunkter ritas ut som mörkgråa prickar.
Till skillnad från trafikplatserna så ritas alla i databasen existerande tåg dock ut. Detta på grund
av att det är få tåg som kör i enbart Värmland vid godtyckligt angiven tidpunkt, och att få in
fler tåg som kan påvisa programmets funktionalitet tycktes vara bra. Tågen, likt trafikplatserna,
ritas ut med JavaScript, men datan hämtas inte direkt från databasen, utan från de två separata
PHP-sidorna. En JavaScript-funktion körs kontinuerligt med ett intervall på 1 000
Figur 4.12 Värmlands (med omnejd) alla trafikplatser
42
millisekunder, där den exekverar de två PHP-sidorna, genom ett Ajax-anrop, för att kunna läsa
in den data som ligger där, och för att kunna se till att den data som hämtas från databasen är
den senaste. Datan som hämtas är två olika textsträngar, som sedan konverteras till mer läsbart
JSON-format (se Figur 4.9 och 4.10). Med hjälp av reguljära uttryck läses varje JSON-objekt
av för att kunna få ut tågens information (som beskrivet i början på detta delkapitel).
Skulle ett avläst tåg inte redan finnas på kartan, eller vid uppstart av programmet, ritas tågen ut
på kartan med en gul färg. Tågets annonserade/publika nummer skrivs ut på markören. Om
tågen dock redan finns så uppdateras istället deras koordinater och eventuellt färg.
1. Ett tåg med en hastighet över 30 km/h utanför trafikplats markeras med grön färg (vit för
tåg som inte har ett annonserat/publikt nummer).
2. Tåg som kör mellan 10 och 30 km/h markeras med gul färg. Detta för att kunna se om
ett tåg åker ovanligt långsamt.
3. Ett tåg som kör över 10 km/h och är i närheten av en trafikplats markeras istället med en
lila färg. Dessa tåg accelererar eller bromsar vanligtvis in inför en station, eller kör
igenom en mötespunkt. Detta för att en operatör ska kunna se när ett tåg passerar en
trafikplats.
4. Ett tåg som har en hastighet på mindre än 10 km/h i närheten av en trafikplats markeras
med ljusblå färg. Detta för att urskilja på de tåg som står stilla på mötespunkter eller
stationer mot de tåg som står stilla ute på räls där de inte ska stå stilla.
5. Ett tåg som har en hastighet på mindre än 10 km/h utanför trafikplats markeras med röd
färg. Detta för att en operatör snabbt ska kunna se att det eventuellt kan ha skett en
olycka, så som urspårning, krock med till exempel personbil vid vägkorsning, krock med
vilt djur m.fl.
43
5 Utvärdering
I detta kapitlet ges ett resultat på hur projektet har gått och även en utvärdering av systemet och
de mål som sattes för projektet. Vidare kommer dessutom de problem som uppstått under
projektet att presenteras och även en mer detaljerad förklaring till problemen.
5.1 Resultat
Projektet resulterade i ett enkelt datainsamlingssystem med en grafisk visualisering av den
inhämtade datan (se Figur 5.1). I kapitel 3 presenterades ett antal användarfall som sattes som
mål för projektet.
• Det första målet var att kunna följa tåg i realtid på en karta. Detta mål fullföljdes, även
om det inte är de tåg som var önskat att presenteras så är det fortfarande tåg i realtid
som man kan se förflytta sig på kartan.
Figur 5.1 Översikt över de aktiva tågen i Värmland
44
• Det andra målet var att kunna se om tågen står stilla vilket fullföljes under projektet. Då
alla tåg markeras med olika färger beroende på deras hastighet och placering. Tåg som
står stilla på plats där de inte ska göra det markeras som röda.
• Det tredje målet var att kunna se tåg som ankommer, avgår eller har varit på en specifik
trafikplats. På grund av den lilla tiden som fanns kvar och avsaknad av rätt data så kunde
inte detta mål fullföljas. Detta främst på grund av svårigheter i att få tillgång till data
över tågens planerade körväg eller störningsinformation, vilket gjorde att det inte gick
att göra några beräkningar på hur tågen skulle köra. Då implementeringen startade
mycket senare än vad som faktiskt var planerat så fanns det inte tillräckligt med tid för
att komma på en lämplig lösning för att få in den datan som behövdes.
Emellertid går det dock att se tåg som avgår och ankommer till en trafikplats, i och med
den visuella delen av kartan, dock inte genom att välja en trafikplats och se planerade
ankomster och avgångar.
Systemet kan idag användas till att följa tåg som körs av Värmlandstrafik, Norrtåg och TÅGAB
och kan enkelt konfigureras om för att istället presentera andra tågkompaniers tåg om deras
data blir tillgänglig.
5.2 Problem
Detta projekt har haft flera problem som under hösten har fördröjt utvecklingen med flera
veckor. I detta delkapitel ges en mer detaljerad förklaring av de problem och motgångar som
stöttes på under projektets gång, och vilka konsekvenser dessa har haft på projektet.
GDPR-skyddade data och tillgång till API:er
Ett problem som stöttes på från SJ var inhämtningen av data över tågens positioner mm. Tanken
från början var att använda SJ:s data över de tåg som idag på något sätt samlade in GPS-
information. Dessvärre blev väntan på att få tillgång till API:erna lång (enda fram till december)
och under tiden hade ett byte skett till att hämta data från andra källor (Tidigare omnämnda
Oxyfi). Det som försvårade mycket var att data som hämtades in kunde innehålla information
om personal och detta minskade möjligheterna att få tillgång till den då den var GDPR-skyddad.
Annat som försvårade tillgång till datan var att vi ständigt skickades runt till flertalet olika
personer. När frågan om tillgång till API:erna ställdes så skickades frågan vidare till näste
person i ledet. Inom kort så hade det blivit till en ond cirkel med många samtal och mail fram
45
och tillbaka mellan oss och trafikledningen, där resultatet var många veckors väntan och till
slut ingen tillgång till någon data alls.
API:er som inte ville fungera
Redan de första veckorna i september hittades ett till synes väl fungerande API för ett
webbgränssnitt som kunde rita ut punkter på en karta. Detta API tillhandahölls av MapBox, och
kartan som användes hämtade MapBox från Open Street Map. Efter att ha först läst på om
API:ets funktionalitet under en vecka påbörjades testning av API:et för att se huruvida det
fungerade som det skulle. Under några veckor experimenterades det med realtidsuppdaterande
exempeldata som MapBox refererade till i sina manualer. Först så testades själva kartan i sig,
för att se om den överhuvudtaget gick att rendera i webbläsaren. Detta verkade fungera mycket
bra, så testningarna fortsatte med att försöka få en punkt att röra sig på kartan. Detta fungerade
även det, så tester med den existerande exempeldata som tillhandahölls började genomföras.
Samtidigt som dessa tester gjordes börjades det även experimenteras med olika kartdesigner för
att kunna ta bort så mycket onödig information som möjligt för att kunna få ett enkelt och
prestandavänligt gränssnitt som användaren sedan kan jobba med. Prestandan är i det här fallet
mycket viktig då flera animerade punkter kan komma att göra programmet långsamt och till
synes oresponsivt. MapBox erbjuder sina användare att kunna redigera de färger som
kartobjekten har, samt att till och med ta bort objekt från kartan helt och hållet. Detta var något
som önskades att användas till åtminstone en av de flera vyer som var planerade att
implementeras, där prioriteringen enbart skulle ligga på att visa räls, tåg, trafikplatser, samt att
kunna urskilja mellan land och vatten.
Med hjälp av tilesets och datasets kan punkter i GeoJSON-format på förhand ritas ut på kartan
och därmed öka prestandan på kartan och på så sätt kunna rita ut så många trafikplatser som
möjligt utan att prestandan tar för mycket stryk. Ett godtyckligt antal punkter fick därför ritas
ut först för att testa funktionaliteten och prestandan av kartan. Detta gjordes innan inhämtningen
av alla trafikplatsers koordinater, namn och annan information från trafikverkets sida och deras
API:er.
När såväl detta sedan testades i samband med testningen av realtidsdatan och de manuellt
angivna trafikplatserna verkade inga konkreta problem uppstå. Däremot gick allt snett när
testningen av egen realtidsdata började. Denna så kallade realtidsdata var i själva verket en
46
GeoJSON-sträng, kopierad från den exempeldata som redan använts vid tidigare tester, inlagd
i en GeoJSON-fil som låg i en lokal mapp på datorn. När bytet skedde till den lokala filen
istället för exempeldatan ville punkten inte längre ritas ut. Flertaliga försök med olika filtyper
så som JSON och HTML testades med till ingen nytta. Filerna testades även att läggas upp på
en webbserver som vi hade temporär tillgång till men inte heller det verkade fungera. Länge
undersöktes det om det möjligtvis kan ha varit en websocket av något slag som pushade upp
datan, men exempeldatan visade sig ligga på ett HTTPS-server vilket gjorde att vi fick börja
leta efter andra lösningar på problemet.
Vid det här laget hade redan halva oktober passerat och fram till början på november försökte
en lösning hittas på detta problem men förgäves hittades inga lösningar vare sig på MapBox
egna webbsida, dokumentation eller tutorials. Inte heller fanns det någon lösning på flertalet
programmeringsinriktade webbforum. Vid det här laget hade implementationen av inhämtning
av data från trafikverket påbörjats. Dessutom hittades ett API för inhämtning av tågpositioner
på www.trafiklab.se genom Oxyfi och deras verktyg (som nämndes i förra delkapitlet) så att
kunna hämta hem faktiska data gavs en högre fokus än innan. Sökandet efter lösningar till
MapBox-problemet fortsatte bredvid.
Så sent som i början på december hittades till sist PubNub, ett API specialiserat på att kunna
rita ut flertalet olika punkter på en karta från realtidsdata. PubNub skulle fungera på både
MapBox och Google Maps. Med tanke på de kartfunktionaliteter som MapBox hade valdes det
att försöka få det att fungera med MapBox. Problem uppstod dock ganska fort då det
uppdagades att PubNub var gjord för en äldre, numera otillgänglig, version av MapBox. Det
resulterade i att kartan fick till slut istället göras i Google Maps. Kort därefter fanns till slut en
fungerande karta med statiska tåg- och stationsmarkörer. Koden för realtidsuppdateraren hade
inte skrivits ännu.
Så till sist i mitten på december lyckades tågen till slut att röra på sig på kartan. Ett annat
problem uppstod istället då det upptäcktes att tågens koordinater på kartan inte stämde överens
med vad de egentligen borde ha. Efter en lång dag med att samla in koordinater från olika tåg
landet över, genom att hämta deras koordinater när de stannat vid stationer och sedan jämfört
dessa koordinater mot varandra så insågs ganska fort att problemet låg i att Google Maps
koordinater och Oxyfi:s koordinater var på olika format. Google Maps använde sig av grader
47
och decimaler, medan Oxyfi använde sig av grader, minuter och decimaler. Efter att Oxyfi:s
koordinater konverterats om i tåglokaliseraren lyckades kartan till sist att fungera som den
skulle.
48
6 Slutsats
Här summeras projektet genom att presentera en helhetsbild av hur det har gått och även förslag
på hur systemet kan vidareutvecklas.
6.1 Helhetsbild av projektet
Projektet i det stora hela kunde gått mycket bättre till, under de förutsättningar att projektets
uppgift hade varit bättre definierad från början. Det vi inte tänkte på vid kursens start var
riskerna för att påbörja arbetet utan att få tillräckligt med information från trafikledningen. Vi
startade första veckan med att försöka definiera projektet bättre men upptäckte mycket tidigt
att det skulle uppkomma stora problem och att grundidén behövde omformas samt nya riktlinjer
behövde sättas. På grund av faktorer som avsaknad av material från SJ gjorde att projektet under
lång tid spenderades på att definiera specifikationen.
Trots dessa förutsättningar så tog projektet fart och resulterade i ett simpelt men fullt fungerande
verktyg för att följa ett antal tåg. Två av de mål som sattes vid framtagandet av den nya
specifikationen avklarades helt.
Vi hoppades hinna bygga ut systemet med ytterligare funktionalitet men är i grund och botten
nöjda med vår insats och tror att vi gjort allt som varit möjligt utifrån förutsättningarna. Vidare
presenteras exempel på hur vi skulle vidareutvecklat systemet.
6.2 Vidareutveckling av systemet
Vid en vidareutveckling av systemet kan man lägga till flera olika funktionaliteter. Hade vi haft
tid hade vi försökt implementera några av dessa. Exempel på funktionaliteter är möjligheten att
välja ett specifikt tåg och få fram dess planerade körväg, och hur den håller sig till sin tidtabell.
Med tanke på att kartan blev långsam att ladda när många trafikplatser renderades kan endast
det valda tågets planerade passeringar tänkas visas. Annat exempel på funktionalitet är att välja
en trafikplats och kunna se vilka tåg som passerat, alternativt välja en sträcka och se vilka tåg
som kör på sträckan, detta. Ytterligare exempel kan vara möjligheten att se planerade möten,
som eventuellt skulle kunna planeras om vid förseningar. Slutligen vore det fördelaktigt om
49
MapBox hade fungerat från start. Sannolikt hade vi kunnat haft fler funktioner, men också en
bättre design på kartan.
50
Referenser
Informationskällor
[1] SJ, “OM SJ,” [Online]. Tillgänglig: https://www.sj.se/sv/om/om-sj [Hämtad: 2018-10-
05].
[2] Techopedia, ”Structured Query Language” [Online] Tillgänglig:
https://www.techopedia.com/definition/1245/structured-query-language-sql
[Hämtad: 2019-01-16].
[3] Tutoralspoint, ”C - programspråk” [Online] Tillgänglig:
https://www.tutorialspoint.com/cprogramming/c_overview.htm [Hämtad: 2019-01-16].
[4] Techopedia, ”C++ - programspråk” [Online] Tillgänglig:
https://www.techopedia.com/definition/26184/c-programming-language
[Hämtad: 2019-01-16].
[5] Techopedia, ”Java” [Online] Tillgänglig:
https://www.techopedia.com/definition/3927/java [Hämtad: 2019-01-16].
[6] Techopedia, ”C-sharp” [Online] Tillgänglig:
https://www.techopedia.com/definition/26272/c-sharp [Hämtad: 2019-01-16].
[7] W3SCHOOLS, ”PHP” [Online] Tillgänglig:
http://php.net/manual/en/intro-whatis.php [Hämtad: 2018-12-05].
[8] W3SCHOOLS, ”HTML” [Online] Tillgänglig:
https://www.w3schools.com/html/html_intro.asp [Hämtad: 2018-12-05].
[9] Techopedia, ”JavaScript” [Online] Tillgänglig:
https://www.techopedia.com/definition/3929/javascript-js [Hämtad: 2019-01-16].
[10] Microsoft, ”Azure” [Online] Tillgänglig:
https://azure.microsoft.com/sv-se/overview/what-is-azure/ [Hämtad: 2018-12-21].
[11] Microsoft, ”Azure Database” [Online] Tillgänglig:
https://docs.microsoft.com/sv-se/azure/sql-database/ [Hämtad: 2018-12-21].
51
[12] Apache, ”Kafka” [Online] Tillgänglig:
https://kafka.apache.org/intro [Hämtad: 2018-12-21].
[13] Apache, ”Kafka documentation” [Online] Tillgänglig:
https://kafka.apache.org/documentation/ [Hämtad: 2019-01-16].
[14] Kubernetes, ”What is Kubernetes” [Online] Tillgänglig:
https://kubernetes.io/docs/concepts/overview/what-is-kubernetes [Hämtad: 2018-12-21].
[15] Kubernetes, ”Pods” [Online] Tillgänglig:
https://kubernetes.io/docs/concepts/workloads/pods/pod/ [Hämtad: 2019-01-16].
[16] OpenSource, ”Docker” [Online] Tillgänglig: [Hämtad: 2019-01-16].
https://opensource.com/resources/what-docker [Hämtad: 2018-12-23].
[17] Docker, ”Containers” [Online] Tillgänglig:
https://www.docker.com/resources/what-container [Hämtad: 2018-12-23].
[18] Microsoft, ”Git” [Online] Tillgänglig:
https://docs.microsoft.com/en-us/azure/devops/learn/git/what-is-git
[Hämtad: 2019-01-16].
[19] Atlassian, ”Version control” [Online] Tillgänglig:
https://www.atlassian.com/git/tutorials/what-is-version-control [Hämtad: 2019-01-04].
[20] Atlassian, ”What is git” [Online] Tillgänglig:
https://www.atlassian.com/git/tutorials/what-is-git [Hämtad: 2019-01-04].
[21] MySQL, ”Workbench” [Online] Tillgänglig:
https://www.mysql.com/products/workbench/ [Hämtad: 2019-01-04].
[22] OpenStreetMap, ”About Openstreetmap” [Online] Tillgänglig:
https://www.openstreetmap.org/about [Hämtad: 2019-01-04].
[23] Wikipedia, ”MapBox” [Online] Tillgänglig:
https://en.wikipedia.org/wiki/Mapbox [Hämtad: 2019-01-04].
[24] Wikipedia, ”Leaflet” [Online] Tillgänglig:
52
https://en.wikipedia.org/wiki/Leaflet_(software) [Hämtad: 2019-01-04].
[25] Wikipedia, ”Google Maps” [Online] Tillgänglig:
https://en.wikipedia.org/wiki/Google_Maps [Hämtad: 2019-01-05].
[26] PubNub, ”PubNub” [Online] Tillgänglig:
https://www.pubnub.com/solutions/realtime-updates/ [Hämtad: 2019-01-05].
[27] PubNub, ”EON” [Online] Tillgänglig:
https://www.pubnub.com/developers/eon/ [Hämtad: 2019-01-05].
[28] Apachefriends, ”XAMPP” [Online] Tillgänglig:
https://www.apachefriends.org/about.html [Hämtad: 2019-01-16].
[29] GPSworld, ”NMEA” [Online] Tillgänglig:
https://www.gpsworld.com/what-exactly-is-gps-nmea-data/ [Hämtad: 2019-01-05].
[30] W3SCHOOLS, ”Ajax” [Online] Tillgänglig:
https://www.w3schools.com/js/js_ajax_intro.asp [Hämtad: 2019-01-05].
[31] JSON, ”JSON” [Online] Tillgänglig:
https://www.json.org/ [Hämtad: 2019-01-05].
[32] Fitzgerald. Introducing Regular Expressions (Första upplagan). Sebastopol: O’Reilley
Media Inc; 2012. Kapitel 1, What Is a Regular Expression?; s 1-11
[33] Trafikverket, ”RFID” [Online] Tillgänglig:
https://www.trafikverket.se/tjanster/system-och-verktyg/trafik/identifiering-och-
positionering-av-jarnvagsfordon-rfid/ [Hämtad: 2019-01-03].
[34] Trafiklab, ”Oxyfi” [Online] Tillgänglig:
https://www.trafiklab.se/api/oxyfi-realtidspositionering [Hämtad: 2019-01-20].
[35] W3SCHOOLS, ”CSS” [Online] Tillgänglig:
https://www.w3schools.com/css/css_intro.asp [Hämtad: 2019-01-16].
[36] GisGeography, ”World Geodetic System” [Online] Tillgänglig:
https://gisgeography.com/wgs84-world-geodetic-system/ [Hämtad: 2019-01-16].
53
Bildkällor
[37] JSON, ”JSON object” [Online] Tillgänglig:
https://www.json.org/object.gif [Hämtad: 2019-01-05].
[38] JSON, ”JSON array” [Online] Tillgänglig:
https://www.json.org/array.gif [Hämtad: 2019-01-05].
[39] JSON, ”JSON value” [Online] Tillgänglig:
https://www.json.org/value.gif [Hämtad: 2019-01-05].
[40] JSON, ”JSON string” [Online] Tillgänglig:
https://www.json.org/string.gif [Hämtad: 2019-01-05].
[41] JSON, ”JSON number” [Online] Tillgänglig:
https://www.json.org/number.gif [Hämtad: 2019-01-05].
[42] Trafikverket, ”RFID” [Online] Tillgänglig:
https://www.trafikverket.se/contentassets/1cdf41b979b24b8eafe1b2f2d421c72e/identifi
ering_och_positionering_brodtext.png [Hämtad: 2019-01-05].
54
A Bilagor
A.1 Stationsdataomvandlaren
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Linq; using MySql.Data; using MySql.Data.MySqlClient; namespace StationLocator { class Program { static void Main(string[] args) { string Server = "SERVER_LOCATION", UserID = "USER", Password = "PASSWORD", Database = "DATABASE"; string Credentials = @"server=" + Server + ";userid=" + UserID + ";password=" + Password + ";database=" + Database; string StationTable = "TrainStations"; bool ConnectionSuccess = false; MySqlConnection conn = null; WebClient webclient = new WebClient(); do { try { conn = new MySqlConnection(Credentials); conn.Open(); Console.WriteLine("MySQL version: {0}", conn.ServerVersion); Console.WriteLine("--- Download data sample ---"); webclient.UploadStringCompleted += (obj, arguments) => { if (arguments.Cancelled == true)
55
{ Console.Write("User Cancelled Request"); } else if (arguments.Error != null) { Console.WriteLine(arguments.Error.Message); Console.Write("Request Failed"); } else { string Result = arguments.Result.Replace("\"", "'"); Regex RGX = new Regex(@"\{\'(AdvertisedLocationName)':'" + @"(.{0,40})',.?'"(Geometry)':{'WGS84':'POINT.{0,2}" + @"\(([0-9.]*) ([0-9.]*)\)'},'(LocationSignature)': " + @"'(.{1,20})','(Prognosticated)':(true|false)}"); MySqlCommand RMVT = new MySqlCommand(); RMVT.Connection = conn; RMVT.CommandText = "DROP TABLE IF EXISTS " + StationTable + ";"; bool RemoveSuccess = true;
do { try { RMVT.ExecuteNonQuery(); Console.WriteLine("Removed previous instance of table"); RemoveSuccess = true; } catch (MySql.Data.MySqlClient.MySqlException error) { Console.WriteLine("Could not execute query!"); RemoveSuccess = false; } catch (System.InvalidOperationException error) { RemoveSuccess = false; try { conn.Open(); } catch (MySqlException e) { Console.WriteLine("Connection to the server was lost! " + "Reconnecting."); } } }
56
while (!RemoveSuccess); MySqlCommand CT = new MySqlCommand(); CT.Connection = conn; MySqlCommand AddStation = new MySqlCommand(); AddStation.Connection = conn; bool first = true, QuerySuccess = false; string LogText = "", geojson = ""; foreach (Match Set in RGX.Matches(Result)) { do { try { if (first) { first = false; CT.CommandText = "CREATE TABLE IF NOT EXISTS " + StationTable + " (" + Set.Groups[1].Value + " TEXT, " + Set.Groups[6].Value + " VARCHAR(11), " + Set.Groups[8].Value + " TEXT, Y DOUBLE, X DOUBLE, " + "PRIMARY KEY (" + Set.Groups[6].Value + "));"; Console.WriteLine(CT.CommandText); LogText += CT.CommandText + "\n\n"; geojson += "{\n\t\"features\": [\n"; CT.ExecuteNonQuery(); Console.WriteLine("Added new instance of table"); QuerySuccess = true; } AddStation.CommandText = "INSERT INTO " + StationTable + " (" + Set.Groups[1].Value + ", " + Set.Groups[6].Value + ", " + Set.Groups[8].Value + ", X, Y) " + "VALUES ('" + Set.Groups[2].Value + "', '" + Set.Groups[7].Value + "', '" + Set.Groups[9].Value + "', '" + Set.Groups[4].Value + "', '" + Set.Groups[5].Value + "');"; AddStation.ExecuteNonQuery(); QuerySuccess = true; Console.WriteLine("'" + Set.Groups[2].Value + "', '" + Set.Groups[7].Value + "', '" + Set.Groups[9].Value + "', '" + Set.Groups[4].Value + "', '" + Set.Groups[5].Value + "'"); LogText += AddStation.CommandText + "\n";
57
geojson += "\t\t{\n" + "\t\t\t\"type\": \"Feature\",\n" + "\t\t\t\"properties\": {\n" + "\t\t\t\t\"title\": \"" + Set.Groups[2].Value + "\"\n" + "\t\t\t},\n" + "\t\t\t\"geometry\": {\n" + "\t\t\t\t\"coordinates\": [\n" + "\t\t\t\t\t" + Set.Groups[4].Value + ",\n" + "\t\t\t\t\t" + Set.Groups[5].Value + "\n" + "\t\t\t\t],\n" + "\t\t\t\t\"type\": \"Point\"\n" + "\t\t\t}\n" + "\t\t},\n"; } catch (MySql.Data.MySqlClient.MySqlException error) { Console.WriteLine("Could not execute query!"); QuerySuccess = true; } catch (System.InvalidOperationException error) { QuerySuccess = false; try { conn.Open(); } catch (MySqlException e) { Console.WriteLine("Connection to the server was lost! " + "Reconnecting."); } } } while (!QuerySuccess); } DirectoryInfo OutputDir = Directory.CreateDirectory("C:\\__TSLT"); File.Create("C:\\__TSLT\\output.log").Dispose(); File.WriteAllText("C:\\__TSLT\\output.log", geojson); Console.Write("Data downloaded"); } Console.WriteLine(", press 'X' to exit."); }; ConnectionSuccess = true; }
catch (MySqlException e)
58
{ Console.WriteLine("Connection error, trying to reconnect!"); ConnectionSuccess = false; } finally { if (conn != null) { //conn.Close(); } } if (ConnectionSuccess) { try { // API server url Uri address = new Uri("http://api.trafikinfo." + "trafikverket.se/v1/data.json"); string requestBody = "<REQUEST>" + "<LOGIN authenticationkey='SECRET_API_KEY'/>" + "<QUERY objecttype='TrainStation' >" + "<INCLUDE>AdvertisedLocationName</INCLUDE>" + "<INCLUDE>LocationSignature</INCLUDE>" + "<INCLUDE>Geometry.WGS84</INCLUDE>" + "<INCLUDE>Prognosticated</INCLUDE>" + "</QUERY>" + "</REQUEST>"; Console.WriteLine("Fetching data ... (press 'C' to cancel)"); webclient.UploadStringAsync(address, "POST", requestBody); } catch (UriFormatException) { Console.WriteLine("Malformed url, press 'X' to exit."); } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine("An error occured, press 'X' to exit."); } char keychar = ' '; while (keychar != 'X') { keychar = Char.ToUpper(Console.ReadKey().KeyChar); if (keychar == 'C') { webclient.CancelAsync(); } } }
59
} while (!ConnectionSuccess); } } }
A.2 Tågdataomvandlaren
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Linq; using WebSocketSharp; using MySql.Data; using MySql.Data.MySqlClient; namespace TrainLocator { class Program { static void Main(string[] args) { //Connect to database string Server = "SERVER_LOCATION", UserID = "USER", Password = "PASSWORD", Database = "DATABASE"; string Credentials = @"server=" + Server + ";userid=" + UserID + ";password=" + Password + ";database=" + Database; string TrainTable = "TrainLocations"; bool ConnectionSuccess = false; double SpeedMultiplier = 1.852; MySqlConnection conn = null; const string TrainPositionRGX = @"\$GPRMC,([0-9]{2})([0-9]{2})" + @"([0-9]{2})([.0-9]*){0,},(A|V),([0-9.]*),(N|S),([0-9.]*)," + @"(E|W),([0-9.]*),([0-9.]*),([0-9]{2})([0-9]{2})([0-9]{2})," + @"([0-9.]*|),(E|W|)(.{1,6}),([0-9]+)\.trains.se,,(;?([0-9]+)" + @"\.(public|internal)\.trains.se@[0-9-]*(;?([0-9]+)\." + @"(public|internal)\.trains.se@[0-9-]*(;?([0-9]+)" + @"\.(public|internal)\.trains.se@[0-9-]*(;?([0-9]+)" + @"\.(public|internal)\.trains.se@[0-9-]*)?)?)?)?,oxyfi";
60
do { try { conn = new MySqlConnection(Credentials); conn.Open(); Console.WriteLine("MySQL version: {0}", conn.ServerVersion); MySqlCommand RMVT = new MySqlCommand(); RMVT.Connection = conn; RMVT.CommandText = "DROP TABLE IF EXISTS " + TrainTable; bool RemoveSuccess = true; do { try { RMVT.ExecuteNonQuery(); Console.WriteLine("Removed previous instance of table"); RemoveSuccess = true; } catch (MySql.Data.MySqlClient.MySqlException error) { Console.WriteLine("Could not execute query!"); RemoveSuccess = false; } catch (System.InvalidOperationException error) { RemoveSuccess = false; try { conn.Open(); } catch (MySqlException e) { Console.WriteLine("Connection to the server was lost! " + "Reconnecting."); } } } while (!RemoveSuccess); MySqlCommand CT = new MySqlCommand(); CT.Connection = conn; CT.CommandText = "CREATE TABLE IF NOT EXISTS " + TrainTable + "(Vehicle INT, PublicNumber INT, InternalNumber INT, " + "Time DATETIME, Active TEXT, Y DOUBLE, X DOUBLE, " + "Speed DOUBLE, Angle DOUBLE, PRIMARY KEY (Vehicle, Time))"; bool QuerySuccess = true;
61
do { try { CT.ExecuteNonQuery(); Console.WriteLine("Added new instance of table"); QuerySuccess = true; } catch (MySql.Data.MySqlClient.MySqlException error) { Console.WriteLine("Could not execute query!"); QuerySuccess = false; } catch (System.InvalidOperationException error) { QuerySuccess = false; try { conn.Open(); } catch (MySqlException e) { Console.WriteLine("Connection to the server was lost! " + "Reconnecting."); } } } while (!QuerySuccess); MySqlCommand SQL = new MySqlCommand(); SQL.Connection = conn; using (var ws = new WebSocket("wss://api.oxyfi.com/trainpos" + "/listen?v=1&key=SECRET_API_KEY")) { ws.OnMessage += (sender, e) => { Match Train = Regex.Match(e.Data, TrainPositionRGX); if (Train.Success) { double Vehicle = 0, PublicNumber = 0, InternalNumber = 0, TrainX = 0, TrainY = 0, TrainSpeed = 0, TrainAngle = 0;
62
if (Train.Groups[18].Value != null && Train.Groups[18].Value != "") Vehicle = Convert.ToDouble(Train.Groups[18].Value); if (Train.Groups[20].Value != null && Train.Groups[20].Value != "" && Train.Groups[21].Value == "public") { PublicNumber = Convert.ToDouble(Train.Groups[20].Value); } else if (Train.Groups[23].Value != null && Train.Groups[23].Value != "" && Train.Groups[24].Value == "public") { PublicNumber = Convert.ToDouble(Train.Groups[23].Value); } else if(Train.Groups[26].Value != null && Train.Groups[26].Value != "" && Train.Groups[27].Value == "public") { PublicNumber = Convert.ToDouble(Train.Groups[26].Value); } else if (Train.Groups[29].Value != null && Train.Groups[29].Value != "" && Train.Groups[30].Value == "public") { PublicNumber = Convert.ToDouble(Train.Groups[29].Value); } if (Train.Groups[20].Value != null && Train.Groups[20].Value != "" && Train.Groups[21].Value == "internal") { InternalNumber = Convert.ToDouble(Train.Groups[20].Value); } else if (Train.Groups[23].Value != null && Train.Groups[23].Value != "" && Train.Groups[24].Value == "internal") { InternalNumber = Convert.ToDouble(Train.Groups[23].Value); } else if (Train.Groups[26].Value != null && Train.Groups[26].Value != "" && Train.Groups[27].Value == "internal") { InternalNumber = Convert.ToDouble(Train.Groups[26].Value); }
63
else if (Train.Groups[29].Value != null && Train.Groups[29].Value != "" && Train.Groups[30].Value == "internal") { InternalNumber = Convert.ToDouble(Train.Groups[29].Value); } if (Train.Groups[8].Value != null && Train.Groups[8].Value != "") { TrainX = (int)(Convert.ToDouble(Train.Groups[8].Value) / 100) + ((Convert.ToDouble(Train.Groups[8].Value) / 100) - (int)(Convert.ToDouble(Train.Groups[8].Value) / 100)) / 0.6; } if (Train.Groups[6].Value != null && Train.Groups[6].Value != "") { TrainY = (int)(Convert.ToDouble(Train.Groups[6].Value) / 100) + ((Convert.ToDouble(Train.Groups[6].Value) / 100) - (int)(Convert.ToDouble(Train.Groups[6].Value) / 100)) / 0.6; } if (Train.Groups[10].Value != null && Train.Groups[10].Value != "") TrainSpeed = Convert.ToDouble(Train.Groups[10].Value) * SpeedMultiplier; if (Train.Groups[11].Value != null && Train.Groups[11].Value != "") TrainAngle = Convert.ToDouble(Train.Groups[11].Value); try { SQL.CommandText = "INSERT INTO " + TrainTable + " (Vehicle, PublicNumber, InternalNumber, Time, " + "Active, Y, X, Speed, Angle)" + " VALUES ('" + Vehicle + "', '" + PublicNumber + "', '" + InternalNumber + "', '20" + Train.Groups[14].Value + "-" + Train.Groups[13].Value + "-" + Train.Groups[12].Value + " " + Train.Groups[1].Value + ":" + Train.Groups[2].Value + ":" + Train.Groups[3].Value + "', '" + Train.Groups[5].Value + "', '" + TrainY + "', '" + TrainX + "', '" + TrainSpeed + "', '" + TrainAngle + "');"; SQL.ExecuteNonQuery();
64
Console.WriteLine("INSERT INTO " + TrainTable + " (Vehicle, PublicNumber, InternalNumber, " + "Time, Active, X, Y, Speed, Angle)" + " VALUES ('" + Vehicle + "', '" + PublicNumber + "', '" + InternalNumber + "', '20" + Train.Groups[14].Value + "-" + Train.Groups[13].Value + "-" + Train.Groups[12].Value + " " + Train.Groups[1].Value + ":" + Train.Groups[2].Value + ":" + Train.Groups[3].Value + "', '" + Train.Groups[5].Value + "', '" + TrainX + "', '" + TrainY + "', '" + TrainSpeed + "', '" + TrainAngle + "');"); } catch (MySql.Data.MySqlClient.MySqlException) { } catch (System.InvalidOperationException exception) { try { conn.Open(); } catch (MySqlException ex) { Console.WriteLine("Connection to the server was lost! " + "Reconnecting."); } } } else { Console.WriteLine("Something went wrong"); } }; ws.WaitTime = TimeSpan.FromMilliseconds(10000); ws.Connect(); ws.Send("TEST"); Console.ReadKey(true); } } catch (MySqlException e) { Console.WriteLine("Connection error, trying to reconnect!"); ConnectionSuccess = false; } finally { if (conn != null) { //conn.Close();
65
} } } while (!ConnectionSuccess); } } }
A.3 Webbgränsnittet
A.3.1 Index
<!DOCTYPE html> <?php //include "trains.php"; $servername = "SERVER_LOCATION"; $username = "USER"; $password = "PASSWORD"; $database = "DATABASE"; $conn = new mysqli($servername, $username, $password, $database); if ($conn->connect_error) die("Connection failed: " . $conn->connect_error); $stationQuery = "SELECT Y, X FROM trainstations WHERE " . "Prognosticated = 'true' AND Y >= '59.03609 ' AND " . "Y <= '60.216595 ' AND X >= '12.093117' AND X <= '14.55';"; $meetingQuery = "SELECT Y, X FROM trainstations WHERE " . "Prognosticated = 'false' AND Y >= '59.03609 ' AND " . "Y <= '60.216595 ' AND X >= '12.093117' AND X <= '14.55';"; $trainQuery = "SELECT t1.Vehicle, t1.PublicNumber, t1.Time, " . "t1.Y, t1.X FROM trainlocations t1 INNER JOIN ( SELECT Vehicle, " . "max(Time) as MaxTime FROM trainlocations GROUP BY Vehicle ) t2 " . "ON t1.Vehicle = t2.Vehicle AND t1.Time = t2.MaxTime;"; $stationResult = $conn->query($stationQuery); $meetingResult = $conn->query($meetingQuery); $trainResult = $conn->query($trainQuery); ?> <html> <head> <title>Simple Map</title> <meta name="viewport" content="initial-scale=1.0"> <meta charset="utf-8">
66
<style> </style> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" /> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" /> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"> </script> <script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.19.0.min.js"> </script> </head> <body> <div id="map-canvas" style="width:100%;height:700px;"></div> <script> window.lat = 59.6; window.lng = 13.5; var iconBase = "icons/"; var icons = { station: { icon: iconBase + 'station.png' }, meeting: { icon: iconBase + 'meeting.png' }, trainMoving: { icon: iconBase + 'trainMoving.png' }, trainParked: { icon: iconBase + 'trainParked.png' }, trainSlow: { icon: iconBase + 'trainSlow.png' }, trainStopped: { icon: iconBase + 'trainStopped.png' }, } var map; var stations = []; var trains = new Object(); var initialize = function() { map =
67
new google.maps.Map(document.getElementById('map-canvas'), { center:{ lat:lat, lng:lng }, zoom:8 }); <?php if ($stationResult->num_rows > 0) { while($row = $stationResult->fetch_assoc()){ ?>stations.push(new google.maps.Marker({ position:{ <?php echo "lat:" . $row["Y"] . ",\n\t\t\t\t\tlng:" . $row["X"] . "\n"; ?> }, icon: { path: google.maps.SymbolPath.CIRCLE, scale: 2, fillColor: 'black', fillOpacity: 1.0, strokeColor: 'black', zIndex: 2 }, map:map })); <?php } } if ($meetingResult->num_rows > 0) { while($row = $meetingResult->fetch_assoc()){ ?>stations.push(new google.maps.Marker({ position:{ <?php echo "lat:" . $row["Y"] . ",\n\t\t\t\t\tlng:" . $row["X"] . "\n"; ?> }, icon: { path: google.maps.SymbolPath.CIRCLE, scale: 2, fillColor: 'gray', fillOpacity: 1.0, strokeColor: 'gray', zIndex: 1 }, map:map })); <?php } }
68
?> }; window.initialize = initialize; setInterval(function() { var rowRegex = /\{"Vehicle":"(\d+).{30,35}"Y":"(\d+(\.(\d+)?)?)","X":"(\d+(\.(\d+)?)?)","PublicNumber":"(\d+)","Speed":"(\d+(\.(\d+)?)?)"}/g; var dataRegex = /\{"Vehicle":"(\d+).{30,35}"Y":"(\d+(\.(\d+)?)?)","X":"(\d+(\.(\d+)?)?)","PublicNumber":"(\d+)","Speed":"(\d+(\.(\d+)?)?)"}/; var json_station = JSON.parse("[]"); $.ajax({ type: 'POST', url: 'stations.php', success: function(stationData){ json_station = JSON.parse(JSON.stringify(stationData)); }, async: false }); $.ajax({ type: 'POST', url: 'trains.php', async: false, success: function(trainData){ var json_obj = JSON.parse(JSON.stringify(trainData)); var match = json_obj.match(rowRegex); for (var i in match){ var matchTrain = match[i].match(dataRegex); try { trains[matchTrain[1]].setPosition({ lng:Number(matchTrain[5]), lat:Number(matchTrain[2]), alt:0 }); } catch(err){ console.log("Train : " + matchTrain[1] + " - lat: " + matchTrain[2] + " - lng: " + matchTrain[5]); trains[matchTrain[1]] = new google.maps.Marker({ position:{ lng:Number(matchTrain[5]), lat:Number(matchTrain[2]) }, icon: { path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'yellow', fillOpacity: 1.0,
69
strokeColor: 'yellow', zIndex: 3 }, label: matchTrain[8], labelOrigin: new google.maps.Point(200,0), map:map }); } var StationRegex = new RegExp('\\{"Vehicle":"(' + Number(matchTrain[1]) + ')","PublicNumber":"(\\d+)"'); var matchStation = json_station.match(StationRegex); if (matchStation != null){ if (matchTrain[1] == matchStation[1]) { if (Number(matchTrain[9]) > 10){ try { trains[matchStation[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'fuchsia', fillOpacity: 1.0, strokeColor: 'fuchsia', zIndex: 3 }); } catch (err){ console.log(err); } } else { try { trains[matchStation[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'aqua', fillOpacity: 1.0, strokeColor: 'aqua', zIndex: 3 }); } catch (err){ console.log(err); } } } else { if (Number(matchTrain[9]) < 10){ try { trains[matchTrain[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE,
70
scale: 4, fillColor: 'red', fillOpacity: 1.0, strokeColor: 'red', zIndex: 3 }); } catch (err){ console.log(err); } } else if (Number(matchTrain[9]) < 30){ try { trains[matchTrain[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'yellow', fillOpacity: 1.0, strokeColor: 'yellow', zIndex: 3 }); } catch (err){ console.log(err); } } else if (Number(matchTrain[8]) == 0){ try { trains[matchTrain[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'white', fillOpacity: 1.0, strokeColor: 'white', zIndex: 3 }); } catch (err){ console.log(err); } } else { try { trains[matchTrain[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'lime', fillOpacity: 1.0, strokeColor: 'lime',
71
zIndex: 3 }); } catch (err){ console.log(err); } } } } else { if (Number(matchTrain[9]) < 10){ try { trains[matchTrain[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'red', fillOpacity: 1.0, strokeColor: 'red', zIndex: 3 }); } catch (err){ console.log(err); } } else if (Number(matchTrain[9]) < 30){ try { trains[matchTrain[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'yellow', fillOpacity: 1.0, strokeColor: 'yellow', zIndex: 3 }); } catch (err){ console.log(err); } } else if (Number(matchTrain[8]) == 0){ try { trains[matchTrain[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'white', fillOpacity: 1.0, strokeColor: 'white', zIndex: 3
72
}); } catch (err){ console.log(err); } } else { try { trains[matchTrain[1]].setIcon({ path: google.maps.SymbolPath.CIRCLE, scale: 4, fillColor: 'lime', fillOpacity: 1.0, strokeColor: 'lime', zIndex: 3 }); } catch (err){ console.log(err); } } } } } }); }, 1000); </script> <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=SECRET_API_KEY&callback=initialize" async defer> </script> </body> </html>
A.3.2 Trains
<?php $trainData = array(); $servername = "SERVER_LOCATION"; $username = "USER"; $password = "PASSWORD"; $database = "DATABASE"; $conn = new mysqli($servername, $username, $password, $database); if ($conn->connect_error) die("Connection failed: " . $conn->connect_error); $trainQuery = "SELECT t1.Vehicle, t1.Time, t1.Y, t1.X, t1.PublicNumber, " .
73
"t1.Speed FROM trainlocations t1 INNER JOIN ( SELECT Vehicle, max(Time) " . "as MaxTime FROM trainlocations GROUP BY Vehicle ) t2 " . "ON t1.Vehicle = t2.Vehicle AND t1.Time = t2.MaxTime;"; $trainResult = $conn->query($trainQuery); if ($trainResult->num_rows > 0) { while($row = $trainResult->fetch_assoc()){ array_push($trainData, $row); } } echo json_encode($trainData); ?>
A.3.3 Stations
<?php $stationData = array(); $servername = "SERVER_LOCATION"; $username = "USER"; $password = "PASSWORD"; $database = "DATABASE"; $conn = new mysqli($servername, $username, $password, $database); if ($conn->connect_error) die("Connection failed: " . $conn->connect_error); $stationQuery = "SELECT DISTINCT trainlocations.Vehicle, " . "trainlocations.PublicNumber, trainlocations.Speed FROM " . "trainlocations INNER JOIN trainstations ON trainlocations.Y " . "< (trainstations.Y + 0.02) AND trainlocations.Y > " . "(trainstations.Y - 0.02) AND trainlocations.X < " . "(trainstations.X + 0.02) AND trainlocations.X > " . "(trainstations.X - 0.02) INNER JOIN (SELECT DISTINCT " . "Vehicle, Max(Time) as MaxTime FROM trainlocations " . "GROUP BY Vehicle) t2 ON trainlocations.Vehicle" . " = t2.Vehicle AND trainlocations.Time = t2.MaxTime ORDER BY " . "trainlocations.PublicNumber DESC, trainlocations.Vehicle " . "ASC, trainlocations.Speed DESC"; $stationResult = $conn->query($stationQuery); if ($stationResult->num_rows > 0) { while($row = $stationResult->fetch_assoc()){ array_push($stationData, $row); } } echo json_encode($stationData); ?>