aboutsummaryrefslogtreecommitdiff
path: root/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2025-11-06 16:48:43 +0100
committerAriel Costas Guerrero <ariel@costas.dev>2025-11-06 16:48:43 +0100
commit817b29603e5b7f79bfe6489eebf73961e6ca93f2 (patch)
tree09057b38ca6ed23ce7852b62ae1d5c97a91cfe9b /src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs
parent94658119f2c0b555f83e870fbeb50150f18fc38d (diff)
Move controllers, add config, read stop schedules from local directory
Diffstat (limited to 'src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs')
-rw-r--r--src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs185
1 files changed, 185 insertions, 0 deletions
diff --git a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs
new file mode 100644
index 0000000..e82baed
--- /dev/null
+++ b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs
@@ -0,0 +1,185 @@
+using System.Globalization;
+using System.Text;
+using System.Text.Json;
+using Costasdev.Busurbano.Backend.Configuration;
+using Costasdev.Busurbano.Backend.Types;
+using Costasdev.VigoTransitApi;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.Options;
+using SysFile = System.IO.File;
+
+namespace Costasdev.Busurbano.Backend.Controllers;
+
+[ApiController]
+[Route("api/vigo")]
+public class VigoController : ControllerBase
+{
+ private readonly ILogger<VigoController> _logger;
+ private readonly VigoTransitApiClient _api;
+ private readonly AppConfiguration _configuration;
+
+ public VigoController(HttpClient http, IOptions<AppConfiguration> options, ILogger<VigoController> logger)
+ {
+ _logger = logger;
+ _api = new VigoTransitApiClient(http);
+ _configuration = options.Value;
+ }
+
+ [HttpGet("GetStopEstimates")]
+ public async Task<IActionResult> Run(
+ [FromQuery] int id
+ )
+ {
+ try
+ {
+ var response = await _api.GetStopEstimates(id);
+ // Return only the estimates array, not the stop metadata
+ return new OkObjectResult(response.Estimates);
+ }
+ catch (InvalidOperationException)
+ {
+ return BadRequest("Stop not found");
+ }
+ }
+
+ [HttpGet("GetStopTimetable")]
+ public async Task<IActionResult> GetStopTimetable(
+ [FromQuery] int stopId,
+ [FromQuery] string date
+ )
+ {
+ // Validate date format
+ if (!DateTime.TryParseExact(date, "yyyy-MM-dd", null, DateTimeStyles.None, out _))
+ {
+ return BadRequest("Invalid date format. Please use yyyy-MM-dd format.");
+ }
+
+ try
+ {
+ var timetableData = await LoadTimetable(stopId.ToString(), date);
+
+ return new OkObjectResult(timetableData);
+ }
+ catch (FileNotFoundException ex)
+ {
+ _logger.LogError(ex, "Stop data not found for stop {StopId} on date {Date}", stopId, date);
+ return StatusCode(404, $"Stop data not found for stop {stopId} on date {date}");
+ }
+ catch(Exception ex)
+ {
+ _logger.LogError(ex, "Error loading stop data");
+ return StatusCode(500, "Error loading timetable");
+ }
+ }
+
+ /*private StopEstimate[] LoadDebugEstimates()
+ {
+ var file = @"C:\Users\ariel\Desktop\GetStopEstimates.json";
+ var contents = System.IO.File.ReadAllText(file);
+ return JsonSerializer.Deserialize<StopEstimate[]>(contents, JsonSerializerOptions.Web)!;
+ }
+
+ private ScheduledStop[] LoadDebugTimetable()
+ {
+ var file = @"C:\Users\ariel\Desktop\GetStopTimetable.json";
+ var contents = System.IO.File.ReadAllText(file);
+ return JsonSerializer.Deserialize<ScheduledStop[]>(contents)!;
+ }*/
+
+ [HttpGet("GetStopArrivalsMerged")]
+ public async Task<IActionResult> GetStopArrivalsMerged(
+ [FromQuery] int stopId
+ )
+ {
+ StringBuilder outputBuffer = new();
+
+ var now = DateTime.Now.AddSeconds(60 - DateTime.Now.Second);
+ var realtimeTask = _api.GetStopEstimates(stopId);
+ var timetableTask = LoadTimetable(stopId.ToString(), now.ToString("yyyy-MM-dd"));
+
+ await Task.WhenAll(realtimeTask, timetableTask);
+
+ var realTimeEstimates = realtimeTask.Result.Estimates;
+ var timetable = timetableTask.Result;
+
+ foreach (var estimate in realTimeEstimates)
+ {
+ outputBuffer.AppendLine($"Parsing estimate with line={estimate.Line}, route={estimate.Route} and minutes={estimate.Minutes} - Arrives at {now.AddMinutes(estimate.Minutes):HH:mm}");
+ var fullArrivalTime = now.AddMinutes(estimate.Minutes);
+
+ var possibleCirculations = timetable
+ .Where(c => c.Line.Trim() == estimate.Line.Trim() && c.Route.Trim() == estimate.Route.Trim())
+ .OrderBy(c => c.CallingDateTime())
+ .ToArray();
+
+ outputBuffer.AppendLine($"Found {possibleCirculations.Length} potential circulations");
+
+ ScheduledStop? closestCirculation = null;
+ int closestCirculationTime = int.MaxValue;
+
+ foreach (var circulation in possibleCirculations)
+ {
+ var diffBetweenScheduleAndTrip = (int)Math.Round((fullArrivalTime - circulation.CallingDateTime()).TotalMinutes);
+ var diffBetweenNowAndSchedule = (int)(fullArrivalTime - now).TotalMinutes;
+
+ var tolerance = Math.Max(2, diffBetweenNowAndSchedule * 0.15); // Positive amount of minutes
+ if (diffBetweenScheduleAndTrip <= -tolerance)
+ {
+ break;
+ }
+
+ if (diffBetweenScheduleAndTrip < closestCirculationTime)
+ {
+ closestCirculation = circulation;
+ closestCirculationTime = diffBetweenScheduleAndTrip;
+ }
+
+ }
+
+ if (closestCirculation == null)
+ {
+ outputBuffer.AppendLine("**No circulation matched. List of all of them:**");
+ foreach (var circulation in possibleCirculations)
+ {
+ // Circulation A 03LP000_008003_16 stopping at 05/11/2025 22:06:00 (diff: -03:29:59.2644092)
+ outputBuffer.AppendLine($"Circulation {circulation.TripId} stopping at {circulation.CallingDateTime()} (diff: {fullArrivalTime - circulation.CallingDateTime():HH:mm})");
+ }
+ outputBuffer.AppendLine();
+
+ continue;
+ }
+
+ if (closestCirculationTime > 0)
+ {
+ outputBuffer.Append($"Closest circulation is {closestCirculation.TripId} and arriving {closestCirculationTime} minutes LATE");
+ }
+ else if (closestCirculationTime == 0)
+ {
+ outputBuffer.Append($"Closest circulation is {closestCirculation.TripId} and arriving ON TIME");
+ }
+ else
+ {
+ outputBuffer.Append($"Closest circulation is {closestCirculation.TripId} and arriving {Math.Abs(closestCirculationTime)} minutes EARLY");
+ }
+
+ outputBuffer.AppendLine(
+ $" -- Circulation expected at {closestCirculation.CallingDateTime():HH:mm)}");
+
+ outputBuffer.AppendLine();
+ }
+
+ return Ok(outputBuffer.ToString());
+ }
+
+ private async Task<List<ScheduledStop>> LoadTimetable(string stopId, string dateString)
+ {
+ var file = Path.Combine(_configuration.ScheduleBasePath, dateString, stopId + ".json");
+ if (!SysFile.Exists(file))
+ {
+ throw new FileNotFoundException();
+ }
+ var contents = await SysFile.ReadAllTextAsync(file);
+ return JsonSerializer.Deserialize<List<ScheduledStop>>(contents)!;
+ }
+}