diff --git a/common/modules/mixly-modules/common/ampy-file-tree.js b/common/modules/mixly-modules/common/ampy-file-tree.js index 9ac5afa9..6e2ea621 100644 --- a/common/modules/mixly-modules/common/ampy-file-tree.js +++ b/common/modules/mixly-modules/common/ampy-file-tree.js @@ -1,17 +1,34 @@ goog.loadJs('common', () => { +goog.require('Mixly.Env'); goog.require('Mixly.FileTree'); goog.require('Mixly.Electron.AmpyFS'); goog.require('Mixly.Web.AmpyFS'); +goog.require('Mixly.WebSocket.AmpyFS'); goog.provide('Mixly.AmpyFileTree'); const { + Env, FileTree, Electron = {}, - Web = {} + Web = {}, + WebSocket = {} } = Mixly; -const { AmpyFS } = goog.isElectron? Electron : Web; + +let currentObj = null; + +if (goog.isElectron) { + currentObj = Electron; +} else { + if (Env.hasSocketServer) { + currentObj = WebSocket; + } else { + currentObj = Web; + } +} + +const { AmpyFS } = currentObj; class AmpyFileTree extends FileTree { diff --git a/common/modules/mixly-modules/deps.json b/common/modules/mixly-modules/deps.json index daffa0b8..5f947408 100644 --- a/common/modules/mixly-modules/deps.json +++ b/common/modules/mixly-modules/deps.json @@ -2,9 +2,11 @@ { "path": "/common/ampy-file-tree.js", "require": [ + "Mixly.Env", "Mixly.FileTree", "Mixly.Electron.AmpyFS", - "Mixly.Web.AmpyFS" + "Mixly.Web.AmpyFS", + "Mixly.WebSocket.AmpyFS" ], "provide": [ "Mixly.AmpyFileTree" @@ -1718,6 +1720,34 @@ "Mixly.WebCompiler" ] }, + { + "path": "/web-socket/ampy-fs.js", + "require": [ + "path", + "Mixly.Env", + "Mixly.FS", + "Mixly.Debug", + "Mixly.MJSON", + "Mixly.WebSocket.Ampy" + ], + "provide": [ + "Mixly.WebSocket.AmpyFS" + ] + }, + { + "path": "/web-socket/ampy.js", + "require": [ + "path", + "Mustache", + "Mixly.Ampy", + "Mixly.Env", + "Mixly.Serial", + "Mixly.WebSocket" + ], + "provide": [ + "Mixly.WebSocket.Ampy" + ] + }, { "path": "/web-socket/arduino-shell.js", "require": [ @@ -1784,7 +1814,8 @@ "Mixly.WebSocket", "Mixly.WebSocket.Serial", "Mixly.WebSocket.ArduShell", - "Mixly.WebSocket.BU" + "Mixly.WebSocket.BU", + "Mixly.WebSocket.Ampy" ], "provide": [ "Mixly.WebSocket.Socket" diff --git a/common/modules/mixly-modules/web-socket/ampy-fs.js b/common/modules/mixly-modules/web-socket/ampy-fs.js new file mode 100644 index 00000000..5e96c853 --- /dev/null +++ b/common/modules/mixly-modules/web-socket/ampy-fs.js @@ -0,0 +1,201 @@ +goog.loadJs('web', () => { + +goog.require('path'); +goog.require('Mixly.Env'); +goog.require('Mixly.FS'); +goog.require('Mixly.Debug'); +goog.require('Mixly.MJSON'); +goog.require('Mixly.WebSocket.Ampy'); +goog.provide('Mixly.WebSocket.AmpyFS'); + +const { + Env, + FS, + Debug, + MJSON, + WebSocket +} = Mixly; + +const { Ampy } = WebSocket; + + +class AmpyFS extends FS { + #ampy_ = null; + #port_ = ''; + #baud_ = 115200; + #decoder_ = new TextDecoder('utf8'); + + constructor() { + super(); + this.#ampy_ = new Ampy(); + } + + async rename(oldPath, newPath) { + let stdout = '', error = null; + try { + stdout = await this.#ampy_.rename(this.#port_, this.#baud_, oldPath, newPath); + } catch (e) { + error = e; + Debug.error(error); + } + return [error, stdout]; + } + + async createFile(filePath) { + let stdout = '', error = null; + try { + stdout = await this.#ampy_.mkfile(this.#port_, this.#baud_, filePath); + } catch (e) { + error = e; + Debug.error(error); + } + return [error, stdout]; + } + + async readFile(filePath) { + let stdout = '', error = null; + try { + stdout = await this.#ampy_.get(this.#port_, this.#baud_, filePath); + stdout = this.#decoder_.decode(this.#ampy_.unhexlify(stdout)); + } catch (e) { + error = e; + Debug.error(error); + } + return [error, stdout]; + } + + async writeFile(filePath, data) { + let stdout = '', error = null; + try { + stdout = await this.#ampy_.put(this.#port_, this.#baud_, filePath, data); + } catch (e) { + error = e; + Debug.error(error); + } + return [error, stdout]; + } + + async isFile(filePath) { + /*const [error, stdout] = await this.readDirectory(filePath); + if (error) { + return true; + } + return false;*/ + let error = null; + if (path.extname(filePath)) { + return [error, true]; + } else { + return [error, false]; + } + } + + async renameFile(oldFilePath, newFilePath) { + return this.rename(oldFilePath, newFilePath); + } + + // async moveFile(oldFilePath, newFilePath) {} + + // async copyFile(oldFilePath, newFilePath) {} + + async deleteFile(filePath) { + let stdout = '', error = null; + try { + stdout = await this.#ampy_.rm(this.#port_, this.#baud_, filePath); + } catch (e) { + error = e; + Debug.error(error); + } + return [error, stdout]; + } + + async createDirectory(folderPath) { + let stdout = '', error = null; + try { + stdout = await this.#ampy_.mkdir(this.#port_, this.#baud_, folderPath); + } catch (e) { + error = e; + Debug.error(error); + } + return [error, stdout]; + } + + async readDirectory(folderPath) { + let stdout = [], error = null; + try { + const output = await this.#ampy_.ls(this.#port_, this.#baud_, folderPath); + const dirs = Array.from(new Set(output.split('\r\n'))); + for (let i in dirs) { + if (!dirs[i]) { + continue; + } + stdout.push(MJSON.parse(dirs[i].replaceAll('\'', '"'))); + } + } catch (e) { + error = e; + Debug.error(error); + } + return [error, stdout]; + } + + async isDirectory(folderPath) { + /*const [error, stdout] = await this.readDirectory(folderPath); + if (error) { + return false; + } + return true;*/ + let error = null; + if (path.extname(folderPath)) { + return [error, false]; + } else { + return [error, true]; + } + } + + async isDirectoryEmpty(folderPath) { + /*const [error, stdout] = await this.readDirectory(folderPath); + let isEmpty = false; + if (error || !stdout.length) { + isEmpty = true; + }*/ + return [null, false]; + } + + async renameDirectory(oldFolderPath, newFolderPath) { + return this.rename(oldFolderPath, newFolderPath); + } + + // async moveDirectory(oldFolderPath, newFolderPath) {} + + // async copyDirectory(oldFolderPath, newFolderPath) {} + + async deleteDirectory(folderPath) { + let stdout = '', error = null; + try { + stdout = await this.#ampy_.rmdir(this.#port_, this.#baud_, folderPath); + } catch (e) { + error = e; + Debug.error(error); + } + return [error, stdout]; + } + + setPortName(port) { + this.#port_ = port; + } + + getPortName() { + return this.#port_; + } + + setBaudRate(baud) { + this.#baud_ = baud; + } + + getBaudRate() { + return this.#baud_; + } +} + +WebSocket.AmpyFS = AmpyFS; + +}); \ No newline at end of file diff --git a/common/modules/mixly-modules/web-socket/ampy.js b/common/modules/mixly-modules/web-socket/ampy.js new file mode 100644 index 00000000..1248bf25 --- /dev/null +++ b/common/modules/mixly-modules/web-socket/ampy.js @@ -0,0 +1,108 @@ +goog.loadJs('web', () => { + +goog.require('path'); +goog.require('Mustache'); +goog.require('Mixly.Ampy'); +goog.require('Mixly.Env'); +goog.require('Mixly.Serial'); +goog.require('Mixly.WebSocket'); +goog.provide('Mixly.WebSocket.Ampy'); + +const { + Ampy, + Env, + Serial, + WebSocket +} = Mixly; + + +class AmpyExt extends Ampy { + static { + this.mixlySocket = null; + this.socket = null; + + this.getSocket = function () { + return this.socket; + } + + this.getMixlySocket = function () { + return this.mixlySocket; + } + + this.init = function (mixlySocket) { + this.mixlySocket = mixlySocket; + this.socket = mixlySocket.getSocket(); + } + } + + constructor() { + super(); + } + + async ls(port, baud, folderPath) { + return this.exec('ampy.ls', port, baud, folderPath); + } + + async get(port, baud, filePath) { + return this.exec('ampy.get', port, baud, filePath); + } + + async mkdir(port, baud, folderPath) { + return this.exec('ampy.mkdir', port, baud, folderPath); + } + + async mkfile(port, baud, filePath) { + return this.exec('ampy.mkfile', port, baud, filePath); + } + + async isdir(port, baud, folderPath) { + return this.exec('ampy.isdir', port, baud, folderPath); + } + + async isfile(port, baud, filePath) { + return this.exec('ampy.isfile', port, baud, filePath); + } + + async put(port, baud, filePath, data) { + return this.exec('ampy.put', port, baud, filePath, data); + } + + async rm(port, baud, filePath) { + return this.exec('ampy.rm', port, baud, filePath); + } + + async rmdir(port, baud, folderPath) { + return this.exec('ampy.rmdir', port, baud, folderPath); + } + + async rename(port, baud, oldPath, newPath) { + return this.exec('ampy.rename', port, baud, oldPath, newPath); + } + + async run(port, baud, filePath) { + return this.exec('ampy.run', port, baud, filePath); + } + + async exec(eventType, port, ...args) { + const portsName = Serial.getCurrentPortsName(); + if (!portsName.includes(port)) { + throw new Error('无可用串口'); + return; + } + const { mainStatusBarTabs } = Mixly; + const statusBarSerial = mainStatusBarTabs.getStatusBarById(port); + if (statusBarSerial) { + await statusBarSerial.close(); + } + const mixlySocket = AmpyExt.getMixlySocket(); + const output = await mixlySocket.emitAsync(eventType, port, ...args); + if (output[0]) { + throw new Error(output[0]); + } + return output[1]; + } +} + +WebSocket.Ampy = AmpyExt; + +}); \ 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 5f16b860..09c22031 100644 --- a/common/modules/mixly-modules/web-socket/socket.js +++ b/common/modules/mixly-modules/web-socket/socket.js @@ -6,6 +6,7 @@ goog.require('Mixly.WebSocket'); goog.require('Mixly.WebSocket.Serial'); goog.require('Mixly.WebSocket.ArduShell'); goog.require('Mixly.WebSocket.BU'); +goog.require('Mixly.WebSocket.Ampy'); goog.provide('Mixly.WebSocket.Socket'); const { @@ -18,7 +19,8 @@ const { Socket, Serial, ArduShell, - BU + BU, + Ampy } = WebSocket; @@ -76,6 +78,7 @@ Socket.init = function () { Serial.init(mixlySocket); ArduShell.init(mixlySocket); BU.init(mixlySocket); + Ampy.init(mixlySocket); } }); diff --git a/common/modules/mixly-modules/web-socket/web-socket.js b/common/modules/mixly-modules/web-socket/web-socket.js index 157d2ef1..340ed668 100644 --- a/common/modules/mixly-modules/web-socket/web-socket.js +++ b/common/modules/mixly-modules/web-socket/web-socket.js @@ -50,6 +50,33 @@ class WebSocket { } } + async emitAsync(eventName, ...args) { + return new Promise((resolve, reject) => { + if (this.isConnected()) { + const callback = (...callbackArgs) => { + if (callbackArgs[0].error) { + reject(callbackArgs[0].error); + return; + } + resolve(...callbackArgs); + } + let emitStatus = { + finished: false + }; + let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => { + if (emitStatus.finished) { + return; + } + emitStatus.finished = true; + callback(...callbackArgs); + }); + this.#detectStatus_(emitStatus, callback); + } else { + reject('socket is not connected'); + } + }) + } + getSocket() { return this.#socket_; }