From db5f3e6adae28b5cec4fc149bc71b5898105892d Mon Sep 17 00:00:00 2001 From: Robert Schmid Date: Sat, 16 Dec 2023 20:25:19 +0100 Subject: [PATCH 01/10] Add INotifyPropertyChanged, remove manual WebSockets --- src/Point/Connections/ConnectionFactory.cs | 6 +- src/Point/Point.cs | 95 +++++----------------- src/Point/Point.csproj | 4 + src/Point/Services/PointService.cs | 12 +-- src/Point/Startup.cs | 8 +- src/Point/WebsocketDispatcherMiddleware.cs | 38 --------- src/Point/appsettings.json | 4 +- 7 files changed, 37 insertions(+), 130 deletions(-) delete mode 100644 src/Point/WebsocketDispatcherMiddleware.cs diff --git a/src/Point/Connections/ConnectionFactory.cs b/src/Point/Connections/ConnectionFactory.cs index c993c8e..a862b32 100644 --- a/src/Point/Connections/ConnectionFactory.cs +++ b/src/Point/Connections/ConnectionFactory.cs @@ -19,12 +19,12 @@ public IPointToInterlockingConnection CreateConnection(IServiceProvider x) { var connectionProtocol = _configuration.GetSection("ConnectionSettings").Get()?.ConnectionProtocol; switch (connectionProtocol){ case ConnectionProtocol.EulynxBaseline4R1: - return ActivatorUtilities.CreateInstance(x, _configuration); + return ActivatorUtilities.CreateInstance(x, _configuration, CancellationToken.None); case ConnectionProtocol.EulynxBaseline4R2: - return ActivatorUtilities.CreateInstance(x, _configuration); + return ActivatorUtilities.CreateInstance(x, _configuration, CancellationToken.None); case null: _logger.LogWarning($"No connection protocol specified. Using EulynxBaseline4R2."); - return ActivatorUtilities.CreateInstance(x, _configuration); + return ActivatorUtilities.CreateInstance(x, _configuration, CancellationToken.None); default: throw new NotImplementedException($"Unknown connection protocol {connectionProtocol}."); } diff --git a/src/Point/Point.cs b/src/Point/Point.cs index f2e97ca..d1082fc 100644 --- a/src/Point/Point.cs +++ b/src/Point/Point.cs @@ -4,7 +4,10 @@ using Grpc.Core; +using PropertyChanged.SourceGenerator; + using System.Net.WebSockets; +using System.Reactive; using System.Reactive.Linq; using System.Runtime.CompilerServices; using System.Text; @@ -12,28 +15,36 @@ namespace EulynxLive.Point { - - public class Point : BackgroundService + public partial class Point : BackgroundService { - public bool AllPointMachinesCrucial { get; } - public GenericPointState PointState { get; private set; } public IPointToInterlockingConnection Connection { get; } + public bool AllPointMachinesCrucial { get; } + public bool ObserveAbilityToMove { get; } + + + [Notify] + private GenericPointState _pointState; + [Notify] + private SimulatedPointState _simulatedPointState; + [Notify] + private bool _initialized; + private readonly ILogger _logger; private readonly IConnectionProvider _connectionProvider; private readonly Func _simulateTimeout; private readonly PointConfiguration _config; - private readonly List _webSockets; - private bool _initialized; - private SimulatedPointState _simulatedPointState; public Point(ILogger logger, IConfiguration configuration, IPointToInterlockingConnection connection, IConnectionProvider connectionProvider, Func simulateTimeout) { + Connection = connection; + _connectionProvider = connectionProvider; _logger = logger; _simulateTimeout = simulateTimeout; _config = configuration.GetSection("PointSettings").Get() ?? throw new Exception("No configuration provided"); AllPointMachinesCrucial = _config.AllPointMachinesCrucial; + ObserveAbilityToMove = _config.ObserveAbilityToMove; // Validate the configuration. if (_config.InitialLastCommandedPointPosition == null && _config.InitialPointPosition != GenericPointPosition.NoEndPosition) @@ -51,15 +62,13 @@ public Point(ILogger logger, IConfiguration configuration, IPointToInterl throw new InvalidOperationException("If the ability to move is observed, the initial ability to move must be set."); } - _webSockets = new List(); - var initialPointPosition = ( _config.InitialLastCommandedPointPosition == GenericPointPosition.Left && _config.InitialPointPosition == GenericPointPosition.Right || _config.InitialLastCommandedPointPosition == GenericPointPosition.Right && _config.InitialPointPosition == GenericPointPosition.Left) ? GenericPointPosition.UnintendedPosition : _config.InitialPointPosition; - PointState = new GenericPointState + _pointState = new GenericPointState ( LastCommandedPointPosition: _config.InitialLastCommandedPointPosition, DegradedPointPosition: RespectAllPointMachinesCrucial(_config.InitialDegradedPointPosition), @@ -75,33 +84,6 @@ public Point(ILogger logger, IConfiguration configuration, IPointToInterl SimulateTimeoutLeft: false, SimulateTimeoutRight: false ); - Connection = connection; - _connectionProvider = connectionProvider; - } - - public async Task HandleWebSocket(WebSocket webSocket) - { - _webSockets.Add(webSocket); - try - { - await UpdateWebClient(webSocket); - - while (true) - { - byte[] messageBuffer = new byte[1024]; - var buffer = new ArraySegment(messageBuffer); - var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None); - if (result.CloseStatus.HasValue) - { - break; - } - } - } - catch (WebSocketException) - { - // Do nothing, the WebSocket has died. - } - _webSockets.Remove(webSocket); } public async Task SendSciMessage(SciMessage message) @@ -167,7 +149,7 @@ public async Task SetAbilityToMove(AbilityToMoveMessage abilityToMoveMessage) /// /// Puts the point into an unintended position immediately. /// - public async Task PutIntoUnintendedPosition(DegradedPositionMessage simulatedPositionMessage) + public void PutIntoUnintendedPosition(DegradedPositionMessage simulatedPositionMessage) { if (AllPointMachinesCrucial && simulatedPositionMessage.DegradedPosition) { @@ -183,7 +165,6 @@ public async Task PutIntoUnintendedPosition(DegradedPositionMessage simulatedPos var notDegradedPosition = AllPointMachinesCrucial ? GenericDegradedPointPosition.NotApplicable : GenericDegradedPointPosition.NotDegraded; var degradedPosition = PointState.PointPosition == GenericPointPosition.Left ? GenericDegradedPointPosition.DegradedLeft : GenericDegradedPointPosition.DegradedRight; SetPointState(GenericPointPosition.UnintendedPosition, simulatedPositionMessage.DegradedPosition ? degradedPosition : notDegradedPosition); - await UpdateConnectedWebClients(); } /// @@ -268,7 +249,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { throw new Exception("Unable to initialize connection"); } - await UpdateConnectedWebClients(); _initialized = true; await AllMovePointCommands(stoppingToken) @@ -333,7 +313,6 @@ private async Task HandleCommandedPointPosition(GenericPointPosition commandedPo { SetPointState(GenericPointPosition.NoEndPosition, RespectAllPointMachinesCrucial(PointState.DegradedPointPosition)); await Connection.SendPointPosition(PointState); - await UpdateConnectedWebClients(); } try @@ -356,8 +335,6 @@ private async Task HandleCommandedPointPosition(GenericPointPosition commandedPo var (newPointPosition, newDegradedPointPosition) = HandlePreventedPointPosition(commandedPointPosition, simulatedState); SetPointState(newPointPosition, RespectAllPointMachinesCrucial(newDegradedPointPosition)); - await UpdateConnectedWebClients(); - _logger.LogInformation("End position reached."); await Connection.SendPointPosition(PointState); } @@ -431,38 +408,6 @@ public async Task Reset() { _logger.LogInformation("Resetting point."); _initialized = false; - await UpdateConnectedWebClients(); - } - - private async Task UpdateConnectedWebClients() - { - try - { - var tasks = _webSockets.Select(UpdateWebClient); - await Task.WhenAll(tasks); - } - catch (Exception) - { - // Some client likely has an issue, ignore - } - } - - private async Task UpdateWebClient(WebSocket webSocket) - { - var positions = new Dictionary { - {GenericPointPosition.Right, "right"}, - {GenericPointPosition.Left, "left"}, - {GenericPointPosition.NoEndPosition, "noEndPosition"}, - {GenericPointPosition.UnintendedPosition, "unintendedPosition"}, - }; - var options = new JsonSerializerOptions { WriteIndented = true }; - var serializedState = JsonSerializer.Serialize(new - { - initialized = _initialized, - position = positions[PointState.PointPosition] - }, options); - var serializedStateBytes = Encoding.UTF8.GetBytes(serializedState); - await webSocket.SendAsync(serializedStateBytes, WebSocketMessageType.Text, true, CancellationToken.None); } } } diff --git a/src/Point/Point.csproj b/src/Point/Point.csproj index 2ef837f..61b4bf4 100644 --- a/src/Point/Point.csproj +++ b/src/Point/Point.csproj @@ -23,6 +23,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/Point/Services/PointService.cs b/src/Point/Services/PointService.cs index 1c8d31d..5cf5bf0 100644 --- a/src/Point/Services/PointService.cs +++ b/src/Point/Services/PointService.cs @@ -63,22 +63,22 @@ override public Task SchedulePreventRightEndPosition(PreventedPositionMes return Task.FromResult(new Empty()); } - public override async Task PutIntoTrailedPosition(DegradedPositionMessage request, ServerCallContext context) + public override Task PutIntoTrailedPosition(DegradedPositionMessage request, ServerCallContext context) { if (_point.Connection.Configuration.ConnectionProtocol != ConnectionProtocol.EulynxBaseline4R1) throw new RpcException(new Status(StatusCode.InvalidArgument, "Only supported for EulynxBaseline4R1")); - await _point.PutIntoUnintendedPosition(request); - return new Empty(); + _point.PutIntoUnintendedPosition(request); + return Task.FromResult(new Empty()); } - public override async Task PutIntoUnintendedPosition(DegradedPositionMessage request, ServerCallContext context) + public override Task PutIntoUnintendedPosition(DegradedPositionMessage request, ServerCallContext context) { if (_point.Connection.Configuration.ConnectionProtocol != ConnectionProtocol.EulynxBaseline4R2) throw new RpcException(new Status(StatusCode.InvalidArgument, "Only supported for EulynxBaseline4R2")); - await _point.PutIntoUnintendedPosition(request); - return new Empty(); + _point.PutIntoUnintendedPosition(request); + return Task.FromResult(new Empty()); } public override Task GetDegradedPointPosition(Empty request, ServerCallContext context) diff --git a/src/Point/Startup.cs b/src/Point/Startup.cs index 4d773d3..073cc9a 100644 --- a/src/Point/Startup.cs +++ b/src/Point/Startup.cs @@ -20,6 +20,8 @@ public void ConfigureServices(IServiceCollection services) services.AddGrpc(); services.AddGrpcReflection(); + services.AddTransient(); + services.AddTransient(); services.AddSingleton(x => x.GetRequiredService().CreateConnection(x)); // In production, the React files will be served from this directory @@ -61,12 +63,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseSpaStaticFiles(); app.UseRouting(); - - // For live updating the Signal UI. - app.UseWebSockets(); - // Sends the websockets to the Simulator. - app.UseMiddleware(); - app.UseGrpcWeb(); app.UseEndpoints(endpoints => diff --git a/src/Point/WebsocketDispatcherMiddleware.cs b/src/Point/WebsocketDispatcherMiddleware.cs deleted file mode 100644 index a54807a..0000000 --- a/src/Point/WebsocketDispatcherMiddleware.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.AspNetCore.Http; -using System.Net.WebSockets; -using System.Threading.Tasks; - -namespace EulynxLive.Point -{ - public class WebsocketDispatcherMiddleware - { - private readonly RequestDelegate _next; - - public WebsocketDispatcherMiddleware(RequestDelegate next) - { - _next = next; - } - - public async Task InvokeAsync(HttpContext context, Point simulator) - { - if (context.Request.Path == "/ws") - { - if (context.WebSockets.IsWebSocketRequest) - { - using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync()) - { - await simulator.HandleWebSocket(webSocket); - } - } - else - { - context.Response.StatusCode = 400; - } - } - else - { - await _next(context); - } - } - } -} diff --git a/src/Point/appsettings.json b/src/Point/appsettings.json index b944f06..eeb45b9 100644 --- a/src/Point/appsettings.json +++ b/src/Point/appsettings.json @@ -1,7 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Trace", + "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } @@ -11,7 +11,7 @@ "SimulatedTransitioningTimeSeconds": 0, "ObserveAbilityToMove": false, "AllPointMachinesCrucial": true, - "InitialLastCommandedPointPosition": "null", + "InitialLastCommandedPointPosition": "", "InitialPointPosition": "NoEndPosition", "InitialDegradedPointPosition": "NotApplicable", "InitialAbilityToMove": "AbleToMove" From 896f7123be7817b909733de0189f1f97241fc4c8 Mon Sep 17 00:00:00 2001 From: Robert Schmid Date: Sat, 16 Dec 2023 21:11:51 +0100 Subject: [PATCH 02/10] Show state and configuration in UI --- .../CommandingAndReversingTests.cs | 4 +- .../Point/Baseline4R2/ReportStatusTests.cs | 4 +- .../Point/PointTest.cs | 6 +- src/Point/Hubs/StatusHub.cs | 7 + src/Point/Point.cs | 27 +- src/Point/Services/PointService.cs | 7 +- src/Point/Startup.cs | 7 + src/Point/appsettings.Development.json | 4 +- src/Point/appsettings.json | 3 +- src/Point/rasta-point-web/package-lock.json | 677 ++++++++++++++++-- src/Point/rasta-point-web/package.json | 9 +- src/Point/rasta-point-web/public/index.html | 4 +- .../rasta-point-web/public/manifest.json | 2 +- src/Point/rasta-point-web/src/App.tsx | 50 +- src/Point/rasta-point-web/src/Point.tsx | 154 ++-- .../rasta-point-web/src/SimulatorState.ts | 5 - 16 files changed, 761 insertions(+), 209 deletions(-) create mode 100644 src/Point/Hubs/StatusHub.cs delete mode 100644 src/Point/rasta-point-web/src/SimulatorState.ts diff --git a/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs b/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs index 3677c8f..56adaa6 100644 --- a/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs +++ b/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs @@ -3,7 +3,9 @@ using EulynxLive.FieldElementSubsystems.Interfaces; using EulynxLive.Messages.Baseline4R2; using EulynxLive.Point; +using EulynxLive.Point.Hubs; +using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -70,7 +72,7 @@ private static (EulynxLive.Point.Point point, Task pointTask, List recei var connection = new PointToInterlockingConnection(Mock.Of>(), configuration, CancellationToken.None); - var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask); + var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask, Mock.Of>()); if (simulateTimeouts) { point.EnableTimeoutLeft(); point.EnableTimeoutRight(); diff --git a/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs b/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs index 3b9e4d7..b62b916 100644 --- a/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs +++ b/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs @@ -3,7 +3,9 @@ using EulynxLive.FieldElementSubsystems.Interfaces; using EulynxLive.Messages.Baseline4R2; using EulynxLive.Point; +using EulynxLive.Point.Hubs; +using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -57,7 +59,7 @@ private static (EulynxLive.Point.Point point, Task, List receivedBytes) var connection = new PointToInterlockingConnection(Mock.Of>(), configuration, CancellationToken.None); connection.Connect(mockConnection.Object); - var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask); + var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask, Mock.Of>()); async Task SimulatePoint() { diff --git a/src/FieldElementSubsystems.Test/Point/PointTest.cs b/src/FieldElementSubsystems.Test/Point/PointTest.cs index f3f6c2e..2b89f2f 100644 --- a/src/FieldElementSubsystems.Test/Point/PointTest.cs +++ b/src/FieldElementSubsystems.Test/Point/PointTest.cs @@ -7,6 +7,8 @@ using EulynxLive.Point.Proto; using Google.Protobuf; using EulynxLive.Point; +using EulynxLive.Point.Hubs; +using Microsoft.AspNetCore.SignalR; namespace FieldElementSubsystems.Test; @@ -36,7 +38,7 @@ private static (EulynxLive.Point.Point, Func simulatePoint, Mock()!; var mockConnection = CreateDefaultMockConnection(config); - var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, mockConnection.Object, Mock.Of(), () => Task.CompletedTask); + var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, mockConnection.Object, Mock.Of(), () => Task.CompletedTask, Mock.Of>()); async Task SimulatePoint() { @@ -334,7 +336,7 @@ public async Task Test_PutIntoUnintendedPosition(bool simulatedDegradedPosition, { DegradedPosition = simulatedDegradedPosition }; - await point.PutIntoUnintendedPosition(message); + point.PutIntoUnintendedPosition(message); cancel.Cancel(); return await new TaskCompletionSource().Task; diff --git a/src/Point/Hubs/StatusHub.cs b/src/Point/Hubs/StatusHub.cs new file mode 100644 index 0000000..3ce5c7c --- /dev/null +++ b/src/Point/Hubs/StatusHub.cs @@ -0,0 +1,7 @@ +using Microsoft.AspNetCore.SignalR; + +namespace EulynxLive.Point.Hubs; + +public class StatusHub : Hub +{ +} diff --git a/src/Point/Point.cs b/src/Point/Point.cs index d1082fc..b5c03d6 100644 --- a/src/Point/Point.cs +++ b/src/Point/Point.cs @@ -1,17 +1,15 @@ using EulynxLive.FieldElementSubsystems.Configuration; using EulynxLive.FieldElementSubsystems.Interfaces; +using EulynxLive.Point.Hubs; using EulynxLive.Point.Proto; - using Grpc.Core; - +using Microsoft.AspNetCore.SignalR; using PropertyChanged.SourceGenerator; - +using System.ComponentModel; using System.Net.WebSockets; using System.Reactive; using System.Reactive.Linq; using System.Runtime.CompilerServices; -using System.Text; -using System.Text.Json; namespace EulynxLive.Point { @@ -35,7 +33,7 @@ public partial class Point : BackgroundService private readonly Func _simulateTimeout; private readonly PointConfiguration _config; - public Point(ILogger logger, IConfiguration configuration, IPointToInterlockingConnection connection, IConnectionProvider connectionProvider, Func simulateTimeout) + public Point(ILogger logger, IConfiguration configuration, IPointToInterlockingConnection connection, IConnectionProvider connectionProvider, Func simulateTimeout, IHubContext statusHub) { Connection = connection; _connectionProvider = connectionProvider; @@ -84,6 +82,11 @@ public Point(ILogger logger, IConfiguration configuration, IPointToInterl SimulateTimeoutLeft: false, SimulateTimeoutRight: false ); + + Observable.FromEventPattern(h => PropertyChanged += h, h => PropertyChanged -= h) + .Select(x => Unit.Default) + .Merge(Observable.Interval(TimeSpan.FromSeconds(1)).Select(x => Unit.Default)) + .Subscribe(x => statusHub.Clients.All.SendAsync("ReceiveStatus", _initialized, PointState, SimulatedPointState, new { AllPointMachinesCrucial, ObserveAbilityToMove, _config.ConnectionProtocol })); } public async Task SendSciMessage(SciMessage message) @@ -241,7 +244,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) _logger.LogTrace("Connecting..."); var conn = _connectionProvider.Connect(Connection.Configuration, stoppingToken); Connection.Connect(conn); - await Reset(); + Reset(); try { var success = await Connection.InitializeConnection(PointState, _config.ObserveAbilityToMove, stoppingToken); @@ -261,18 +264,18 @@ await AllMovePointCommands(stoppingToken) { // TODO: Since this is gRPC-specific, catch and re-throw this in the GrpcConnectionProvider _logger.LogWarning("Could not communicate with remote endpoint."); - await Reset(); + Reset(); await Task.Delay(1000, stoppingToken); } catch (OperationCanceledException) { - await Reset(); + Reset(); return; } catch (Exception ex) { _logger.LogWarning(ex, "Exception during simulation."); - await Reset(); + Reset(); await Task.Delay(1000, stoppingToken); } } @@ -292,6 +295,8 @@ private async Task HandleCommandedPointPosition(GenericPointPosition commandedPo // Make a copy of the current state, so that it is not modified while the point is moving. var simulatedState = _simulatedPointState; + PointState = PointState with { LastCommandedPointPosition = commandedPointPosition }; + if (_config.ObserveAbilityToMove && PointState.AbilityToMove == GenericAbilityToMove.UnableToMove) { _logger.LogInformation("Point is unable to move."); @@ -404,7 +409,7 @@ private bool ShouldSimulateTimeout(GenericPointPosition commandedPointPosition, throw new ArgumentException("Invalid commanded position", nameof(commandedPosition)); } - public async Task Reset() + public void Reset() { _logger.LogInformation("Resetting point."); _initialized = false; diff --git a/src/Point/Services/PointService.cs b/src/Point/Services/PointService.cs index 5cf5bf0..3dc6ac2 100644 --- a/src/Point/Services/PointService.cs +++ b/src/Point/Services/PointService.cs @@ -16,10 +16,11 @@ public PointService(Point point) _point = point; } - public override async Task Reset(Empty request, ServerCallContext context) + public override Task Reset(Empty request, ServerCallContext context) { - await _point.Reset(); - return new Empty(); + // TODO: We have to disconnect here! + _point.Reset(); + return Task.FromResult(new Empty()); } public override Task ScheduleTimeoutRight(Empty request, ServerCallContext context) diff --git a/src/Point/Startup.cs b/src/Point/Startup.cs index 073cc9a..6ac57d4 100644 --- a/src/Point/Startup.cs +++ b/src/Point/Startup.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer; using EulynxLive.Point.Services; using EulynxLive.Point.Connections; +using EulynxLive.Point.Hubs; +using System.Text.Json.Serialization; namespace EulynxLive.Point { @@ -17,6 +19,8 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); + services.AddSignalR() + .AddJsonProtocol(options => options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumConverter())); services.AddGrpc(); services.AddGrpcReflection(); @@ -65,10 +69,13 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseRouting(); app.UseGrpcWeb(); + app.UseEndpoints(endpoints => { endpoints.MapGrpcService().EnableGrpcWeb(); endpoints.MapGrpcReflectionService(); + + endpoints.MapHub("/status"); }); app.UseSpa(spa => diff --git a/src/Point/appsettings.Development.json b/src/Point/appsettings.Development.json index cbf447c..dfa1321 100644 --- a/src/Point/appsettings.Development.json +++ b/src/Point/appsettings.Development.json @@ -1,7 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Trace", + "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } @@ -12,6 +12,6 @@ "LocalRastaId": 100, "RemoteId": "INTERLOCKING", "RemoteEndpoint": "http://localhost:50051", - "AllPointMachinesCrucial": true + "ObserveAbilityToMove": true } } diff --git a/src/Point/appsettings.json b/src/Point/appsettings.json index eeb45b9..c10b2ca 100644 --- a/src/Point/appsettings.json +++ b/src/Point/appsettings.json @@ -14,7 +14,8 @@ "InitialLastCommandedPointPosition": "", "InitialPointPosition": "NoEndPosition", "InitialDegradedPointPosition": "NotApplicable", - "InitialAbilityToMove": "AbleToMove" + "InitialAbilityToMove": "AbleToMove", + "ConnectionProtocol": "EulynxBaseline4R1" }, "Kestrel": { "EndPoints": { diff --git a/src/Point/rasta-point-web/package-lock.json b/src/Point/rasta-point-web/package-lock.json index 228963f..22f76a0 100644 --- a/src/Point/rasta-point-web/package-lock.json +++ b/src/Point/rasta-point-web/package-lock.json @@ -8,14 +8,15 @@ "name": "rasta-point-web", "version": "0.1.0", "dependencies": { - "@types/node": "^16.11.64", - "@types/react": "^17.0.50", - "@types/react-dom": "^17.0.17", + "@types/node": "^16.18.68", + "@types/react": "^17.0.73", + "@types/react-dom": "^17.0.25", "google-protobuf": "^3.18.0-rc.1", "grpc-web": "^1.2.1", "react": "^17.0.2", "react-dom": "^17.0.2", - "typescript": "^4.8.4" + "react-signalr": "^0.2.18", + "typescript": "^4.9.5" }, "devDependencies": { "@types/google-protobuf": "^3.15.12", @@ -3211,6 +3212,18 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@microsoft/signalr": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-7.0.14.tgz", + "integrity": "sha512-dnS7gSJF5LxByZwJaj82+F1K755ya7ttPT+JnSeCBef3sL8p8FBkHePXphK8NSuOquIb7vsphXWa28A+L2SPpw==", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^2.0.2", + "fetch-cookie": "^2.0.3", + "node-fetch": "^2.6.7", + "ws": "^7.4.5" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -3436,6 +3449,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -3766,6 +3784,19 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "8.4.7", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.7.tgz", @@ -3888,9 +3919,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.18.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.0.tgz", - "integrity": "sha512-LqYqYzYvnbCaQfLAwRt0zboqnsViwhZm+vjaMSqcfN36vulAg7Pt0T83q4WZO2YOBw3XdyHi8cQ88H22zmULOA==" + "version": "16.18.68", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.68.tgz", + "integrity": "sha512-sG3hPIQwJLoewrN7cr0dwEy+yF5nD4D/4FxtQpFciRD/xwUzgD+G05uxZHv5mhfXo4F9Jkp13jjn0CC2q325sg==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -3928,9 +3959,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "17.0.50", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.50.tgz", - "integrity": "sha512-ZCBHzpDb5skMnc1zFXAXnL3l1FAdi+xZvwxK+PkglMmBrwjpp9nKaWuEvrGnSifCJmBFGxZOOFuwC6KH/s0NuA==", + "version": "17.0.73", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.73.tgz", + "integrity": "sha512-6AcjgPIVsXTIsFDgsGW0iQhvg0xb2vt2qAWgXyncnVNRaW9ZXTTwAh7RQoh7PzK1AhjPoGDvUBkdAREih9n5oQ==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3938,9 +3969,9 @@ } }, "node_modules/@types/react-dom": { - "version": "17.0.17", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz", - "integrity": "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==", + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.25.tgz", + "integrity": "sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==", "dependencies": { "@types/react": "^17" } @@ -4426,11 +4457,21 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -5168,6 +5209,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -5869,6 +5918,18 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", @@ -6340,7 +6401,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -6770,6 +6830,94 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", + "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/enhanced-resolve": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", @@ -7720,6 +7868,14 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -7735,6 +7891,14 @@ "node": ">=0.8.x" } }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -7941,6 +8105,15 @@ "bser": "2.1.1" } }, + "node_modules/fetch-cookie": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.1.0.tgz", + "integrity": "sha512-39+cZRbWfbibmj22R2Jy6dmTbAWC+oqun1f1FzQaNurkPDUP4C38jpeZbiXCR88RKRVDp8UcDrbFXkNhN+NjYg==", + "dependencies": { + "set-cookie-parser": "^2.4.8", + "tough-cookie": "^4.0.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -8721,6 +8894,11 @@ "he": "bin/he" } }, + "node_modules/hermes-channel": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/hermes-channel/-/hermes-channel-2.1.2.tgz", + "integrity": "sha512-PVH+X8/S9J6XItQgIRLlsrwXUmb/v13wxvcZgqohnnlUZZOEWbWZ07bLsuQ9dEMnNpT9APvBuVV50W5QmDt4pA==" + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -11647,6 +11825,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, "node_modules/js-sdsl": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", @@ -12112,7 +12295,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -12121,7 +12303,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -12252,8 +12433,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -12290,7 +12470,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -12311,6 +12490,44 @@ "tslib": "^2.0.3" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -14367,14 +14584,12 @@ "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, "engines": { "node": ">=6" } @@ -14407,8 +14622,7 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -14767,6 +14981,22 @@ } } }, + "node_modules/react-signalr": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/react-signalr/-/react-signalr-0.2.18.tgz", + "integrity": "sha512-iu3mMRUqc2ruLkab7e+yEj6dgYYwRhENNYfz2/AXc3wBVSVU8nlNw5IXOpGdrIII4XtRwVsMtResL8pU3ph3EA==", + "dependencies": { + "@microsoft/signalr": "^7.0.10", + "hermes-channel": "^2.1.2", + "js-cookie": "^2.2.1", + "socket.io": "^4.2.0", + "socket.io-client": "^4.2.0", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "react": ">=16.13" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -15061,8 +15291,7 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { "version": "1.22.1", @@ -15597,6 +15826,11 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -15668,6 +15902,77 @@ "node": ">=8" } }, + "node_modules/socket.io": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", + "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-client": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", + "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -16406,7 +16711,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", - "dev": true, "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -16421,7 +16725,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, "engines": { "node": ">= 4.0.0" } @@ -16560,9 +16863,9 @@ } }, "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16711,7 +17014,6 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -16752,7 +17054,6 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, "bin": { "uuid": "dist/bin/uuid" } @@ -16775,7 +17076,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -17672,7 +17972,6 @@ "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, "engines": { "node": ">=8.3.0" }, @@ -17701,6 +18000,14 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -19993,6 +20300,18 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "@microsoft/signalr": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-7.0.14.tgz", + "integrity": "sha512-dnS7gSJF5LxByZwJaj82+F1K755ya7ttPT+JnSeCBef3sL8p8FBkHePXphK8NSuOquIb7vsphXWa28A+L2SPpw==", + "requires": { + "abort-controller": "^3.0.0", + "eventsource": "^2.0.2", + "fetch-cookie": "^2.0.3", + "node-fetch": "^2.6.7", + "ws": "^7.4.5" + } + }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -20146,6 +20465,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -20372,6 +20696,19 @@ "@types/node": "*" } }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "requires": { + "@types/node": "*" + } + }, "@types/eslint": { "version": "8.4.7", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.7.tgz", @@ -20494,9 +20831,9 @@ "dev": true }, "@types/node": { - "version": "16.18.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.0.tgz", - "integrity": "sha512-LqYqYzYvnbCaQfLAwRt0zboqnsViwhZm+vjaMSqcfN36vulAg7Pt0T83q4WZO2YOBw3XdyHi8cQ88H22zmULOA==" + "version": "16.18.68", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.68.tgz", + "integrity": "sha512-sG3hPIQwJLoewrN7cr0dwEy+yF5nD4D/4FxtQpFciRD/xwUzgD+G05uxZHv5mhfXo4F9Jkp13jjn0CC2q325sg==" }, "@types/parse-json": { "version": "4.0.0", @@ -20534,9 +20871,9 @@ "dev": true }, "@types/react": { - "version": "17.0.50", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.50.tgz", - "integrity": "sha512-ZCBHzpDb5skMnc1zFXAXnL3l1FAdi+xZvwxK+PkglMmBrwjpp9nKaWuEvrGnSifCJmBFGxZOOFuwC6KH/s0NuA==", + "version": "17.0.73", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.73.tgz", + "integrity": "sha512-6AcjgPIVsXTIsFDgsGW0iQhvg0xb2vt2qAWgXyncnVNRaW9ZXTTwAh7RQoh7PzK1AhjPoGDvUBkdAREih9n5oQ==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -20544,9 +20881,9 @@ } }, "@types/react-dom": { - "version": "17.0.17", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz", - "integrity": "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==", + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.25.tgz", + "integrity": "sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==", "requires": { "@types/react": "^17" } @@ -20929,11 +21266,18 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, "requires": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -21484,6 +21828,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -22025,6 +22374,15 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", @@ -22355,7 +22713,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -22684,6 +23041,61 @@ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true }, + "engine.io": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", + "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "dependencies": { + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + } + } + }, + "engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + }, + "dependencies": { + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + } + } + }, + "engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==" + }, "enhanced-resolve": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", @@ -23385,6 +23797,11 @@ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -23397,6 +23814,11 @@ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, + "eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==" + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -23569,6 +23991,15 @@ "bser": "2.1.1" } }, + "fetch-cookie": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.1.0.tgz", + "integrity": "sha512-39+cZRbWfbibmj22R2Jy6dmTbAWC+oqun1f1FzQaNurkPDUP4C38jpeZbiXCR88RKRVDp8UcDrbFXkNhN+NjYg==", + "requires": { + "set-cookie-parser": "^2.4.8", + "tough-cookie": "^4.0.0" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -24129,6 +24560,11 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "hermes-channel": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/hermes-channel/-/hermes-channel-2.1.2.tgz", + "integrity": "sha512-PVH+X8/S9J6XItQgIRLlsrwXUmb/v13wxvcZgqohnnlUZZOEWbWZ07bLsuQ9dEMnNpT9APvBuVV50W5QmDt4pA==" + }, "hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -26302,6 +26738,11 @@ } } }, + "js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, "js-sdsl": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", @@ -26668,14 +27109,12 @@ "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "requires": { "mime-db": "1.52.0" } @@ -26769,8 +27208,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multicast-dns": { "version": "7.2.5", @@ -26797,8 +27235,7 @@ "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, "neo-async": { "version": "2.6.2", @@ -26816,6 +27253,35 @@ "tslib": "^2.0.3" } }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -28165,14 +28631,12 @@ "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "q": { "version": "1.5.1", @@ -28192,8 +28656,7 @@ "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, "queue-microtask": { "version": "1.2.3", @@ -28462,6 +28925,19 @@ "workbox-webpack-plugin": "^6.4.1" } }, + "react-signalr": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/react-signalr/-/react-signalr-0.2.18.tgz", + "integrity": "sha512-iu3mMRUqc2ruLkab7e+yEj6dgYYwRhENNYfz2/AXc3wBVSVU8nlNw5IXOpGdrIII4XtRwVsMtResL8pU3ph3EA==", + "requires": { + "@microsoft/signalr": "^7.0.10", + "hermes-channel": "^2.1.2", + "js-cookie": "^2.2.1", + "socket.io": "^4.2.0", + "socket.io-client": "^4.2.0", + "uuid": "^8.3.2" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -28693,8 +29169,7 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "resolve": { "version": "1.22.1", @@ -29086,6 +29561,11 @@ "send": "0.18.0" } }, + "set-cookie-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -29142,6 +29622,56 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "socket.io": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", + "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + } + }, + "socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "requires": { + "ws": "~8.11.0" + }, + "dependencies": { + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + } + } + }, + "socket.io-client": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", + "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + } + }, "sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -29684,7 +30214,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", - "dev": true, "requires": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -29695,8 +30224,7 @@ "universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" } } }, @@ -29808,9 +30336,9 @@ } }, "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==" + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, "unbox-primitive": { "version": "1.0.2", @@ -29908,7 +30436,6 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -29945,8 +30472,7 @@ "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-to-istanbul": { "version": "8.1.1", @@ -29962,8 +30488,7 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "w3c-hr-time": { "version": "1.0.2", @@ -30690,7 +31215,6 @@ "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, "requires": {} }, "xml-name-validator": { @@ -30705,6 +31229,11 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/src/Point/rasta-point-web/package.json b/src/Point/rasta-point-web/package.json index c1cb86b..9f31b41 100644 --- a/src/Point/rasta-point-web/package.json +++ b/src/Point/rasta-point-web/package.json @@ -4,14 +4,15 @@ "homepage": "./", "private": true, "dependencies": { - "@types/node": "^16.11.64", - "@types/react": "^17.0.50", - "@types/react-dom": "^17.0.17", + "@types/node": "^16.18.68", + "@types/react": "^17.0.73", + "@types/react-dom": "^17.0.25", "google-protobuf": "^3.18.0-rc.1", "grpc-web": "^1.2.1", "react": "^17.0.2", "react-dom": "^17.0.2", - "typescript": "^4.8.4" + "react-signalr": "^0.2.18", + "typescript": "^4.9.5" }, "devDependencies": { "@types/google-protobuf": "^3.15.12", diff --git a/src/Point/rasta-point-web/public/index.html b/src/Point/rasta-point-web/public/index.html index d9309f0..094ed1b 100644 --- a/src/Point/rasta-point-web/public/index.html +++ b/src/Point/rasta-point-web/public/index.html @@ -6,7 +6,7 @@ - RaSTA Point + Point diff --git a/src/Point/rasta-point-web/public/manifest.json b/src/Point/rasta-point-web/public/manifest.json index ca1854d..d7716a1 100644 --- a/src/Point/rasta-point-web/public/manifest.json +++ b/src/Point/rasta-point-web/public/manifest.json @@ -1,6 +1,6 @@ { "short_name": "Point", - "name": "RaSTA Point", + "name": "Point", "start_url": ".", "display": "standalone", "theme_color": "#000000", diff --git a/src/Point/rasta-point-web/src/App.tsx b/src/Point/rasta-point-web/src/App.tsx index 004cc0d..37cf64a 100644 --- a/src/Point/rasta-point-web/src/App.tsx +++ b/src/Point/rasta-point-web/src/App.tsx @@ -1,12 +1,52 @@ +import { useState } from 'react'; import './App.css'; import Point from './Point'; +import { createSignalRContext } from 'react-signalr'; + +const { Provider, useSignalREffect } = createSignalRContext(); + +export type PointState = { + 'lastCommandedPointPosition': 'NoEndPosition', + 'pointPosition': 'NoEndPosition', + 'degradedPointPosition': 'NotApplicable', + 'abilityToMove': 'AbleToMove' +}; + +export type SimulatedPointState = { + "preventedPositionLeft": "DoNotPrevent", + "preventedPositionRight": "DoNotPrevent", + "degradedPositionLeft": boolean, + "degradedPositionRight": boolean, + "simulateTimeoutLeft": boolean, + "simulateTimeoutRight": boolean +}; + +export type SimulatorConfiguration = { + observeAbilityToMove: boolean, + allPointMachinesCrucial: boolean, + connectionProtocol: 'EulynxBaseline4R1' | 'EulynxBaseline4R2', +}; function App() { - return ( -
- -
- ); + const [initialized, setInitialized] = useState(false); + const [pointState, setPointState] = useState(null); + const [simulatedPointState, setSimulatedPointState] = useState(null); + const [simulatorConfiguration, setSimulatorConfiguration] = useState(null); + + useSignalREffect("ReceiveStatus", (initialized: boolean, pointState: PointState, simulatedPointState: SimulatedPointState, simulatorConfiguration: SimulatorConfiguration) => { + setInitialized(initialized); + setPointState(pointState); + setSimulatedPointState(simulatedPointState); + setSimulatorConfiguration(simulatorConfiguration); + }, []); + + return ( +
+ + + +
+ ); } export default App; diff --git a/src/Point/rasta-point-web/src/Point.tsx b/src/Point/rasta-point-web/src/Point.tsx index 6a75383..933ebb0 100644 --- a/src/Point/rasta-point-web/src/Point.tsx +++ b/src/Point/rasta-point-web/src/Point.tsx @@ -1,19 +1,15 @@ import React, { Component } from 'react'; import './Point.css'; import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; -import { SimulatorState } from './SimulatorState'; import { PointClient } from './proto/PointServiceClientPb'; import { PreventedPosition, AbilityToMoveMessage, AbilityToMove, PreventedPositionMessage, DegradedPositionMessage } from './proto/point_pb'; +import { PointState, SimulatedPointState, SimulatorConfiguration } from './App'; -interface PointState { - webSocket: WebSocket | null, - connected: boolean; - position: string; -} +type PointProps = { initialized: boolean, pointState: PointState | null, simulatedPointState: SimulatedPointState | null, simulatorConfiguration: SimulatorConfiguration | null }; -class Point extends Component<{}, PointState> { - constructor(p: {}) { +class Point extends Component { + constructor(p: PointProps) { super(p); this.state = { webSocket: null, @@ -22,124 +18,88 @@ class Point extends Component<{}, PointState> { }; } - componentDidMount() { - var loc = window.location, new_uri; - if (loc.protocol === "https:") { - new_uri = "wss:"; - } else { - new_uri = "ws:"; - } - new_uri += "//" + loc.host; - if (loc.pathname.endsWith('/')) { - new_uri += loc.pathname + "ws"; - } else { - new_uri += loc.pathname + "/ws"; - } - const ws = new WebSocket(new_uri); - this.setState({ - webSocket: ws, - }) - ws.onopen = (event) => { - console.log("connected"); - } - ws.onerror = (error) => { - console.error(error); - } - ws.onmessage = (event) => { - this.receiveMessage(event); - }; - } - - componentWillUnmount() { - if (this.state.webSocket) { - this.state.webSocket.close(); - this.setState({ - webSocket: null, - }) - } - } - - private receiveMessage(event: MessageEvent): any { - if (typeof event.data !== 'string') { - return; - } - const state = JSON.parse(event.data) as SimulatorState; - this.showPointState(state); - } - - private showPointState(state: SimulatorState) { - this.setState({ - connected: state.initialized ?? false, - position: state.position, - }); - } - render() { const url = window.location.protocol + '//' + window.location.host + (window.location.pathname.endsWith('/') ? window.location.pathname.slice(0, -1) : window.location.pathname); const client = new PointClient(url); return (
-

EULYNX Point Simulator

-

Connection to Interlocking

-

{this.state.connected ? 'connected' : 'disconnected'}

-

Position

-

{this.state.position}

+

Point Simulator

+

Connection to Electronic Interlocking

+

{this.props.initialized ? 'connected' : 'disconnected'}

+

Simulator Configuration

+

Observe ability to move: {this.props.simulatorConfiguration?.observeAbilityToMove ? 'yes' : 'no'}

+

All point machines crucial: {this.props.simulatorConfiguration?.allPointMachinesCrucial ? 'yes' : 'no'}

+

Connection protocol: {this.props.simulatorConfiguration?.connectionProtocol}

+ +

Point State

+

Last Commanded Point Position: {this.props.pointState?.lastCommandedPointPosition}

+

Point Position: {this.props.pointState?.pointPosition}

+

Degraded Point Position: {this.props.pointState?.degradedPointPosition}

+ let request = new DegradedPositionMessage(); + request.setDegradedposition(false); + await client.putIntoUnintendedPosition(request, null); + }}>Set to unintended and not degraded position -

+ let request = new DegradedPositionMessage(); + request.setDegradedposition(true); + await client.putIntoUnintendedPosition(request, null); + }}>Set to unintended and degraded position +

Ability to Move: {this.props.pointState?.abilityToMove}

+ {this.props.simulatorConfiguration?.observeAbilityToMove && ( + <> + + + + )} + +

Enabled Irregularities

+

Prevent position left: {this.props.simulatedPointState?.preventedPositionLeft}

+

Prevent position right: {this.props.simulatedPointState?.preventedPositionRight}

-

+

Degraded position left: {this.props.simulatedPointState?.degradedPositionLeft ? 'yes' : 'no'}

+ let request = new PreventedPositionMessage(); + request.setPosition(PreventedPosition.SETNOENDPOSITION); + request.setDegradedposition(true) + await client.schedulePreventLeftEndPosition(request, null); + }}>Degrade position left +

Degraded position right: {this.props.simulatedPointState?.degradedPositionRight ? 'yes' : 'no'}

-

+ let request = new PreventedPositionMessage(); + request.setPosition(PreventedPosition.SETNOENDPOSITION); + request.setDegradedposition(true) + await client.schedulePreventRightEndPosition(request, null); + }}>Degrade position right +

Simulate timeout left: {this.props.simulatedPointState?.simulateTimeoutLeft ? 'yes' : 'no'}

+

Simulate timeout right: {this.props.simulatedPointState?.simulateTimeoutRight ? 'yes' : 'no'}

- - -

diff --git a/src/Point/rasta-point-web/src/SimulatorState.ts b/src/Point/rasta-point-web/src/SimulatorState.ts deleted file mode 100644 index 9ba1f0f..0000000 --- a/src/Point/rasta-point-web/src/SimulatorState.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface SimulatorState -{ - initialized: boolean; - position: string; -}; From 6b172a125c7a94b663f64b9983f30b18435e8f3f Mon Sep 17 00:00:00 2001 From: Robert Schmid Date: Sat, 16 Dec 2023 21:22:43 +0100 Subject: [PATCH 03/10] Implement overriding messages --- .../PointToInterlockingConnection.cs | 14 ++++++++++++-- .../PointToInterlockingConnection.cs | 14 ++++++++++++-- .../Interfaces/IPointToInterlockingConnection.cs | 3 ++- src/Point/Services/PointService.cs | 5 +++-- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/FieldElementSubsystems/Connections/EulynxBaseline4R1/PointToInterlockingConnection.cs b/src/FieldElementSubsystems/Connections/EulynxBaseline4R1/PointToInterlockingConnection.cs index 1bce76c..24f776c 100644 --- a/src/FieldElementSubsystems/Connections/EulynxBaseline4R1/PointToInterlockingConnection.cs +++ b/src/FieldElementSubsystems/Connections/EulynxBaseline4R1/PointToInterlockingConnection.cs @@ -4,6 +4,7 @@ using EulynxLive.FieldElementSubsystems.Configuration; using Grpc.Core; using EulynxLive.Messages.Baseline4R1; +using System.Threading.Channels; namespace EulynxLive.FieldElementSubsystems.Connections.EulynxBaseline4R1; @@ -14,6 +15,9 @@ public class PointToInterlockingConnection : IPointToInterlockingConnection private readonly string _localId; private readonly string _remoteId; public PointConfiguration Configuration { get; } + + private readonly Channel _overrideMessages; + public CancellationToken TimeoutToken => _timeout.Token; public IConnection? CurrentConnection { get; private set; } @@ -37,6 +41,7 @@ public PointToInterlockingConnection( _localId = config.LocalId; _remoteId = config.RemoteId; Configuration = config; + _overrideMessages = Channel.CreateUnbounded(); } public void Connect(IConnection connection) @@ -108,13 +113,13 @@ public async Task ReceiveMovePointCommand(CancellationToke private async Task SendMessage(Message message) { - if (CurrentConnection == null) throw new InvalidOperationException("Connection is null. Did you call Connect()?"); - await CurrentConnection.SendAsync(message.ToByteArray()); + await SendMessage(message.ToByteArray()); } private async Task SendMessage(byte[] message) { if (CurrentConnection == null) throw new InvalidOperationException("Connection is null. Did you call Connect()?"); + if (_overrideMessages.Reader.TryRead(out var overrideMessage)) message = overrideMessage; await CurrentConnection.SendAsync(message); } @@ -142,6 +147,11 @@ public async Task SendSciMessage(byte[] message) await SendMessage(message); } + public async Task OverrideNextSciMessage(byte[] message) + { + await _overrideMessages.Writer.WriteAsync(message); + } + public async Task SendAbilityToMove(GenericPointState pointState) { var abilityToMove = new AbilityToMove(pointState); diff --git a/src/FieldElementSubsystems/Connections/EulynxBaseline4R2/PointToInterlockingConnection.cs b/src/FieldElementSubsystems/Connections/EulynxBaseline4R2/PointToInterlockingConnection.cs index 5a24831..1ee35c9 100644 --- a/src/FieldElementSubsystems/Connections/EulynxBaseline4R2/PointToInterlockingConnection.cs +++ b/src/FieldElementSubsystems/Connections/EulynxBaseline4R2/PointToInterlockingConnection.cs @@ -4,6 +4,7 @@ using EulynxLive.FieldElementSubsystems.Configuration; using Grpc.Core; using EulynxLive.Messages.Baseline4R2; +using System.Threading.Channels; namespace EulynxLive.FieldElementSubsystems.Connections.EulynxBaseline4R2; @@ -13,6 +14,9 @@ public class PointToInterlockingConnection : IPointToInterlockingConnection private readonly string _localId; private readonly string _remoteId; public PointConfiguration Configuration { get; } + + private readonly Channel _overrideMessages; + public CancellationToken TimeoutToken => _timeout.Token; public IConnection? CurrentConnection { get; private set; } @@ -36,6 +40,7 @@ public PointToInterlockingConnection( _localId = config.LocalId; _remoteId = config.RemoteId; Configuration = config; + _overrideMessages = Channel.CreateUnbounded(); } public void Connect(IConnection connection) @@ -107,13 +112,13 @@ public async Task ReceiveMovePointCommand(CancellationToke private async Task SendMessage(Message message) { - if (CurrentConnection == null) throw new InvalidOperationException("Connection is null. Did you call Connect()?"); - await CurrentConnection.SendAsync(message.ToByteArray()); + await SendMessage(message.ToByteArray()); } private async Task SendMessage(byte[] message) { if (CurrentConnection == null) throw new InvalidOperationException("Connection is null. Did you call Connect()?"); + if (_overrideMessages.Reader.TryRead(out var overrideMessage)) message = overrideMessage; await CurrentConnection.SendAsync(message); } @@ -141,6 +146,11 @@ public async Task SendSciMessage(byte[] message) await SendMessage(message); } + public async Task OverrideNextSciMessage(byte[] message) + { + await _overrideMessages.Writer.WriteAsync(message); + } + public async Task SendAbilityToMove(GenericPointState pointState) { var abilityToMove = new AbilityToMove(pointState); diff --git a/src/FieldElementSubsystems/Interfaces/IPointToInterlockingConnection.cs b/src/FieldElementSubsystems/Interfaces/IPointToInterlockingConnection.cs index f0962cd..6bb59f9 100644 --- a/src/FieldElementSubsystems/Interfaces/IPointToInterlockingConnection.cs +++ b/src/FieldElementSubsystems/Interfaces/IPointToInterlockingConnection.cs @@ -9,7 +9,8 @@ public interface IPointToInterlockingConnection { void Connect(IConnection connection); Task SendPointPosition(GenericPointState state); - Task SendSciMessage(byte[] state); + Task SendSciMessage(byte[] message); + Task OverrideNextSciMessage(byte[] message); Task SendTimeoutMessage(); Task InitializeConnection(GenericPointState state, bool observeAbilityToMove, CancellationToken cancellationToken); Task ReceiveMovePointCommand(CancellationToken stoppingToken); diff --git a/src/Point/Services/PointService.cs b/src/Point/Services/PointService.cs index 3dc6ac2..00a8d2b 100644 --- a/src/Point/Services/PointService.cs +++ b/src/Point/Services/PointService.cs @@ -47,9 +47,10 @@ public override async Task SendSciMessage(SciMessage request, ServerCallC return new Empty(); } - public override Task OverrideSciMessage(SciMessage request, ServerCallContext context) + public override async Task OverrideSciMessage(SciMessage request, ServerCallContext context) { - throw new NotImplementedException(); + await _point.Connection.OverrideNextSciMessage(request.Message.ToByteArray()); + return new Empty(); } public override Task SchedulePreventLeftEndPosition(PreventedPositionMessage request, ServerCallContext context) From 9dee703acf31fc69e194dafd9b9cdb5f741b43b6 Mon Sep 17 00:00:00 2001 From: Robert Schmid Date: Sat, 16 Dec 2023 21:24:20 +0100 Subject: [PATCH 04/10] Conditionally visible buttons --- src/Point/rasta-point-web/src/Point.tsx | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Point/rasta-point-web/src/Point.tsx b/src/Point/rasta-point-web/src/Point.tsx index 933ebb0..ee716ab 100644 --- a/src/Point/rasta-point-web/src/Point.tsx +++ b/src/Point/rasta-point-web/src/Point.tsx @@ -78,19 +78,23 @@ class Point extends Component { await client.schedulePreventRightEndPosition(request, null); }}>Prevent position right

Degraded position left: {this.props.simulatedPointState?.degradedPositionLeft ? 'yes' : 'no'}

- + {this.props.simulatorConfiguration?.allPointMachinesCrucial === false && ( + + )}

Degraded position right: {this.props.simulatedPointState?.degradedPositionRight ? 'yes' : 'no'}

- + {this.props.simulatorConfiguration?.allPointMachinesCrucial === false && ( + + )}

Simulate timeout left: {this.props.simulatedPointState?.simulateTimeoutLeft ? 'yes' : 'no'}

+ ))} +
+ + + ) +} + +function Point(props: PointProps) { + const sendCommand = async (sender: (client: PointClient) => Promise) => { const url = window.location.protocol + '//' + window.location.host + (window.location.pathname.endsWith('/') ? window.location.pathname.slice(0, -1) : window.location.pathname); const client = new PointClient(url); + try { + await sender(client) + } catch (e) { + alert(e); + } + }; + + const unintendedLabel = props.simulatorConfiguration?.connectionProtocol === 'EulynxBaseline4R1' ? 'Trailed' : 'Unintended'; + + return ( +
+

Point Simulator

+ +
+ +
+

Simulator Configuration

+
{props.simulatorConfiguration?.connectionProtocol ?? 'Loading...'}
+ + +
+ +
+

Point State

+
+
+ Electronic Interlocking Connection +
+
{props.initialized ? 'connected' : 'disconnected'}
+
+ +
+
+ End Position +
+ sendCommand(async (client) => { + if (props.simulatorConfiguration?.connectionProtocol === 'EulynxBaseline4R1') { + await client.putIntoTrailedPosition(new DegradedPositionMessage().setDegradedposition(false), null); + } else if (props.simulatorConfiguration?.connectionProtocol === 'EulynxBaseline4R2') { + await client.putIntoUnintendedPosition(new DegradedPositionMessage().setDegradedposition(false), null); + } + }) + }, + { label: 'Right', active: props.pointState?.pointPosition === 'Right' ?? false, disabled: true }, + ]} /> +
+ +
+
+ Degraded Point Position +
+ +
+ +
+
+ Last Commanded Point Position +
+ +
+ + {props.simulatorConfiguration?.observeAbilityToMove && ( +
+
+ Ability to Move +
+ sendCommand((client) => client.setAbilityToMove(new AbilityToMoveMessage().setAbility(AbilityToMove.ABLE_TO_MOVE), null)) }, + { label: 'Unable to Move', active: props.pointState?.abilityToMove === 'UnableToMove' ?? false, onClick: () => sendCommand((client) => client.setAbilityToMove(new AbilityToMoveMessage().setAbility(AbilityToMove.UNABLE_TO_MOVE), null)) }, + ]} /> +
+ )} +
+ +
+

Simulate Irregularities

+ +
+
+ Prevent left end position +
+ + sendCommand((client) => client.scheduleTimeoutLeft(new EnableMovementFailedMessage().setEnablemovementfailed(enable), null))} /> + + sendCommand((client) => client.schedulePreventLeftEndPosition(new PreventedPositionMessage().setPosition(PreventedPosition.DONOTPREVENT).setDegradedposition(props.simulatedPointState?.degradedPositionLeft ?? false), null)) + }, + { + label: unintendedLabel, active: props.simulatedPointState?.preventedPositionLeft === 'SetUnintendedOrTrailed' ?? false, + onClick: () => sendCommand((client) => client.schedulePreventLeftEndPosition(new PreventedPositionMessage().setPosition(PreventedPosition.SETUNINTENDEDORTRAILED).setDegradedposition(props.simulatedPointState?.degradedPositionLeft ?? false), null)) + }, + { + label: 'No End Position', active: props.simulatedPointState?.preventedPositionLeft === 'SetNoEndPosition' ?? false, + onClick: () => sendCommand((client) => client.schedulePreventLeftEndPosition(new PreventedPositionMessage().setPosition(PreventedPosition.SETNOENDPOSITION).setDegradedposition(props.simulatedPointState?.degradedPositionLeft ?? false), null)) + }, + ]} /> + + sendCommand((client) => client.schedulePreventLeftEndPosition(new PreventedPositionMessage().setDegradedposition(enable).setPosition({ + 'DoNotPrevent': PreventedPosition.DONOTPREVENT, + 'SetUnintendedOrTrailed': PreventedPosition.SETUNINTENDEDORTRAILED, + 'SetNoEndPosition': PreventedPosition.SETNOENDPOSITION, + 'none': null + }[props.simulatedPointState?.preventedPositionLeft ?? 'none'] ?? PreventedPosition.DONOTPREVENT), null))} + /> +
+ +
+
+ Prevent right end position +
+ + sendCommand((client) => client.scheduleTimeoutRight(new EnableMovementFailedMessage().setEnablemovementfailed(enable), null))} /> + + sendCommand((client) => client.schedulePreventRightEndPosition(new PreventedPositionMessage().setPosition(PreventedPosition.DONOTPREVENT).setDegradedposition(props.simulatedPointState?.degradedPositionRight ?? false), null)) + }, + { + label: unintendedLabel, active: props.simulatedPointState?.preventedPositionRight === 'SetUnintendedOrTrailed' ?? false, + onClick: () => sendCommand((client) => client.schedulePreventRightEndPosition(new PreventedPositionMessage().setPosition(PreventedPosition.SETUNINTENDEDORTRAILED).setDegradedposition(props.simulatedPointState?.degradedPositionRight ?? false), null)) + }, + { + label: 'No End Position', active: props.simulatedPointState?.preventedPositionRight === 'SetNoEndPosition' ?? false, + onClick: () => sendCommand((client) => client.schedulePreventRightEndPosition(new PreventedPositionMessage().setPosition(PreventedPosition.SETNOENDPOSITION).setDegradedposition(props.simulatedPointState?.degradedPositionRight ?? false), null)) + }, + ]} /> + + sendCommand((client) => client.schedulePreventRightEndPosition(new PreventedPositionMessage().setDegradedposition(enable).setPosition({ + 'DoNotPrevent': PreventedPosition.DONOTPREVENT, + 'SetUnintendedOrTrailed': PreventedPosition.SETUNINTENDEDORTRAILED, + 'SetNoEndPosition': PreventedPosition.SETNOENDPOSITION, + 'none': null + }[props.simulatedPointState?.preventedPositionRight ?? 'none'] ?? PreventedPosition.DONOTPREVENT), null))} + /> +
+ +
+
+ Connectivity +
+ +
+
- return ( -
-

Point Simulator

-

Connection to Electronic Interlocking

-

{this.props.initialized ? 'connected' : 'disconnected'}

-

Simulator Configuration

-

Observe ability to move: {this.props.simulatorConfiguration?.observeAbilityToMove ? 'yes' : 'no'}

-

All point machines crucial: {this.props.simulatorConfiguration?.allPointMachinesCrucial ? 'yes' : 'no'}

-

Connection protocol: {this.props.simulatorConfiguration?.connectionProtocol}

- -

Point State

-

Last Commanded Point Position: {this.props.pointState?.lastCommandedPointPosition}

-

Point Position: {this.props.pointState?.pointPosition}

-

Degraded Point Position: {this.props.pointState?.degradedPointPosition}

- - -

Ability to Move: {this.props.pointState?.abilityToMove}

- {this.props.simulatorConfiguration?.observeAbilityToMove && ( - <> - - - - )} - -

Enabled Irregularities

-

Prevent position left: {this.props.simulatedPointState?.preventedPositionLeft}

- -

Prevent position right: {this.props.simulatedPointState?.preventedPositionRight}

- -

Degraded position left: {this.props.simulatedPointState?.degradedPositionLeft ? 'yes' : 'no'}

- {this.props.simulatorConfiguration?.allPointMachinesCrucial === false && ( - - )} -

Degraded position right: {this.props.simulatedPointState?.degradedPositionRight ? 'yes' : 'no'}

- {this.props.simulatorConfiguration?.allPointMachinesCrucial === false && ( - - )} -

Simulate timeout left: {this.props.simulatedPointState?.simulateTimeoutLeft ? 'yes' : 'no'}

- -

Simulate timeout right: {this.props.simulatedPointState?.simulateTimeoutRight ? 'yes' : 'no'}

- -

-
- ); - } + +
+ ); } export default Point; diff --git a/src/Point/rasta-point-web/src/index.css b/src/Point/rasta-point-web/src/index.css index a41a98c..09f5c86 100644 --- a/src/Point/rasta-point-web/src/index.css +++ b/src/Point/rasta-point-web/src/index.css @@ -1,9 +1,124 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - background-color: #eee; +@tailwind base; +@tailwind components; +@tailwind utilities; + + +/* Switch element */ + +.form-switch{ + position: relative; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + width: 44px; + } + + .form-switch label{ + display: block; + height: 1.5rem; + cursor: pointer; + overflow: hidden; + border-radius: 9999px; + } + + .form-switch label > span:first-child{ + position: absolute; + display: block; + border-radius: 9999px; + width: 20px; + height: 20px; + top: 2px; + left: 2px; + right: 50%; + transition: all .15s ease-out; + } + + .form-switch input[type="checkbox"]:checked + label{ + --tw-bg-opacity: 1; + background-color: rgb(99 102 241 / var(--tw-bg-opacity)); + } + + .form-switch input[type="checkbox"]:checked + label > span:first-child { + left: 22px; + } + + .form-switch input[type="checkbox"]:disabled + label{ + cursor: not-allowed; + } + + .form-switch input[type="checkbox"]:disabled:not(:checked) + label { + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(226 232 240 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(241 245 249 / var(--tw-bg-opacity)); + } + + :is(.dark .form-switch input[type="checkbox"]:disabled + label){ + --tw-border-opacity: 1; + border-color: rgb(51 65 85 / var(--tw-border-opacity)); + background-color: rgb(51 65 85 / 0.2); + } + + .form-switch input[type="checkbox"]:disabled + label > span:first-child{ + --tw-bg-opacity: 1; + background-color: rgb(148 163 184 / var(--tw-bg-opacity)); + } + + :is(.dark .form-switch input[type="checkbox"]:disabled + label > span:first-child){ + --tw-bg-opacity: 1; + background-color: rgb(71 85 105 / var(--tw-bg-opacity)); + } + +/* Buttons */ + +.btn, +.btn-lg, +.btn-sm, +.btn-xs{ + display: inline-flex; + align-items: center; + justify-content: center; + border-width: 1px; + border-color: transparent; + font-size: 0.875rem; + line-height: 1.5715; + font-weight: 500; + line-height: 1.25rem; + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +.btn{ + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.btn-lg{ + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.btn-sm{ + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.btn-xs{ + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.125rem; + padding-bottom: 0.125rem; } diff --git a/src/Point/rasta-point-web/src/proto/PointServiceClientPb.ts b/src/Point/rasta-point-web/src/proto/PointServiceClientPb.ts index 08a2f7d..0715350 100644 --- a/src/Point/rasta-point-web/src/proto/PointServiceClientPb.ts +++ b/src/Point/rasta-point-web/src/proto/PointServiceClientPb.ts @@ -430,26 +430,26 @@ export class PointClient { methodDescriptorScheduleTimeoutRight = new grpcWeb.MethodDescriptor( '/point.Point/ScheduleTimeoutRight', grpcWeb.MethodType.UNARY, + point_pb.EnableMovementFailedMessage, google_protobuf_empty_pb.Empty, - google_protobuf_empty_pb.Empty, - (request: google_protobuf_empty_pb.Empty) => { + (request: point_pb.EnableMovementFailedMessage) => { return request.serializeBinary(); }, google_protobuf_empty_pb.Empty.deserializeBinary ); scheduleTimeoutRight( - request: google_protobuf_empty_pb.Empty, + request: point_pb.EnableMovementFailedMessage, metadata?: grpcWeb.Metadata | null): Promise; scheduleTimeoutRight( - request: google_protobuf_empty_pb.Empty, + request: point_pb.EnableMovementFailedMessage, metadata: grpcWeb.Metadata | null, callback: (err: grpcWeb.RpcError, response: google_protobuf_empty_pb.Empty) => void): grpcWeb.ClientReadableStream; scheduleTimeoutRight( - request: google_protobuf_empty_pb.Empty, + request: point_pb.EnableMovementFailedMessage, metadata?: grpcWeb.Metadata | null, callback?: (err: grpcWeb.RpcError, response: google_protobuf_empty_pb.Empty) => void) { @@ -473,26 +473,26 @@ export class PointClient { methodDescriptorScheduleTimeoutLeft = new grpcWeb.MethodDescriptor( '/point.Point/ScheduleTimeoutLeft', grpcWeb.MethodType.UNARY, + point_pb.EnableMovementFailedMessage, google_protobuf_empty_pb.Empty, - google_protobuf_empty_pb.Empty, - (request: google_protobuf_empty_pb.Empty) => { + (request: point_pb.EnableMovementFailedMessage) => { return request.serializeBinary(); }, google_protobuf_empty_pb.Empty.deserializeBinary ); scheduleTimeoutLeft( - request: google_protobuf_empty_pb.Empty, + request: point_pb.EnableMovementFailedMessage, metadata?: grpcWeb.Metadata | null): Promise; scheduleTimeoutLeft( - request: google_protobuf_empty_pb.Empty, + request: point_pb.EnableMovementFailedMessage, metadata: grpcWeb.Metadata | null, callback: (err: grpcWeb.RpcError, response: google_protobuf_empty_pb.Empty) => void): grpcWeb.ClientReadableStream; scheduleTimeoutLeft( - request: google_protobuf_empty_pb.Empty, + request: point_pb.EnableMovementFailedMessage, metadata?: grpcWeb.Metadata | null, callback?: (err: grpcWeb.RpcError, response: google_protobuf_empty_pb.Empty) => void) { diff --git a/src/Point/rasta-point-web/src/proto/point_pb.d.ts b/src/Point/rasta-point-web/src/proto/point_pb.d.ts index 120e837..5aaff10 100644 --- a/src/Point/rasta-point-web/src/proto/point_pb.d.ts +++ b/src/Point/rasta-point-web/src/proto/point_pb.d.ts @@ -81,6 +81,24 @@ export namespace DegradedPositionMessage { } } +export class EnableMovementFailedMessage extends jspb.Message { + getEnablemovementfailed(): boolean; + setEnablemovementfailed(value: boolean): EnableMovementFailedMessage; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): EnableMovementFailedMessage.AsObject; + static toObject(includeInstance: boolean, msg: EnableMovementFailedMessage): EnableMovementFailedMessage.AsObject; + static serializeBinaryToWriter(message: EnableMovementFailedMessage, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): EnableMovementFailedMessage; + static deserializeBinaryFromReader(message: EnableMovementFailedMessage, reader: jspb.BinaryReader): EnableMovementFailedMessage; +} + +export namespace EnableMovementFailedMessage { + export type AsObject = { + enablemovementfailed: boolean, + } +} + export class PointPositionMessage extends jspb.Message { getPosition(): PointPosition; setPosition(value: PointPosition): PointPositionMessage; diff --git a/src/Point/rasta-point-web/src/proto/point_pb.js b/src/Point/rasta-point-web/src/proto/point_pb.js index fa2e879..8242a32 100644 --- a/src/Point/rasta-point-web/src/proto/point_pb.js +++ b/src/Point/rasta-point-web/src/proto/point_pb.js @@ -26,6 +26,7 @@ goog.object.extend(proto, google_protobuf_empty_pb); goog.exportSymbol('proto.point.AbilityToMove', null, global); goog.exportSymbol('proto.point.AbilityToMoveMessage', null, global); goog.exportSymbol('proto.point.DegradedPositionMessage', null, global); +goog.exportSymbol('proto.point.EnableMovementFailedMessage', null, global); goog.exportSymbol('proto.point.PointDegradedPosition', null, global); goog.exportSymbol('proto.point.PointDegradedPositionMessage', null, global); goog.exportSymbol('proto.point.PointPosition', null, global); @@ -117,6 +118,27 @@ if (goog.DEBUG && !COMPILED) { */ proto.point.DegradedPositionMessage.displayName = 'proto.point.DegradedPositionMessage'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.point.EnableMovementFailedMessage = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.point.EnableMovementFailedMessage, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.point.EnableMovementFailedMessage.displayName = 'proto.point.EnableMovementFailedMessage'; +} /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a @@ -736,6 +758,136 @@ proto.point.DegradedPositionMessage.prototype.setDegradedposition = function(val +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.point.EnableMovementFailedMessage.prototype.toObject = function(opt_includeInstance) { + return proto.point.EnableMovementFailedMessage.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.point.EnableMovementFailedMessage} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.point.EnableMovementFailedMessage.toObject = function(includeInstance, msg) { + var f, obj = { + enablemovementfailed: jspb.Message.getBooleanFieldWithDefault(msg, 1, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.point.EnableMovementFailedMessage} + */ +proto.point.EnableMovementFailedMessage.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.point.EnableMovementFailedMessage; + return proto.point.EnableMovementFailedMessage.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.point.EnableMovementFailedMessage} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.point.EnableMovementFailedMessage} + */ +proto.point.EnableMovementFailedMessage.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setEnablemovementfailed(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.point.EnableMovementFailedMessage.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.point.EnableMovementFailedMessage.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.point.EnableMovementFailedMessage} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.point.EnableMovementFailedMessage.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getEnablemovementfailed(); + if (f) { + writer.writeBool( + 1, + f + ); + } +}; + + +/** + * optional bool enableMovementFailed = 1; + * @return {boolean} + */ +proto.point.EnableMovementFailedMessage.prototype.getEnablemovementfailed = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.point.EnableMovementFailedMessage} returns this + */ +proto.point.EnableMovementFailedMessage.prototype.setEnablemovementfailed = function(value) { + return jspb.Message.setProto3BooleanField(this, 1, value); +}; + + + + + if (jspb.Message.GENERATE_TO_OBJECT) { /** * Creates an object representation of this proto. diff --git a/src/Point/rasta-point-web/tailwind.config.js b/src/Point/rasta-point-web/tailwind.config.js new file mode 100644 index 0000000..c0958ec --- /dev/null +++ b/src/Point/rasta-point-web/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./src/**/*.{js,jsx,ts,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +} + diff --git a/src/ProtobufInterfaces/proto/point.proto b/src/ProtobufInterfaces/proto/point.proto index 767f08c..d182c83 100644 --- a/src/ProtobufInterfaces/proto/point.proto +++ b/src/ProtobufInterfaces/proto/point.proto @@ -38,8 +38,8 @@ service Point { rpc SchedulePreventRightEndPosition (PreventedPositionMessage) returns (google.protobuf.Empty) {} rpc SchedulePreventLeftEndPosition (PreventedPositionMessage) returns (google.protobuf.Empty) {} - rpc ScheduleTimeoutRight (google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ScheduleTimeoutLeft (google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc ScheduleTimeoutRight (EnableMovementFailedMessage) returns (google.protobuf.Empty) {} + rpc ScheduleTimeoutLeft (EnableMovementFailedMessage) returns (google.protobuf.Empty) {} rpc Reset (google.protobuf.Empty) returns (google.protobuf.Empty) {} } @@ -49,7 +49,7 @@ enum AbilityToMove { UNABLE_TO_MOVE = 1; } -message AbilityToMoveMessage{ +message AbilityToMoveMessage { AbilityToMove ability = 1; } @@ -66,6 +66,10 @@ message DegradedPositionMessage { bool degradedPosition = 1; } +message EnableMovementFailedMessage { + bool enableMovementFailed = 1; +} + message PointPositionMessage { PointPosition position = 1; } From c8959769a1fff5557c08cee1660e46895a5e6e0a Mon Sep 17 00:00:00 2001 From: Robert Schmid Date: Mon, 18 Dec 2023 00:39:43 +0100 Subject: [PATCH 07/10] Fix tests --- .../Point/Baseline4R2/CommandingAndReversingTests.cs | 8 +++++--- .../Point/Baseline4R2/ReportStatusTests.cs | 4 +++- src/FieldElementSubsystems.Test/Point/PointTest.cs | 8 +++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs b/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs index 56adaa6..c4c712b 100644 --- a/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs +++ b/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs @@ -72,10 +72,12 @@ private static (EulynxLive.Point.Point point, Task pointTask, List recei var connection = new PointToInterlockingConnection(Mock.Of>(), configuration, CancellationToken.None); - var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask, Mock.Of>()); + var mockHubContext = new Mock>(); + mockHubContext.Setup(x => x.Clients.All.SendCoreAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask, mockHubContext.Object); if (simulateTimeouts) { - point.EnableTimeoutLeft(); - point.EnableTimeoutRight(); + point.EnableTimeoutLeft(true); + point.EnableTimeoutRight(true); } async Task SimulatePoint() diff --git a/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs b/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs index b62b916..23657d4 100644 --- a/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs +++ b/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs @@ -59,7 +59,9 @@ private static (EulynxLive.Point.Point point, Task, List receivedBytes) var connection = new PointToInterlockingConnection(Mock.Of>(), configuration, CancellationToken.None); connection.Connect(mockConnection.Object); - var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask, Mock.Of>()); + var mockHubContext = new Mock>(); + mockHubContext.Setup(x => x.Clients.All.SendCoreAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask, mockHubContext.Object); async Task SimulatePoint() { diff --git a/src/FieldElementSubsystems.Test/Point/PointTest.cs b/src/FieldElementSubsystems.Test/Point/PointTest.cs index 2b89f2f..fad5d76 100644 --- a/src/FieldElementSubsystems.Test/Point/PointTest.cs +++ b/src/FieldElementSubsystems.Test/Point/PointTest.cs @@ -38,7 +38,9 @@ private static (EulynxLive.Point.Point, Func simulatePoint, Mock()!; var mockConnection = CreateDefaultMockConnection(config); - var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, mockConnection.Object, Mock.Of(), () => Task.CompletedTask, Mock.Of>()); + var mockHubContext = new Mock>(); + mockHubContext.Setup(x => x.Clients.All.SendCoreAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, mockConnection.Object, Mock.Of(), () => Task.CompletedTask, mockHubContext.Object); async Task SimulatePoint() { @@ -95,7 +97,7 @@ public async Task Test_TimeoutLeft() .SetupSequence(m => m.ReceiveMovePointCommand(It.IsAny())) .Returns(() => { - point.EnableTimeoutLeft(); + point.EnableTimeoutLeft(true); return Task.FromResult(GenericPointPosition.Left); }) .Returns(() => @@ -123,7 +125,7 @@ public async Task Test_TimeoutRight() .SetupSequence(m => m.ReceiveMovePointCommand(It.IsAny())) .Returns(() => { - point.EnableTimeoutRight(); + point.EnableTimeoutRight(true); return Task.FromResult(GenericPointPosition.Right); }) .Returns(() => From dc8d070fbeb6c66a113ea9fab88a28696de5fca7 Mon Sep 17 00:00:00 2001 From: Robert Schmid Date: Mon, 18 Dec 2023 01:07:40 +0100 Subject: [PATCH 08/10] Add more tests --- .../Point/PointServiceTest.cs | 240 ++++++++++++++++++ .../rasta-levelcrossing-web/public/index.html | 4 +- .../public/manifest.json | 2 +- src/Point/IPoint.cs | 21 ++ src/Point/Point.cs | 8 +- src/Point/Services/PointService.cs | 20 +- src/Point/Startup.cs | 1 + 7 files changed, 281 insertions(+), 15 deletions(-) create mode 100644 src/FieldElementSubsystems.Test/Point/PointServiceTest.cs create mode 100644 src/Point/IPoint.cs diff --git a/src/FieldElementSubsystems.Test/Point/PointServiceTest.cs b/src/FieldElementSubsystems.Test/Point/PointServiceTest.cs new file mode 100644 index 0000000..d1476a4 --- /dev/null +++ b/src/FieldElementSubsystems.Test/Point/PointServiceTest.cs @@ -0,0 +1,240 @@ +using EulynxLive.FieldElementSubsystems.Interfaces; +using EulynxLive.Point; +using EulynxLive.Point.Proto; +using EulynxLive.Point.Services; + +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; + +using Grpc.Core; + +using Moq; + +namespace FieldElementSubsystems.Test; + +public class PointServiceTest() +{ + [Fact] + public void TestReset() + { + // Arrange + var point = new Mock(); + var pointService = new PointService(point.Object); + + // Act + pointService.Reset(new Empty(), Mock.Of()); + + // Assert + point.Verify(x => x.Reset(), Times.Once); + } + + [Fact] + public void TestScheduleTimeoutRight() + { + // Arrange + var point = new Mock(); + var pointService = new PointService(point.Object); + + // Act + pointService.ScheduleTimeoutRight(new EnableMovementFailedMessage() { EnableMovementFailed = true }, Mock.Of()); + + // Assert + point.Verify(x => x.EnableTimeoutRight(true), Times.Once); + } + + [Fact] + public void TestScheduleTimeoutLeft() + { + // Arrange + var point = new Mock(); + var pointService = new PointService(point.Object); + + // Act + pointService.ScheduleTimeoutLeft(new EnableMovementFailedMessage() { EnableMovementFailed = true }, Mock.Of()); + + // Assert + point.Verify(x => x.EnableTimeoutLeft(true), Times.Once); + } + + [Fact] + public async Task TestSetAbilityToMove() + { + // Arrange + var point = new Mock(); + var pointService = new PointService(point.Object); + + // Act + await pointService.SetAbilityToMove(new AbilityToMoveMessage() { Ability = AbilityToMove.AbleToMove }, Mock.Of()); + + // Assert + point.Verify(x => x.SetAbilityToMove(It.IsAny()), Times.Once); + } + + [Fact] + public async Task TestSendSciMessage() + { + // Arrange + var point = new Mock(); + var pointService = new PointService(point.Object); + + // Act + await pointService.SendSciMessage(new SciMessage() { Message = ByteString.CopyFrom(new byte[] { 0x01 }) }, Mock.Of()); + + // Assert + point.Verify(x => x.SendSciMessage(It.IsAny()), Times.Once); + } + + [Fact] + public async Task TestOverrideSciMessage() + { + // Arrange + var point = new Mock(); + point.Setup(x => x.Connection.OverrideNextSciMessage(It.IsAny())) + .Returns(Task.FromResult(0)); + var pointService = new PointService(point.Object); + + // Act + await pointService.OverrideSciMessage(new SciMessage() { Message = ByteString.CopyFrom(new byte[] { 0x01 }) }, Mock.Of()); + + // Assert + point.Verify(x => x.Connection.OverrideNextSciMessage(It.IsAny()), Times.Once); + } + + [Fact] + public void TestSchedulePreventLeftEndPosition() + { + // Arrange + var point = new Mock(); + var pointService = new PointService(point.Object); + + // Act + pointService.SchedulePreventLeftEndPosition(new PreventedPositionMessage() { Position = PreventedPosition.SetNoEndPosition, DegradedPosition = true }, Mock.Of()); + + // Assert + point.Verify(x => x.PreventLeftEndPosition(It.IsAny()), Times.Once); + } + + [Fact] + public void TestSchedulePreventRightEndPosition() + { + // Arrange + var point = new Mock(); + var pointService = new PointService(point.Object); + + // Act + pointService.SchedulePreventRightEndPosition(new PreventedPositionMessage() { Position = PreventedPosition.SetNoEndPosition, DegradedPosition = true }, Mock.Of()); + + // Assert + point.Verify(x => x.PreventRightEndPosition(It.IsAny()), Times.Once); + } + + [Fact] + public async Task TestPutIntoTrailedPosition() + { + // Arrange + var point = new Mock(); + point.Setup(x => x.ConnectionProtocol) + .Returns(EulynxLive.FieldElementSubsystems.Configuration.ConnectionProtocol.EulynxBaseline4R1); + point.Setup(x => x.PointState) + .Returns(new GenericPointState(LastCommandedPointPosition: null, + PointPosition: GenericPointPosition.Left, + DegradedPointPosition: GenericDegradedPointPosition.NotApplicable, + AbilityToMove: GenericAbilityToMove.AbleToMove + )); + var pointService = new PointService(point.Object); + + // Act + await pointService.PutIntoTrailedPosition(new DegradedPositionMessage() { DegradedPosition = true }, Mock.Of()); + + // Assert + point.Verify(x => x.PutIntoUnintendedPosition(It.IsAny()), Times.Once); + } + + [Fact] + public async Task TestPutIntoTrailedPositionThrowsIfNotSupported() + { + // Arrange + var point = new Mock(); + point.Setup(x => x.ConnectionProtocol) + .Returns(EulynxLive.FieldElementSubsystems.Configuration.ConnectionProtocol.EulynxBaseline4R2); + point.Setup(x => x.PointState) + .Returns(new GenericPointState(LastCommandedPointPosition: null, + PointPosition: GenericPointPosition.Left, + DegradedPointPosition: GenericDegradedPointPosition.NotApplicable, + AbilityToMove: GenericAbilityToMove.AbleToMove + )); + var pointService = new PointService(point.Object); + + // Assert + await Assert.ThrowsAsync(() => pointService.PutIntoTrailedPosition(new DegradedPositionMessage() { DegradedPosition = true }, Mock.Of())); + } + + [Fact] + public async Task TestPutIntoUnintendedPosition() + { + // Arrange + var point = new Mock(); + point.Setup(x => x.ConnectionProtocol) + .Returns(EulynxLive.FieldElementSubsystems.Configuration.ConnectionProtocol.EulynxBaseline4R2); + var pointService = new PointService(point.Object); + + // Act + await pointService.PutIntoUnintendedPosition(new DegradedPositionMessage() { DegradedPosition = true }, Mock.Of()); + + // Assert + point.Verify(x => x.PutIntoUnintendedPosition(It.IsAny()), Times.Once); + } + + [Fact] + public async Task TestPutIntoUnintendedPositionThrowsIfNotSupported() + { + // Arrange + var point = new Mock(); + point.Setup(x => x.ConnectionProtocol) + .Returns(EulynxLive.FieldElementSubsystems.Configuration.ConnectionProtocol.EulynxBaseline4R1); + var pointService = new PointService(point.Object); + + // Assert + await Assert.ThrowsAsync(() => pointService.PutIntoUnintendedPosition(new DegradedPositionMessage() { DegradedPosition = true }, Mock.Of())); + } + + [Fact] + public async Task TestGetDegradedPointPosition() + { + // Arrange + var point = new Mock(); + point.Setup(x => x.PointState) + .Returns(new GenericPointState(LastCommandedPointPosition: null, + PointPosition: GenericPointPosition.Left, + DegradedPointPosition: GenericDegradedPointPosition.NotApplicable, + AbilityToMove: GenericAbilityToMove.AbleToMove + )); + var pointService = new PointService(point.Object); + + // Act + var result = await pointService.GetDegradedPointPosition(new Empty(), Mock.Of()); + + // Assert + Assert.Equal(PointDegradedPosition.NotApplicable, result.Position); + } + + [Fact] + public async Task TestGetPointPosition() + { + // Arrange + var point = new Mock(); + point.Setup(x => x.PointState) + .Returns(new GenericPointState(LastCommandedPointPosition: null, + PointPosition: GenericPointPosition.Left, + DegradedPointPosition: GenericDegradedPointPosition.NotApplicable, + AbilityToMove: GenericAbilityToMove.AbleToMove + )); + var pointService = new PointService(point.Object); + + // Act + var result = await pointService.GetPointPosition(new Empty(), Mock.Of()); + + // Assert + Assert.Equal(PointPosition.Left, result.Position); + } +} diff --git a/src/LevelCrossing/rasta-levelcrossing-web/public/index.html b/src/LevelCrossing/rasta-levelcrossing-web/public/index.html index d9309f0..094ed1b 100644 --- a/src/LevelCrossing/rasta-levelcrossing-web/public/index.html +++ b/src/LevelCrossing/rasta-levelcrossing-web/public/index.html @@ -6,7 +6,7 @@ - RaSTA Point + Point diff --git a/src/LevelCrossing/rasta-levelcrossing-web/public/manifest.json b/src/LevelCrossing/rasta-levelcrossing-web/public/manifest.json index ca1854d..d7716a1 100644 --- a/src/LevelCrossing/rasta-levelcrossing-web/public/manifest.json +++ b/src/LevelCrossing/rasta-levelcrossing-web/public/manifest.json @@ -1,6 +1,6 @@ { "short_name": "Point", - "name": "RaSTA Point", + "name": "Point", "start_url": ".", "display": "standalone", "theme_color": "#000000", diff --git a/src/Point/IPoint.cs b/src/Point/IPoint.cs new file mode 100644 index 0000000..3e93262 --- /dev/null +++ b/src/Point/IPoint.cs @@ -0,0 +1,21 @@ +using EulynxLive.FieldElementSubsystems.Configuration; +using EulynxLive.FieldElementSubsystems.Interfaces; +using EulynxLive.Point.Proto; + +namespace EulynxLive.Point +{ + public interface IPoint { + ConnectionProtocol? ConnectionProtocol { get; } + IPointToInterlockingConnection Connection { get; } + GenericPointState PointState { get; } + + void EnableTimeoutLeft(bool enableMovementFailed); + void EnableTimeoutRight(bool enableMovementFailed); + void PreventLeftEndPosition(PreventedPositionMessage request); + void PreventRightEndPosition(PreventedPositionMessage request); + Task PutIntoUnintendedPosition(DegradedPositionMessage request); + void Reset(); + Task SendSciMessage(SciMessage request); + Task SetAbilityToMove(AbilityToMoveMessage request); + } +} diff --git a/src/Point/Point.cs b/src/Point/Point.cs index fdd03b5..6d7eca1 100644 --- a/src/Point/Point.cs +++ b/src/Point/Point.cs @@ -2,9 +2,13 @@ using EulynxLive.FieldElementSubsystems.Interfaces; using EulynxLive.Point.Hubs; using EulynxLive.Point.Proto; + using Grpc.Core; + using Microsoft.AspNetCore.SignalR; + using PropertyChanged.SourceGenerator; + using System.ComponentModel; using System.Net.WebSockets; using System.Reactive; @@ -13,13 +17,13 @@ namespace EulynxLive.Point { - public partial class Point : BackgroundService + public partial class Point : BackgroundService, IPoint { public IPointToInterlockingConnection Connection { get; } public bool AllPointMachinesCrucial { get; } public bool ObserveAbilityToMove { get; } - + public ConnectionProtocol? ConnectionProtocol => _config.ConnectionProtocol; [Notify] private GenericPointState _pointState; diff --git a/src/Point/Services/PointService.cs b/src/Point/Services/PointService.cs index 22ee001..745f0f6 100644 --- a/src/Point/Services/PointService.cs +++ b/src/Point/Services/PointService.cs @@ -9,9 +9,9 @@ namespace EulynxLive.Point.Services { public class PointService : PointBase { - private readonly Point _point; + private readonly IPoint _point; - public PointService(Point point) + public PointService(IPoint point) { _point = point; } @@ -65,22 +65,22 @@ override public Task SchedulePreventRightEndPosition(PreventedPositionMes return Task.FromResult(new Empty()); } - public override Task PutIntoTrailedPosition(DegradedPositionMessage request, ServerCallContext context) + public override async Task PutIntoTrailedPosition(DegradedPositionMessage request, ServerCallContext context) { - if (_point.Connection.Configuration.ConnectionProtocol != ConnectionProtocol.EulynxBaseline4R1) + if (_point.ConnectionProtocol != ConnectionProtocol.EulynxBaseline4R1) throw new RpcException(new Status(StatusCode.InvalidArgument, "Only supported for EulynxBaseline4R1")); - _point.PutIntoUnintendedPosition(request); - return Task.FromResult(new Empty()); + await _point.PutIntoUnintendedPosition(request); + return new Empty(); } - public override Task PutIntoUnintendedPosition(DegradedPositionMessage request, ServerCallContext context) + public override async Task PutIntoUnintendedPosition(DegradedPositionMessage request, ServerCallContext context) { - if (_point.Connection.Configuration.ConnectionProtocol != ConnectionProtocol.EulynxBaseline4R2) + if (_point.ConnectionProtocol != ConnectionProtocol.EulynxBaseline4R2) throw new RpcException(new Status(StatusCode.InvalidArgument, "Only supported for EulynxBaseline4R2")); - _point.PutIntoUnintendedPosition(request); - return Task.FromResult(new Empty()); + await _point.PutIntoUnintendedPosition(request); + return new Empty(); } public override Task GetDegradedPointPosition(Empty request, ServerCallContext context) diff --git a/src/Point/Startup.cs b/src/Point/Startup.cs index 7ebce02..440d443 100644 --- a/src/Point/Startup.cs +++ b/src/Point/Startup.cs @@ -43,6 +43,7 @@ public void ConfigureServices(IServiceCollection services) var simulateTimout = async () => await Task.Delay(new Random().Next(1, 5) * 1000); return ActivatorUtilities.CreateInstance(x, simulateTimout); }); + services.AddSingleton(x => x.GetRequiredService()); } catch (Exception e) { From 099cf681d1a0205a5b2dbde56248f8aca9befd15 Mon Sep 17 00:00:00 2001 From: Robert Schmid Date: Mon, 18 Dec 2023 01:12:40 +0100 Subject: [PATCH 09/10] Fix startup --- src/Point/Point.cs | 5 +---- src/Point/Startup.cs | 8 ++------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Point/Point.cs b/src/Point/Point.cs index 6d7eca1..c3b404d 100644 --- a/src/Point/Point.cs +++ b/src/Point/Point.cs @@ -34,15 +34,13 @@ public partial class Point : BackgroundService, IPoint private readonly ILogger _logger; private readonly IConnectionProvider _connectionProvider; - private readonly Func _simulateTimeout; private readonly PointConfiguration _config; - public Point(ILogger logger, IConfiguration configuration, IPointToInterlockingConnection connection, IConnectionProvider connectionProvider, Func simulateTimeout, IHubContext statusHub) + public Point(ILogger logger, IConfiguration configuration, IPointToInterlockingConnection connection, IConnectionProvider connectionProvider, IHubContext statusHub) { Connection = connection; _connectionProvider = connectionProvider; _logger = logger; - _simulateTimeout = simulateTimeout; _config = configuration.GetSection("PointSettings").Get() ?? throw new Exception("No configuration provided"); AllPointMachinesCrucial = _config.AllPointMachinesCrucial; @@ -338,7 +336,6 @@ private async Task HandleCommandedPointPosition(GenericPointPosition commandedPo { _logger.LogInformation("Timeout"); await Connection.SendTimeoutMessage(); - await _simulateTimeout(); } else { diff --git a/src/Point/Startup.cs b/src/Point/Startup.cs index 440d443..a54dc4e 100644 --- a/src/Point/Startup.cs +++ b/src/Point/Startup.cs @@ -38,12 +38,7 @@ public void ConfigureServices(IServiceCollection services) try { - services.AddSingleton(x => - { - var simulateTimout = async () => await Task.Delay(new Random().Next(1, 5) * 1000); - return ActivatorUtilities.CreateInstance(x, simulateTimout); - }); - services.AddSingleton(x => x.GetRequiredService()); + services.AddSingleton(); } catch (Exception e) { @@ -51,6 +46,7 @@ public void ConfigureServices(IServiceCollection services) Environment.Exit(1); } + services.AddSingleton(x => x.GetRequiredService()); _ = services.AddHostedService(provider => provider.GetRequiredService()); } From ccd6230c3e75a5e1c82cecc5fe50974a464701fd Mon Sep 17 00:00:00 2001 From: Robert Schmid Date: Mon, 18 Dec 2023 01:15:08 +0100 Subject: [PATCH 10/10] Update tests --- .../Point/Baseline4R2/CommandingAndReversingTests.cs | 2 +- .../Point/Baseline4R2/ReportStatusTests.cs | 2 +- src/FieldElementSubsystems.Test/Point/PointTest.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs b/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs index c4c712b..676058c 100644 --- a/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs +++ b/src/FieldElementSubsystems.Test/Point/Baseline4R2/CommandingAndReversingTests.cs @@ -74,7 +74,7 @@ private static (EulynxLive.Point.Point point, Task pointTask, List recei var mockHubContext = new Mock>(); mockHubContext.Setup(x => x.Clients.All.SendCoreAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); - var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask, mockHubContext.Object); + var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, mockHubContext.Object); if (simulateTimeouts) { point.EnableTimeoutLeft(true); point.EnableTimeoutRight(true); diff --git a/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs b/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs index 23657d4..b0a750e 100644 --- a/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs +++ b/src/FieldElementSubsystems.Test/Point/Baseline4R2/ReportStatusTests.cs @@ -61,7 +61,7 @@ private static (EulynxLive.Point.Point point, Task, List receivedBytes) var mockHubContext = new Mock>(); mockHubContext.Setup(x => x.Clients.All.SendCoreAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); - var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, () => Task.CompletedTask, mockHubContext.Object); + var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, connection, connectionProvider.Object, mockHubContext.Object); async Task SimulatePoint() { diff --git a/src/FieldElementSubsystems.Test/Point/PointTest.cs b/src/FieldElementSubsystems.Test/Point/PointTest.cs index fad5d76..691ee9a 100644 --- a/src/FieldElementSubsystems.Test/Point/PointTest.cs +++ b/src/FieldElementSubsystems.Test/Point/PointTest.cs @@ -40,7 +40,7 @@ private static (EulynxLive.Point.Point, Func simulatePoint, Mock>(); mockHubContext.Setup(x => x.Clients.All.SendCoreAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); - var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, mockConnection.Object, Mock.Of(), () => Task.CompletedTask, mockHubContext.Object); + var point = new EulynxLive.Point.Point(Mock.Of>(), configuration, mockConnection.Object, Mock.Of(), mockHubContext.Object); async Task SimulatePoint() {