commit 6811f8728148d5733110452d04726e2778a3d84c Author: Simon Rieger Date: Sat Sep 28 13:00:29 2024 +0200 first commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..19e0180 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +db/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19e0180 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +db/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8bf4675 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.19 + +WORKDIR /app + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +RUN go build -o main . + +CMD ["./main"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2e28bdd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +version: '3' + +services: + app: + build: . + depends_on: + - mariadb + - db-rest + environment: + - DB_HOST=mariadb + - DB_USER=root + - DB_PASSWORD=password + - DB_NAME=traindb + - DB_DSN=root:password@tcp(mariadb:3306)/traindb + - API_BASE_URL=http://db-rest:3000 + - MAX_RESULTS=100 + - BUS=false + # Hildesheim HBF, Braunschweig HBF, Hannover HBF + - STATION_IDS=8000169,8000049,8000152 + restart: always + networks: + default: + + mariadb: + image: mariadb:10.5 + environment: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: traindb + volumes: + - ./init.sql:/docker-entrypoint-initdb.d/init.sql + - ./db:/var/lib/mysql + restart: always + networks: + default: + dns: + ipv4_address: 172.28.0.65 + + db-rest: + image: docker.io/derhuerst/db-rest:6 + ports: + - 127.0.0.1:3010:3000 + restart: always + networks: + default: + +networks: + dns: + name: dns + external: true diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..01972ed --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module train-tracker + +go 1.19 + +require ( + github.com/go-sql-driver/mysql v1.8.1 + github.com/google/uuid v1.6.0 +) + +require filippo.io/edwards25519 v1.1.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..999c59c --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/init.sql b/init.sql new file mode 100644 index 0000000..c4d94d6 --- /dev/null +++ b/init.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS trips ( + id VARCHAR(36) PRIMARY KEY, + latitude DOUBLE, + longitude DOUBLE, + timestamp DATETIME, + train_name VARCHAR(50), + fahrt_nr VARCHAR(20) +); diff --git a/main.go b/main.go new file mode 100644 index 0000000..f197574 --- /dev/null +++ b/main.go @@ -0,0 +1,131 @@ +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) + } + includeBus, err := strconv.ParseBool(os.Getenv("BUS")) + if err != nil { + log.Fatalf("Ungültiger Wert für BUS: %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, includeBus) + for _, dep := range departures { + savePosition(db, dep) + } + } + time.Sleep(1 * time.Minute) + } +} + +func fetchDepartures(apiBaseURL, stationID string, maxResults int, includeBus bool) []Departure { + url := fmt.Sprintf("%s/stops/%s/departures?results=%d&bus=%t", apiBaseURL, stationID, maxResults, includeBus) + 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) + } +}