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