From 9bbd0e6720d2288ca125430df8a8feab1b42e150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=AB=8B=E5=B8=AE?= <3294713004@qq.com> Date: Sun, 1 Dec 2024 14:44:49 +0800 Subject: [PATCH] =?UTF-8?q?Update:=20=E6=9B=B4=E6=96=B0WebSocket=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/deps.json | 4 + common/modules/mixly-modules/common/app.js | 31 +- common/modules/mixly-modules/common/debug.js | 6 +- common/modules/mixly-modules/common/loader.js | 6 +- .../mixly-modules/common/statusbar-serial.js | 18 +- common/modules/mixly-modules/deps.json | 46 ++- .../modules/mixly-modules/electron/serial.js | 5 +- .../mixly-modules/web-socket/arduino-shell.js | 380 +++++++++--------- .../mixly-modules/web-socket/serial.js | 316 +++++++++++++++ .../mixly-modules/web-socket/socket.js | 295 +------------- common/modules/mixly-modules/web/serial.js | 15 +- common/modules/web-modules/socket.io.min.js | 7 + common/templates/html/footerbar.html | 2 + 13 files changed, 611 insertions(+), 520 deletions(-) create mode 100644 common/modules/mixly-modules/web-socket/serial.js create mode 100644 common/modules/web-modules/socket.io.min.js diff --git a/common/deps.json b/common/deps.json index 3496fbce..ea037a01 100644 --- a/common/deps.json +++ b/common/deps.json @@ -231,6 +231,10 @@ "path": "modules/web-modules/mixio.js", "provide": ["MixIO"], "require": ["mqtt"] + }, { + "path": "modules/web-modules/socket.io.min.js", + "provide": ["io"], + "require": [] }, { "path": "modules/web-modules/dayjs/dayjs.min.js", "provide": ["dayjs"], diff --git a/common/modules/mixly-modules/common/app.js b/common/modules/mixly-modules/common/app.js index 28c03304..2d02bd79 100644 --- a/common/modules/mixly-modules/common/app.js +++ b/common/modules/mixly-modules/common/app.js @@ -27,6 +27,8 @@ goog.require('Mixly.Web.BU'); goog.require('Mixly.Web.FS'); goog.require('Mixly.Web.File'); goog.require('Mixly.Web.Serial'); +goog.require('Mixly.WebSocket.Serial'); +goog.require('Mixly.WebSocket.ArduShell'); goog.provide('Mixly.App'); const { @@ -44,11 +46,24 @@ const { Component, EditorMix, Electron = {}, - Web = {} + Web = {}, + WebSocket = {} } = Mixly; const { Loader } = Electron; +let currentObj = null; + +if (goog.isElectron) { + currentObj = Electron; +} else { + if (Env.hasSocketServer) { + currentObj = WebSocket; + } else { + currentObj = Web; + } +} + const { FS, File, @@ -57,7 +72,7 @@ const { BU, PythonShell, Serial -} = goog.isElectron? Electron : Web; +} = currentObj; const { BOARD, SELECTED_BOARD } = Config; @@ -149,7 +164,7 @@ class App extends Component { id: 'port-add-btn', displayText: Msg.Lang['nav.btn.addDevice'], preconditionFn: () => { - if (goog.isElectron) { + if (goog.isElectron || Env.hasSocketServer) { return false; } return true; @@ -165,7 +180,10 @@ class App extends Component { id: 'arduino-compile-btn', displayText: Msg.Lang['nav.btn.compile'], preconditionFn: () => { - if (!goog.isElectron || !SELECTED_BOARD?.nav?.compile) { + if (!goog.isElectron && !Env.hasSocketServer && !env.hasCompiler) { + return false; + } + if (!SELECTED_BOARD?.nav?.compile || !SELECTED_BOARD?.nav?.upload) { return false; } const workspace = Workspace.getMain(); @@ -190,7 +208,10 @@ class App extends Component { id: 'arduino-upload-btn', displayText: Msg.Lang['nav.btn.upload'], preconditionFn: () => { - if (!goog.isElectron || !SELECTED_BOARD?.nav?.compile || !SELECTED_BOARD?.nav?.upload) { + if (!goog.isElectron && !Env.hasSocketServer && !env.hasCompiler) { + return false; + } + if (!SELECTED_BOARD?.nav?.compile || !SELECTED_BOARD?.nav?.upload) { return false; } const workspace = Workspace.getMain(); diff --git a/common/modules/mixly-modules/common/debug.js b/common/modules/mixly-modules/common/debug.js index 41395576..5e34625d 100644 --- a/common/modules/mixly-modules/common/debug.js +++ b/common/modules/mixly-modules/common/debug.js @@ -11,10 +11,10 @@ for (let key in console) { continue; } Debug[key] = (...args) => { - if (!SOFTWARE.debug) { - console.log(`[${key.toUpperCase()}]`, ...args); - } else { + if (SOFTWARE.debug) { console[key](...args); + } else { + console.log(`[${key.toUpperCase()}]`, ...args); } } } diff --git a/common/modules/mixly-modules/common/loader.js b/common/modules/mixly-modules/common/loader.js index c5d053be..2d10697e 100644 --- a/common/modules/mixly-modules/common/loader.js +++ b/common/modules/mixly-modules/common/loader.js @@ -43,6 +43,9 @@ const { Socket } = WebSocket; window.addEventListener('load', () => { + if (!goog.isElectron && Env.hasSocketServer) { + Socket.init(); + } const app = new App($('body')[0]); Mixly.app = app; const $xml = $(goog.get(Env.boardIndexPath)); @@ -84,9 +87,6 @@ Loader.start = () => { if (!goog.isElectron && window.location.host.indexOf('mixly.cn')) { window.userOpEvents = new UserOPEvents(); } - if (Env.hasSocketServer) { - Socket.init(); - } if (goog.isElectron && typeof LibManager === 'object') { LibManager.init(() => Loader.init()); } else { diff --git a/common/modules/mixly-modules/common/statusbar-serial.js b/common/modules/mixly-modules/common/statusbar-serial.js index 056c127f..05dcf12b 100644 --- a/common/modules/mixly-modules/common/statusbar-serial.js +++ b/common/modules/mixly-modules/common/statusbar-serial.js @@ -16,6 +16,7 @@ goog.require('Mixly.StatusBarSerialOutput'); goog.require('Mixly.StatusBarSerialChart'); goog.require('Mixly.Electron.Serial'); goog.require('Mixly.Web.Serial'); +goog.require('Mixly.WebSocket.Serial'); goog.provide('Mixly.StatusBarSerial'); const { @@ -32,10 +33,23 @@ const { StatusBarSerialOutput, StatusBarSerialChart, Electron = {}, - Web = {} + Web = {}, + WebSocket = {} } = Mixly; -const { Serial } = goog.isElectron ? Electron : Web; +let currentObj = null; + +if (goog.isElectron) { + currentObj = Electron; +} else { + if (Env.hasSocketServer) { + currentObj = WebSocket; + } else { + currentObj = Web; + } +} + +const { Serial } = currentObj; const { SELECTED_BOARD } = Config; diff --git a/common/modules/mixly-modules/deps.json b/common/modules/mixly-modules/deps.json index 75cd666d..90b7fba0 100644 --- a/common/modules/mixly-modules/deps.json +++ b/common/modules/mixly-modules/deps.json @@ -46,7 +46,9 @@ "Mixly.Web.BU", "Mixly.Web.FS", "Mixly.Web.File", - "Mixly.Web.Serial" + "Mixly.Web.Serial", + "Mixly.WebSocket.Serial", + "Mixly.WebSocket.ArduShell" ], "provide": [ "Mixly.App" @@ -1064,7 +1066,8 @@ "Mixly.StatusBarSerialOutput", "Mixly.StatusBarSerialChart", "Mixly.Electron.Serial", - "Mixly.Web.Serial" + "Mixly.Web.Serial", + "Mixly.WebSocket.Serial" ], "provide": [ "Mixly.StatusBarSerial" @@ -1460,7 +1463,6 @@ { "path": "/electron/serial.js", "require": [ - "layui", "Mixly.Serial", "Mixly.Env", "Mixly.Msg", @@ -1717,15 +1719,12 @@ "path": "/web-socket/arduino-shell.js", "require": [ "layui", - "Mixly.Env", - "Mixly.LayerExt", - "Mixly.Config", - "Mixly.Title", + "dayjs.duration", "Mixly.Boards", - "Mixly.MFile", - "Mixly.MArray", + "Mixly.Debug", + "Mixly.LayerExt", "Mixly.Msg", - "Mixly.WebSocket.Socket", + "Mixly.Workspace", "Mixly.WebSocket.Serial" ], "provide": [ @@ -1766,18 +1765,27 @@ "Mixly.WebSocket.File" ] }, + { + "path": "/web-socket/serial.js", + "require": [ + "Mixly.Serial", + "Mixly.Env", + "Mixly.Msg", + "Mixly.Debug", + "Mixly.Registry", + "Mixly.WebSocket" + ], + "provide": [ + "Mixly.WebSocket.Serial" + ] + }, { "path": "/web-socket/socket.js", "require": [ - "path", - "Mixly.Env", - "Mixly.Config", - "Mixly.Boards", - "Mixly.Command", - "Mixly.MJSON", - "Mixly.MArray", - "Mixly.LayerExt", - "Mixly.WebSocket" + "io", + "Mixly.WebSocket", + "Mixly.WebSocket.Serial", + "Mixly.WebSocket.ArduShell" ], "provide": [ "Mixly.WebSocket.Socket" diff --git a/common/modules/mixly-modules/electron/serial.js b/common/modules/mixly-modules/electron/serial.js index 1b1093fa..b431abcd 100644 --- a/common/modules/mixly-modules/electron/serial.js +++ b/common/modules/mixly-modules/electron/serial.js @@ -1,6 +1,5 @@ goog.loadJs('electron', () => { -goog.require('layui'); goog.require('Mixly.Serial'); goog.require('Mixly.Env'); goog.require('Mixly.Msg'); @@ -27,8 +26,6 @@ const { Electron } = Mixly; -const { form } = layui; - class ElectronSerial extends Serial { static { @@ -106,7 +103,7 @@ class ElectronSerial extends Serial { }); this.#serialport_.on('error', (error) => { - this.onError(error); + this.onError(String(error)); this.onClose(1); }); diff --git a/common/modules/mixly-modules/web-socket/arduino-shell.js b/common/modules/mixly-modules/web-socket/arduino-shell.js index 199caf62..7dfcc611 100644 --- a/common/modules/mixly-modules/web-socket/arduino-shell.js +++ b/common/modules/mixly-modules/web-socket/arduino-shell.js @@ -1,228 +1,216 @@ goog.loadJs('web', () => { goog.require('layui'); -goog.require('Mixly.Env'); -goog.require('Mixly.LayerExt'); -goog.require('Mixly.Config'); -goog.require('Mixly.Title'); +goog.require('dayjs.duration'); goog.require('Mixly.Boards'); -goog.require('Mixly.MFile'); -goog.require('Mixly.MArray'); +goog.require('Mixly.Debug'); +goog.require('Mixly.LayerExt'); goog.require('Mixly.Msg'); -goog.require('Mixly.WebSocket.Socket'); +goog.require('Mixly.Workspace'); goog.require('Mixly.WebSocket.Serial'); goog.provide('Mixly.WebSocket.ArduShell'); const { - Env, - LayerExt, - Title, Boards, - MFile, - MArray, + Debug, + LayerExt, Msg, - Config + Workspace, + WebSocket } = Mixly; -const { BOARD, SOFTWARE, USER } = Config; -const { - Socket, - ArduShell, - Serial -} = Mixly.WebSocket; +const { Serial } = WebSocket; -/** -* @function 编译 -* @description 开始一个编译过程 -* @return void -*/ -ArduShell.initCompile = () => { - Socket.connect((WS) => { - layer.closeAll(); - }, () => { - ArduShell.compile(); - }); -} -/** -* @function 编译 -* @description 开始一个编译过程 -* @return void -*/ -ArduShell.compile = () => { - const { mainStatusBarTabs } = Mixly; - const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); - mainStatusBarTabs.changeTo('output'); - ArduShell.compiling = true; - ArduShell.uploading = false; - const boardType = Boards.getSelectedBoardCommandParam(); - mainStatusBarTabs.show(); - const layerNum = layer.open({ - type: 1, - title: Msg.Lang['shell.compiling'] + "...", - content: $('#mixly-loader-div'), - shade: LayerExt.SHADE_NAV, - resize: false, - closeBtn: 0, - success: (layero, index) => { - $(".layui-layer-page").css("z-index", "198910151"); - $("#mixly-loader-btn").off("click").click(() => { - $("#mixly-loader-btn").css('display', 'none'); - layer.title(Msg.Lang['shell.aborting'] + '...', index); - ArduShell.cancel(); +class WebSocketArduShell { + static { + this.socket = null; + this.shell = null; + + this.init = function (socket) { + this.socket = socket; + this.shell = new WebSocketArduShell(); + socket.on('arduino.dataEvent', (data) => { + const { mainStatusBarTabs } = Mixly; + const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); + statusBarTerminal.addValue(data); }); - statusBarTerminal.setValue(Msg.Lang['shell.compiling'] + "...\n"); - const code = MFile.getCode(); - Socket.sendCommand({ - obj: 'ArduShell', - func: 'compile', - args: [ index, boardType, code ] - }); - }, - end: () => { - $('#mixly-loader-div').css('display', 'none'); - $("layui-layer-shade" + layerNum).remove(); - $("#mixly-loader-btn").off("click"); - $("#mixly-loader-btn").css('display', 'inline-block'); - } - }); -} -/** -* @function 初始化上传 -* @description 关闭已打开的串口,获取当前所连接的设备数,然后开始上传程序 -* @return void -*/ -ArduShell.initUpload = () => { - Socket.connect((WS) => { - layer.closeAll(); - }, () => { - ArduShell.compiling = false; - ArduShell.uploading = true; - const boardType = Boards.getSelectedBoardCommandParam(); - const uploadType = Boards.getSelectedBoardConfigParam('upload_method'); - let port = Serial.getSelectedPortName(); - switch (uploadType) { - case 'STLinkMethod': - case 'jlinkMethod': - case 'usb': - port = 'None'; - break; - } - if (port) { - ArduShell.upload(boardType, port); - } else { - layer.msg(Msg.Lang['statusbar.serial.noDevice'], { - time: 1000 + socket.on('arduino.errorEvent', (data) => { + const { mainStatusBarTabs } = Mixly; + const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); + statusBarTerminal.addValue(data); }); } - }); -} -/** -* @function 上传程序 -* @description 通过所选择串口号开始一个上传过程 -* @return void -*/ -ArduShell.upload = (boardType, port) => { - const { mainStatusBarTabs } = Mixly; - const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); - mainStatusBarTabs.changeTo("output"); - const layerNum = layer.open({ - type: 1, - title: Msg.Lang['shell.uploading'] + "...", - content: $('#mixly-loader-div'), - shade: LayerExt.SHADE_NAV, - resize: false, - closeBtn: 0, - success: function (layero, index) { - $(".layui-layer-page").css("z-index", "198910151"); - $("#mixly-loader-btn").off("click").click(() => { - $("#mixly-loader-btn").css('display', 'none'); - layer.title(Msg.Lang['shell.aborting'] + '...', index); - ArduShell.cancel(); - }); + this.initCompile = function () { + const { mainStatusBarTabs } = Mixly; + const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); + mainStatusBarTabs.changeTo('output'); mainStatusBarTabs.show(); - statusBarTerminal.setValue(Msg.Lang['shell.uploading'] + "...\n"); - const code = MFile.getCode(); - Socket.sendCommand({ - obj: 'ArduShell', - func: 'upload', - args: [ index, boardType, port, code ] - }); - }, - end: function () { - $('#mixly-loader-div').css('display', 'none'); - $("layui-layer-shade" + layerNum).remove(); - $("#mixly-loader-btn").off("click"); - $("#mixly-loader-btn").css('display', 'inline-block'); + const mainWorkspace = Workspace.getMain(); + const editor = mainWorkspace.getEditorsManager().getActive(); + const code = editor.getCode(); + statusBarTerminal.setValue(`${Msg.Lang['shell.compiling']}...\n`); + this.shell.compile(code) + .then((info) => { + this.endCallback(info.code, info.time); + }) + .catch(Debug.error); } - }); -} -ArduShell.operateSuccess = (type, layerNum, port, baud, timeCostStr) => { - const { mainStatusBarTabs } = Mixly; - const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); - layer.close(layerNum); - const value = statusBarTerminal.getValue(); - let prefix = ''; - if (value.lastIndexOf('\n') !== value.length - 1) { - prefix = '\n'; + this.initUpload = function () { + const { mainStatusBarTabs } = Mixly; + const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); + mainStatusBarTabs.changeTo('output'); + mainStatusBarTabs.show(); + statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`); + const port = Serial.getSelectedPortName(); + const mainWorkspace = Workspace.getMain(); + const editor = mainWorkspace.getEditorsManager().getActive(); + const code = editor.getCode(); + const statusBarSerial = mainStatusBarTabs.getStatusBarById(port); + const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve(); + closePromise + .then(() => { + return this.shell.upload(port, code) + }) + .then((info) => { + this.endCallback(info.code, info.time); + if (info.code || !Serial.portIsLegal(port)) { + return; + } + mainStatusBarTabs.add('serial', port); + mainStatusBarTabs.changeTo(port); + const statusBarSerial = mainStatusBarTabs.getStatusBarById(port); + statusBarSerial.open() + .then(() => { + const baudRates = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g); + if (!baudRates.length) { + return statusBarSerial.setBaudRate(9600); + } else { + return statusBarSerial.setBaudRate(baudRates[0] - 0); + } + }) + .catch(Debug.error); + }) + .catch(Debug.error); + } + + this.endCallback = function (code, time) { + const { mainStatusBarTabs } = Mixly; + const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); + mainStatusBarTabs.changeTo('output'); + let message = ''; + if (code) { + message = (this.shell.isCompiling() ? Msg.Lang['shell.compileFailed'] : Msg.Lang['shell.uploadFailed']); + statusBarTerminal.addValue('==' + message + '==\n'); + } else { + message = (this.shell.isCompiling() ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']); + statusBarTerminal.addValue(`==${message}(${Msg.Lang['shell.timeCost']} ${ + dayjs.duration(time).format('HH:mm:ss.SSS') + })==\n`); + } + layer.msg(message, { time: 1000 }); + } } - statusBarTerminal.addValue(prefix); - const message = (type === 'compile' ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']); - statusBarTerminal.addValue("==" + message + "(" + Msg.Lang['shell.timeCost'] + " " + timeCostStr + ")==\n"); - layer.msg(message, { - time: 1000 - }); - if (type === 'upload' && port) { - Serial.connect(port, baud - 0); + + #running_ = false; + #upload_ = false; + #layerNum_ = null; + + constructor() {} + + async compile(code) { + return new Promise(async (resolve, reject) => { + this.#running_ = true; + this.#upload_ = false; + await this.showProgress(); + const key = Boards.getSelectedBoardCommandParam(); + const config = { key, code }; + WebSocketArduShell.socket.emit('arduino.compile', config, (response) => { + const [error, result] = response; + this.hideProgress(); + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); } - ArduShell.compiling = false; - ArduShell.uploading = false; -} -ArduShell.operateOnError = (type, layerNum, error) => { - const { mainStatusBarTabs } = Mixly; - const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); - statusBarTerminal.addValue(error); -} - -ArduShell.operateEndError = (type, layerNum, error) => { - const { mainStatusBarTabs } = Mixly; - const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); - layer.close(layerNum); - const value = statusBarTerminal.getValue(); - let prefix = ''; - if (value.lastIndexOf('\n') !== value.length - 1) { - prefix = '\n'; + async upload(port, code) { + return new Promise(async (resolve, reject) => { + this.#running_ = true; + this.#upload_ = true; + await this.showProgress(); + const key = Boards.getSelectedBoardCommandParam(); + const config = { key, code, port }; + WebSocketArduShell.socket.emit('arduino.upload', config, (response) => { + const [error, result] = response; + this.hideProgress(); + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); + } + + async kill() { + return new Promise(async (resolve, reject) => { + WebSocketArduShell.socket.emit('arduino.kill', (response) => { + const [error, result] = response; + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); + } + + async showProgress() { + return new Promise((resolve, reject) => { + this.#layerNum_ = layer.open({ + type: 1, + title: `${this.isCompiling ? Msg.Lang['shell.compiling'] : Msg.Lang['shell.uploading']}...`, + content: $('#mixly-loader-div'), + shade: LayerExt.SHADE_NAV, + resize: false, + closeBtn: 0, + success: () => { + $('#mixly-loader-btn').off('click').click(() => { + $('#mixly-loader-btn').css('display', 'none'); + layer.title(`${Msg.Lang['shell.aborting']}...`, this.layerNum); + this.kill().catch(Debug.error); + }); + resolve(); + }, + end: function () { + $('#mixly-loader-btn').off('click'); + $('#mixly-loader-btn').css('display', 'inline-block'); + } + }); + }) + } + + hideProgress() { + layer.close(this.#layerNum_); + } + + isUploading() { + return this.#running_ && this.#upload_; + } + + isCompiling() { + return this.#running_ && !this.#upload_; } - statusBarTerminal.addValue(prefix); - error && statusBarTerminal.addValue(error + '\n'); - const message = (type === 'compile' ? Msg.Lang['shell.compileFailed'] : Msg.Lang['shell.uploadFailed']); - statusBarTerminal.addValue("==" + message + "==\n"); - ArduShell.compiling = false; - ArduShell.uploading = false; } -/** -* @function 取消编译或上传 -* @description 取消正在执行的编译或上传过程 -* @return void -*/ -ArduShell.cancel = function () { - Socket.sendCommand({ - obj: 'ArduShell', - func: 'cancel', - args: [] - }); -} - -ArduShell.addValue = function (data) { - const { mainStatusBarTabs } = Mixly; - const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); - statusBarTerminal.addValue(data); -} +WebSocket.ArduShell = WebSocketArduShell; }); \ No newline at end of file diff --git a/common/modules/mixly-modules/web-socket/serial.js b/common/modules/mixly-modules/web-socket/serial.js new file mode 100644 index 00000000..382fa5ee --- /dev/null +++ b/common/modules/mixly-modules/web-socket/serial.js @@ -0,0 +1,316 @@ +goog.loadJs('web', () => { + +goog.require('Mixly.Serial'); +goog.require('Mixly.Env'); +goog.require('Mixly.Msg'); +goog.require('Mixly.Debug'); +goog.require('Mixly.Registry'); +goog.require('Mixly.WebSocket'); +goog.provide('Mixly.WebSocket.Serial'); + + +const { + Serial, + Env, + Msg, + Debug, + Registry, + WebSocket +} = Mixly; + + +class WebSocketSerial extends Serial { + static { + this.eventRegistry = new Registry(); + this.socket = null; + + this.getConfig = function () { + return Serial.getConfig(); + } + + this.getSelectedPortName = function () { + return Serial.getSelectedPortName(); + } + + this.getCurrentPortsName = function () { + return Serial.getCurrentPortsName(); + } + + this.getPorts = async function () { + return new Promise((resolve, reject) => { + this.socket.emit('serial.getPorts', (response) => { + const [error, result] = response; + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); + } + + this.refreshPorts = function () { + this.getPorts() + .then((ports) => { + Serial.renderSelectBox(ports); + }) + .catch(Debug.error); + } + + this.init = function (socket) { + this.socket = socket; + + socket.on('serial.attachEvent', () => { + this.refreshPorts(); + }); + + socket.on('serial.detachEvent', () => { + this.refreshPorts(); + }); + + socket.on('serial.bufferEvent', (port, buffer) => { + const eventName = `${port}-buffer`; + if (!this.eventRegistry.hasKey(eventName)) { + return; + } + const event = this.eventRegistry.getItem(eventName); + event(buffer); + }); + + socket.on('serial.stringEvent', (port, str) => { + const eventName = `${port}-string`; + if (!this.eventRegistry.hasKey(eventName)) { + return; + } + const event = this.eventRegistry.getItem(eventName); + event(str); + }); + + socket.on('serial.errorEvent', (port, error) => { + const eventName = `${port}-error`; + if (!this.eventRegistry.hasKey(eventName)) { + return; + } + const event = this.eventRegistry.getItem(eventName); + event(error); + }); + + socket.on('serial.openEvent', (port) => { + const eventName = `${port}-open`; + if (!this.eventRegistry.hasKey(eventName)) { + return; + } + const event = this.eventRegistry.getItem(eventName); + event(); + }); + + socket.on('serial.closeEvent', (port, code) => { + const eventName = `${port}-close`; + if (!this.eventRegistry.hasKey(eventName)) { + return; + } + const event = this.eventRegistry.getItem(eventName); + event(code); + }); + } + + this.getSocket = function () { + return this.socket; + } + + this.getEventRegistry = function () { + return this.eventRegistry; + } + } + + constructor(port) { + super(port); + this.#addEventsListener_(); + const socket = WebSocketSerial.getSocket(); + socket.emit('serial.create', port); + } + + #addEventsListener_() { + const port = this.getPortName(); + const eventRegistry = WebSocketSerial.getEventRegistry(); + eventRegistry.register(`${port}-buffer`, (buffer) => { + this.onBuffer(buffer); + }); + eventRegistry.register(`${port}-string`, (str) => { + this.onString(str); + }); + eventRegistry.register(`${port}-error`, (error) => { + this.onError(String(error)); + this.onClose(1); + }); + eventRegistry.register(`${port}-open`, () => { + this.onOpen(); + }); + eventRegistry.register(`${port}-close`, (code) => { + this.onClose(code); + }); + } + + async open(baud) { + return new Promise((resolve, reject) => { + const portsName = Serial.getCurrentPortsName(); + const currentPort = this.getPortName(); + if (!portsName.includes(currentPort)) { + reject('无可用串口'); + return; + } + if (this.isOpened()) { + resolve(); + return; + } + baud = baud ?? this.getBaudRate(); + const socket = WebSocketSerial.getSocket(); + socket.emit('serial.open', this.getPortName(), baud, (response) => { + const [error, result] = response; + if (error) { + this.onError(error); + reject(error); + } else { + super.open(baud); + this.setBaudRate(baud); + resolve(result); + } + }); + }); + } + + async close() { + return new Promise((resolve, reject) => { + if (!this.isOpened()) { + resolve(); + return; + } + super.close(); + const socket = WebSocketSerial.getSocket(); + socket.emit('serial.close', this.getPortName(), (response) => { + const [error, result] = response; + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); + } + + async setBaudRate(baud) { + return new Promise((resolve, reject) => { + if (!this.isOpened() + || this.getBaudRate() === baud + || !this.baudRateIsLegal(baud)) { + resolve(); + return; + } + const socket = WebSocketSerial.getSocket(); + socket.emit('serial.setBaudRate', this.getPortName(), baud, (response) => { + const [error,] = response; + if (error) { + reject(error); + } else { + super.setBaudRate(baud); + this.setDTRAndRTS(this.getDTR(), this.getRTS()).finally(resolve); + } + }); + }); + } + + async send(data) { + return new Promise((resolve, reject) => { + if (!this.isOpened()) { + resolve(); + return; + } + const socket = WebSocketSerial.getSocket(); + socket.emit('serial.send', this.getPortName(), data, (response) => { + const [error, result] = response; + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); + } + + async sendString(str) { + return this.send(str); + } + + async sendBuffer(buffer) { + return this.send(buffer); + } + + async setDTRAndRTS(dtr, rts) { + return new Promise((resolve, reject) => { + if (!this.isOpened()) { + resolve(); + return; + } + const socket = WebSocketSerial.getSocket(); + socket.emit('serial.setDTRAndRTS', this.getPortName(), dtr, rts, (response) => { + const [error, result] = response; + if (error) { + reject(error); + } else { + super.setDTRAndRTS(dtr, rts); + resolve(result); + } + }); + }); + } + + async setDTR(dtr) { + return this.setDTRAndRTS(dtr, this.getRTS()); + } + + async setRTS(rts) { + return this.setDTRAndRTS(this.getDTR(), rts); + } + + onBuffer(buffer) { + super.onBuffer(buffer); + for (let i = 0; i < buffer.length; i++) { + super.onByte(buffer[i]); + } + const string = this.decodeBuffer(buffer); + if (!string) { + return; + } + for (let char of string) { + super.onChar(char); + } + } + + async dispose() { + return new Promise((resolve, reject) => { + const port = this.getPortName(); + const eventRegistry = WebSocketSerial.getEventRegistry(); + eventRegistry.unregister(`${port}-buffer`); + eventRegistry.unregister(`${port}-string`); + eventRegistry.unregister(`${port}-error`); + eventRegistry.unregister(`${port}-open`); + eventRegistry.unregister(`${port}-close`); + super.dispose() + .then(() => { + const socket = WebSocketSerial.getSocket(); + socket.emit('serial.dispose', port, ([error, result]) => { + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }) + .catch(reject); + }) + } +} + +WebSocket.Serial = WebSocketSerial; + +}); \ No newline at end of file diff --git a/common/modules/mixly-modules/web-socket/socket.js b/common/modules/mixly-modules/web-socket/socket.js index e09e28d6..0309a8f3 100644 --- a/common/modules/mixly-modules/web-socket/socket.js +++ b/common/modules/mixly-modules/web-socket/socket.js @@ -1,294 +1,25 @@ goog.loadJs('web', () => { -goog.require('path'); -goog.require('Mixly.Env'); -goog.require('Mixly.Config'); -goog.require('Mixly.Boards'); -goog.require('Mixly.Command'); -goog.require('Mixly.MJSON'); -goog.require('Mixly.MArray'); -goog.require('Mixly.LayerExt'); +goog.require('io'); goog.require('Mixly.WebSocket'); +goog.require('Mixly.WebSocket.Serial'); +goog.require('Mixly.WebSocket.ArduShell'); goog.provide('Mixly.WebSocket.Socket'); -const { - Env, - Config, - Boards, - Command, - MJSON, - MArray, - LayerExt -} = Mixly; +const { WebSocket } = Mixly; +const { Socket, Serial, ArduShell } = WebSocket; -const { BOARD, SELECTED_BOARD, SOFTWARE } = Config; -const { Socket } = Mixly.WebSocket; - -Socket.obj = null; -Socket.url = 'ws://127.0.0.1/socket'; -Socket.jsonArr = []; -Socket.connected = false; -Socket.initFunc = null; -Socket.debug = SOFTWARE.debug; -BOARD.server = { ...SOFTWARE.webSocket }; -let { hostname, protocol, port } = window.location; -if (protocol === 'http:') { - Socket.protocol = 'ws:'; -} else { - Socket.protocol = 'wss:'; -} -if (port) { - port = ':' + port; -} -Socket.url = Socket.protocol + '//' + hostname + port + '/socket'; -Socket.IPAddress = hostname; - -let lockReconnect = false; //避免重复连接 -let timeoutFlag = true; -let timeoutSet = null; -let reconectNum = 0; -const timeout = 5000; //超时重连间隔 - -function reconnect () { - if (lockReconnect) return; - lockReconnect = true; - //没连接上会一直重连,设置延迟避免请求过多 - setTimeout(function () { - timeoutFlag = true; - Socket.init(); - console.info(`正在重连第${reconectNum + 1}次`); - reconectNum++; - lockReconnect = false; - }, timeout); //这里设置重连间隔(ms) -} - -//心跳检测 -const heartCheck = { - timeout, //毫秒 - timeoutObj: null, - serverTimeoutObj: null, - reset: function () { - clearInterval(this.timeoutObj); - clearTimeout(this.serverTimeoutObj); - return this; - }, - start: function () { - const self = this; - let count = 0; - let WS = Socket; - this.timeoutObj = setInterval(() => { - if (count < 3) { - if (WS.obj.readyState === 1) { - WS.obj.send('HeartBeat'); - console.info(`HeartBeat第${count + 1}次`); - } - count++; - } else { - clearInterval(this.timeoutObj); - count = 0; - if (WS.obj.readyState === 0 && WS.obj.readyState === 1) { - WS.obj.close(); - } - } - }, self.timeout); - } -} - -Socket.init = (onopenFunc = (data) => {}, doFunc = () => {}) => { - if (Socket.connected) { - if (Socket.initFunc) { - Socket.initFunc(); - Socket.initFunc = null; - } - doFunc(); - return; - } - - timeoutSet = setTimeout(() => { - if (timeoutFlag && reconectNum < 3) { - console.info(`重连`); - reconectNum++; - Socket.init(); - } - }, timeout); - - let WS = Socket; - WS.obj = new WebSocket(WS.url); - WS.obj.onopen = () => { - const { mainStatusBarTabs } = Mixly; - const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); - console.log('已连接' + WS.url); - mainStatusBarTabs.show(); - mainStatusBarTabs.changeTo('output'); - statusBarTerminal.setValue(WS.url + '连接成功\n'); - WS.connected = true; - Socket.toggleUIToolbar(true); - Socket.initFunc = doFunc; - reconectNum = 0; - timeoutFlag = false; - clearTimeout(timeoutSet); - heartCheck.reset().start(); - onopenFunc(WS); - }; - - WS.obj.onmessage = (event) => { - heartCheck.reset().start(); - let command = Command.parse(event.data); - command = MJSON.decode(command); - if (Socket.debug) - console.log('receive -> ', event.data); - /*if (command && command.obj && command.function) { - if (command.type === 1) { - let args = command.args ?? []; - try { - if (window[command.obj][command.function]) - window[command.obj][command.function](...args); - } catch (e) { - console.log(e); - } - } - }*/ - Command.run(command); - }; - - WS.obj.onerror = (event) => { - console.log('WebSocket error: ', event); - reconnect(); //重连 - }; - - WS.obj.onclose = (event) => { - const { mainStatusBarTabs } = Mixly; - const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output'); - WS.connected = false; - console.log('已断开' + WS.url); - mainStatusBarTabs.show(); - mainStatusBarTabs.changeTo('output'); - statusBarTerminal.setValue(WS.url + '连接断开,请在设置中重新连接\n'); - let ports = [ ...mainStatusBarTabs.statusBarIndexToIds ]; - MArray.remove(ports, 'output'); - for (let i = 0; i < ports.length; i++) { - const statusBarSerial = mainStatusBarTabs.getStatusBarById(ports[i]); - statusBarSerial.close(ports[i]); - } - Socket.toggleUIToolbar(false); - layer.closeAll(); - Mixly.WebSocket.BU.burning = false; - Mixly.WebSocket.BU.uploading = false; - Mixly.WebSocket.ArduShell.compiling = false; - Mixly.WebSocket.ArduShell.uploading = false; - - console.info(`关闭`, event.code); - if (event.code !== 1000) { - timeoutFlag = false; - clearTimeout(timeoutSet); - reconnect(); - } else { - clearInterval(heartCheck.timeoutObj); - clearTimeout(heartCheck.serverTimeoutObj); - } - } -} - -Socket.sendCommand = (command) => { - let WS = Mixly.WebSocket.Socket; - if (!WS.connected) { - layer.msg('未连接' + WS.url, {time: 1000}); - return; - } - let commandStr = ''; - - try { - commandStr = JSON.stringify(MJSON.encode(command)); - if (Socket.debug) - console.log('send -> ', commandStr); - } catch (e) { - console.log(e); - return; - } - WS.obj.send(commandStr); -} - -Socket.clickConnect = () => { - if (Socket.connected) { - Socket.disconnect(); - } else { - Socket.connect((WS) => { - layer.closeAll(); - layer.msg(WS.url + '连接成功', { time: 1000 }); - }); - } -} - -Socket.openLoadingBox = (title, successFunc = () => {}, endFunc = () => {}) => { - layer.open({ - type: 1, - title: title, - content: $('#mixly-loader-div'), - shade: LayerExt.SHADE_ALL, - closeBtn: 0, - success: function () { - $("#webusb-cancel").css("display","none"); - $(".layui-layer-page").css("z-index", "198910151"); - successFunc(); - }, - end: function () { - $("#mixly-loader-div").css("display", "none"); - $(".layui-layer-shade").remove(); - $("#webusb-cancel").css("display", "unset"); - if (Socket.connected) - endFunc(); - } +Socket.init = function () { + const socket = io('wss://127.0.0.1:4000', { + path: '/mixly-socket/', + reconnectionDelayMax: 10000, + transports: ['websocket'], + protocols: ['my-protocol-v1'] }); -} -Socket.connect = (onopenFunc = (data) => {}, doFunc = () => {}) => { - if (Socket.connected) { - doFunc(); - return; - } - let title = '连接中...'; - Socket.openLoadingBox(title, () => { - setTimeout(() => { - Socket.init(onopenFunc); - }, 1000); - }, doFunc); -} - -Socket.disconnect = () => { - if (!Socket.connected) - return; - let title = '断开中...'; - Socket.openLoadingBox(title, () => { - Socket.obj.close(); - }); -} - -Socket.toggleUIToolbar = (connected) => { - try { - if (connected) { - $('#socket-connect-btn').html(Blockly.Msg.MSG['disconnect']); - $('#socket-connect-btn').removeClass('icon-link').addClass('icon-unlink'); - } else { - $('#socket-connect-btn').html(Blockly.Msg.MSG['connect']); - $('#socket-connect-btn').removeClass('icon-unlink').addClass('icon-link'); - } - } catch (e) { - console.log(e); - } -} - -Socket.updateSelectedBoardConfig = (info) => { - Env.currentPlatform = info.currentPlatform; - info.clientPath = info.clientPath.replaceAll('\\', '/'); - Env.clientPath = info.clientPath; - info.appPath = info.appPath.replaceAll('\\', '/'); - Env.srcDirPath = info.appPath; - Env.indexDirPath = path.join(Env.srcDirPath, 'boards'); - Env.boardDirPath = path.join(Env.srcDirPath, BOARD.boardIndex, '../'); - Env.python3Path = info.python3Path; - const boardType = Boards.getSelectedBoardName(); - Boards.changeTo(boardType); + Serial.init(socket); + ArduShell.init(socket); } }); diff --git a/common/modules/mixly-modules/web/serial.js b/common/modules/mixly-modules/web/serial.js index d4f90a3b..aae81b64 100644 --- a/common/modules/mixly-modules/web/serial.js +++ b/common/modules/mixly-modules/web/serial.js @@ -94,12 +94,15 @@ class WebSerial extends Serial { this.refreshPorts(); }); } - navigator?.serial?.getPorts().then((serialports) => { - for (let serialport of serialports) { - this.addPort(serialport); - } - }); - this.addEventsListener(); + + if (!Env.hasSocketServer) { + navigator?.serial?.getPorts().then((serialports) => { + for (let serialport of serialports) { + this.addPort(serialport); + } + }); + this.addEventsListener(); + } } #serialport_ = null; diff --git a/common/modules/web-modules/socket.io.min.js b/common/modules/web-modules/socket.io.min.js new file mode 100644 index 00000000..521c3229 --- /dev/null +++ b/common/modules/web-modules/socket.io.min.js @@ -0,0 +1,7 @@ +/*! + * Socket.IO v4.8.1 + * (c) 2014-2024 Guillermo Rauch + * Released under the MIT License. + */ +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(t="undefined"!=typeof globalThis?globalThis:t||self).io=n()}(this,(function(){"use strict";function t(t,n){(null==n||n>t.length)&&(n=t.length);for(var i=0,r=Array(n);i=n.length?{done:!0}:{done:!1,value:n[e++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,u=!0,h=!1;return{s:function(){r=r.call(n)},n:function(){var t=r.next();return u=t.done,t},e:function(t){h=!0,s=t},f:function(){try{u||null==r.return||r.return()}finally{if(h)throw s}}}}function e(){return e=Object.assign?Object.assign.bind():function(t){for(var n=1;n1?{type:l[i],data:t.substring(1)}:{type:l[i]}:d},N=function(t,n){if(B){var i=function(t){var n,i,r,e,o,s=.75*t.length,u=t.length,h=0;"="===t[t.length-1]&&(s--,"="===t[t.length-2]&&s--);var f=new ArrayBuffer(s),c=new Uint8Array(f);for(n=0;n>4,c[h++]=(15&r)<<4|e>>2,c[h++]=(3&e)<<6|63&o;return f}(t);return C(i,n)}return{base64:!0,data:t}},C=function(t,n){return"blob"===n?t instanceof Blob?t:new Blob([t]):t instanceof ArrayBuffer?t:t.buffer},T=String.fromCharCode(30);function U(){return new TransformStream({transform:function(t,n){!function(t,n){y&&t.data instanceof Blob?t.data.arrayBuffer().then(k).then(n):b&&(t.data instanceof ArrayBuffer||w(t.data))?n(k(t.data)):g(t,!1,(function(t){p||(p=new TextEncoder),n(p.encode(t))}))}(t,(function(i){var r,e=i.length;if(e<126)r=new Uint8Array(1),new DataView(r.buffer).setUint8(0,e);else if(e<65536){r=new Uint8Array(3);var o=new DataView(r.buffer);o.setUint8(0,126),o.setUint16(1,e)}else{r=new Uint8Array(9);var s=new DataView(r.buffer);s.setUint8(0,127),s.setBigUint64(1,BigInt(e))}t.data&&"string"!=typeof t.data&&(r[0]|=128),n.enqueue(r),n.enqueue(i)}))}})}function M(t){return t.reduce((function(t,n){return t+n.length}),0)}function x(t,n){if(t[0].length===n)return t.shift();for(var i=new Uint8Array(n),r=0,e=0;e1?n-1:0),r=1;r1&&void 0!==arguments[1]?arguments[1]:{};return t+"://"+this.i()+this.o()+this.opts.path+this.u(n)},i.i=function(){var t=this.opts.hostname;return-1===t.indexOf(":")?t:"["+t+"]"},i.o=function(){return this.opts.port&&(this.opts.secure&&Number(443!==this.opts.port)||!this.opts.secure&&80!==Number(this.opts.port))?":"+this.opts.port:""},i.u=function(t){var n=function(t){var n="";for(var i in t)t.hasOwnProperty(i)&&(n.length&&(n+="&"),n+=encodeURIComponent(i)+"="+encodeURIComponent(t[i]));return n}(t);return n.length?"?"+n:""},n}(I),X=function(t){function n(){var n;return(n=t.apply(this,arguments)||this).h=!1,n}s(n,t);var r=n.prototype;return r.doOpen=function(){this.v()},r.pause=function(t){var n=this;this.readyState="pausing";var i=function(){n.readyState="paused",t()};if(this.h||!this.writable){var r=0;this.h&&(r++,this.once("pollComplete",(function(){--r||i()}))),this.writable||(r++,this.once("drain",(function(){--r||i()})))}else i()},r.v=function(){this.h=!0,this.doPoll(),this.emitReserved("poll")},r.onData=function(t){var n=this;(function(t,n){for(var i=t.split(T),r=[],e=0;e0&&void 0!==arguments[0]?arguments[0]:{};return e(t,{xd:this.xd},this.opts),new Y(tt,this.uri(),t)},n}(K);function tt(t){var n=t.xdomain;try{if("undefined"!=typeof XMLHttpRequest&&(!n||z))return new XMLHttpRequest}catch(t){}if(!n)try{return new(L[["Active"].concat("Object").join("X")])("Microsoft.XMLHTTP")}catch(t){}}var nt="undefined"!=typeof navigator&&"string"==typeof navigator.product&&"reactnative"===navigator.product.toLowerCase(),it=function(t){function n(){return t.apply(this,arguments)||this}s(n,t);var r=n.prototype;return r.doOpen=function(){var t=this.uri(),n=this.opts.protocols,i=nt?{}:_(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(i.headers=this.opts.extraHeaders);try{this.ws=this.createSocket(t,n,i)}catch(t){return this.emitReserved("error",t)}this.ws.binaryType=this.socket.binaryType,this.addEventListeners()},r.addEventListeners=function(){var t=this;this.ws.onopen=function(){t.opts.autoUnref&&t.ws.C.unref(),t.onOpen()},this.ws.onclose=function(n){return t.onClose({description:"websocket connection closed",context:n})},this.ws.onmessage=function(n){return t.onData(n.data)},this.ws.onerror=function(n){return t.onError("websocket error",n)}},r.write=function(t){var n=this;this.writable=!1;for(var i=function(){var i=t[r],e=r===t.length-1;g(i,n.supportsBinary,(function(t){try{n.doWrite(i,t)}catch(t){}e&&R((function(){n.writable=!0,n.emitReserved("drain")}),n.setTimeoutFn)}))},r=0;rMath.pow(2,21)-1){u.enqueue(d);break}e=v*Math.pow(2,32)+a.getUint32(4),r=3}else{if(M(i)t){u.enqueue(d);break}}}})}(Number.MAX_SAFE_INTEGER,t.socket.binaryType),r=n.readable.pipeThrough(i).getReader(),e=U();e.readable.pipeTo(n.writable),t.U=e.writable.getWriter();!function n(){r.read().then((function(i){var r=i.done,e=i.value;r||(t.onPacket(e),n())})).catch((function(t){}))}();var o={type:"open"};t.query.sid&&(o.data='{"sid":"'.concat(t.query.sid,'"}')),t.U.write(o).then((function(){return t.onOpen()}))}))}))},r.write=function(t){var n=this;this.writable=!1;for(var i=function(){var i=t[r],e=r===t.length-1;n.U.write(i).then((function(){e&&R((function(){n.writable=!0,n.emitReserved("drain")}),n.setTimeoutFn)}))},r=0;r8e3)throw"URI too long";var n=t,i=t.indexOf("["),r=t.indexOf("]");-1!=i&&-1!=r&&(t=t.substring(0,i)+t.substring(i,r).replace(/:/g,";")+t.substring(r,t.length));for(var e,o,s=ut.exec(t||""),u={},h=14;h--;)u[ht[h]]=s[h]||"";return-1!=i&&-1!=r&&(u.source=n,u.host=u.host.substring(1,u.host.length-1).replace(/;/g,":"),u.authority=u.authority.replace("[","").replace("]","").replace(/;/g,":"),u.ipv6uri=!0),u.pathNames=function(t,n){var i=/\/{2,9}/g,r=n.replace(i,"/").split("/");"/"!=n.slice(0,1)&&0!==n.length||r.splice(0,1);"/"==n.slice(-1)&&r.splice(r.length-1,1);return r}(0,u.path),u.queryKey=(e=u.query,o={},e.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,(function(t,n,i){n&&(o[n]=i)})),o),u}var ct="function"==typeof addEventListener&&"function"==typeof removeEventListener,at=[];ct&&addEventListener("offline",(function(){at.forEach((function(t){return t()}))}),!1);var vt=function(t){function n(n,i){var r;if((r=t.call(this)||this).binaryType="arraybuffer",r.writeBuffer=[],r.M=0,r.I=-1,r.R=-1,r.L=-1,r._=1/0,n&&"object"===c(n)&&(i=n,n=null),n){var o=ft(n);i.hostname=o.host,i.secure="https"===o.protocol||"wss"===o.protocol,i.port=o.port,o.query&&(i.query=o.query)}else i.host&&(i.hostname=ft(i.host).host);return $(r,i),r.secure=null!=i.secure?i.secure:"undefined"!=typeof location&&"https:"===location.protocol,i.hostname&&!i.port&&(i.port=r.secure?"443":"80"),r.hostname=i.hostname||("undefined"!=typeof location?location.hostname:"localhost"),r.port=i.port||("undefined"!=typeof location&&location.port?location.port:r.secure?"443":"80"),r.transports=[],r.D={},i.transports.forEach((function(t){var n=t.prototype.name;r.transports.push(n),r.D[n]=t})),r.opts=e({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,addTrailingSlash:!0,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!1},i),r.opts.path=r.opts.path.replace(/\/$/,"")+(r.opts.addTrailingSlash?"/":""),"string"==typeof r.opts.query&&(r.opts.query=function(t){for(var n={},i=t.split("&"),r=0,e=i.length;r1))return this.writeBuffer;for(var t,n=1,i=0;i=57344?i+=3:(r++,i+=4);return i}(t):Math.ceil(1.33*(t.byteLength||t.size))),i>0&&n>this.L)return this.writeBuffer.slice(0,i);n+=2}return this.writeBuffer},i.W=function(){var t=this;if(!this._)return!0;var n=Date.now()>this._;return n&&(this._=0,R((function(){t.F("ping timeout")}),this.setTimeoutFn)),n},i.write=function(t,n,i){return this.J("message",t,n,i),this},i.send=function(t,n,i){return this.J("message",t,n,i),this},i.J=function(t,n,i,r){if("function"==typeof n&&(r=n,n=void 0),"function"==typeof i&&(r=i,i=null),"closing"!==this.readyState&&"closed"!==this.readyState){(i=i||{}).compress=!1!==i.compress;var e={type:t,data:n,options:i};this.emitReserved("packetCreate",e),this.writeBuffer.push(e),r&&this.once("flush",r),this.flush()}},i.close=function(){var t=this,n=function(){t.F("forced close"),t.transport.close()},i=function i(){t.off("upgrade",i),t.off("upgradeError",i),n()},r=function(){t.once("upgrade",i),t.once("upgradeError",i)};return"opening"!==this.readyState&&"open"!==this.readyState||(this.readyState="closing",this.writeBuffer.length?this.once("drain",(function(){t.upgrading?r():n()})):this.upgrading?r():n()),this},i.B=function(t){if(n.priorWebsocketSuccess=!1,this.opts.tryAllTransports&&this.transports.length>1&&"opening"===this.readyState)return this.transports.shift(),this.q();this.emitReserved("error",t),this.F("transport error",t)},i.F=function(t,n){if("opening"===this.readyState||"open"===this.readyState||"closing"===this.readyState){if(this.clearTimeoutFn(this.Y),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),ct&&(this.P&&removeEventListener("beforeunload",this.P,!1),this.$)){var i=at.indexOf(this.$);-1!==i&&at.splice(i,1)}this.readyState="closed",this.id=null,this.emitReserved("close",t,n),this.writeBuffer=[],this.M=0}},n}(I);vt.protocol=4;var lt=function(t){function n(){var n;return(n=t.apply(this,arguments)||this).Z=[],n}s(n,t);var i=n.prototype;return i.onOpen=function(){if(t.prototype.onOpen.call(this),"open"===this.readyState&&this.opts.upgrade)for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{},r="object"===c(n)?n:i;return(!r.transports||r.transports&&"string"==typeof r.transports[0])&&(r.transports=(r.transports||["polling","websocket","webtransport"]).map((function(t){return st[t]})).filter((function(t){return!!t}))),t.call(this,n,r)||this}return s(n,t),n}(lt);pt.protocol;var dt="function"==typeof ArrayBuffer,yt=function(t){return"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(t):t.buffer instanceof ArrayBuffer},bt=Object.prototype.toString,wt="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===bt.call(Blob),gt="function"==typeof File||"undefined"!=typeof File&&"[object FileConstructor]"===bt.call(File);function mt(t){return dt&&(t instanceof ArrayBuffer||yt(t))||wt&&t instanceof Blob||gt&&t instanceof File}function kt(t,n){if(!t||"object"!==c(t))return!1;if(Array.isArray(t)){for(var i=0,r=t.length;i=0&&t.num1?e-1:0),s=1;s1?i-1:0),e=1;ei.l.retries&&(i.it.shift(),n&&n(t));else if(i.it.shift(),n){for(var e=arguments.length,o=new Array(e>1?e-1:0),s=1;s0&&void 0!==arguments[0]&&arguments[0];if(this.connected&&0!==this.it.length){var n=this.it[0];n.pending&&!t||(n.pending=!0,n.tryCount++,this.flags=n.flags,this.emit.apply(this,n.args))}},o.packet=function(t){t.nsp=this.nsp,this.io.ct(t)},o.onopen=function(){var t=this;"function"==typeof this.auth?this.auth((function(n){t.vt(n)})):this.vt(this.auth)},o.vt=function(t){this.packet({type:Bt.CONNECT,data:this.lt?e({pid:this.lt,offset:this.dt},t):t})},o.onerror=function(t){this.connected||this.emitReserved("connect_error",t)},o.onclose=function(t,n){this.connected=!1,delete this.id,this.emitReserved("disconnect",t,n),this.yt()},o.yt=function(){var t=this;Object.keys(this.acks).forEach((function(n){if(!t.sendBuffer.some((function(t){return String(t.id)===n}))){var i=t.acks[n];delete t.acks[n],i.withError&&i.call(t,new Error("socket has been disconnected"))}}))},o.onpacket=function(t){if(t.nsp===this.nsp)switch(t.type){case Bt.CONNECT:t.data&&t.data.sid?this.onconnect(t.data.sid,t.data.pid):this.emitReserved("connect_error",new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));break;case Bt.EVENT:case Bt.BINARY_EVENT:this.onevent(t);break;case Bt.ACK:case Bt.BINARY_ACK:this.onack(t);break;case Bt.DISCONNECT:this.ondisconnect();break;case Bt.CONNECT_ERROR:this.destroy();var n=new Error(t.data.message);n.data=t.data.data,this.emitReserved("connect_error",n)}},o.onevent=function(t){var n=t.data||[];null!=t.id&&n.push(this.ack(t.id)),this.connected?this.emitEvent(n):this.receiveBuffer.push(Object.freeze(n))},o.emitEvent=function(n){if(this.bt&&this.bt.length){var i,e=r(this.bt.slice());try{for(e.s();!(i=e.n()).done;){i.value.apply(this,n)}}catch(t){e.e(t)}finally{e.f()}}t.prototype.emit.apply(this,n),this.lt&&n.length&&"string"==typeof n[n.length-1]&&(this.dt=n[n.length-1])},o.ack=function(t){var n=this,i=!1;return function(){if(!i){i=!0;for(var r=arguments.length,e=new Array(r),o=0;o0&&t.jitter<=1?t.jitter:0,this.attempts=0}_t.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var n=Math.random(),i=Math.floor(n*this.jitter*t);t=1&Math.floor(10*n)?t+i:t-i}return 0|Math.min(t,this.max)},_t.prototype.reset=function(){this.attempts=0},_t.prototype.setMin=function(t){this.ms=t},_t.prototype.setMax=function(t){this.max=t},_t.prototype.setJitter=function(t){this.jitter=t};var Dt=function(t){function n(n,i){var r,e;(r=t.call(this)||this).nsps={},r.subs=[],n&&"object"===c(n)&&(i=n,n=void 0),(i=i||{}).path=i.path||"/socket.io",r.opts=i,$(r,i),r.reconnection(!1!==i.reconnection),r.reconnectionAttempts(i.reconnectionAttempts||1/0),r.reconnectionDelay(i.reconnectionDelay||1e3),r.reconnectionDelayMax(i.reconnectionDelayMax||5e3),r.randomizationFactor(null!==(e=i.randomizationFactor)&&void 0!==e?e:.5),r.backoff=new _t({min:r.reconnectionDelay(),max:r.reconnectionDelayMax(),jitter:r.randomizationFactor()}),r.timeout(null==i.timeout?2e4:i.timeout),r.st="closed",r.uri=n;var o=i.parser||xt;return r.encoder=new o.Encoder,r.decoder=new o.Decoder,r.et=!1!==i.autoConnect,r.et&&r.open(),r}s(n,t);var i=n.prototype;return i.reconnection=function(t){return arguments.length?(this.kt=!!t,t||(this.skipReconnect=!0),this):this.kt},i.reconnectionAttempts=function(t){return void 0===t?this.At:(this.At=t,this)},i.reconnectionDelay=function(t){var n;return void 0===t?this.jt:(this.jt=t,null===(n=this.backoff)||void 0===n||n.setMin(t),this)},i.randomizationFactor=function(t){var n;return void 0===t?this.Et:(this.Et=t,null===(n=this.backoff)||void 0===n||n.setJitter(t),this)},i.reconnectionDelayMax=function(t){var n;return void 0===t?this.Ot:(this.Ot=t,null===(n=this.backoff)||void 0===n||n.setMax(t),this)},i.timeout=function(t){return arguments.length?(this.Bt=t,this):this.Bt},i.maybeReconnectOnOpen=function(){!this.ot&&this.kt&&0===this.backoff.attempts&&this.reconnect()},i.open=function(t){var n=this;if(~this.st.indexOf("open"))return this;this.engine=new pt(this.uri,this.opts);var i=this.engine,r=this;this.st="opening",this.skipReconnect=!1;var e=It(i,"open",(function(){r.onopen(),t&&t()})),o=function(i){n.cleanup(),n.st="closed",n.emitReserved("error",i),t?t(i):n.maybeReconnectOnOpen()},s=It(i,"error",o);if(!1!==this.Bt){var u=this.Bt,h=this.setTimeoutFn((function(){e(),o(new Error("timeout")),i.close()}),u);this.opts.autoUnref&&h.unref(),this.subs.push((function(){n.clearTimeoutFn(h)}))}return this.subs.push(e),this.subs.push(s),this},i.connect=function(t){return this.open(t)},i.onopen=function(){this.cleanup(),this.st="open",this.emitReserved("open");var t=this.engine;this.subs.push(It(t,"ping",this.onping.bind(this)),It(t,"data",this.ondata.bind(this)),It(t,"error",this.onerror.bind(this)),It(t,"close",this.onclose.bind(this)),It(this.decoder,"decoded",this.ondecoded.bind(this)))},i.onping=function(){this.emitReserved("ping")},i.ondata=function(t){try{this.decoder.add(t)}catch(t){this.onclose("parse error",t)}},i.ondecoded=function(t){var n=this;R((function(){n.emitReserved("packet",t)}),this.setTimeoutFn)},i.onerror=function(t){this.emitReserved("error",t)},i.socket=function(t,n){var i=this.nsps[t];return i?this.et&&!i.active&&i.connect():(i=new Lt(this,t,n),this.nsps[t]=i),i},i.wt=function(t){for(var n=0,i=Object.keys(this.nsps);n=this.At)this.backoff.reset(),this.emitReserved("reconnect_failed"),this.ot=!1;else{var i=this.backoff.duration();this.ot=!0;var r=this.setTimeoutFn((function(){n.skipReconnect||(t.emitReserved("reconnect_attempt",n.backoff.attempts),n.skipReconnect||n.open((function(i){i?(n.ot=!1,n.reconnect(),t.emitReserved("reconnect_error",i)):n.onreconnect()})))}),i);this.opts.autoUnref&&r.unref(),this.subs.push((function(){t.clearTimeoutFn(r)}))}},i.onreconnect=function(){var t=this.backoff.attempts;this.ot=!1,this.backoff.reset(),this.emitReserved("reconnect",t)},n}(I),Pt={};function $t(t,n){"object"===c(t)&&(n=t,t=void 0);var i,r=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2?arguments[2]:void 0,r=t;i=i||"undefined"!=typeof location&&location,null==t&&(t=i.protocol+"//"+i.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?i.protocol+t:i.host+t),/^(https?|wss?):\/\//.test(t)||(t=void 0!==i?i.protocol+"//"+t:"https://"+t),r=ft(t)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";var e=-1!==r.host.indexOf(":")?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+e+":"+r.port+n,r.href=r.protocol+"://"+e+(i&&i.port===r.port?"":":"+r.port),r}(t,(n=n||{}).path||"/socket.io"),e=r.source,o=r.id,s=r.path,u=Pt[o]&&s in Pt[o].nsps;return n.forceNew||n["force new connection"]||!1===n.multiplex||u?i=new Dt(e,n):(Pt[o]||(Pt[o]=new Dt(e,n)),i=Pt[o]),r.query&&!n.query&&(n.query=r.queryKey),i.socket(r.path,n)}return e($t,{Manager:Dt,Socket:Lt,io:$t,connect:$t}),$t})); + diff --git a/common/templates/html/footerbar.html b/common/templates/html/footerbar.html index 0a9bf622..78a5509e 100644 --- a/common/templates/html/footerbar.html +++ b/common/templates/html/footerbar.html @@ -44,6 +44,8 @@ align-items: center; } + div[m-id="{{d.mId}}"] > .footerbar-item > div > p, + div[m-id="{{d.mId}}"] > .footerbar-item > div > div > p, div[m-id="{{d.mId}}"] > .footerbar-item > button > a { color: #fff; font-size: 12px;