AuroraSseEndpoint.vala 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. using Astralis;
  2. using Invercargill;
  3. using Invercargill.DataStructures;
  4. using Inversion;
  5. using Spry;
  6. /**
  7. * AuroraSseEndpoint - Singleton SSE endpoint for aurora updates
  8. *
  9. * Broadcasts aurora canvas and stats updates to all connected clients
  10. * when aurora actions are triggered (BoostSolarWind, CalmAurora, etc.)
  11. *
  12. * Uses the Astralis SseEndpoint pattern - NOT continuations, since
  13. * continuations are for per-connection streaming, not broadcasting.
  14. */
  15. public class AuroraSseEndpoint : SseEndpoint {
  16. private AuroraState aurora_state;
  17. public AuroraSseEndpoint(AuroraState aurora_state) {
  18. this.aurora_state = aurora_state;
  19. }
  20. /// Retry interval: clients should wait 2 seconds before reconnecting
  21. public override uint retry_interval { get { return 2000; } }
  22. /// Called when a new client connects - send current state
  23. public override async void new_connection(HttpContext http_context, RouteContext route_context, SseStream stream) {
  24. print(@"Aurora SSE client connected (total: $(get_open_streams().length))\n");
  25. // Send current aurora state to new client
  26. try {
  27. yield send_aurora_update(stream);
  28. } catch (Error e) {
  29. print(@"Failed to send aurora update: $(e.message)\n");
  30. }
  31. stream.disconnected.connect(() => {
  32. print(@"Aurora SSE client disconnected\n");
  33. });
  34. }
  35. /// Boost solar wind and broadcast update
  36. public async void boost_solar_wind() {
  37. aurora_state.boost_solar_wind();
  38. yield broadcast_aurora_update();
  39. }
  40. /// Calm aurora and broadcast update
  41. public async void calm() {
  42. aurora_state.calm();
  43. yield broadcast_aurora_update();
  44. }
  45. /// Shift colors and broadcast update
  46. public async void shift_colors() {
  47. aurora_state.shift_colors();
  48. yield broadcast_aurora_update();
  49. }
  50. /// Add wave and broadcast update
  51. public async void add_wave() {
  52. aurora_state.add_wave();
  53. yield broadcast_aurora_update();
  54. }
  55. /// Broadcast aurora update to all connected clients
  56. private async void broadcast_aurora_update() {
  57. try {
  58. // Create canvas HTML
  59. string canvas_html = build_canvas_html();
  60. var canvas_event = new SseEvent.with_type("aurora-canvas", canvas_html);
  61. // Create stats HTML
  62. string stats_html = build_stats_html();
  63. var stats_event = new SseEvent.with_type("aurora-stats", stats_html);
  64. // Broadcast both events
  65. yield broadcast_event(canvas_event);
  66. yield broadcast_event(stats_event);
  67. } catch (Error e) {
  68. printerr(@"Failed to broadcast aurora update: $(e.message)\n");
  69. }
  70. }
  71. /// Send aurora update to a single stream (for new connections)
  72. private async void send_aurora_update(SseStream stream) throws Error {
  73. string canvas_html = build_canvas_html();
  74. yield stream.send_event(new SseEvent.with_type("aurora-canvas", canvas_html));
  75. string stats_html = build_stats_html();
  76. yield stream.send_event(new SseEvent.with_type("aurora-stats", stats_html));
  77. }
  78. /// Build the canvas HTML with all wave components
  79. private string build_canvas_html() {
  80. var builder = new StringBuilder();
  81. int wave_num = 0;
  82. foreach (var wave in aurora_state.get_waves()) {
  83. // Build the wave div with inline styles
  84. builder.append(@"<div class=\"aurora-wave\" style=\"--wave-y: $(wave.y_offset)%%; --wave-amplitude: $(wave.amplitude)px; --wave-freq: $(wave.frequency); --wave-color1: $(wave.color1); --wave-color2: $(wave.color2); --wave-opacity: $(wave.opacity); --wave-delay: $(wave.animation_delay)s; animation-delay: var(--wave-delay);\"></div>");
  85. wave_num++;
  86. }
  87. return builder.str;
  88. }
  89. /// Build the stats HTML
  90. private string build_stats_html() {
  91. return @"<div class=\"aurora-stat\"><div class=\"aurora-stat-value\">%.1f km/s</div><div class=\"aurora-stat-label\">Solar Wind</div></div><div class=\"aurora-stat\"><div class=\"aurora-stat-value\">$(aurora_state.wave_count)</div><div class=\"aurora-stat-label\">Waves</div></div><div class=\"aurora-stat\"><div class=\"aurora-stat-value\">%.0f%%</div><div class=\"aurora-stat-label\">Intensity</div></div><div class=\"aurora-stat\"><div class=\"aurora-stat-value\">$(aurora_state.color_mode)</div><div class=\"aurora-stat-label\">Color Mode</div></div>".printf(
  92. aurora_state.solar_wind_speed,
  93. aurora_state.intensity * 100
  94. );
  95. }
  96. }