Files
Print_server/EpsonPrintService/PuduRobotClient.cs

119 lines
3.9 KiB
C#

using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
namespace EpsonPrintService;
public sealed class PuduRobotClient
{
private readonly HttpClient _http;
private readonly string _baseUrl;
private readonly string _username;
private readonly string _password;
private string? _token;
public PuduRobotClient(string baseUrl, string username, string password, HttpClient? http = null)
{
_baseUrl = baseUrl.TrimEnd('/');
_username = username;
_password = password;
_http = http ?? new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
}
public async Task SendToTableAsync(string robotSn, string targetPoint, CancellationToken ct = default)
{
await EnsureTokenAsync(ct);
try
{
await PostAddCommandAsync(robotSn, targetPoint, ct);
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
// Token expired — refresh once and retry.
_token = null;
await EnsureTokenAsync(ct);
await PostAddCommandAsync(robotSn, targetPoint, ct);
}
}
private async Task EnsureTokenAsync(CancellationToken ct)
{
if (_token is not null) return;
using var resp = await _http.PostAsJsonAsync(
$"{_baseUrl}/api/Auth/login",
new { username = _username, password = _password },
ct);
resp.EnsureSuccessStatusCode();
var bodyText = await resp.Content.ReadAsStringAsync(ct);
var token = ExtractToken(bodyText);
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException($"Pudu login: no token returned. Body: {bodyText}");
_token = token;
}
private async Task PostAddCommandAsync(string robotSn, string targetPoint, CancellationToken ct)
{
using var req = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/api/Robots/add-command")
{
Content = JsonContent.Create(new { Sn = robotSn, TargetPoint = targetPoint })
};
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);
using var resp = await _http.SendAsync(req, ct);
resp.EnsureSuccessStatusCode();
}
// Recursive search so we match the token regardless of wrapping (e.g. {"data":{"token":"..."}}).
// Mirrors the behavior of `sed -n 's/.*"token":"\([^"]*\)".*/\1/p'` in send_robot.sh.
private static string? ExtractToken(string json)
{
try
{
using var doc = JsonDocument.Parse(json);
return FindTokenProperty(doc.RootElement);
}
catch (JsonException)
{
return null;
}
}
private static string? FindTokenProperty(JsonElement element)
{
switch (element.ValueKind)
{
case JsonValueKind.Object:
foreach (var prop in element.EnumerateObject())
{
if ((prop.Name.Equals("token", StringComparison.OrdinalIgnoreCase)
|| prop.Name.Equals("accessToken", StringComparison.OrdinalIgnoreCase))
&& prop.Value.ValueKind == JsonValueKind.String)
{
return prop.Value.GetString();
}
}
foreach (var prop in element.EnumerateObject())
{
var nested = FindTokenProperty(prop.Value);
if (nested is not null) return nested;
}
return null;
case JsonValueKind.Array:
foreach (var item in element.EnumerateArray())
{
var nested = FindTokenProperty(item);
if (nested is not null) return nested;
}
return null;
default:
return null;
}
}
}