Files
cbb eac3717f9a feat: 添加局域网设备发现功能和链路胶囊优化
- 实现局域网设备发现面板,支持探测ESP32中继板并自动切换端点
- 添加POST /api/lan/probe接口用于设备探测,返回三态判决结果
- 实现快扫和全段扫描两种模式,支持种子地址优先探测
- 添加端点唯一写手机制,防止多处同时修改设备IP
- 优化链路胶囊UI显示WiFi/EtherCAT/BLE连接状态和设备名称
- 修复EtherCAT链路OTA刷写期间PDO循环暂停和AL状态处理问题
- 调整PDO周期设置范围为0.125-4ms,支持亚毫秒显示为微秒
- 添加ESP32 WiFi使能开关控件样式和数据来源角标显示
2026-10-13 00:48:34 +08:00

119 lines
4.4 KiB
JavaScript
Raw Permalink 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.
/**
* base-url.js - 设备 IP / 基础 URL 公共助手
*
* 各页面共用 localStorage key dcu_esp_ip(OTA / CAN / 电机页一致)。
* 本模块只提供纯 localStorage + 默认值的通用实现;页面特有的输入框回退
* (如 CAN 页 el.toolIp、OTA 页 #espIp)由各自入口在此基础上包装。
*
* 写入一律经 saveDeviceIp 的 IPv4 校验:这是"哪个端点算当前设备"的唯一落盘点,
* 脏值在这里被拒掉,比在每个调用方各判一次可靠(固件 AP/掉租约时会上报
* ip:"0.0.0.0",见 ESP32_COMM/main/server/http_handlers.cpp:1776)。
*/
export var LS_ESP_IP = 'dcu_esp_ip';
export var DEFAULT_ESP_IP = '192.168.31.127';
/** @description 最近一次"选中"的设备快照(ip + 版本指纹),供端点一致性核对。 */
export var LS_ESP_DEVICE = 'dcu_esp_device';
/** @description 局域网发现结果缓存(带 observedAt),只给 UI 当候选种子用。 */
export var LS_LAN_DEVICES = 'dcu_lan_devices';
/**
* @description 严格点分十进制 IPv4,并拒掉不能当 API 目标的保留段。
* 0/8(含固件脏值 0.0.0.0)与 255.255.255.255 直接拒;169.254/16 是链路本地,
* 板子不会在那儿服务 HTTP,也拒——宁可让人重填,也不要网页打到一个
* 永远不通的地址上还显示"已连接"。
* @param {string} s 待校验字符串
* @returns {boolean}
*/
export function isValidIpv4(s) {
var parts = String(s == null ? '' : s).trim().split('.');
if (parts.length !== 4) return false;
var nums = [];
for (var i = 0; i < 4; i++) {
var p = parts[i];
if (!/^\d{1,3}$/.test(p)) return false;
var n = parseInt(p, 10);
if (n > 255) return false;
nums.push(n);
}
if (nums[0] === 0 || nums[0] === 255) return false;
if (nums[0] === 169 && nums[1] === 254) return false;
return true;
}
/** @description 读取 localStorage 中保存的设备 IP,无有效值返回空串。 */
export function getSavedDeviceIp() {
try {
var saved = localStorage.getItem(LS_ESP_IP);
if (saved && saved.trim()) return saved.trim();
} catch (_) {}
return '';
}
/**
* @description 保存设备 IP。非法值**拒绝且不改写现值**,返回 false——
* 不能用 DEFAULT_ESP_IP 去"兜住"脏输入,那会把配置自证成连接成功。
* @param {string} ip
* @returns {boolean} 是否真的写进去了
*/
export function saveDeviceIp(ip) {
var v = String(ip == null ? '' : ip).trim();
if (!isValidIpv4(v)) return false;
try { localStorage.setItem(LS_ESP_IP, v); } catch (_) { return false; }
return true;
}
/** @description 设备 IP:localStorage 优先,否则默认值。 */
export function getDeviceIp() {
return getSavedDeviceIp() || DEFAULT_ESP_IP;
}
/** @description 拼接设备基础 URL(http://<ip>)。 */
export function getBaseUrl() {
return 'http://' + getDeviceIp();
}
/**
* @description 读最近一次选中的设备指纹;结构不对一律当没有。
* @returns {{ip:string, version:string, git_hash:string, observedAt:number, source:string}|null}
*/
export function getDeviceMeta() {
try {
var raw = localStorage.getItem(LS_ESP_DEVICE);
if (!raw) return null;
var o = JSON.parse(raw);
if (!o || typeof o !== 'object' || !isValidIpv4(o.ip)) return null;
return o;
} catch (_) {}
return null;
}
/** @description 写设备指纹(与 IP 同时机,由 deviceLink.selectDevice 统一调用)。 */
export function saveDeviceMeta(meta) {
try {
localStorage.setItem(LS_ESP_DEVICE, JSON.stringify(meta || {}));
} catch (_) {}
}
/**
* @description 读发现结果缓存;返回数组(可能为空),坏数据直接丢弃不猜。
* @returns {Array<object>}
*/
export function getLanDevices() {
try {
var raw = localStorage.getItem(LS_LAN_DEVICES);
if (!raw) return [];
var arr = JSON.parse(raw);
if (!Array.isArray(arr)) return [];
return arr.filter(function (d) { return d && isValidIpv4(d.ip); });
} catch (_) {}
return [];
}
/** @description 存发现结果缓存(上限 16 条,超了截断——它只是候选种子,不是台账)。 */
export function saveLanDevices(list) {
var arr = Array.isArray(list) ? list.slice(0, 16) : [];
try {
localStorage.setItem(LS_LAN_DEVICES, JSON.stringify(arr));
} catch (_) {}
}