@aiot-toolkit/emulator 2.0.2-beta.1 → 2.0.2-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,245 +35,272 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
- const JavascriptDefaultCompileOption_1 = __importDefault(require("@aiot-toolkit/aiotpack/lib/compiler/javascript/JavascriptDefaultCompileOption"));
39
- const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
38
+ const shared_utils_1 = require("@aiot-toolkit/shared-utils");
40
39
  const adbMiwt = __importStar(require("@miwt/adb"));
41
40
  const child_process_1 = require("child_process");
42
41
  const fs_extra_1 = __importDefault(require("fs-extra"));
43
42
  const os_1 = __importDefault(require("os"));
44
43
  const path_1 = __importDefault(require("path"));
45
- const portfinder_1 = __importDefault(require("portfinder"));
46
44
  const ws_1 = __importStar(require("ws"));
47
- const constants_1 = require("../static/constants");
48
45
  const utils_1 = require("../utils");
49
46
  const common_1 = __importDefault(require("./common"));
47
+ const dayjs_1 = __importDefault(require("dayjs"));
50
48
  const MAX_RECONNECT_COUNT = 8;
51
49
  /**
52
50
  * MiwearInstance
53
- * 针对 vela4.0 的镜像
51
+ * 针对 Vela正式版(4.0)的镜像
54
52
  */
55
53
  class MiwearInstance extends common_1.default {
56
54
  constructor(params) {
57
55
  super(params);
56
+ this.params = params;
57
+ this.quickappStartedFlag = /quickapp_rpk_installer_init|rpk installer init done/;
58
58
  this.appPathInEmulator = '/data/quickapp/app';
59
59
  this.reconnectCount = 0;
60
+ this.emulatorStartedFlag = /quickapp_rpk_installer_init|rpk installer init done/;
60
61
  }
61
- /** 在goldfish模拟器中运行快应用 */
62
+ /**
63
+ * 1. 启动模拟器
64
+ * 2. 启动成功后,adb连接模拟器
65
+ * 3. 连接成功后,在模拟器中启动快应用
66
+ */
62
67
  start(options) {
63
68
  return __awaiter(this, void 0, void 0, function* () {
64
69
  this.startOptions = options;
70
+ this.sn = `emulator-${this.startOptions.adbPort}`;
65
71
  // 启动模拟器
66
72
  yield this.startGoldfish(options);
67
73
  // adb连接模拟器
68
- const connected = yield this.connectGoldfish();
74
+ const connected = yield this.isConnected();
69
75
  if (connected) {
70
- ColorConsole_1.default.info('### Emulator ### Goldfish emulator connected successfully');
76
+ shared_utils_1.ColorConsole.info('### Emulator ### Goldfish emulator connected successfully');
77
+ // 如果是首次启动,创建server端监听事件
71
78
  if (this.isFirstStart && this.startOptions.serverPort) {
72
79
  yield this.createWebsockeServer();
73
80
  }
81
+ // 在模拟器中启动快应用
74
82
  this.startupQuickApp(options);
75
83
  this.isFirstStart = false;
76
84
  }
77
85
  else {
78
- ColorConsole_1.default.throw('### Emulator ### Failed to connect emulator, please check whether the adb is normal');
86
+ const msg = '### Emulator ### Failed to connect emulator, please check whether the adb is normal';
87
+ shared_utils_1.ColorConsole.throw(msg);
88
+ throw new Error(msg);
79
89
  }
80
90
  });
81
91
  }
82
- /** 启动goldfish模拟器 */
92
+ /**
93
+ * 启动模拟器
94
+ * 1. 通过options生成模拟器的启动命令
95
+ * 2. 执行启动命令
96
+ * 3. 判断模拟器是否启动成功
97
+ * 3.1 若disableNSH=true,输出流中匹配到/quickapp_rpk_installer_init|rpk installer init done/,认为模拟器启动成功了
98
+ * 3.2 若disableNSH=false,认为8s过后模拟器启动成功了
99
+ * @param options
100
+ * @returns
101
+ */
83
102
  startGoldfish(options) {
84
103
  var _a;
85
104
  return __awaiter(this, void 0, void 0, function* () {
86
- const { avdName, devtool } = options;
105
+ const { avdName, devtool, origin = 'terminal' } = options;
106
+ // 获取emulator bin的绝对路径
87
107
  const emulatorBin = this.getEmulatorBinPath();
88
- ColorConsole_1.default.log(`### Emulator ### emulator path: ${emulatorBin}`);
108
+ shared_utils_1.ColorConsole.log(`### Emulator ### emulator path: ${emulatorBin}`);
109
+ // 获取vela镜像的绝对路径
89
110
  const avdInfo = this.velaAvdCls.getVelaAvdInfo(avdName);
90
111
  const { avdArch, avdImagePath } = avdInfo;
91
- this.adbPort = yield portfinder_1.default.getPortPromise({ port: this.adbPort });
92
- ColorConsole_1.default.log(`### Emulator ### adb port: ${this.adbPort}`);
112
+ shared_utils_1.ColorConsole.log(`### Emulator ### adb port: ${options.adbPort}`);
93
113
  if (!avdImagePath) {
94
- return ColorConsole_1.default.throw(`### Emulator ### Unable to find vela image via avd`);
114
+ return shared_utils_1.ColorConsole.throw(`### Emulator ### Unable to find vela image via avd`);
95
115
  }
96
116
  const nuttxBinPath = path_1.default.resolve(avdImagePath, 'nuttx');
97
- ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
117
+ shared_utils_1.ColorConsole.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
98
118
  // 端口映射
99
- let portMappingStr = `-network-user-mode-options hostfwd=tcp:127.0.0.1:${this.adbPort}-10.0.2.15:5555`;
119
+ let portMappingStr = ``;
100
120
  if (devtool) {
101
- this.debugPort = yield portfinder_1.default.getPortPromise({ port: this.debugPort });
102
- portMappingStr += `,hostfwd=tcp:127.0.0.1:${this.debugPort}-10.0.2.15:101`;
121
+ portMappingStr += `-network-user-mode-options hostfwd=tcp:127.0.0.1:${options.debugPort}-10.0.2.15:101`;
103
122
  }
104
- // 文件系统配置,第一次使用fatfs镜像挂载,后续使用adb push更新应用
105
- const systemImageBin = path_1.default.resolve(avdImagePath, 'vela_resource.img');
106
- const dataImageBin = path_1.default.resolve(avdImagePath, 'data.img');
107
- const imageMountStr = `-drive index=0,id=system,if=none,format=raw,file=${systemImageBin} \
123
+ // 设备挂载节点
124
+ const systemImageBin = path_1.default.resolve(avdImagePath, 'vela_resource.bin'); // 只读
125
+ const dataImageBin = path_1.default.resolve(avdImagePath, 'vela_data.bin'); // 可读可写
126
+ const coreBin = path_1.default.resolve(avdImagePath, 'coredump.core'); // 只读
127
+ // 复制可写文件到AVD目录下(多模拟器实例时需要)
128
+ const dataImageBinInAvd = path_1.default.join(this.avdHome, `${avdName}.avd`, 'vela_data.bin');
129
+ if (!fs_extra_1.default.existsSync(dataImageBinInAvd)) {
130
+ // vela_data.bin不存在时直接copy
131
+ fs_extra_1.default.copyFileSync(dataImageBin, dataImageBinInAvd);
132
+ }
133
+ else {
134
+ // vela_data.bin存在时,如果.export里的时间晚于avd里的时候,说明更新了镜像,需要重新copy
135
+ const statsInAvd = fs_extra_1.default.statSync(dataImageBinInAvd);
136
+ const stats = fs_extra_1.default.statSync(dataImageBin);
137
+ if ((0, dayjs_1.default)(stats.mtime).isAfter(statsInAvd.mtime)) {
138
+ fs_extra_1.default.copyFileSync(dataImageBin, dataImageBinInAvd);
139
+ }
140
+ }
141
+ // 挂载节点配置,只读文件加入read-only标识
142
+ const imageMountStr = `-drive index=0,id=system,if=none,format=raw,file=${systemImageBin},read-only \
108
143
  -device virtio-blk-device,bus=virtio-mmio-bus.0,drive=system \
109
- -drive index=1,id=userdata,if=none,format=raw,file=${dataImageBin} \
144
+ -drive index=1,id=userdata,if=none,format=raw,file=${dataImageBinInAvd} \
110
145
  -device virtio-blk-device,bus=virtio-mmio-bus.1,drive=userdata \
146
+ -drive index=2,id=vendor,if=none,format=raw,file=${coreBin},read-only \
147
+ -device virtio-blk-device,bus=virtio-mmio-bus.4,drive=vendor \
111
148
  -device virtio-snd,bus=virtio-mmio-bus.2 -allow-host-audio -semihosting`;
112
- // vnc配置
149
+ // grpc配置
113
150
  let windowStr = '';
114
- let vncStr = '';
115
- if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.vncPort) {
116
- windowStr = '-no-window';
117
- const portSuffix = this.startOptions.vncPort - constants_1.defaultVncPort;
118
- vncStr = `-vnc :${portSuffix}`;
151
+ let grpcStr = '';
152
+ if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.grpcPort) {
153
+ windowStr = '-qt-hide-window';
154
+ grpcStr = ` -idle-grpc-timeout 300 -grpc ${this.startOptions.grpcPort}`;
119
155
  }
120
- const stdioType = options.disableNSH ? 'pipe' : 'inherit';
121
- // 启动goldfish的命令和参数
122
- const cmd = `${emulatorBin} -nuttx -avd ${avdName} -avd-arch ${avdArch} -show-kernel -kernel ${nuttxBinPath} ${portMappingStr} ${windowStr} -qemu ${vncStr} ${imageMountStr}`;
156
+ // 启动模拟器的命令和参数
157
+ const cmd = `${emulatorBin} -nuttx -avd ${avdName} -port ${options.adbPort} -avd-arch ${avdArch} -show-kernel -kernel ${nuttxBinPath} ${portMappingStr} ${windowStr} ${grpcStr} -qemu ${imageMountStr}`;
123
158
  const spawnArgs = cmd.split(' ');
124
159
  const spawnBin = spawnArgs.shift();
125
- ColorConsole_1.default.log(`### Emulator ### Start CMD: ${cmd}`);
160
+ shared_utils_1.ColorConsole.log(`### Emulator ### Start CMD: ${cmd}`);
126
161
  return new Promise((resolve) => {
127
162
  var _a, _b, _c;
128
- this.goldfishProcess = (0, child_process_1.spawn)(spawnBin, spawnArgs, { stdio: stdioType, shell: true });
129
- (_a = this.goldfishProcess.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
130
- const stderrCb = options.stderrCallback || console.log;
131
- stderrCb(data.toString());
163
+ this.goldfishProcess = (0, child_process_1.spawn)(spawnBin, spawnArgs, {
164
+ stdio: 'pipe',
165
+ shell: true,
166
+ cwd: this.sdkHome
132
167
  });
168
+ // 监听模拟器的退出事件
133
169
  this.goldfishProcess.on('exit', (code) => {
134
- ColorConsole_1.default.error(`### Emulator ### Goldfish emulator exited with code ${code}`);
170
+ shared_utils_1.ColorConsole.error(`### Emulator ### Goldfish emulator exited with code ${code}`);
135
171
  if (options.exitCallback) {
136
172
  options.exitCallback(code);
137
173
  }
138
174
  });
139
- if (options.disableNSH) {
140
- (_c = (_b = this.goldfishProcess) === null || _b === void 0 ? void 0 : _b.stdout) === null || _c === void 0 ? void 0 : _c.on('data', (data) => {
141
- const msg = data.toString();
142
- const stdoutCb = options.stdoutCallback || console.log;
143
- stdoutCb(msg);
144
- if (msg.match(/quickapp_rpk_installer_init|rpk installer init done/)) {
145
- ColorConsole_1.default.info(`### Emulator ### Goldfish emulator starts successfully`);
146
- resolve();
147
- }
148
- });
149
- }
150
- else {
151
- setTimeout(() => {
152
- ColorConsole_1.default.info(`### Emulator ### Goldfish emulator starts successfully`);
175
+ // 监听错误流
176
+ (_a = this.goldfishProcess.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
177
+ const stderrCb = options.stderrCallback || console.log;
178
+ stderrCb(data.toString());
179
+ });
180
+ // 监听输出流
181
+ (_c = (_b = this.goldfishProcess) === null || _b === void 0 ? void 0 : _b.stdout) === null || _c === void 0 ? void 0 : _c.on('data', (data) => {
182
+ const msg = data.toString();
183
+ const stdoutCb = options.stdoutCallback || console.log;
184
+ stdoutCb(msg);
185
+ // 应用安装成功,则启动它
186
+ if (msg.match(/InstallState_Finished|install finished/)) {
187
+ shared_utils_1.ColorConsole.info(`### Emulator ### Install quickapp successfully`);
188
+ this.launchQuickapp();
189
+ }
190
+ // 匹配到,则认为模拟器启动成功
191
+ if (msg.match(this.emulatorStartedFlag)) {
192
+ shared_utils_1.ColorConsole.info(`### Emulator ### Goldfish emulator starts successfully`);
153
193
  resolve();
154
- }, 8000);
155
- ColorConsole_1.default.info(`### Emulator ### Goldfish emulator starts successfully`);
156
- resolve();
157
- }
158
- });
159
- });
160
- }
161
- /** 通过adb连接模拟器 */
162
- connectGoldfish() {
163
- return __awaiter(this, void 0, void 0, function* () {
164
- let adbConnected = false;
165
- const connectFn = () => __awaiter(this, void 0, void 0, function* () {
166
- const sn = `127.0.0.1:${this.adbPort}`;
167
- while (!adbConnected) {
168
- const adbKillCmd = `adb kill-server`;
169
- ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbKillCmd}`);
170
- yield adbMiwt.execAdbCmdAsync(adbKillCmd);
171
- const adbConnectCmd = `adb connect ${sn}`;
172
- ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbConnectCmd}`);
173
- const str = yield adbMiwt.execAdbCmdAsync(adbConnectCmd);
174
- ColorConsole_1.default.log(`### Emulator ### ${str}`);
175
- const devices = yield adbMiwt.getAdbDevices();
176
- ColorConsole_1.default.log(`### Emulator ### adb devices: ${JSON.stringify(devices)}`);
177
- adbConnected =
178
- devices.filter((item) => item.sn === sn && item.status === 'device').length > 0;
179
- }
180
- Promise.resolve(adbConnected);
194
+ }
195
+ });
181
196
  });
182
- yield Promise.race([
183
- connectFn(),
184
- new Promise((resolve) => {
185
- setTimeout(() => resolve(false), 600 * 1000);
186
- })
187
- ]);
188
- return adbConnected;
189
197
  });
190
198
  }
191
- /** 在goldfish中启动快应用 */
199
+ /**
200
+ * 在模拟器中启动快应用
201
+ * 1. 检查release目录是否有打包好的rpk
202
+ * 2. 检查当前是否为调试模式.
203
+ * 若是,将debugger_ip.cfg推到模拟器的/data/目录下
204
+ * 若否,则要删除模拟器中已有的data/debugger_ip.cfg
205
+ * 3. 调用installRpkToAppList将当前快应用安装到模拟器的应用列表中
206
+ * @param options
207
+ */
192
208
  startupQuickApp(options) {
209
+ var _a;
193
210
  return __awaiter(this, void 0, void 0, function* () {
194
211
  const { package: appPackageName } = this.projectInfo;
195
- const releaseDir = path_1.default.resolve(this.projectPath, JavascriptDefaultCompileOption_1.default.releasePath || 'dist');
212
+ const releaseDir = this.isRpk
213
+ ? path_1.default.resolve(this.projectPath, '../')
214
+ : path_1.default.resolve(this.projectPath, ((_a = this.params.compilerOption) === null || _a === void 0 ? void 0 : _a.releasePath) || 'dist');
196
215
  const files = fs_extra_1.default
197
216
  .readdirSync(releaseDir)
198
- .filter(item => item.includes(appPackageName) && item.endsWith('.rpk'));
199
- if (files.length < 0) {
200
- ColorConsole_1.default.error(`### Emulator the rpk does not exist`);
217
+ .filter((item) => item.includes(appPackageName) && item.endsWith('.rpk'));
218
+ if (files.length === 0) {
219
+ shared_utils_1.ColorConsole.error(`### Emulator the rpk does not exist`);
201
220
  }
202
221
  const rpkPath = path_1.default.resolve(releaseDir, files[0]);
203
222
  // 调试模式需要push一个文件至miwear中
204
- const sn = `127.0.0.1:${this.adbPort}`;
205
223
  if (options.devtool) {
206
224
  const debuggerCfgFile = path_1.default.join(__dirname, '../static/debugger_ip.cfg');
207
- yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} push ${debuggerCfgFile} /data/debugger_ip.cfg`);
225
+ yield adbMiwt.execAdbCmdAsync(`adb -s ${this.sn} push ${debuggerCfgFile} /data/debugger_ip.cfg`);
208
226
  }
209
227
  else {
210
- adbMiwt.execAdbCmdAsync(`adb -s ${sn} shell rm /data/debugger_ip.cfg`);
228
+ yield adbMiwt.execAdbCmdAsync(`adb -s ${this.sn} shell rm /data/debugger_ip.cfg`);
211
229
  }
212
230
  this.installRpkToAppList(rpkPath, this.appPathInEmulator);
213
231
  });
214
232
  }
215
- /** 将快应用安装到应用列表 */
233
+ /**
234
+ * 将快应用安装到模拟器的应用列表
235
+ * 1. 使用adb push将打包好的rpk推到模拟器的/data/quickapp/app/
236
+ * 2. nsh中调用pm install命令安装应用
237
+ * @param rpkPath rpk的绝对目录
238
+ * @param targetDir 要将rpk放到模拟器的哪个目录下
239
+ */
216
240
  installRpkToAppList(rpkPath, targetDir) {
217
241
  return __awaiter(this, void 0, void 0, function* () {
218
- try {
219
- const sn = `127.0.0.1:${this.adbPort}`;
220
- const { package: packageName } = this.projectInfo;
221
- const rpkName = path_1.default.basename(rpkPath);
222
- // 1. 查询应用是否已经安装,如果安装了,则先卸载
223
- const lsCmd = `adb -s ${sn} shell ls ${this.appPathInEmulator}`;
224
- const res = yield adbMiwt.execAdbCmdAsync(lsCmd);
225
- if (res.includes(packageName)) {
226
- const uninstallCmd = `adb -s ${sn} shell pm uninstall ${packageName}`;
227
- ColorConsole_1.default.info(`### Emulator ### Excuting cmd: ${uninstallCmd}`);
228
- adbMiwt.execAdbCmdAsync(uninstallCmd);
229
- // 这里等待2s的作用是等qemu执行完uninstall操作的一系列工作
230
- yield (0, utils_1.sleep)(2000);
231
- }
232
- // 2. adb push应用的rpk
233
- const targetPath = `${targetDir}/${packageName}.rpk`;
234
- const pushCmd = `adb -s ${sn} push ${rpkPath} ${targetPath}`;
235
- ColorConsole_1.default.info(`### Emulator ### Excuting cmd: ${pushCmd}`);
236
- yield adbMiwt.execAdbCmdAsync(pushCmd);
237
- yield (0, utils_1.sleep)(100);
238
- // 3. 安装应用
239
- const installCmd = `adb -s ${sn} shell pm install ${targetPath}`;
240
- ColorConsole_1.default.info(`### Emulator ### Excuting cmd: ${installCmd}`);
241
- adbMiwt.execAdbCmdAsync(installCmd);
242
- }
243
- catch (e) { }
242
+ const { package: packageName } = this.projectInfo;
243
+ // 1. adb push应用的rpk
244
+ const targetPath = `${targetDir}/${packageName}.rpk`;
245
+ const pushCmd = `adb -s ${this.sn} push "${rpkPath}" ${targetPath}`;
246
+ shared_utils_1.ColorConsole.info(`### Emulator ### Excuting cmd: ${pushCmd}`);
247
+ yield adbMiwt.execAdbCmdAsync(pushCmd);
248
+ const installCmd = `adb -s ${this.sn} shell pm install ${targetPath}`;
249
+ shared_utils_1.ColorConsole.info(`### Emulator ### Excuting cmd: ${installCmd}`);
250
+ yield adbMiwt.execAdbCmdAsync(installCmd);
251
+ });
252
+ }
253
+ /** 使用am start启动快应用 */
254
+ launchQuickapp() {
255
+ return __awaiter(this, void 0, void 0, function* () {
256
+ const { package: packageName } = this.projectInfo;
257
+ const startCmd = `adb -s ${this.sn} shell am start ${packageName}`;
258
+ shared_utils_1.ColorConsole.info(`### Emulator ### Excuting cmd: ${startCmd}`);
259
+ adbMiwt.execAdbCmdAsync(startCmd);
244
260
  });
245
261
  }
262
+ /** 连接模拟器中的调试服务,创建debugSocket
263
+ * 主要用于热更新时,通过debugSocket通知调试服务更新页面
264
+ * 设置了重连机制,会重复连接8次
265
+ */
246
266
  initDebugSocket() {
247
- if (this.debugSocket && this.debugSocket.OPEN) {
248
- return Promise.resolve();
249
- }
250
- this.debugSocket = new ws_1.default(`ws://localhost:${this.debugPort}`);
251
- this.debugSocket.onopen = () => {
252
- ColorConsole_1.default.info(`### Emulator debugSocket connect success`);
253
- return Promise.resolve();
254
- };
255
- this.debugSocket.onerror = (e) => {
267
+ return new Promise((resolve, reject) => {
256
268
  var _a;
257
- // 重连机制
258
- (_a = this.debugSocket) === null || _a === void 0 ? void 0 : _a.terminate();
259
- if (this.reconnectCount < MAX_RECONNECT_COUNT) {
260
- ColorConsole_1.default.info(`### Emulator the ${this.reconnectCount + 1}th time to reconnect debug server`);
261
- this.reconnectCount++;
262
- setTimeout(() => this.initDebugSocket(), 2000);
269
+ if (this.debugSocket && this.debugSocket.OPEN) {
270
+ return resolve();
263
271
  }
264
- else {
272
+ this.debugSocket = new ws_1.default(`ws://localhost:${(_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.debugPort}`);
273
+ this.debugSocket.onopen = () => {
274
+ shared_utils_1.ColorConsole.info(`### Emulator debugSocket connect success`);
275
+ return resolve();
276
+ };
277
+ this.debugSocket.onerror = (errorEvent) => {
278
+ var _a;
279
+ // 重连机制
280
+ (_a = this.debugSocket) === null || _a === void 0 ? void 0 : _a.terminate();
281
+ if (this.reconnectCount < MAX_RECONNECT_COUNT) {
282
+ shared_utils_1.ColorConsole.info(`### Emulator the ${this.reconnectCount + 1}th time to reconnect debug server`);
283
+ this.reconnectCount++;
284
+ setTimeout(() => this.initDebugSocket(), 2000);
285
+ }
286
+ else {
287
+ this.debugSocket = undefined;
288
+ this.reconnectCount = 0;
289
+ return reject(`### Emulator debugSocket connect failed, ${errorEvent.message}`);
290
+ }
291
+ };
292
+ this.debugSocket.onclose = (closeEvent) => {
265
293
  this.debugSocket = undefined;
266
294
  this.reconnectCount = 0;
267
- return Promise.reject(`### Emulator debugSocket connect failed`);
268
- }
269
- };
270
- this.debugSocket.onclose = (e) => {
271
- this.debugSocket = undefined;
272
- this.reconnectCount = 0;
273
- ColorConsole_1.default.log(`### Emulator debugSocket connect close: ${e.data}`);
274
- };
295
+ shared_utils_1.ColorConsole.log(`### Emulator debugSocket connect close: ${closeEvent.reason}`);
296
+ };
297
+ });
275
298
  }
276
- /** 通知模拟器更新 */
299
+ /** 通知模拟器更新
300
+ * 1. 确保已经成功连接模拟器中的调试服务
301
+ * 2. 使用adb push将build文件下的内容推到/data/quickapp/app/${packageName}
302
+ * 3. 发送Page.reload命令给调试服务,通知更新
303
+ */
277
304
  handleUpdate() {
278
305
  var _a, _b;
279
306
  return __awaiter(this, void 0, void 0, function* () {
@@ -281,47 +308,87 @@ class MiwearInstance extends common_1.default {
281
308
  this.reconnectCount = 0;
282
309
  yield this.initDebugSocket();
283
310
  // 1. 将整包重新推到miwear中(TODO:增量更新)
284
- const sn = `127.0.0.1:${this.adbPort}`;
285
- const { package: appPackageName } = this.projectInfo;
286
- // windows平台adb push时无法使用通配符,需另外处理
287
- if (os_1.default.platform() === 'win32') {
288
- const rmCmd = `adb -s ${sn} shell rm -r ${this.appPathInEmulator}/${appPackageName}`;
289
- yield adbMiwt.execAdbCmdAsync(rmCmd);
290
- const sourcePath = path_1.default.resolve(this.projectPath, './build');
291
- const pushCmd = `adb -s ${sn} push ${sourcePath} ${this.appPathInEmulator}/${appPackageName}`;
292
- yield adbMiwt.execAdbCmdAsync(pushCmd);
293
- }
294
- else {
295
- const sourcePath = path_1.default.resolve(this.projectPath, './build/*');
296
- yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} push ${sourcePath} ${this.appPathInEmulator}/${appPackageName}`);
297
- }
298
- ColorConsole_1.default.info(`### Emulator push to ${this.appPathInEmulator}/${appPackageName} successfully`);
311
+ yield this.pushBuild();
299
312
  // 2. 下发CDP命令给调试服务告知刷新
300
313
  if (((_a = this.debugSocket) === null || _a === void 0 ? void 0 : _a.readyState) === ws_1.default.OPEN) {
301
314
  (_b = this.debugSocket) === null || _b === void 0 ? void 0 : _b.send(JSON.stringify({ id: 10000, method: 'Page.reload', params: {} }));
302
315
  }
303
316
  }
304
317
  catch (e) {
305
- ColorConsole_1.default.error(`${e}`);
318
+ shared_utils_1.ColorConsole.error(`${e}`);
319
+ }
320
+ });
321
+ }
322
+ /** 热更新时push目录 */
323
+ pushBuild() {
324
+ return __awaiter(this, void 0, void 0, function* () {
325
+ const { package: appPackageName } = this.projectInfo;
326
+ // windows平台adb push时无法使用通配符,需另外处理;
327
+ // 项目路径包含空格时,无法通过加引号来使用 * 通配符
328
+ if (os_1.default.platform() === 'win32' || this.projectPath.indexOf(' ') > 0) {
329
+ const rmCmd = `adb -s ${this.sn} shell rm -r ${this.appPathInEmulator}/${appPackageName}`;
330
+ yield adbMiwt.execAdbCmdAsync(rmCmd);
331
+ const sourcePath = path_1.default.resolve(this.projectPath, './build');
332
+ const pushCmd = `adb -s ${this.sn} push "${sourcePath}" ${this.appPathInEmulator}/${appPackageName}`;
333
+ yield adbMiwt.execAdbCmdAsync(pushCmd);
334
+ }
335
+ else {
336
+ const sourcePath = path_1.default.resolve(this.projectPath, './build/*');
337
+ yield adbMiwt.execAdbCmdAsync(`adb -s ${this.sn} push ${sourcePath} ${this.appPathInEmulator}/${appPackageName}`);
338
+ }
339
+ shared_utils_1.ColorConsole.info(`### Emulator push to ${this.appPathInEmulator}/${appPackageName} successfully`);
340
+ });
341
+ }
342
+ /** 在模拟器中重启快应用(基于am命令,需要保证镜像中已经有am功能)
343
+ * 1. 使用adb push将build文件下的内容推到/data/quickapp/app/${packageName}
344
+ * 2. nsh中执行am stop命令退出快应用
345
+ * 3. nsh中执行am start命令启动快应用
346
+ */
347
+ pushAndReloadApp() {
348
+ return __awaiter(this, void 0, void 0, function* () {
349
+ // 1. 将整包重新推到miwear中(TODO:增量更新)
350
+ yield this.pushBuild();
351
+ yield this.reloadApp();
352
+ });
353
+ }
354
+ reloadApp() {
355
+ return __awaiter(this, void 0, void 0, function* () {
356
+ try {
357
+ const { package: appPackageName } = this.projectInfo;
358
+ // 2. 执行am stop和am start命令
359
+ const stopCmd = `adb -s ${this.sn} shell am stop ${appPackageName}`;
360
+ yield adbMiwt.execAdbCmdAsync(stopCmd);
361
+ shared_utils_1.ColorConsole.info(`### Emulator stop ${appPackageName} successfully`);
362
+ // 这里是为了等am stop命令清除资源等
363
+ yield (0, utils_1.sleep)(500);
364
+ const startCmd = `adb -s ${this.sn} shell am start ${appPackageName}`;
365
+ yield adbMiwt.execAdbCmdAsync(startCmd);
366
+ shared_utils_1.ColorConsole.info(`### Emulator start ${appPackageName} successfully`);
367
+ }
368
+ catch (e) {
369
+ shared_utils_1.ColorConsole.error(`${e}`);
306
370
  }
307
371
  });
308
372
  }
309
- /** 创建server */
373
+ /**
374
+ * 创建server端,监听打包过程中client端发来的消息
375
+ */
310
376
  createWebsockeServer() {
311
377
  var _a;
312
378
  return __awaiter(this, void 0, void 0, function* () {
313
379
  const wsServer = new ws_1.WebSocketServer({
314
- port: (_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.serverPort,
380
+ port: (_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.serverPort
315
381
  });
316
- wsServer.on('connection', socket => {
317
- ColorConsole_1.default.success(`### App Socket server ### Websocket connects to websocket server`);
318
- socket.on('error', err => {
319
- ColorConsole_1.default.error(`### App Socket server ### Websocket server error: ${err.message}`);
382
+ wsServer.on('connection', (socket) => {
383
+ shared_utils_1.ColorConsole.success(`### App Socket server ### Websocket connects to websocket server`);
384
+ socket.on('error', (err) => {
385
+ shared_utils_1.ColorConsole.error(`### App Socket server ### Websocket server error: ${err.message}`);
320
386
  });
387
+ // data的格式:{ type: string, data: any }
321
388
  socket.on('message', (data) => __awaiter(this, void 0, void 0, function* () {
322
389
  var _a;
323
390
  const message = JSON.parse(data.toString());
324
- ColorConsole_1.default.log(`### App Socket server ### Websocket server get data: ${data}`);
391
+ shared_utils_1.ColorConsole.log(`### App Socket server ### Websocket server get data: ${data}`);
325
392
  if (message.type === 'restart') {
326
393
  // 非调试模式下无法热更新
327
394
  if (!((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.devtool)) {
@@ -0,0 +1,8 @@
1
+ import GoldfishInstance from './dev';
2
+ /**
3
+ * 不带 miwear 的 4.0 镜像
4
+ */
5
+ declare class PreInstance extends GoldfishInstance {
6
+ appRunDir: string;
7
+ }
8
+ export default PreInstance;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const dev_1 = __importDefault(require("./dev"));
7
+ const miwear_1 = __importDefault(require("./miwear"));
8
+ /**
9
+ * 不带 miwear 的 4.0 镜像
10
+ */
11
+ class PreInstance extends dev_1.default {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.appRunDir = '/data/quickapp/app';
15
+ }
16
+ }
17
+ // pre 镜像的 启动 方式与 MiwearInstance 一致
18
+ PreInstance.prototype.startGoldfish = miwear_1.default.prototype.startGoldfish;
19
+ exports.default = PreInstance;
@@ -2,20 +2,48 @@
2
2
  import { ChildProcess } from 'child_process';
3
3
  import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
4
4
  import CommonInstance from './common';
5
+ /**
6
+ * OldGoldfishInstance
7
+ * 针对 Vela开发版(dev, 0.0.2)的镜像
8
+ */
5
9
  declare class OldGoldfishInstance extends CommonInstance {
6
10
  private host9pPort;
7
11
  v9fsProcess: ChildProcess | undefined;
8
12
  constructor(params: INewGoldfishInstanceParams);
9
- /** 在goldfish模拟器中运行快应用 */
13
+ /**
14
+ * 1. 启动9p server
15
+ * 2. 将打包好的rpk推到host的挂载目录
16
+ * 3. 启动模拟器
17
+ * 4. 模拟器启动成功后,adb连接模拟器
18
+ * 5. 连接成功后,在模拟器中启动快应用
19
+ */
10
20
  start(options: IStartOptions): Promise<void>;
11
- /** 在goldfish中启动快应用 */
21
+ /**
22
+ * 在模拟器中启动快应用(快应用都在模拟器的/data/app目录下)
23
+ * 1. 是否为调试模式
24
+ * 若是,在模拟器终端执行vapp app/${packageName} &
25
+ * 若否,在模拟器终端执行vapp --jsdebugger=10.0.2.15:101 app/${packageName} &
26
+ */
12
27
  startupQuickApp(options: IStartOptions): void;
13
- /** host启动9pServer */
28
+ /** host启动9pServer
29
+ * 作用是将本地的quickappMountDir目录挂载到模拟器的/data目录
30
+ */
14
31
  ensure9pServerRunnning(): Promise<void>;
15
- /** 启动goldfish模拟器 */
32
+ /**
33
+ * 启动模拟器
34
+ * 1. 通过options生成模拟器的启动命令
35
+ * 2. 执行启动命令
36
+ * 3. 判断模拟器是否启动成功
37
+ * 3.1 若disableNSH=true,输出流中匹配到/quickapp_rpk_installer_init|rpk installer init done/,认为模拟器启动成功了
38
+ * 3.2 若disableNSH=false,认为8s过后模拟器启动成功了
39
+ * @param options
40
+ * @returns
41
+ */
16
42
  startGoldfish(options: IStartOptions): Promise<void>;
17
- /** 通过adb连接模拟器 */
18
- connectGoldfish(): Promise<boolean>;
43
+ /**
44
+ * 推送文件到本地的${sdkHome}/qa/app/${packageName}目录
45
+ * @param sourceRoot 源目录
46
+ */
19
47
  pushRpk(sourceRoot: string): Promise<void>;
20
48
  /** 停止模拟器并释放相关资源 */
21
49
  stop(): Promise<void>;