/** * @file ws-client.js * @brief WebSocket 客户端封装 — 自动重连 + 事件分发 */ var WsClient = { ws: null, url: '', status: 'disconnected', timer: null, msgCbs: [], statusCbs: [], onMsg: function(cb) { this.msgCbs.push(cb); }, onStatus: function(cb) { this.statusCbs.push(cb); }, connect: function(url) { if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { return; } this.url = url; this._setStatus('connecting'); try { this.ws = new WebSocket(url); this.ws.binaryType = 'arraybuffer'; } catch (e) { this._setStatus('disconnected'); this._schedule(); return; } this.ws.onopen = function() { var self = WsClient; self._setStatus('connected'); if (self.timer) { clearTimeout(self.timer); self.timer = null; } self.send({ type: 'get_cmds' }); }; this.ws.onclose = function() { var self = WsClient; self._setStatus('disconnected'); self.ws = null; self._schedule(); }; this.ws.onerror = function() {}; this.ws.onmessage = function(e) { var self = WsClient; var raw = e.data; if (raw instanceof ArrayBuffer) { self.msgCbs.forEach(function(cb) { try { cb(null, raw); } catch(ex) {} }); return; } var data = null; try { data = JSON.parse(raw); } catch(ex) {} self.msgCbs.forEach(function(cb) { try { cb(data || raw, null); } catch(ex) {} }); }; }, send: function(obj) { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { if (typeof toast === 'function') { toast(I18n.t('toast.ws_error'), 'error'); } return; } var raw = (typeof obj === 'object') ? JSON.stringify(obj) : String(obj); this.ws.send(raw); }, _setStatus: function(s) { if (this.status === s) return; this.status = s; var dot = document.getElementById('nav-status-dot'); var txt = document.getElementById('nav-status-text'); if (dot) { dot.className = 'nav-status-dot ' + s; } if (txt) { var map = { connected: I18n.t('status.connected'), disconnected: I18n.t('status.disconnected'), connecting: I18n.t('status.connecting') }; txt.textContent = map[s] || s; } this.statusCbs.forEach(function(cb) { cb(s); }); }, _schedule: function() { var self = this; if (self.timer) return; self.timer = setTimeout(function() { self.timer = null; if (self.status === 'disconnected') { self.connect(self.url); } }, 3000); } };