1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
using Enmarcha.Backend.Helpers;
using Enmarcha.Backend.Types.Arrivals;
using Enmarcha.Sources.Tussa;
using Arrival = Enmarcha.Backend.Types.Arrivals.Arrival;
namespace Enmarcha.Backend.Services.Processors.RealTime;
public class TussaRealTimeProcessor : AbstractRealTimeProcessor
{
private readonly SantiagoRealtimeEstimatesProvider _realtime;
private readonly FeedService _feedService;
private readonly ILogger<TussaRealTimeProcessor> _logger;
public TussaRealTimeProcessor(
SantiagoRealtimeEstimatesProvider realtime,
FeedService feedService,
ILogger<TussaRealTimeProcessor> logger)
{
_realtime = realtime;
_feedService = feedService;
_logger = logger;
}
public override async Task ProcessAsync(ArrivalsContext context)
{
if (!context.StopId.StartsWith("tussa:")) return;
var normalizedCode = _feedService.NormalizeStopCode("tussa", context.StopCode);
if (!int.TryParse(normalizedCode, out var numericStopId)) return;
try
{
var realtime = await _realtime.GetEstimatesForStop(numericStopId);
System.Diagnostics.Activity.Current?.SetTag("realtime.count", realtime.Count);
var usedTripIds = new HashSet<string>();
foreach (var estimate in realtime)
{
var bestMatch = context.Arrivals
.Where(a => !usedTripIds.Contains(a.TripId))
.Where(a => a.Route.RouteIdInGtfs.Trim() == estimate.Id.ToString())
.Select(a => new
{
Arrival = a,
TimeDiff = estimate.MinutesToArrive - a.Estimate.Minutes, // RealTime - Schedule
RouteMatch = true
})
.Where(x => x.RouteMatch) // Strict route matching
.Where(x => x.TimeDiff is >= -5 and <= 35) // Allow 2m early (RealTime < Schedule) or 25m late (RealTime > Schedule)
.OrderBy(x => Math.Abs(x.TimeDiff)) // Best time fit
.FirstOrDefault();
if (bestMatch is null)
{
context.Arrivals.Add(new Arrival
{
TripId = $"tussa:rt:{estimate.Id}:{estimate.MinutesToArrive}",
Route = new RouteInfo
{
GtfsId = $"tussa:{estimate.Id}",
ShortName = estimate.Sinoptico,
Colour = estimate.Colour,
TextColour = ContrastHelper.GetBestTextColour(estimate.Colour)
},
Headsign = new HeadsignInfo
{
Badge = "T.REAL",
Destination = estimate.Name
},
Estimate = new ArrivalDetails
{
Minutes = estimate.MinutesToArrive,
Precision = ArrivalPrecision.Confident
}
});
continue;
}
var arrival = bestMatch.Arrival;
arrival.Estimate.Minutes = estimate.MinutesToArrive;
arrival.Estimate.Precision = ArrivalPrecision.Confident;
usedTripIds.Add(arrival.TripId);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching Santiago real-time data for stop {StopId}", context.StopId);
}
}
}
|