From 96f8cfa55cfde6dbccf53f5db94e04ac1cbdc7e5 Mon Sep 17 00:00:00 2001 From: shellixa Date: Fri, 3 Jul 2026 11:41:25 +0200 Subject: [PATCH] make it work for my tram board adds webserer with endpoint /line?number=3 you need to specify a post body text using b64-encoded, semicolon-separated names of the stops index of stop name corresponds to returned value (led index) --- cmd/tracker/main.go | 51 ++++++++++++++----- cmd/web/main.go | 98 +++++++++++++++++++++++++++++++++++++ internal/maps/trips.go | 14 ++++-- internal/timetable/trips.go | 34 +++++++++++++ internal/types/types.go | 26 +++++----- 5 files changed, 194 insertions(+), 29 deletions(-) create mode 100644 cmd/web/main.go create mode 100644 internal/timetable/trips.go diff --git a/cmd/tracker/main.go b/cmd/tracker/main.go index 87b7284..97f0439 100644 --- a/cmd/tracker/main.go +++ b/cmd/tracker/main.go @@ -1,37 +1,62 @@ package main import ( + // "fmt" "log" - "shellixa/puvetra/internal/maps" + // "maps" + trmaps "shellixa/puvetra/internal/maps" "shellixa/puvetra/internal/types" + /*"slices" + "strings" */ + "time" ) func main() { log.Printf("Starting up..\n") - trips := maps.GetTrips() + trams := trmaps.GetTrips() var allTrams []types.MapTrip - for _, t := range trips { - if t.Mode == "TRAM" { + for _, t := range trams { + if t.Mode == "TRAM" && t.Trips[0].RouteShortName == "3" { allTrams = append(allTrams, t) } } - var traminfo []map[string]string + var traminfo = make(map[string]types.MapTrip) + now := time.Now() for _, tr := range allTrams { - data := map[string]string{} - data["line"] = tr.Trips[0].RouteShortName - data["currentStation"] = tr.From.Name - data["nextStation"] = tr.To.Name - data["departure"] = tr.Departure - data["arrival"] = tr.Arrival - traminfo = append(traminfo, data) + // Überschreibe [tripId] nur, wenn (OR) + // - [tripId] == nil + // - departure < now && arrival > [tripId].departure + storedTram := traminfo[tr.Trips[0].TripId] + + if tr.Departure.Sub(now).Abs() < storedTram.Departure.Sub(now).Abs() { + // Current is past now, Stored is future now + traminfo[tr.Trips[0].TripId] = tr + } } log.Printf("%+v\n", traminfo) log.Printf("%d", len(traminfo)) -} + /* + activeLights := make(map[int]bool) + + for _, tram := range allTrams { + // Calculate current Station name depending on current time + realStation := tram.From + if now.Sub(depTime) > arrTime.Sub(now) { + // log.Printf("> %v, %v\n", now.Sub(tram.Departure), tram.Arrival.Sub(now)) + realStation = tram.To + } + // log.Printf("From='%s' To='%s' Real='%s'\n", tram.From.Name, tram.To.Name, realStation.Name) + activeLights[stationLookupTable[strings.ToLower(realStation.Name)]] = true + + } + + fmt.Println(slices.Collect(maps.Keys(activeLights))) + */ +} diff --git a/cmd/web/main.go b/cmd/web/main.go new file mode 100644 index 0000000..8b060f3 --- /dev/null +++ b/cmd/web/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "encoding/base64" + "encoding/json" + "io" + "log" + "maps" + "net/http" + trmaps "shellixa/puvetra/internal/maps" + "shellixa/puvetra/internal/types" + "slices" + "strings" + "time" +) + +func main() { + + mux := http.NewServeMux() + + server := &http.Server{ + Handler: mux, + Addr: ":8080", + } + + mux.HandleFunc("POST /line", GetLineTrams) + + err := server.ListenAndServe() + if err != nil { + log.Println(err) + } +} + +func GetLineTrams(w http.ResponseWriter, r *http.Request) { + + // log.Printf("%+v\n", r) + b64Body := base64.NewDecoder(base64.StdEncoding, r.Body) + defer r.Body.Close() + + body, _ := io.ReadAll(b64Body) + + stationSeq := strings.Split(string(body), ";") + + tramLine := r.URL.Query().Get("number") + + trams := trmaps.GetTrips() + + var allTrams []types.MapTrip + + for _, t := range trams { + if t.Mode == "TRAM" && t.Trips[0].RouteShortName == tramLine { + allTrams = append(allTrams, t) + } + } + + + var traminfo = make(map[string]types.MapTrip) + + now := time.Now() + for _, tr := range allTrams { + // Überschreibe [tripId] nur, wenn (OR) + // - [tripId] == nil + // - departure < now && arrival > [tripId].departure + storedTram := traminfo[tr.Trips[0].TripId] + + if tr.Departure.Sub(now).Abs() < storedTram.Departure.Sub(now).Abs() { + // Current is past now, Stored is future now + traminfo[tr.Trips[0].TripId] = tr + } + } + + stationLookupTable := make(map[string]int) + for i, v := range stationSeq { + stationLookupTable[strings.ToLower(v)] = i + } + + activeLights := make(map[int]bool) + for _, tram := range slices.Collect(maps.Values(traminfo)) { + // Calculate current Station name depending on current time + realStation := tram.From + if tram.Departure.Sub(now).Abs() > tram.Arrival.Sub(now).Abs() { + // log.Printf("> %v, %v\n", now.Sub(depTime), arrTime.Sub(now)) + realStation = tram.To + } + // log.Printf("From='%s' To='%s' Real='%s'\n", tram.From.Name, tram.To.Name, realStation.Name) + activeLights[stationLookupTable[strings.ToLower(realStation.Name)]] = true + + } + + lightIds := slices.Collect(maps.Keys(activeLights)) + // log.Println(lightIds) + + enc := json.NewEncoder(w) + err := enc.Encode(lightIds) + if err != nil { + log.Println(err) + } +} diff --git a/internal/maps/trips.go b/internal/maps/trips.go index 72f8e33..7722925 100644 --- a/internal/maps/trips.go +++ b/internal/maps/trips.go @@ -2,6 +2,7 @@ package maps import ( "encoding/json" + "io" "log" "net/http" "shellixa/puvetra/internal/types" @@ -16,9 +17,12 @@ func GetTrips() []types.MapTrip { } q := req.URL.Query() - now := time.Now().Format(time.RFC3339) - q.Add("startTime", now) - q.Add("endTime", now) + now := time.Now() + delta := time.Minute * 5 + lowerBound := now.Add(-delta).Format(time.RFC3339) + upperBound := now.Add(delta).Format(time.RFC3339) + q.Add("startTime", lowerBound) + q.Add("endTime", upperBound) q.Add("min", "51.1626,13.5609") q.Add("max", "50.9973,13.8596") q.Add("zoom", "14") @@ -34,9 +38,11 @@ func GetTrips() []types.MapTrip { defer response.Body.Close() var trips []types.MapTrip - if err := json.NewDecoder(response.Body).Decode(&trips); err != nil { + buffer, _ := io.ReadAll(response.Body) + if err = json.Unmarshal(buffer, &trips); err != nil { log.Fatal(err) } + log.Printf("component='Transitous API' message='Response was %d bytes'", len(buffer)) return trips diff --git a/internal/timetable/trips.go b/internal/timetable/trips.go new file mode 100644 index 0000000..af53a21 --- /dev/null +++ b/internal/timetable/trips.go @@ -0,0 +1,34 @@ +package timetable + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "shellixa/puvetra/internal/types" +) + +func GetTrip(tripId string) types.Trip { + client := http.Client{} + url := fmt.Sprintf("https://api.transitous.org/api/v6/trip?tripId=%s", tripId) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Fatal(err) + } + + response, err := client.Do(req) + + if err != nil { + log.Fatal(err) + } + + defer response.Body.Close() + + var trip types.Trip + if err := json.NewDecoder(response.Body).Decode(&trip); err != nil { + log.Fatal(err) + } + + return trip + +} diff --git a/internal/types/types.go b/internal/types/types.go index 7a256d5..938bc4a 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -1,5 +1,7 @@ package types +import "time" + type TripMeta struct { TripId string @@ -12,20 +14,20 @@ type MapTrip struct { RouteColor string Mode string Distance float32 - Departure string - Arrival string + Departure time.Time + Arrival time.Time From Place To Place - ScheduledDeparture string - ScheduledArrival string + ScheduledDeparture time.Time + ScheduledArrival time.Time RealTime bool Polyline string } type Trip struct { Duration int64 - StartTime string - EndTine string + StartTime time.Time + EndTine time.Time Transfers int Id string Legs []TripLeg @@ -36,10 +38,10 @@ type TripLeg struct { From Place To Place Duration int64 - StartTime string - EndTime string - ScheduledStartTime string - ScheduledEndTime string + StartTime time.Time + EndTime time.Time + ScheduledStartTime time.Time + ScheduledEndTime time.Time RealTime bool Scheduled bool Distance float32 @@ -53,8 +55,8 @@ type Place struct { StopId string Lat float32 Lon float32 - Arrival string - Departure string + Arrival time.Time + Departure time.Time Track string StopCode string Description string