Skip to content

Commit

Permalink
[FEATURE] Read and display distance in kilometers to next station
Browse files Browse the repository at this point in the history
  • Loading branch information
eliashaeussler committed Sep 13, 2024
1 parent 4b9cc53 commit 07abc23
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 17 deletions.
79 changes: 75 additions & 4 deletions grafana-dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@
"overrides": []
},
"gridPos": {
"h": 7,
"h": 5,
"w": 5,
"x": 19,
"y": 2
Expand Down Expand Up @@ -349,7 +349,7 @@
"h": 3,
"w": 5,
"x": 19,
"y": 9
"y": 7
},
"id": 3,
"options": {
Expand Down Expand Up @@ -415,10 +415,10 @@
"overrides": []
},
"gridPos": {
"h": 5,
"h": 4,
"w": 5,
"x": 19,
"y": 12
"y": 10
},
"id": 4,
"options": {
Expand Down Expand Up @@ -456,6 +456,77 @@
],
"title": "Next Station",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "fixed"
},
"decimals": 2,
"fieldMinMax": true,
"mappings": [],
"noValue": "0",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "lengthkm"
},
"overrides": []
},
"gridPos": {
"h": 3,
"w": 5,
"x": 19,
"y": 14
},
"id": 6,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"text": {},
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.4.3",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"editorMode": "code",
"exemplar": false,
"expr": "train_distance",
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "A"
}
],
"title": "Distance to Next Station",
"type": "stat"
}
],
"refresh": "5s",
Expand Down
72 changes: 59 additions & 13 deletions metrics_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import time
import urllib
from urllib.request import urlopen
from geopy.distance import geodesic
from prometheus_client import start_http_server
from prometheus_client.core import GaugeMetricFamily, REGISTRY

Expand All @@ -35,10 +36,14 @@ def collect(self):
'Delay in minutes of the current trip',
labels=['train', 'next_station']
)
distance_metric = GaugeMetricFamily(
'train_distance',
'Distance in km until next station of the current trip'
)

# Collect speed and delay information
speed = self.collect_speed()
train, next_station, delay = self.collect_trip()
speed, current_location = self.collect_status()
train, next_station, delay, next_station_location = self.collect_trip()

# Handle speed metric
if speed is not None:
Expand All @@ -56,27 +61,50 @@ def collect(self):
else:
trip_metric.add_metric(['Unknown', 'Unknown'], 0)

# Handle distance metric
if current_location is not None and next_station_location is not None:
distance = self.calculate_distance(current_location, next_station_location)
distance_metric.add_metric([], distance)
self.logger.debug('Extracted current location from JSON response: %s', current_location)
self.logger.debug(
'Extracted next station location from JSON response: %s',
next_station_location
)
self.logger.debug('Calculated distance: %.2f', distance)
else:
distance_metric.add_metric([], 0)

yield speed_metric
yield trip_metric
yield distance_metric

def collect_speed(self):
def collect_status(self):
"""
Collect speed metrics from DB onboard status API.
Collect status metrics from DB onboard status API.
"""

try:
with urlopen(self.STATUS_URL, timeout=20) as res:
status_response = json.load(res)
except urllib.error.URLError as error:
self.logger.error('Error while fetching JSON from %s: %s', self.STATUS_URL, str(error))
return None
return None, None
except json.decoder.JSONDecodeError as error:
self.logger.error('Error while decoding JSON from %s: %s', self.STATUS_URL, str(error))
return None
return None, None

return status_response.get('speed', 0.0)
speed = status_response.get('speed', 0.0)
latitude = status_response.get('latitude')
longitude = status_response.get('longitude')

def collect_trip(self):
if latitude is None or longitude is None:
coordinates = None
else:
coordinates = (latitude, longitude)

return speed, coordinates

def collect_trip(self): # pylint: disable=too-many-locals
"""
Collect trip metrics from DB onboard trip API.
"""
Expand All @@ -86,10 +114,10 @@ def collect_trip(self):
trip_response = json.load(res)
except urllib.error.URLError as error:
self.logger.error('Error while fetching JSON from %s: %s', self.TRIP_URL, str(error))
return None, None, None
return None, None, None, None
except json.decoder.JSONDecodeError as error:
self.logger.error('Error while decoding JSON from %s: %s', self.TRIP_URL, str(error))
return None, None, None
return None, None, None, None

# Fetch train, next station and all stops
trip = trip_response.get('trip', {})
Expand All @@ -101,14 +129,25 @@ def collect_trip(self):

# Early return if next station is not defined
if next_station is None:
return train, None, None
return train, None, None, None

for stop in all_stops:
station = stop.get('station', {})
eva_nr = station.get('evaNr')
station_name = station.get('name', '')
delay = stop.get('timetable', {}).get('arrivalDelay', 0)

# Get coordinates
coordinates = station.get('geocoordinates', {})
latitude = coordinates.get('latitude')
longitude = coordinates.get('longitude')

# Handle coordinates
if latitude is not None and longitude is not None:
location = (latitude, longitude)
else:
location = None

# Remove leading plus (+) character from delay information
if isinstance(delay, str) and delay.startswith('+'):
delay = int(delay.lstrip('+'))
Expand All @@ -117,9 +156,16 @@ def collect_trip(self):

# Return delay data if eva number matches with next station
if eva_nr == next_station:
return train, station_name, delay
return train, station_name, delay, location

return train, None, None, None

def calculate_distance(self, coordinates_from, coordinates_to):
"""
Calculate distance in kilometers between two coordinates.
"""

return train, None, None
return geodesic(coordinates_from, coordinates_to).km

def main():
"""
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
geopy >= 2.4.1, < 3.0
prometheus_client >= 0.20.0, < 0.21
requests >= 2.32.2, < 3.0

0 comments on commit 07abc23

Please sign in to comment.