/** * transport.js — 电机控制台的传输选择层 * * 电机页支持两条链路,业务逻辑(解码、节点列表、STATUS 面板、波形、命令序列、 * 流控)**完全共用**,只有"数据怎么进出"这一层不同: * * wifi ESP32 固件 80 端口的 HTTP API(原有实现,行为未变) * ecat EtherCAT 过程数据,经本地页面服务器 /ecat/api/* 代理到 * tools/ecat 的 ecat_native.exe(C/SOEM 守护进程) * * 传输实例必须实现的接口(详见两个实现文件的注释): * id / label / supportsOta / supportsPeriodSync * sendHint(ch) -> string * probe() -> statusData 连接探测,抛错=没连上 * fetchStatus(wantFull) -> statusData 健康检查 * describeStatus(data) -> string[] #devStatus 文本片段 * reportedIp(data) -> string|null 设备回报 IP(ECAT 恒 null) * fetchMotorState() -> {have_data, age_ms, active_count, boot_active, motors[]} * fetchFeedback() -> {channels:[{ch,enabled,valid,rx,tx,err,tec,rec,bus_state}]} * sendFrames(frames, {ch}) -> results[] frames=[{id,dlc,data}] * sendBurst(frames, {suffix,dlc}) -> results[] * fetchVersionNode(node) -> {node,seq,version,at?}|null * 可选:drainPendingRx() -> Promise 版本等待期强制消费批量接收区(ECAT) * setPeriod(ms) -> void 不支持则空实现 * teardown() -> void 切走时收尾 * 可选:takeNodeIdReport(node)、debug() * * motors[] 每项的字段契约(ECAT 实现负责补平差异): * {node, ch, online, dlc, rx_count, seq, data(64hex), config_data(48hex|"")} */ "use strict"; import { createWifiTransport } from "./transport-wifi.js"; import { createEcatTransport } from "./transport-ecat.js?v=20260923i"; /** localStorage key:记住上次选的传输,刷新页面后保持 */ export const LS_TRANSPORT = "dcu_motor_transport"; export const TRANSPORT_IDS = ["wifi", "ecat"]; /** @description 按 id 创建传输实例。未知 id 回退到 wifi。 */ export function createTransport(id, ctx) { return (id === "ecat") ? createEcatTransport(ctx) : createWifiTransport(ctx); } /** @description 读上次选择的传输 id(默认 wifi —— 不改变老用户的既有行为)。 */ export function loadTransportId() { try { const v = localStorage.getItem(LS_TRANSPORT); if (v && TRANSPORT_IDS.includes(v)) return v; } catch (_) {} return "wifi"; } /** @description 保存传输选择。 */ export function saveTransportId(id) { try { localStorage.setItem(LS_TRANSPORT, id); } catch (_) {} }