/** * spi.js - SPI 帧构建与发送 * * 从 app.js 的「SPI 发送」段提取。 * 操作 DOM: #spiType / #spiData / #spiTimeout / #spiHint / #spiResp / #spiRespText * * 未拆分的函数(通过 window.* 引用): * window.getBaseUrl / window.showToast / window.addLog */ import { showToast } from '../common/toast.js'; import { addLog } from '../common/log.js'; function updateSpiHint() { var typeHex = document.getElementById('spiType').value.trim(); var dataHex = document.getElementById('spiData').value.trim().replace(/\s/g, ''); var hint = document.getElementById('spiHint'); if (!typeHex || !/^[0-9a-fA-F]{1,2}$/.test(typeHex)) { hint.className = 'helper-note err'; hint.innerHTML = 'type 需填 1 字节 hex(如 01, B0)。'; return; } if (dataHex && !/^[0-9a-fA-F]*$/.test(dataHex)) { hint.className = 'helper-note err'; hint.innerHTML = '数据包含非 HEX 字符。'; return; } if (dataHex && dataHex.length % 2 !== 0) { hint.className = 'helper-note err'; hint.innerHTML = 'HEX 长度必须为偶数。'; return; } var bytes = dataHex ? dataHex.length / 2 : 0; var totalOnWire = 5 + bytes; hint.className = 'helper-note ok'; hint.innerHTML = 'type=0x' + typeHex.toUpperCase() + '' + (bytes > 0 ? ',payload: ' + bytes + 'B' : ',无 payload') + ',线上 ' + totalOnWire + 'B(SOF+TYPE+LEN+PAYLOAD+CRC)'; } export async function sendSpiFrame() { var typeHex = document.getElementById('spiType').value.trim(); var dataStr = document.getElementById('spiData').value.trim(); var timeout = parseInt(document.getElementById('spiTimeout').value) || 1000; if (!typeHex || !/^[0-9a-fA-F]{1,2}$/.test(typeHex)) { showToast('type 需填 1 字节 hex', 'error'); return; } var dataHex = dataStr.replace(/\s/g, ''); if (dataHex && !/^[0-9a-fA-F]*$/.test(dataHex)) { showToast('数据包含非 HEX 字符', 'error'); return; } if (dataHex && dataHex.length % 2 !== 0) { showToast('HEX 数据长度必须为偶数', 'error'); return; } var txBytes = dataHex ? dataHex.length / 2 : 0; addLog('I', 'SPI', 'type=0x' + typeHex.toUpperCase() + (txBytes > 0 ? ', payload=' + dataHex.toUpperCase() : ', 无负载')); var body = { type: typeHex, data: dataHex.toUpperCase(), timeout_ms: timeout }; try { var resp = await fetch(window.getBaseUrl() + '/spi/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: AbortSignal.timeout(timeout + 2000) }); var result = await resp.json(); if (result.status === 'ok') { var recvHex = result.recv || ''; var respTypeName = {'80': 'ACK', '81': 'WAIT', '82': 'ERROR'}; var respTypeLabel = respTypeName[result.resp_type] || ('0x' + result.resp_type); addLog('I', 'SPI', '→ ' + respTypeLabel + (recvHex ? ' 0x' + recvHex : '') + ' (' + (result.resp_len || 0) + 'B)'); var rd = document.getElementById('spiResp'); rd.style.display = 'block'; document.getElementById('spiRespText').textContent = 'type=0x' + result.type + ' payload=' + result.payload_len + 'B → ' + respTypeLabel + (recvHex ? ' 0x' + recvHex : '') + ' (' + (result.resp_len || 0) + 'B)'; showToast('SPI 发送成功', 'success'); } else { addLog('E', 'SPI', '发送失败: code=' + result.code + ' ' + (result.message || '')); var rd = document.getElementById('spiResp'); rd.style.display = 'block'; document.getElementById('spiRespText').textContent = '错误: code=' + result.code + ' ' + (result.message || ''); showToast('SPI 发送失败', 'error'); } } catch (e) { addLog('E', 'SPI', '发送失败: ' + e.message); showToast('SPI 发送失败: ' + e.message, 'error'); } } export function clearSpiFields() { document.getElementById('spiType').value = '01'; document.getElementById('spiData').value = ''; document.getElementById('spiTimeout').value = '1000'; document.getElementById('spiResp').style.display = 'none'; updateSpiHint(); } // 导出 updateSpiHint 供入口挂载 window.updateSpiHint export { updateSpiHint };