123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- import qs from 'qs';
- // 导入我们之前改写的日志模块
- /**
- * 验证WebSocket服务器URL格式
- * @param {string} wsUrl
- * @returns {boolean}
- */
- function validateWsUrl(wsUrl) {
- if (!wsUrl) {
- console.log('WebSocket服务器地址不能为空', 'error');
- return false;
- }
- if (!wsUrl.startsWith('ws://') && !wsUrl.startsWith('wss://')) {
- console.log('URL格式错误,必须以ws://或wss://开头', 'error');
- return false;
- }
- return true;
- }
- /**
- * 验证设备配置
- * @param {object} config
- * @returns {boolean}
- */
- function validateConfig(config) {
- if (!config.deviceMac) {
- console.log('设备MAC地址不能为空', 'error');
- return false;
- }
- if (!config.clientId) {
- console.log('客户端ID不能为空', 'error');
- return false;
- }
- return true;
- }
- /**
- * 发送OTA请求,验证设备状态
- * @param {string} otaUrl
- * @param {object} config
- * @returns {Promise<boolean>} - 返回一个Promise,resolve为true表示成功,false表示失败
- */
- async function sendOTA(otaUrl, config) {
- console.log('正在进行OTA状态验证...', 'info');
- try {
- // 【修正】uni.request 的 await 返回的是单个 res 对象
- const res = await uni.request({
- url: otaUrl,
- method: 'POST',
- header: {
- 'Content-Type': 'application/json',
- 'Device-Id': config.deviceMac,
- 'Client-Id': config.clientId
- },
- data: {
- version: 0,
- uuid: '',
- application: {
- name: 'xiaozhi-uniapp-test',
- version: '1.0.0',
- compile_time: '2025-04-16 10:00:00',
- idf_version: '4.4.3',
- elf_sha256: '1234567890abcdef1234567890abcdef1234567890abcdef'
- },
- ota: { label: 'xiaozhi-uniapp-test' },
- board: {
- type: 'xiaozhi-uniapp-test',
- ssid: 'xiaozhi-uniapp-test',
- rssi: 0,
- channel: 0,
- ip: '192.168.1.1',
- mac: config.deviceMac
- },
- flash_size: 0,
- minimum_free_heap_size: 0,
- mac_address: config.deviceMac,
- chip_model_name: '',
- chip_info: { model: 0, cores: 0, revision: 0, features: 0 },
- partition_table: [{ label: '', type: 0, subtype: 0, address: 0, size: 0 }]
- }
- });
-
- console.log(res,'res');
- let _res = res[1]||null;
- if(!_res) return false;
-
- if (_res.statusCode >= 200 && _res.statusCode < 300) {
- // 【新增判断】检查返回的数据体中是否包含error字段
- if (_res.data && _res.data.error) {
- // 如果有error字段,说明业务逻辑失败了
- throw new Error(`OTA业务错误: ${_res.data.error}`);
- }
-
- // 只有当状态码正确且没有业务错误时,才算真正成功
- console.log(`OTA验证成功: ${JSON.stringify(_res.data)}`, 'success');
- return true;
- } else {
- throw new Error(`HTTP错误: ${_res.statusCode}`);
- }
- } catch (err) {
- const errorMessage = err.errMsg || err.message || '未知网络错误';
- console.log(`OTA验证失败: ${errorMessage}`, 'error');
- return false;
- }
- }
- /**
- * 【修正版 - 使用qs库】构建带参数的URL
- * @param {string} baseUrl
- * @param {object} params
- * @returns {string}
- */
- function buildUrlWithParams(baseUrl, params) {
- const paramString = qs.stringify(params, { addQueryPrefix: true }); // addQueryPrefix 会自动加上 '?'
- return baseUrl + paramString;
- }
- /**
- * 执行OTA检查并连接WebSocket服务器
- * @param {string} otaUrl - OTA服务器地址
- * @param {string} wsUrl - WebSocket服务器地址
- * @param {object} config - 设备配置对象
- * @returns {Promise<SocketTask|null>} - 返回一个Promise。
- * - 如果连接前置步骤成功,resolve为uniapp的SocketTask对象。
- * - 如果任何步骤失败,resolve为null。
- */
- export async function webSocketConnect(otaUrl, wsUrl, config) {
- if (!validateWsUrl(wsUrl) || !validateConfig(config)) {
- return null; // 验证失败
- }
- // 1. 执行OTA检查
- console.log(otaUrl, config);
- const otaOk = await sendOTA(otaUrl, config);
- console.log(otaOk);
- if (!otaOk) {
- // OTA失败,直接返回,由调用方处理UI状态
- return null;
- }
- // 2. 构建连接URL
- // 小程序环境没有内置URL对象,需要手动拼接
- const params = {
- 'device-id': config.deviceMac,
- 'client-id': config.clientId
- };
- const finalUrl = buildUrlWithParams(wsUrl, params);
- console.log(`准备连接WebSocket: ${finalUrl}`, 'info');
- // 3. 使用 uni.connectSocket 替换 new WebSocket()
- // 它会立即返回一个 SocketTask 对象,连接过程是异步的
- const socketTask = uni.connectSocket({
- url: finalUrl,
- // success/fail回调只代表API调用是否成功,不代表连接是否成功
- success: () => {},
- fail: (err) => {
- console.log(`uni.connectSocket API调用失败: ${JSON.stringify(err)}`, 'error');
- }
- });
- // 4. 返回 SocketTask 对象
- // 调用此函数的组件需要负责监听 onOpen, onMessage, onError, onClose 事件
- return socketTask;
- }
|