feat(motor): 序列每步新增绿色「单发」按钮,支持仅发送该步动作一次
This commit is contained in:
+56
-1
@@ -1997,6 +1997,7 @@ const seq = {
|
||||
round: 1, /* 当前轮(1 起) */
|
||||
stepIdx: 0, /* 当前步索引 */
|
||||
statusText: "未运行",
|
||||
singleIdx: null, /* 正在单发的步骤索引,null = 无单发在途 */
|
||||
};
|
||||
const seqListEl = $("#seqList");
|
||||
const seqStartBtn = $("#seqStartBtn");
|
||||
@@ -2069,6 +2070,9 @@ function renderSeqList() {
|
||||
<input class="seq-num seq-hold" data-f="holdMs" type="number" value="${s.holdMs}" min="0" max="600000" step="100" aria-label="步骤 ${i + 1} 保持时间 ms" />
|
||||
<span class="seq-hold-unit">ms</span>
|
||||
<div class="seq-ops">
|
||||
<button data-op="sendOne" title="单发本步(仅发送这一步的全部电机动作,不推进序列)" class="seq-send-one">
|
||||
<svg width="13" height="13" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M4.5 2.8 11 7l-6.5 4.2z" fill="currentColor"/></svg>
|
||||
</button>
|
||||
<button data-op="addAction" title="本步添加电机动作(复制最后一个动作)">
|
||||
<svg width="13" height="13" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M7 2.5v9M2.5 7h9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||||
</button>
|
||||
@@ -2118,8 +2122,10 @@ function renderSeqList() {
|
||||
|
||||
function updateSeqUi() {
|
||||
const running = seq.running;
|
||||
const sendingIdx = seq.singleIdx;
|
||||
seqStartBtn.textContent = running ? "■ 停止序列" : "▶ 开始序列";
|
||||
seqStartBtn.classList.toggle("btn-primary", running);
|
||||
seqStartBtn.disabled = !running && sendingIdx != null;
|
||||
const st = $("#seqStatus");
|
||||
if (st) {
|
||||
if (running) {
|
||||
@@ -2127,6 +2133,9 @@ function updateSeqUi() {
|
||||
const cnt = cs ? cs.actions.length : 0;
|
||||
st.textContent = `运行中 · 步骤 ${seq.stepIdx + 1}/${seq.steps.length} (${cnt} 电机) · 第 ${seq.round} 轮`;
|
||||
st.classList.add("on");
|
||||
} else if (sendingIdx != null) {
|
||||
st.textContent = `单发步骤 ${sendingIdx + 1}…`;
|
||||
st.classList.add("on");
|
||||
} else {
|
||||
st.textContent = seq.statusText || `未运行 · ${seq.steps.length} 步`;
|
||||
st.classList.remove("on");
|
||||
@@ -2134,7 +2143,9 @@ function updateSeqUi() {
|
||||
}
|
||||
seqListEl.classList.toggle("seq-locked", running);
|
||||
seqListEl.querySelectorAll(".seq-step").forEach((row) => {
|
||||
row.classList.toggle("current", running && parseInt(row.dataset.idx, 10) === seq.stepIdx);
|
||||
const idx = parseInt(row.dataset.idx, 10);
|
||||
row.classList.toggle("current", running && idx === seq.stepIdx);
|
||||
row.classList.toggle("sending", !running && sendingIdx === idx);
|
||||
});
|
||||
seqLoopsInput.disabled = running;
|
||||
$("#seqAddBtn").disabled = running;
|
||||
@@ -2179,6 +2190,10 @@ seqListEl.addEventListener("click", (e) => {
|
||||
const step = seq.steps[i];
|
||||
if (!step) return;
|
||||
const op = btn.dataset.op;
|
||||
if (op === "sendOne") {
|
||||
sendSeqStepOnce(i);
|
||||
return;
|
||||
}
|
||||
if (op === "up" && i > 0) {
|
||||
const t = seq.steps[i]; seq.steps[i] = seq.steps[i - 1]; seq.steps[i - 1] = t;
|
||||
} else if (op === "down" && i < seq.steps.length - 1) {
|
||||
@@ -2231,6 +2246,46 @@ seqStartBtn.addEventListener("click", () => {
|
||||
startSeq();
|
||||
});
|
||||
|
||||
/* 单发一步:只把该步的全部动作经一次 sendBurst 近同时下发,不循环、不推进步骤、不等 holdMs。
|
||||
* 与序列运行互斥(运行中按钮被 .seq-locked 屏蔽),同一时刻只允许一个单发在途。 */
|
||||
async function sendSeqStepOnce(i) {
|
||||
if (seq.running || seq.singleIdx != null) return;
|
||||
const step = seq.steps[i];
|
||||
if (!step || !step.actions.length) return;
|
||||
if (!state.connected) { toast("设备未连接", "err"); return; }
|
||||
if (inBusCooldown()) return;
|
||||
const chBad = step.actions.find((a) => !state.chEnabled[a.ch]);
|
||||
if (chBad) { toast(`步骤 ${i + 1} 的通道 CH${chBad.ch} 已禁用`, "err"); return; }
|
||||
|
||||
seq.singleIdx = i;
|
||||
updateSeqUi();
|
||||
const targets = step.actions.map((a) => `CH${a.ch}·N${a.node}`).join(" ");
|
||||
const time = new Date().toLocaleTimeString("zh-CN", { hour12: false });
|
||||
$("#sbLastSend").textContent = `最近发送: 单发步${i + 1} → ${step.actions.length} 电机 [${targets}] ${time}`;
|
||||
try {
|
||||
const res = await sendBurst(step.actions);
|
||||
if (res.busy) { toast("发送通道忙,请稍后重试", "warn"); return; }
|
||||
if (!res.ok) { toast(`步骤 ${i + 1} 发送失败:${res.desc || "未知错误"}`, "err"); return; }
|
||||
for (const a of step.actions) {
|
||||
state.lastSentMode.set(a.node, parseInt(a.mode, 10) || 0);
|
||||
state.lastSentTargets.set(a.node, {
|
||||
mode: parseInt(a.mode, 10) || 0,
|
||||
angle: parseFloat(a.angle) || 0,
|
||||
velocity: parseFloat(a.velocity) || 0,
|
||||
current: parseFloat(a.current) || 0,
|
||||
voltage: parseFloat(a.voltage) || 0,
|
||||
});
|
||||
}
|
||||
state.txCount++;
|
||||
renderNodeList();
|
||||
seq.statusText = `已单发步骤 ${i + 1}`;
|
||||
toast(`步骤 ${i + 1} 已单发 · ${step.actions.length} 电机 [${targets}]`, "ok");
|
||||
} finally {
|
||||
seq.singleIdx = null;
|
||||
updateSeqUi();
|
||||
}
|
||||
}
|
||||
|
||||
function startSeq() {
|
||||
if (!state.connected) { toast("设备未连接", "err"); return; }
|
||||
if (seq.steps.length === 0) { toast("请先添加至少一个步骤", "warn"); return; }
|
||||
|
||||
Reference in New Issue
Block a user