train-tracker/main.go

164 lines
5.7 KiB
Go

package main
import (
"database/sql"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
_ "github.com/go-sql-driver/mysql"
"github.com/google/uuid"
)
type Departure struct {
CurrentTripPosition struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
} `json:"currentTripPosition"`
When time.Time `json:"when"`
Line struct {
Name string `json:"name"`
FahrtNr string `json:"fahrtNr"`
} `json:"line"`
}
type APIResponse struct {
Departures []Departure `json:"departures"`
}
func main() {
log.SetOutput(os.Stdout)
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("Anwendung gestartet")
dbDSN := os.Getenv("DB_DSN")
apiBaseURL := os.Getenv("API_BASE_URL")
maxResults, err := strconv.Atoi(os.Getenv("MAX_RESULTS"))
if err != nil {
log.Fatalf("Ungültiger Wert für MAX_RESULTS: %v", err)
}
duration, err := strconv.Atoi(os.Getenv("DURATION"))
if err != nil {
log.Fatalf("Ungültiger Wert für DURATION: %v", err)
}
includeBus, err := strconv.ParseBool(os.Getenv("BUS"))
if err != nil {
log.Fatalf("Ungültiger Wert für BUS: %v", err)
}
includeFerry, err := strconv.ParseBool(os.Getenv("FERRY"))
if err != nil {
log.Fatalf("Ungültiger Wert für FERRY: %v", err)
}
includeTram, err := strconv.ParseBool(os.Getenv("TRAM"))
if err != nil {
log.Fatalf("Ungültiger Wert für TRAM: %v", err)
}
includeTaxi, err := strconv.ParseBool(os.Getenv("TAXI"))
if err != nil {
log.Fatalf("Ungültiger Wert für TAXI: %v", err)
}
deleteAfter, err := strconv.Atoi(os.Getenv("DELETE_AFTER_MINUTES"))
if err != nil {
log.Fatalf("Ungültiger Wert für DELETE_AFTER_MINUTES: %v", err)
}
stationIDs := strings.Split(os.Getenv("STATION_IDS"), ",")
db, err := sql.Open("mysql", dbDSN)
if err != nil {
log.Fatal("Fehler beim Verbinden mit der Datenbank: ", err)
}
defer db.Close()
for {
for _, stationID := range stationIDs {
departures := fetchDepartures(apiBaseURL, stationID, maxResults, duration, includeBus, includeFerry, includeTram, includeTaxi)
for _, dep := range departures {
savePosition(db, dep)
}
}
deleteOldEntries(db, deleteAfter)
time.Sleep(1 * time.Minute)
}
}
func fetchDepartures(apiBaseURL, stationID string, maxResults, duration int, includeBus, includeFerry, includeTram, includeTaxi bool) []Departure {
url := fmt.Sprintf("%s/stops/%s/departures?results=%d&duration=%d&bus=%t&ferry=%t&tram=%t&taxi=%t",
apiBaseURL, stationID, maxResults, duration, includeBus, includeFerry, includeTram, includeTaxi)
resp, err := http.Get(url)
if err != nil {
log.Printf("Fehler beim Abrufen der Abfahrten für Station %s: %v\n", stationID, err)
return nil
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("Fehler beim Lesen der Antwort für Station %s: %v\n", stationID, err)
return nil
}
if len(body) == 0 {
log.Printf("Leere Antwort vom Server für Station %s erhalten\n", stationID)
return nil
}
var response APIResponse
err = json.Unmarshal(body, &response)
if err != nil {
log.Printf("Fehler beim Dekodieren der Abfahrten für Station %s: %v\nAntwort-Body: %s\n", stationID, err, string(body))
return nil
}
log.Printf("Erfolgreich %d Abfahrten für Station %s abgerufen\n", len(response.Departures), stationID)
return response.Departures
}
func savePosition(db *sql.DB, dep Departure) {
if dep.CurrentTripPosition.Latitude == 0 && dep.CurrentTripPosition.Longitude == 0 {
log.Println("Keine gültige Position verfügbar")
return
}
today := time.Now().Format("2006-01-02")
var existingID string
err := db.QueryRow("SELECT id FROM trips WHERE fahrt_nr = ? AND DATE(timestamp) = ?", dep.Line.FahrtNr, today).Scan(&existingID)
if err == sql.ErrNoRows {
id := uuid.New().String()
_, err = db.Exec("INSERT INTO trips (id, latitude, longitude, timestamp, train_name, fahrt_nr) VALUES (?, ?, ?, ?, ?, ?)",
id, dep.CurrentTripPosition.Latitude, dep.CurrentTripPosition.Longitude, dep.When, dep.Line.Name, dep.Line.FahrtNr)
if err != nil {
log.Printf("Fehler beim Speichern der neuen Position: %v\n", err)
} else {
log.Printf("Neue Position gespeichert (ID: %s, Zug: %s, FahrtNr: %s)\n", id, dep.Line.Name, dep.Line.FahrtNr)
}
} else if err == nil {
_, err = db.Exec("UPDATE trips SET latitude = ?, longitude = ?, timestamp = ?, train_name = ? WHERE id = ?",
dep.CurrentTripPosition.Latitude, dep.CurrentTripPosition.Longitude, dep.When, dep.Line.Name, existingID)
if err != nil {
log.Printf("Fehler beim Aktualisieren der Position: %v\n", err)
} else {
log.Printf("Position aktualisiert (ID: %s, Zug: %s, FahrtNr: %s)\n", existingID, dep.Line.Name, dep.Line.FahrtNr)
}
} else {
log.Printf("Fehler bei der Überprüfung des existierenden Eintrags: %v\n", err)
}
}
func deleteOldEntries(db *sql.DB, deleteAfterMinutes int) {
deleteTime := time.Now().Add(time.Duration(-deleteAfterMinutes) * time.Minute)
result, err := db.Exec("DELETE FROM trips WHERE timestamp < ?", deleteTime)
if err != nil {
log.Printf("Fehler beim Löschen alter Einträge: %v\n", err)
return
}
rowsAffected, _ := result.RowsAffected()
log.Printf("%d alte Einträge gelöscht\n", rowsAffected)
}