Files
FlyShotHost/src/Flyshot.Server.Host/wwwroot/debug.html
yunxiao.zhu 5193f537b0 ✨ feat(planning): 添加离线飞拍规划能力
* 固化离线飞拍轨迹加载、保存和规划结果。

* 真机初始化失败时保留模型加载后的离线规划能力。

* 更新状态接口、调试页和回归测试覆盖。
2026-06-24 11:40:07 +08:00

295 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Flyshot Replacement 接口调试</title>
<link rel="stylesheet" href="/assets/debug.css">
</head>
<body>
<header>
<div class="topbar">
<h1>Flyshot Replacement 接口调试</h1>
<div class="actions">
<a class="link-button" href="/status.html">回到状态页</a>
<a class="link-button" href="/swagger" target="_blank" rel="noopener">Swagger UI</a>
<button id="reload-spec" type="button">重新加载 OpenAPI</button>
</div>
</div>
</header>
<main>
<section class="meta">
<dl>
<dt>OpenAPI 文档</dt>
<dd id="meta-spec-url">--</dd>
<dt>API 数量</dt>
<dd id="meta-operation-count">--</dd>
<dt>加载状态</dt>
<dd id="meta-status">初始化中...</dd>
</dl>
</section>
<section class="offline-panel" id="offline-planning-panel">
<h2>离线飞拍规划</h2>
<p class="offline-warning">生成轨迹不会向机器人发送命令;只有 RealReady 状态才能执行真实运动。</p>
<div class="offline-grid">
<label>
机器人模型
<input id="offline-robot-name" value="FANUC_LR_Mate_200iD">
</label>
<button id="offline-load-model" type="button">加载模型</button>
<label>
已上传轨迹
<select id="offline-trajectory-select"></select>
</label>
<button id="offline-refresh-trajectories" type="button" class="secondary">刷新列表</button>
<label>
规划方法
<select id="offline-planning-method">
<option value="icsp">icsp</option>
<option value="self-adapt-icsp">self-adapt-icsp</option>
</select>
</label>
<button id="offline-generate" type="button">生成最终发送轨迹</button>
</div>
<details>
<summary>上传飞拍轨迹 JSON</summary>
<textarea id="offline-upload-json" class="body-editor">{
"name": "demo-flyshot",
"waypoints": [[0,0,0,0,0,0],[0.001,0,0,0,0,0],[0.002,0,0,0,0,0],[0.003,0,0,0,0,0]],
"shot_flags": [false,true,false,false],
"offset_values": [0,1,0,0],
"addrs": [[],[7,8],[],[]]
}</textarea>
<button id="offline-upload" type="button">上传并刷新列表</button>
</details>
<div id="offline-planning-status" class="empty-hint">等待离线规划操作。</div>
<div id="offline-planning-summary"></div>
<div class="offline-table-wrap">
<table id="offline-send-table">
<thead>
<tr><th>序号</th><th>发送时间</th><th>轨迹时间</th><th>关节角(deg)</th><th>IO Mask</th><th>IO Value</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="button-row">
<button id="offline-prev-page" type="button" class="secondary">上一页</button>
<span id="offline-page-info">0 / 0</span>
<button id="offline-next-page" type="button" class="secondary">下一页</button>
</div>
<pre id="offline-shot-events" class="response-body"></pre>
<pre id="offline-artifact-paths" class="response-body"></pre>
</section>
<div id="debug-console-app">
<div class="empty-hint">正在加载接口列表...</div>
</div>
</main>
<aside class="history" id="history-panel">
<h3>
<span>调用历史 (本次会话)</span>
<button type="button" id="history-clear" class="secondary">清空</button>
</h3>
<ul id="history-list"></ul>
</aside>
<script id="debug-static-docs" type="application/json">
{
"GET /healthz": {
"summary": "宿主健康检查",
"description": "返回 replacement 宿主的基础健康状态,适合探活和部署验收。",
"requestBodyFields": []
},
"GET /status": {
"summary": "跳转状态页",
"description": "短路由入口,浏览器访问后会跳转到静态状态监控页 `/status.html`。",
"requestBodyFields": []
},
"GET /debug": {
"summary": "跳转调试页",
"description": "短路由入口,浏览器访问后会跳转到静态调试页 `/debug.html`。",
"requestBodyFields": []
},
"GET /api/status/snapshot": {
"summary": "读取状态快照",
"description": "返回当前 ControllerClient 兼容层和 FANUC 运行时的聚合状态快照,供状态页和外部诊断读取。",
"requestBodyFields": []
},
"POST /setup_robot/": {
"summary": "按名称加载机器人",
"description": "只加载机器人模型和 RobotConfig.json,不连接、不使能;成功后可进行离线规划。",
"requestBodyFields": []
},
"GET /enable_robot/": {
"summary": "使能机器人",
"description": "调用兼容层使能机器人,并可通过查询参数传入控制器执行缓冲区大小。",
"requestBodyFields": []
},
"GET /disable_robot/": {
"summary": "去使能机器人",
"description": "调用兼容层关闭机器人使能,通常用于结束调试或执行链路回收。",
"requestBodyFields": []
},
"GET /stop_move/": {
"summary": "停止当前运动",
"description": "触发兼容层的 `StopMove` 语义,用于终止当前点动或轨迹执行。",
"requestBodyFields": []
},
"POST /connect_robot/": {
"summary": "连接机器人控制器",
"description": "根据查询参数 `ip` 连接机器人控制器,建立后续命令与状态链路。",
"requestBodyFields": []
},
"POST /disconnect_robot/": {
"summary": "断开机器人连接",
"description": "主动断开当前机器人控制器连接,释放兼容层运行时资源。",
"requestBodyFields": []
},
"GET /robot_info/": {
"summary": "读取机器人信息",
"description": "返回机器人名称、自由度、速度倍率和服务端版本等聚合元信息。",
"requestBodyFields": []
},
"POST /set_tcp/": {
"summary": "设置 TCP 偏移",
"description": "设置当前工具中心点的三维偏移,单位按旧接口约定使用毫米。",
"requestBodyFields": [
{ "name": "x", "type": "number", "required": true, "description": "TCP 的 X 偏移,单位 mm。" },
{ "name": "y", "type": "number", "required": true, "description": "TCP 的 Y 偏移,单位 mm。" },
{ "name": "z", "type": "number", "required": true, "description": "TCP 的 Z 偏移,单位 mm。" }
]
},
"GET /get_tcp/": {
"summary": "读取 TCP 偏移",
"description": "返回当前生效的 TCP 三维偏移值。",
"requestBodyFields": []
},
"POST /set_io/": {
"summary": "设置数字 IO",
"description": "通过查询参数设置指定端口的 IO 值和 IO 类型,常用于现场脉冲或夹具联动调试。",
"requestBodyFields": []
},
"GET /get_io/": {
"summary": "读取数字 IO",
"description": "读取指定端口和类型的当前 IO 值,便于确认控制柜当前输入输出状态。",
"requestBodyFields": []
},
"GET /get_joint_position/": {
"summary": "读取当前关节位置",
"description": "返回旧 HTTP 层兼容的关节数组外形,便于现场确认当前姿态。",
"requestBodyFields": []
},
"POST /move_joint/": {
"summary": "执行关节点到点运动",
"description": "根据目标关节数组执行一次关节空间点到点运动;实际执行链路会进入兼容层与 J519 运行时。",
"requestBodyFields": [
{ "name": "joints", "type": "array<number>", "required": true, "description": "目标关节数组,按关节顺序给出数值。" }
]
},
"POST /move_pose/": {
"summary": "执行笛卡尔点到点运动",
"description": "以 `[x,y,z,w,p,r]` 直角位姿执行点到点移动。六个字段必须完整提供,`x/y/z` 使用 mm,`w/p/r` 使用 deg,并受当前入口限幅约束。",
"requestBodyFields": [
{ "name": "x", "type": "number", "required": true, "description": "TCP X,单位 mm,当前入口限制在 -1000 到 1000 之间。" },
{ "name": "y", "type": "number", "required": true, "description": "TCP Y,单位 mm,当前入口限制在 -1000 到 1000 之间。" },
{ "name": "z", "type": "number", "required": true, "description": "TCP Z,单位 mm,当前入口限制在 0 到 1200 之间。" },
{ "name": "w", "type": "number", "required": true, "description": "姿态 W,单位 deg,当前入口限制在 -180 到 180 之间。" },
{ "name": "p", "type": "number", "required": true, "description": "姿态 P,单位 deg,当前入口限制在 -90 到 90 之间。" },
{ "name": "r", "type": "number", "required": true, "description": "姿态 R,单位 deg,当前入口限制在 -180 到 180 之间。" }
]
},
"POST /get_nearest_ik/": {
"summary": "求最近 IK",
"description": "根据目标位姿和 seed 关节数组求最近一组 IK 解;当前若底层未实现,会返回 501。",
"requestBodyFields": [
{ "name": "pose", "type": "array<number>", "required": true, "description": "目标位姿数组,约定为 `[x,y,z,qx,qy,qz,qw]`。" },
{ "name": "seed", "type": "array<number>", "required": true, "description": "IK seed 关节数组,用作就近求解参考。" }
]
},
"GET /list_flyShotTraj/": {
"summary": "列出已上传飞拍轨迹",
"description": "返回当前运行目录中已经保存的飞拍轨迹名称列表。",
"requestBodyFields": []
},
"POST /execute_trajectory/": {
"summary": "执行普通轨迹",
"description": "执行普通关节轨迹。该接口兼容两种历史请求体形态:一是对象形态 `{ waypoints, method, save_traj }`,二是直接传纯数组路点;查询参数里的 `method` 和 `save_traj` 仍可覆盖请求体值。",
"requestBodyFields": [
{ "name": "waypoints", "type": "array<array<number>> | array<object>", "required": true, "description": "历史兼容对象形态中的轨迹路点集合;每个路点既可直接是关节数组,也可写成 `{ joints: [...] }`。" },
{ "name": "method", "type": "string", "required": false, "description": "对象形态中的轨迹规划方法,默认 `icsp`。" },
{ "name": "save_traj", "type": "boolean", "required": false, "description": "对象形态中的是否保存轨迹标记,默认 `false`。" },
{ "name": "根数组", "type": "array<array<number>> | array<object>", "required": true, "description": "纯数组形态时,请直接提交路点数组本身,而不是对象包裹层。" },
{ "name": "根数组[].joints", "type": "array<number>", "required": false, "description": "当路点元素是对象形态时,兼容层会读取其中的 `joints` 数组。" }
]
},
"POST /upload_flyshot/": {
"summary": "上传飞拍轨迹",
"description": "上传一条带拍照触发信息的飞拍轨迹。`waypoints`、`shot_flags`、`offset_values`、`addrs` 四组数据长度必须一致。",
"requestBodyFields": [
{ "name": "name", "type": "string", "required": true, "description": "飞拍轨迹名称,后续执行、校验和删除都通过它定位。" },
{ "name": "waypoints", "type": "array<array<number>>", "required": true, "description": "飞拍轨迹的关节路点集合。" },
{ "name": "shot_flags", "type": "array<boolean>", "required": true, "description": "与每个路点对齐的拍照触发标志数组。" },
{ "name": "offset_values", "type": "array<number>", "required": true, "description": "与每个路点对齐的触发偏移周期集合。" },
{ "name": "addrs", "type": "array<array<integer>>", "required": true, "description": "与每个路点对齐的 IO 地址组集合。" }
]
},
"POST /execute_flyshot/": {
"summary": "执行已上传飞拍轨迹",
"description": "按名称执行一条已上传的飞拍轨迹,可控制是否先移动到起点、是否复用缓存、是否保存轨迹以及是否等待执行完成。",
"requestBodyFields": [
{ "name": "name", "type": "string", "required": true, "description": "要执行的飞拍轨迹名称。" },
{ "name": "move_to_start", "type": "boolean", "required": false, "description": "是否先用临时 PTP 轨迹移动到飞拍起点,默认 `true`。" },
{ "name": "method", "type": "string", "required": false, "description": "飞拍轨迹生成方法,默认 `icsp`。" },
{ "name": "save_traj", "type": "boolean", "required": false, "description": "是否保存轨迹信息,默认 `true`。" },
{ "name": "use_cache", "type": "boolean", "required": false, "description": "是否优先复用已缓存的轨迹结果,默认 `true`。" },
{ "name": "wait", "type": "boolean", "required": false, "description": "是否等待整条飞拍轨迹执行完成后再返回,默认 `true`。" }
]
},
"POST /save_traj_info/": {
"summary": "离线生成最终发送轨迹",
"description": "仅要求机器人模型已加载;完成规划、触发绑定、最终 8ms 序列生成和文件导出,并返回完整规划结果。",
"requestBodyFields": [
{ "name": "name", "type": "string", "required": true, "description": "目标飞拍轨迹名称。" },
{ "name": "method", "type": "string", "required": false, "description": "轨迹规划方法,默认 `icsp`。" }
]
},
"POST /is_flyShotTrajValid/": {
"summary": "校验飞拍轨迹有效性",
"description": "检查指定飞拍轨迹是否可生成有效执行结果,并返回轨迹时长;底层未实现时会返回 501。",
"requestBodyFields": [
{ "name": "name", "type": "string", "required": true, "description": "要校验的飞拍轨迹名称。" },
{ "name": "method", "type": "string", "required": false, "description": "轨迹规划方法,默认 `icsp`。" },
{ "name": "save_traj", "type": "boolean", "required": false, "description": "是否同时保存轨迹信息,默认 `true`。" }
]
},
"POST /set_speedRatio/": {
"summary": "设置运行速度倍率",
"description": "设置机器人运行速度倍率。当前接口要求 `speed` 在 `0.01` 到 `1.0` 之间,否则会直接返回 400。",
"requestBodyFields": [
{ "name": "speed", "type": "number", "required": true, "description": "目标速度倍率,允许范围为 0.01 到 1.0。" }
]
},
"POST /delete_flyshot/": {
"summary": "删除飞拍轨迹",
"description": "按名称删除一条已上传的飞拍轨迹及其持久化记录。",
"requestBodyFields": [
{ "name": "name", "type": "string", "required": true, "description": "要删除的飞拍轨迹名称。" }
]
},
"POST /init_mpc_robot": {
"summary": "真机优先初始化",
"description": "执行 `加载模型 -> 连接真机 -> EnableRobot(8)`;连接失败时模型仍保持 OfflineReady。",
"requestBodyFields": [
{ "name": "robot_name", "type": "string", "required": true, "description": "机器人名称,用于加载模型与配置。" },
{ "name": "robot_ip", "type": "string", "required": true, "description": "机器人控制器 IP。" }
]
},
"GET /get_pose": {
"summary": "读取当前末端位姿",
"description": "返回当前末端位姿数组,便于确认机器人 TCP 的当前空间位置。",
"requestBodyFields": []
}
}
</script>
<script src="/assets/debug.js" defer></script>
</body>
</html>