186 lines
6.6 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|