RobotNet/RobotNet.ScriptManager/Models/ScriptRobotManager.cs
2025-10-15 15:15:53 +07:00

186 lines
6.6 KiB
C#

using OpenIddict.Client;
using RobotNet.MapShares.Models;
using RobotNet.RobotShares.Models;
using RobotNet.Script;
using RobotNet.Script.Expressions;
using RobotNet.ScriptManager.Clients;
using RobotNet.ScriptManager.Helpers;
using RobotNet.Shares;
using Serialize.Linq.Serializers;
using System.Linq.Expressions;
using System.Net.Http.Headers;
namespace RobotNet.ScriptManager.Models;
public class ScriptRobotManager(OpenIddictClientService openIddictClient, string RobotManagerUrl, string[] RobotManagerScopes) : IRobotManager
{
private string? CachedToken;
private DateTime TokenExpiry;
private static readonly ExpressionSerializer expressionSerializer = new(new Serialize.Linq.Serializers.JsonSerializer());
public async Task<IRobot?> GetRobotById(string robotId)
{
var robot = new RobotManagerHubClient(robotId, $"{RobotManagerUrl}/hubs/robot-manager", async () =>
{
var result = await openIddictClient.AuthenticateWithClientCredentialsAsync(new()
{
Scopes = [.. RobotManagerScopes],
});
if (result == null || result.AccessToken == null || result.AccessTokenExpirationDate == null)
{
return null;
}
else
{
return result.AccessToken;
}
});
await robot.StartAsync();
return robot;
}
public Task<RobotState> GetRobotState(string robotId) => GetRobotState(robotId, true);
public async Task<RobotState> GetRobotState(string robotId, bool retry)
{
var accessToken = await RequestAccessToken();
if (string.IsNullOrEmpty(accessToken))
{
throw new ArgumentException("Failed to get access token");
}
using var client = new HttpClient()
{
BaseAddress = new Uri(RobotManagerUrl)
};
using var request = new HttpRequestMessage(HttpMethod.Get, $"api/RobotManager/State/{robotId}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
using var response = await client.SendAsync(request);
using var status = response.EnsureSuccessStatusCode();
if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (retry)
{
// Retry with a new access token
CachedToken = null; // Clear cached token to force a new request
return await GetRobotState(robotId, false);
}
else
{
throw new UnauthorizedAccessException("Access token is invalid or expired.");
}
}
else if (status.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new HttpRequestException($"Failed to get robot state: {status.ReasonPhrase}");
}
else
{
var state = await response.Content.ReadFromJsonAsync<MessageResult<RobotStateModel>>();
if (state == null)
{
throw new InvalidOperationException("Failed to deserialize robot state response");
}
else if (!state.IsSuccess)
{
throw new InvalidOperationException($"Robot Manager error: {state.Message}");
}
else
{
if (state.Data == null)
{
throw new InvalidOperationException("Robot state data is null.");
}
return VDA5050ScriptHelper.ConvertToRobotState(state.Data);
}
}
}
public Task<IEnumerable<string>> SearchRobots(string map, string model)
=> SearchRobots(map, model, robot => true, true);
public Task<IEnumerable<string>> SearchRobots(string map, string model, Expression<Func<RobotState, bool>> expr)
=> SearchRobots(map, model, expr, true);
public async Task<IEnumerable<string>> SearchRobots(string map, string model, Expression<Func<RobotState, bool>> expr, bool retry)
{
var accessToken = await RequestAccessToken();
using var client = new HttpClient()
{
BaseAddress = new Uri(RobotManagerUrl)
};
using var request = new HttpRequestMessage(HttpMethod.Post, $"api/RobotManager/Search");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
request.Content = JsonContent.Create(new ElementExpressionModel
{
MapName = map,
ModelName = model,
Expression = expressionSerializer.SerializeText(expr),
});
using var response = await client.SendAsync(request);
using var status = response.EnsureSuccessStatusCode();
if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (retry)
{
// Retry with a new access token
CachedToken = null; // Clear cached token to force a new request
return await SearchRobots(map, model, expr, false);
}
else
{
throw new UnauthorizedAccessException("Access token is invalid or expired.");
}
}
else if (response.EnsureSuccessStatusCode().StatusCode != System.Net.HttpStatusCode.OK)
{
throw new HttpRequestException($"Failed to search robots: {response.ReasonPhrase}");
}
else
{
var result = await response.Content.ReadFromJsonAsync<MessageResult<string[]>>() ?? throw new Exception("Failed to convert result from Robot Manager");
if (result.IsSuccess)
{
return result.Data ?? [];
}
else
{
throw new Exception($"Robot Manager error: {result.Message}");
}
}
}
public async Task<string?> RequestAccessToken()
{
try
{
if (!string.IsNullOrEmpty(CachedToken) && DateTime.UtcNow < TokenExpiry)
{
return CachedToken;
}
var result = await openIddictClient.AuthenticateWithClientCredentialsAsync(new()
{
Scopes = [.. RobotManagerScopes],
});
if (result == null || result.AccessToken == null || result.AccessTokenExpirationDate == null)
{
return null;
}
else
{
TokenExpiry = result.AccessTokenExpirationDate.Value.UtcDateTime;
CachedToken = result.AccessToken;
return CachedToken;
}
}
catch
{
return null;
}
}
}