Files
FlyShotHost/tests/Flyshot.Server.IntegrationTests/StatusEndpointTests.cs
yunxiao.zhu 2cd42f04e5 feat(fanuc): 添加直角坐标点动功能与相关接口
* 新增 `MovePose` 方法,支持以直角坐标执行点到点移动。
* 引入 `LegacyCartesianPoseRequest` 类,处理直角位姿请求体的解析与验证。
* 更新 `LegacyHttpApiController`,实现 `/move_pose/` 路由以支持新功能。
* 增强状态快照元数据,提供机器人初始化状态与已上传轨迹信息。
* 更新前端状态页面,增加直角坐标点动控制面板与步长设置选项。
* 相关文档与测试用例同步更新,确保新功能的完整性与稳定性。
2026-05-14 17:46:42 +08:00

137 lines
6.0 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
namespace Flyshot.Server.IntegrationTests;
/// <summary>
/// 验证状态监控页面和状态快照 API 能读取当前 ControllerClient 兼容层状态。
/// </summary>
public sealed class StatusEndpointTests(FlyshotServerFactory factory) : IClassFixture<FlyshotServerFactory>
{
/// <summary>
/// 验证状态页作为 wwwroot 静态 HTML 暴露,并引用状态快照 API。
/// </summary>
[Fact]
public async Task GetStatusHtml_ReturnsMonitoringStaticPage()
{
using var client = factory.CreateClient();
using var response = await client.GetAsync("/status.html");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.StartsWith("text/html", response.Content.Headers.ContentType?.MediaType);
var html = await response.Content.ReadAsStringAsync();
Assert.Contains("Flyshot Replacement 状态监控", html, StringComparison.Ordinal);
Assert.Contains("/assets/status.css", html, StringComparison.Ordinal);
Assert.Contains("/assets/status.js", html, StringComparison.Ordinal);
using var scriptResponse = await client.GetAsync("/assets/status.js");
Assert.Equal(HttpStatusCode.OK, scriptResponse.StatusCode);
var script = await scriptResponse.Content.ReadAsStringAsync();
Assert.Contains("/api/status/snapshot", script, StringComparison.Ordinal);
}
/// <summary>
/// 状态页应当提供直角坐标点动按钮,并复用现有 MovePose HTTP 接口。
/// </summary>
[Fact]
public async Task GetStatusPageAssets_ExposeCartesianJogControls()
{
using var client = factory.CreateClient();
using var htmlResponse = await client.GetAsync("/status.html");
Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode);
var html = await htmlResponse.Content.ReadAsStringAsync();
Assert.Contains("直角坐标点动", html, StringComparison.Ordinal);
Assert.Contains("id=\"linear-step\"", html, StringComparison.Ordinal);
Assert.Contains("id=\"angular-step\"", html, StringComparison.Ordinal);
foreach (var axis in new[] { "x", "y", "z", "w", "p", "r" })
{
Assert.Contains($"data-axis=\"{axis}\"", html, StringComparison.Ordinal);
Assert.Contains($"data-axis=\"{axis}\" data-direction=\"1\"", html, StringComparison.Ordinal);
Assert.Contains($"data-axis=\"{axis}\" data-direction=\"-1\"", html, StringComparison.Ordinal);
}
using var scriptResponse = await client.GetAsync("/assets/status.js");
Assert.Equal(HttpStatusCode.OK, scriptResponse.StatusCode);
var script = await scriptResponse.Content.ReadAsStringAsync();
Assert.Contains("/move_pose/", script, StringComparison.Ordinal);
Assert.Contains("cartesianPose", script, StringComparison.Ordinal);
Assert.Contains("pointerdown", script, StringComparison.Ordinal);
Assert.Contains("pointerup", script, StringComparison.Ordinal);
}
/// <summary>
/// 验证状态快照 API 会返回运行时连接、使能、速度和机器人元数据。
/// </summary>
[Fact]
public async Task GetStatusSnapshot_ReturnsRuntimeStateAfterLegacyInitialization()
{
using var client = factory.CreateClient();
await InitializeRobotAsync(client);
using (var speedResponse = await client.PostAsJsonAsync("/set_speedRatio/", new { speed = 0.75 }))
{
Assert.Equal(HttpStatusCode.OK, speedResponse.StatusCode);
}
using var response = await client.GetAsync("/api/status/snapshot");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
await using var responseStream = await response.Content.ReadAsStreamAsync();
using var json = await JsonDocument.ParseAsync(responseStream);
var root = json.RootElement;
var snapshot = root.GetProperty("snapshot");
Assert.Equal("ok", root.GetProperty("status").GetString());
Assert.True(root.GetProperty("isSetup").GetBoolean());
Assert.Equal("FANUC_LR_Mate_200iD", root.GetProperty("robotName").GetString());
Assert.Equal(6, root.GetProperty("degreesOfFreedom").GetInt32());
var uploadedTrajectories = root.GetProperty("uploadedTrajectories")
.EnumerateArray()
.Select(static value => value.GetString())
.ToArray();
Assert.Contains("20251015", uploadedTrajectories);
Assert.Contains("UTTC_MS11", uploadedTrajectories);
Assert.Equal("Connected", snapshot.GetProperty("connectionState").GetString());
Assert.True(snapshot.GetProperty("isEnabled").GetBoolean());
Assert.False(snapshot.GetProperty("isInMotion").GetBoolean());
Assert.Equal(0.75, snapshot.GetProperty("speedRatio").GetDouble(), precision: 6);
Assert.Equal(6, snapshot.GetProperty("jointPositions").GetArrayLength());
}
/// <summary>
/// 初始化旧 HTTP 兼容链路,使状态页可以读取一个完整的已连接状态。
/// </summary>
/// <param name="client">测试 HTTP 客户端。</param>
private static async Task InitializeRobotAsync(HttpClient client)
{
using (var setupResponse = await client.PostAsync("/setup_robot/?robot_name=FANUC_LR_Mate_200iD", content: null))
{
Assert.Equal(HttpStatusCode.OK, setupResponse.StatusCode);
}
using (var activeControllerResponse = await client.PostAsync("/set_active_controller/?sim=true", content: null))
{
Assert.Equal(HttpStatusCode.OK, activeControllerResponse.StatusCode);
}
using (var connectRobotResponse = await client.PostAsync("/connect_robot/?ip=192.168.10.101", content: null))
{
Assert.Equal(HttpStatusCode.OK, connectRobotResponse.StatusCode);
}
using (var enableRobotResponse = await client.GetAsync("/enable_robot/"))
{
Assert.Equal(HttpStatusCode.OK, enableRobotResponse.StatusCode);
}
}
}