feat(server): 添加静态状态页与调试入口

- 将状态页、调试页改为 `wwwroot` 静态资源
  - 补充调试配置接口与前端脚本
  - 为兼容层、规划层和运行时补充日志
  - 更新集成测试覆盖新入口
This commit is contained in:
2026-04-29 14:05:02 +08:00
parent 0724efebed
commit c38faddbf0
27 changed files with 2630 additions and 1894 deletions

View File

@@ -12,14 +12,17 @@ namespace Flyshot.Server.Host.Controllers;
public sealed class LegacyHttpApiController : ControllerBase
{
private readonly IControllerClientCompatService _compatService;
private readonly ILogger<LegacyHttpApiController> _logger;
/// <summary>
/// 初始化旧 HTTP 兼容控制器。
/// </summary>
/// <param name="compatService">ControllerClient 兼容服务。</param>
public LegacyHttpApiController(IControllerClientCompatService compatService)
/// <param name="logger">日志记录器。</param>
public LegacyHttpApiController(IControllerClientCompatService compatService, ILogger<LegacyHttpApiController> logger)
{
_compatService = compatService ?? throw new ArgumentNullException(nameof(compatService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
@@ -41,13 +44,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/connect_server/")]
public IActionResult ConnectServer([FromQuery] string server_ip, [FromQuery] int port)
{
_logger.LogInformation("ConnectServer 调用: server_ip={ServerIp}, port={Port}", server_ip, port);
try
{
_compatService.ConnectServer(server_ip, port);
_logger.LogInformation("ConnectServer 成功: server_ip={ServerIp}, port={Port}", server_ip, port);
return Ok(new { status = "connected" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "ConnectServer 失败: server_ip={ServerIp}, port={Port}", server_ip, port);
return LegacyBadRequest("Connect Server failed");
}
}
@@ -80,13 +86,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/setup_robot/")]
public IActionResult SetupRobot([FromQuery] string robot_name)
{
_logger.LogInformation("SetupRobot 调用: robot_name={RobotName}", robot_name);
try
{
_compatService.SetUpRobot(robot_name);
_logger.LogInformation("SetupRobot 成功: robot_name={RobotName}", robot_name);
return Ok(new { status = "robot setup" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "SetupRobot 失败: robot_name={RobotName}", robot_name);
return LegacyBadRequest("SetUpRobot failed");
}
}
@@ -152,13 +161,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpGet("/enable_robot/")]
public IActionResult EnableRobot([FromQuery] int buffer_size = 2)
{
_logger.LogInformation("EnableRobot 调用: buffer_size={BufferSize}", buffer_size);
try
{
_compatService.EnableRobot(buffer_size);
_logger.LogInformation("EnableRobot 成功");
return Ok(new { enable_robot = true });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "EnableRobot 失败");
return LegacyBadRequest("EnableRobot failed");
}
}
@@ -170,13 +182,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpGet("/disable_robot/")]
public IActionResult DisableRobot()
{
_logger.LogInformation("DisableRobot 调用");
try
{
_compatService.DisableRobot();
_logger.LogInformation("DisableRobot 成功");
return Ok(new { disable_robot = true });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "DisableRobot 失败");
return LegacyBadRequest("DisableRobot failed");
}
}
@@ -188,13 +203,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpGet("/stop_move/")]
public IActionResult StopMove()
{
_logger.LogInformation("StopMove 调用");
try
{
_compatService.StopMove();
_logger.LogInformation("StopMove 成功");
return Ok(new { status = "move stopped" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "StopMove 失败");
return LegacyBadRequest("StopMove failed");
}
}
@@ -207,13 +225,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/set_active_controller/")]
public IActionResult SetActiveController([FromQuery] bool sim)
{
_logger.LogInformation("SetActiveController 调用: sim={Sim}", sim);
try
{
_compatService.SetActiveController(sim);
_logger.LogInformation("SetActiveController 成功: sim={Sim}", sim);
return Ok(new { status = "active controller set" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "SetActiveController 失败: sim={Sim}", sim);
return LegacyBadRequest("SetActiveController failed");
}
}
@@ -226,13 +247,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/connect_robot/")]
public IActionResult ConnectRobot([FromQuery] string ip)
{
_logger.LogInformation("ConnectRobot 调用: ip={Ip}", ip);
try
{
_compatService.Connect(ip);
_logger.LogInformation("ConnectRobot 成功: ip={Ip}", ip);
return Ok(new { status = "robot connected" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "ConnectRobot 失败: ip={Ip}", ip);
return LegacyBadRequest("Connect failed");
}
}
@@ -244,13 +268,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/disconnect_robot/")]
public IActionResult DisconnectRobot()
{
_logger.LogInformation("DisconnectRobot 调用");
try
{
_compatService.Disconnect();
_logger.LogInformation("DisconnectRobot 成功");
return Ok(new { status = "robot disconnected" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "DisconnectRobot 失败");
return LegacyBadRequest("Disconnect failed");
}
}
@@ -286,13 +313,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/set_tcp/")]
public IActionResult SetTcp([FromBody] LegacyTcpRequest tcp_data)
{
_logger.LogInformation("SetTcp 调用: x={X}, y={Y}, z={Z}", tcp_data.x, tcp_data.y, tcp_data.z);
try
{
_compatService.SetTcp(tcp_data.x, tcp_data.y, tcp_data.z);
_logger.LogInformation("SetTcp 成功");
return Ok(new { status = "TCP set" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "SetTcp 失败");
return LegacyBadRequest("SetTCP failed");
}
}
@@ -324,13 +354,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/set_io/")]
public IActionResult SetIo([FromQuery] int port, [FromQuery] bool value, [FromQuery] string io_type)
{
_logger.LogInformation("SetIo 调用: port={Port}, value={Value}, io_type={IoType}", port, value, io_type);
try
{
_compatService.SetIo(port, value, io_type);
_logger.LogInformation("SetIo 成功: port={Port}, value={Value}", port, value);
return Ok(new { status = "IO set" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "SetIo 失败: port={Port}, value={Value}", port, value);
return LegacyBadRequest("SetDigitalOutput failed");
}
}
@@ -344,12 +377,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpGet("/get_io/")]
public IActionResult GetIo([FromQuery] int port, [FromQuery] string io_type)
{
_logger.LogInformation("GetIo 调用: port={Port}, io_type={IoType}", port, io_type);
try
{
return Ok(new { value = _compatService.GetIo(port, io_type) });
var value = _compatService.GetIo(port, io_type);
_logger.LogInformation("GetIo 成功: port={Port}, value={Value}", port, value);
return Ok(new { value });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "GetIo 失败: port={Port}", port);
return LegacyBadRequest("GetDigitalOutput failed");
}
}
@@ -379,13 +416,17 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/move_joint/")]
public IActionResult MoveJoint([FromBody] LegacyJointPositionRequest joint_data)
{
_logger.LogInformation("MoveJoint 调用: 关节数={JointCount}", joint_data.joints.Count);
_logger.LogDebug("MoveJoint 路点: {Joints}", string.Join(", ", joint_data.joints.Select(j => j.ToString("F4"))));
try
{
_compatService.MoveJoint(joint_data.joints);
_logger.LogInformation("MoveJoint 成功");
return Ok(new { status = "robot moved" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "MoveJoint 失败");
return LegacyBadRequest("MoveJoint failed");
}
}
@@ -441,16 +482,20 @@ public sealed class LegacyHttpApiController : ControllerBase
[FromQuery] string? method = null,
[FromQuery] bool? save_traj = null)
{
_logger.LogInformation("ExecuteTrajectory 调用: method={Method}, save_traj={SaveTraj}", method ?? "icsp", save_traj ?? false);
try
{
var request = ParseExecuteTrajectoryRequest(waypoints, method, save_traj);
_logger.LogDebug("ExecuteTrajectory 路点数={WaypointCount}, method={Method}", request.Waypoints.Count, request.Method);
_compatService.ExecuteTrajectory(
request.Waypoints,
new TrajectoryExecutionOptions(request.Method, request.SaveTrajectory));
_logger.LogInformation("ExecuteTrajectory 成功: method={Method}", request.Method);
return Ok(new { status = "trajectory executed" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "ExecuteTrajectory 失败");
return LegacyBadRequest("ExecuteTrajectory failed");
}
}
@@ -463,18 +508,30 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/upload_flyshot/")]
public IActionResult UploadFlyshot([FromBody] LegacyFlightTrajectoryRequest trajectory_data)
{
_logger.LogInformation(
"UploadFlyshot 调用: name={Name}, waypoints={WaypointCount}, shot_flags={ShotCount}",
trajectory_data.name,
trajectory_data.waypoints.Count,
trajectory_data.shot_flags.Count(static f => f));
if (trajectory_data.shot_flags.Count != trajectory_data.waypoints.Count)
{
_logger.LogWarning("UploadFlyshot 校验失败: shot_flags长度({ShotFlagsCount}) != 路点数({WaypointCount})",
trajectory_data.shot_flags.Count, trajectory_data.waypoints.Count);
return LegacyValidationError("shot_flags长度必须与路点数量相同");
}
if (trajectory_data.offset_values.Count != trajectory_data.waypoints.Count)
{
_logger.LogWarning("UploadFlyshot 校验失败: offset_values长度({OffsetCount}) != 路点数({WaypointCount})",
trajectory_data.offset_values.Count, trajectory_data.waypoints.Count);
return LegacyValidationError("offset_values长度必须与路点数量相同");
}
if (trajectory_data.addrs.Count != trajectory_data.waypoints.Count)
{
_logger.LogWarning("UploadFlyshot 校验失败: addrs长度({AddrCount}) != 路点数({WaypointCount})",
trajectory_data.addrs.Count, trajectory_data.waypoints.Count);
return LegacyValidationError("addrs长度必须与路点数量相同");
}
@@ -488,10 +545,12 @@ public sealed class LegacyHttpApiController : ControllerBase
addressGroups: trajectory_data.addrs);
_compatService.UploadTrajectory(trajectory);
_logger.LogInformation("UploadFlyshot 成功: name={Name}", trajectory_data.name);
return Ok(new { status = "FlyShot uploaded" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "UploadFlyshot 失败: name={Name}", trajectory_data.name);
return LegacyBadRequest("UploadFlyShotTraj failed");
}
}
@@ -504,6 +563,9 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/execute_flyshot/")]
public IActionResult ExecuteFlyshot([FromBody] LegacyExecuteFlyshotRequest data)
{
_logger.LogInformation(
"ExecuteFlyshot 调用: name={Name}, method={Method}, move_to_start={MoveToStart}, use_cache={UseCache}",
data.name, data.method, data.move_to_start, data.use_cache);
try
{
_compatService.ExecuteTrajectoryByName(
@@ -513,10 +575,12 @@ public sealed class LegacyHttpApiController : ControllerBase
method: data.method,
saveTrajectory: data.save_traj,
useCache: data.use_cache));
_logger.LogInformation("ExecuteFlyshot 成功: name={Name}", data.name);
return Ok(new { status = "FlyShot executed", success = true });
}
catch (Exception exception)
{
_logger.LogError(exception, "ExecuteFlyshot 失败: name={Name}", data.name);
return StatusCode(StatusCodes.Status500InternalServerError, new { detail = exception.Message });
}
}
@@ -529,17 +593,21 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/save_traj_info/")]
public IActionResult SaveTrajectoryInfo([FromBody] LegacyTrajectoryInfoRequest request)
{
_logger.LogInformation("SaveTrajectoryInfo 调用: name={Name}, method={Method}", request.name, request.method);
try
{
_compatService.SaveTrajectoryInfo(request.name, request.method);
_logger.LogInformation("SaveTrajectoryInfo 成功: name={Name}", request.name);
return Ok(new { status = "trajectory info saved", success = true });
}
catch (NotSupportedException exception)
{
_logger.LogWarning(exception, "SaveTrajectoryInfo 不支持: name={Name}", request.name);
return StatusCode(StatusCodes.Status501NotImplemented, new { detail = exception.Message });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "SaveTrajectoryInfo 失败: name={Name}", request.name);
return LegacyBadRequest("SaveTrajInfo failed");
}
}
@@ -552,6 +620,7 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/is_flyShotTrajValid/")]
public IActionResult IsFlyshotTrajectoryValid([FromBody] LegacyFlyshotValidationRequest request)
{
_logger.LogInformation("IsFlyshotTrajectoryValid 调用: name={Name}, method={Method}", request.name, request.method);
try
{
var isValid = _compatService.IsFlyshotTrajectoryValid(
@@ -560,14 +629,17 @@ public sealed class LegacyHttpApiController : ControllerBase
request.method,
request.save_traj);
_logger.LogInformation("IsFlyshotTrajectoryValid 结果: name={Name}, valid={Valid}, duration={Duration}s", request.name, isValid, duration.TotalSeconds);
return Ok(new { success = isValid, valid = isValid, time = duration.TotalSeconds });
}
catch (NotSupportedException exception)
{
_logger.LogWarning(exception, "IsFlyshotTrajectoryValid 不支持: name={Name}", request.name);
return StatusCode(StatusCodes.Status501NotImplemented, new { detail = exception.Message });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "IsFlyshotTrajectoryValid 失败: name={Name}", request.name);
return LegacyBadRequest("IsFlyShotTrajValid failed");
}
}
@@ -580,13 +652,23 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/set_speedRatio/")]
public IActionResult SetSpeedRatio([FromBody] LegacySpeedRatioRequest data)
{
_logger.LogInformation("SetSpeedRatio 调用: speed={Speed}", data.speed);
try
{
// 验证数值 范围符合预期(例如 0.01到 1.0),以避免对控制器造成潜在风险
if (data.speed < 0.01 || data.speed > 1.0)
{
_logger.LogWarning("SetSpeedRatio 参数无效: speed={Speed}", data.speed);
return BadRequest(new { detail = "Speed ratio must be between 0.01 and 1.0." });
}
_compatService.SetSpeedRatio(data.speed);
_logger.LogInformation("SetSpeedRatio 成功: speed={Speed}", data.speed);
return Ok(new { message = "set_speedRatio executed", returnCode = 0 });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "SetSpeedRatio 失败: speed={Speed}", data.speed);
return LegacyBadRequest("set_speedRatio failed");
}
}
@@ -599,13 +681,16 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/delete_flyshot/")]
public IActionResult DeleteFlyshot([FromBody] LegacyNameRequest request)
{
_logger.LogInformation("DeleteFlyshot 调用: name={Name}", request.name);
try
{
_compatService.DeleteTrajectory(request.name);
_logger.LogInformation("DeleteFlyshot 成功: name={Name}", request.name);
return Ok(new { status = "FlyShot deleted" });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "DeleteFlyshot 失败: name={Name}", request.name);
return LegacyBadRequest("DeleteFlyShotTraj failed");
}
}
@@ -618,22 +703,28 @@ public sealed class LegacyHttpApiController : ControllerBase
[HttpPost("/init_mpc_robt")]
public IActionResult InitMpcRobot([FromBody] LegacyInitMpcRobotRequest data)
{
_logger.LogInformation(
"InitMpcRobot 调用: robot_name={RobotName}, robot_ip={RobotIp}, sim={Sim}, server={ServerIp}:{Port}",
data.robot_name, data.robot_ip, data.sim, data.server_ip, data.port);
try
{
_compatService.ConnectServer(data.server_ip, data.port);
_compatService.SetUpRobot(data.robot_name);
if (!_compatService.IsSetUp)
{
_logger.LogWarning("InitMpcRobot 失败: Robot not setup");
return LegacyBadRequest("Robot not setup");
}
_compatService.SetActiveController(data.sim);
_compatService.Connect(data.robot_ip);
_compatService.EnableRobot(2);
_logger.LogInformation("InitMpcRobot 成功: robot_name={RobotName}", data.robot_name);
return Ok(new { message = "init_Success", returnCode = 0 });
}
catch
catch (Exception exception)
{
_logger.LogError(exception, "InitMpcRobot 失败");
return LegacyBadRequest("Connect Server failed");
}
}