Skip to content

Commit

Permalink
Change logic for handling hub uptime, latency, and rssi
Browse files Browse the repository at this point in the history
  • Loading branch information
hhvrc committed Nov 17, 2024
1 parent 9b1e702 commit 864eb8c
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 116 deletions.
12 changes: 5 additions & 7 deletions API/Controller/Admin/GetOnlineDevices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ public async Task<IActionResult> GetOnlineDevices()
Name = dbItem.Name,
ConnectedAt = x.ConnectedAt,
UserAgent = x.UserAgent,
Uptime = x.Uptime,
Latency = x.Latency,
BootedAt = x.BootedAt,
LatencyMs = x.LatencyMs,
Rssi = x.Rssi,
};
})
Expand All @@ -73,10 +73,8 @@ public sealed class AdminOnlineDeviceResponse
public required DateTimeOffset ConnectedAt { get; init; }

public required string? UserAgent { get; init; }
[JsonConverter(typeof(TimeSpanToMillisecondsConverter))]
public required TimeSpan? Uptime { get; init; }
[JsonConverter(typeof(TimeSpanToMillisecondsConverter))]
public required TimeSpan? Latency { get; init; }
public required int Rssi { get; init; }
public required DateTimeOffset BootedAt { get; init; }
public required ushort? LatencyMs { get; init; }
public required int? Rssi { get; init; }
}
}
28 changes: 0 additions & 28 deletions Common/JsonSerialization/TimeSpanToMillisecondsConverter.cs

This file was deleted.

4 changes: 2 additions & 2 deletions Common/Models/WebSocket/LCG/LatencyAnnounceData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public sealed class LatencyAnnounceData
{
public required ulong DeviceLatency { get; set; }
public required ulong OwnLatency { get; set; }
public required ushort DeviceLatency { get; set; }
public required ushort OwnLatency { get; set; }
}
6 changes: 0 additions & 6 deletions Common/Models/WebSocket/LCG/PingResponse.cs

This file was deleted.

8 changes: 3 additions & 5 deletions Common/Redis/DeviceOnline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ public sealed class DeviceOnline
public required DateTimeOffset ConnectedAt { get; set; }
public string? UserAgent { get; set; } = null;

[JsonConverter(typeof(TimeSpanToMillisecondsConverter))]
public TimeSpan? Uptime { get; set; }
[JsonConverter(typeof(TimeSpanToMillisecondsConverter))]
public TimeSpan? Latency { get; set; }
public int Rssi { get; set; }
public DateTimeOffset BootedAt { get; set; }
public ushort? LatencyMs { get; set; }
public int? Rssi { get; set; }
}
6 changes: 3 additions & 3 deletions LiveControlGateway/Controllers/DeviceControllerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ protected override async Task UnregisterConnection()
/// <summary>
/// Keep the device online
/// </summary>
protected async Task SelfOnline(TimeSpan uptime, TimeSpan? latency = null, int rssi = -70) // -70dBm = OK connection
protected async Task SelfOnline(DateTimeOffset bootedAt, ushort? latency = null, int? rssi = null)
{
Logger.LogDebug("Received keep alive from device [{DeviceId}]", CurrentDevice.Id);

Expand All @@ -168,8 +168,8 @@ protected async Task SelfOnline(TimeSpan uptime, TimeSpan? latency = null, int r
FirmwareVersion = _firmwareVersion!,
ConnectedAt = _connected,
UserAgent = _userAgent,
Uptime = uptime,
Latency = latency,
BootedAt = bootedAt,
LatencyMs = latency,
Rssi = rssi
});

Expand Down
2 changes: 1 addition & 1 deletion LiveControlGateway/Controllers/DeviceV1Controller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected override async Task Handle(HubToGatewayMessage data)
switch (payload.Kind)
{
case HubToGatewayMessagePayload.ItemKind.KeepAlive:
await SelfOnline(TimeSpan.FromMilliseconds(payload.KeepAlive.Uptime));
await SelfOnline(DateTimeOffset.UtcNow.Subtract(TimeSpan.FromMilliseconds(payload.KeepAlive.Uptime)));
break;

case HubToGatewayMessagePayload.ItemKind.OtaInstallStarted:
Expand Down
28 changes: 17 additions & 11 deletions LiveControlGateway/Controllers/DeviceV2Controller.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Asp.Versioning;
using System.Diagnostics;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
Expand Down Expand Up @@ -27,7 +28,8 @@ public sealed class DeviceV2Controller : DeviceControllerBase<HubToGatewayMessag
{
private readonly IHubContext<UserHub, IUserHub> _userHubContext;
private readonly Timer _pingTimer;
private DateTimeOffset _lastPingSent = DateTimeOffset.UtcNow;
private long _pingTimestamp = Stopwatch.GetTimestamp();
private ushort _latencyMs = 0;

/// <summary>
/// DI
Expand All @@ -54,12 +56,12 @@ private async void PingTimerElapsed(object? state)
{
try
{
_lastPingSent = DateTimeOffset.UtcNow;
_pingTimestamp = Stopwatch.GetTimestamp();
await QueueMessage(new GatewayToHubMessage
{
Payload = new GatewayToHubMessagePayload(new Ping
{
UnixUtcTime = (ulong)_lastPingSent.ToUnixTimeSeconds()
UnixUtcTime = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
})
});
}
Expand All @@ -68,11 +70,6 @@ await QueueMessage(new GatewayToHubMessage
Logger.LogError(e, "Error while sending ping message to device [{DeviceId}]", CurrentDevice.Id);
}
}

/// <summary>
/// The latency of the last ping
/// </summary>
public TimeSpan Latency { get; private set; }

private OtaUpdateStatus? _lastStatus;

Expand All @@ -90,8 +87,17 @@ protected override async Task Handle(HubToGatewayMessage data)
switch (payload.Kind)
{
case HubToGatewayMessagePayload.ItemKind.Pong:
Latency = DateTimeOffset.UtcNow - _lastPingSent;
await SelfOnline(TimeSpan.FromMilliseconds(payload.Pong.Uptime), Latency, payload.Pong.Rssi);

// Received pong without sending ping, this could be abusing the pong endpoint.
if (_pingTimestamp == 0)
{
// TODO: Kick or warn client.
return;
}

_latencyMs = (ushort)Math.Min(Stopwatch.GetElapsedTime(_pingTimestamp).TotalMilliseconds, ushort.MaxValue); // If someone has a ping higher than 65 seconds, they are messing with us. Cap it to 65 seconds
_pingTimestamp = 0;
await SelfOnline(DateTimeOffset.UtcNow.Subtract(TimeSpan.FromMilliseconds(payload.Pong.Uptime)), _latencyMs, payload.Pong.Rssi);
break;

case HubToGatewayMessagePayload.ItemKind.OtaUpdateStarted:
Expand Down
52 changes: 14 additions & 38 deletions LiveControlGateway/Controllers/LiveControlController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net.WebSockets;
using System.Diagnostics;
using System.Net.WebSockets;
using System.Text.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
Expand Down Expand Up @@ -55,17 +56,14 @@ public sealed class LiveControlController : WebsocketBaseController<IBaseRespons
private Device? _device;
private Dictionary<Guid, LiveShockerPermission> _sharedShockers = new();
private byte _tps = 10;
private long _pingTimestamp = Stopwatch.GetTimestamp();
private ushort _latencyMs = 0;

/// <summary>
/// Connection Id for this connection, unique and random per connection
/// </summary>
public Guid ConnectionId => Guid.NewGuid();

/// <summary>
/// Last latency in milliseconds, 0 initially
/// </summary>
public ulong LastLatency { get; private set; } = 0;

private readonly Timer _pingTimer = new(PingInterval);

/// <summary>
Expand Down Expand Up @@ -332,47 +330,27 @@ private Task ProcessResult(BaseRequest<LiveRequestType> request)
private async Task IntakePong(JsonDocument? requestData)
{
Logger.LogTrace("Intake pong");

var currentTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

PingResponse? pong;
try
{
pong = requestData.NewSlDeserialize<PingResponse>();

if (pong == null)
{
Logger.LogWarning("Error while deserializing pong");
await QueueMessage(new Common.Models.WebSocket.BaseResponse<LiveResponseType>
{
ResponseType = LiveResponseType.InvalidData
});
return;
}
}
catch (Exception e)

// Received pong without sending ping, this could be abusing the pong endpoint.
if (_pingTimestamp == 0)
{
Logger.LogWarning(e, "Error while deserializing frame");
await QueueMessage(new Common.Models.WebSocket.BaseResponse<LiveResponseType>
{
ResponseType = LiveResponseType.InvalidData
});
// TODO: Kick or warn client.
return;
}

var latency = currentTimestamp - pong.Timestamp;
LastLatency = Convert.ToUInt64(Math.Max(0, latency));
_latencyMs = (ushort)Math.Min(Stopwatch.GetElapsedTime(_pingTimestamp).TotalMilliseconds, ushort.MaxValue); // If someone has a ping higher than 65 seconds, they are messing with us. Cap it to 65 seconds
_pingTimestamp = 0;

if (Logger.IsEnabled(LogLevel.Trace))
Logger.LogTrace("Latency: {Latency}ms (raw: {RawLatency}ms)", LastLatency, latency);
Logger.LogTrace("Latency: {Latency}ms", _latencyMs);

await QueueMessage(new Common.Models.WebSocket.BaseResponse<LiveResponseType>
{
ResponseType = LiveResponseType.LatencyAnnounce,
Data = new LatencyAnnounceData
{
DeviceLatency = 0, // TODO: Implement device latency calculation
OwnLatency = LastLatency
OwnLatency = _latencyMs
}
});
}
Expand Down Expand Up @@ -575,13 +553,11 @@ private async Task SendPing()
Logger.LogDebug("Sending ping to live control user [{User}] for device [{Device}]", _currentUser.DbUser.Id,
Id);

_pingTimestamp = Stopwatch.GetTimestamp();
await QueueMessage(new Common.Models.WebSocket.BaseResponse<LiveResponseType>
{
ResponseType = LiveResponseType.Ping,
Data = new PingResponse
{
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
}
Data = new object {} // No data for now
});
}

Expand Down
30 changes: 15 additions & 15 deletions LiveControlGateway/LifetimeManager/DeviceLifetime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ await deviceOnline.InsertAsync(new DeviceOnline
Gateway = data.Gateway,
ConnectedAt = data.ConnectedAt,
UserAgent = data.UserAgent,
Latency = data.Latency,
BootedAt = data.BootedAt,
LatencyMs = data.LatencyMs,
Rssi = data.Rssi,
Uptime = data.Uptime
}, Duration.DeviceKeepAliveTimeout);


Expand All @@ -259,9 +259,9 @@ await deviceOnline.InsertAsync(new DeviceOnline
}

// We cannot rely on the json set anymore, since that also happens with uptime and latency
// as we dont want to send a device online status every time, we will do it here
online.Uptime = data.Uptime;
online.Latency = data.Latency;
// as we don't want to send a device online status every time, we will do it here
online.BootedAt = data.BootedAt;
online.LatencyMs = data.LatencyMs;
online.Rssi = data.Rssi;

var sendOnlineStatusUpdate = false;
Expand Down Expand Up @@ -311,27 +311,27 @@ public readonly struct SelfOnlineData
/// <param name="gateway"></param>
/// <param name="firmwareVersion"></param>
/// <param name="connectedAt"></param>
/// <param name="uptime"></param>
/// <param name="bootedAt"></param>
/// <param name="userAgent"></param>
/// <param name="latency"></param>
/// <param name="latencyMs"></param>
/// <param name="rssi"></param>
public SelfOnlineData(
Guid owner,
string gateway,
SemVersion firmwareVersion,
DateTimeOffset connectedAt,
TimeSpan uptime,
string userAgent,
TimeSpan? latency = null,
int rssi = -70)
DateTimeOffset bootedAt,
ushort? latencyMs = null,
int? rssi = null)
{
Owner = owner;
Gateway = gateway;
FirmwareVersion = firmwareVersion;
ConnectedAt = connectedAt;
Uptime = uptime;
UserAgent = userAgent;
Latency = latency;
BootedAt = bootedAt;
LatencyMs = latencyMs;
Rssi = rssi;
}

Expand Down Expand Up @@ -363,15 +363,15 @@ public SelfOnlineData(
/// <summary>
/// Hub uptime
/// </summary>
public required TimeSpan Uptime { get; init; }
public DateTimeOffset BootedAt { get; init; }

/// <summary>
/// Measured latency
/// </summary>
public TimeSpan? Latency { get; init; } = null;
public ushort? LatencyMs { get; init; } = null;

/// <summary>
/// Wifi rssi
/// </summary>
public int Rssi { get; init; } = -70;
public int? Rssi { get; init; } = null;
}

0 comments on commit 864eb8c

Please sign in to comment.