@aiot-toolkit/emulator 2.0.1-alpha.8 → 2.0.2-batchmanifest-beta.1
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.
- package/README.md +14 -1
- package/lib/avd/index.d.ts +12 -0
- package/lib/avd/index.js +58 -36
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -5
- package/lib/instance/common.d.ts +42 -0
- package/lib/instance/common.js +227 -0
- package/lib/instance/dev.d.ts +35 -0
- package/lib/instance/dev.js +258 -0
- package/lib/instance/index.d.ts +13 -0
- package/lib/instance/index.js +38 -0
- package/lib/instance/miwear.d.ts +74 -0
- package/lib/instance/miwear.js +437 -0
- package/lib/instance/preDev.d.ts +51 -0
- package/lib/instance/preDev.js +285 -0
- package/lib/static/advancedFeatures.ini +1 -0
- package/lib/static/constants.d.ts +17 -0
- package/lib/static/constants.js +25 -3
- package/lib/static/debugger_ip.cfg +1 -0
- package/lib/typing/Avd.js +0 -2
- package/lib/typing/Instance.d.ts +17 -1
- package/lib/typing/Instance.js +0 -2
- package/lib/utils/index.d.ts +13 -0
- package/lib/utils/index.js +88 -0
- package/package.json +8 -4
- package/lib/avd/index.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/instance/goldfish.d.ts +0 -35
- package/lib/instance/goldfish.js +0 -312
- package/lib/instance/goldfish.js.map +0 -1
- package/lib/static/constants.js.map +0 -1
- package/lib/typing/Avd.js.map +0 -1
- package/lib/typing/Instance.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
+
## emulator
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
QEMU模拟器
|
|
4
|
+
|
|
5
|
+
模拟器的介绍可参考[开发帮助文档](https://xiaomi.f.mioffice.cn/docx/doxk4Rk6x67GanHlzQ8bEOrxtEe)里的「模拟器」章节
|
|
6
|
+
|
|
7
|
+
## 目录结构
|
|
8
|
+
|
|
9
|
+
| 目录 | 描述 |
|
|
10
|
+
| -------- | ------------------------------------------------------------------------------- |
|
|
11
|
+
| avd | 模拟器的AVD,配置统一放置$HOME/.android/avd目录下 |
|
|
12
|
+
| instance | 模拟器实例,不同的Vela镜像版本会使用不同的instance,通过findInstance确定 |
|
|
13
|
+
| static | 创建AVD时需要用到的静态资源,常量配置文件 |
|
|
14
|
+
| typing | 接口定义 |
|
|
15
|
+
| utils | 工具函数 |
|
package/lib/avd/index.d.ts
CHANGED
|
@@ -3,10 +3,22 @@ declare class VelaAvdCls {
|
|
|
3
3
|
private avdHome;
|
|
4
4
|
private sdkHome;
|
|
5
5
|
constructor(avdResourcePaths: IAvdResourcePaths);
|
|
6
|
+
/**
|
|
7
|
+
* 创建Vela端的AVD,统一保存在.android目录下
|
|
8
|
+
* 1. 创建.android/advancedFeatures.ini文件
|
|
9
|
+
* 2. 创建.android/${avdName}.ini文件
|
|
10
|
+
* 3. 创建.android/${avdName}.avd/config.ini文件
|
|
11
|
+
* @param avdParams AVD参数,宽高、绑定的镜像路径等
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
6
14
|
createVelaAvd(avdParams: IAvdParams): boolean;
|
|
15
|
+
/** 根据AVD名字获取模拟器的详细信息 */
|
|
7
16
|
getVelaAvdInfo(avdName: string): IAvdParams;
|
|
17
|
+
/** 根据名字删除AVD */
|
|
8
18
|
deleteVelaAvd(avdName: string): boolean;
|
|
19
|
+
/** 获取已经创建的模拟器列表 */
|
|
9
20
|
getVelaAvdList(): IAvdParams[];
|
|
21
|
+
/** 获取模拟器皮肤列表 */
|
|
10
22
|
getVelaSkinList(): string[];
|
|
11
23
|
}
|
|
12
24
|
export default VelaAvdCls;
|
package/lib/avd/index.js
CHANGED
|
@@ -3,10 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
|
|
6
7
|
const fs_1 = __importDefault(require("fs"));
|
|
7
8
|
const path_1 = __importDefault(require("path"));
|
|
8
9
|
const constants_1 = require("../static/constants");
|
|
9
10
|
const Avd_1 = require("../typing/Avd");
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
10
12
|
const EAvdParamsToIni = {
|
|
11
13
|
'arm': {
|
|
12
14
|
abiType: 'armeabi-v7a',
|
|
@@ -20,8 +22,25 @@ class VelaAvdCls {
|
|
|
20
22
|
const { avdHome, sdkHome } = avdResourcePaths;
|
|
21
23
|
this.avdHome = avdHome || constants_1.defaultAvdHome;
|
|
22
24
|
this.sdkHome = sdkHome || constants_1.defaultSDKHome;
|
|
25
|
+
if (!fs_1.default.existsSync(this.avdHome)) {
|
|
26
|
+
fs_1.default.mkdirSync(this.avdHome, { recursive: true });
|
|
27
|
+
}
|
|
23
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* 创建Vela端的AVD,统一保存在.android目录下
|
|
31
|
+
* 1. 创建.android/advancedFeatures.ini文件
|
|
32
|
+
* 2. 创建.android/${avdName}.ini文件
|
|
33
|
+
* 3. 创建.android/${avdName}.avd/config.ini文件
|
|
34
|
+
* @param avdParams AVD参数,宽高、绑定的镜像路径等
|
|
35
|
+
* @returns
|
|
36
|
+
*/
|
|
24
37
|
createVelaAvd(avdParams) {
|
|
38
|
+
// 在.android下创建advancedFeatures.ini文件
|
|
39
|
+
const advancedFeaturesIni = path_1.default.resolve(os_1.default.homedir(), '.android/advancedFeatures.ini');
|
|
40
|
+
if (!fs_1.default.existsSync(advancedFeaturesIni)) {
|
|
41
|
+
const iniSourcePath = path_1.default.join(__dirname, '../static/advancedFeatures.ini');
|
|
42
|
+
fs_1.default.copyFileSync(iniSourcePath, advancedFeaturesIni);
|
|
43
|
+
}
|
|
25
44
|
const { avdName, avdArch, avdWidth, avdHeight, avdSkin, avdImagePath = constants_1.defaultImageHome } = avdParams;
|
|
26
45
|
const avdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
|
|
27
46
|
const avdIni = path_1.default.resolve(this.avdHome, `${avdName}.ini`);
|
|
@@ -62,9 +81,10 @@ class VelaAvdCls {
|
|
|
62
81
|
return true;
|
|
63
82
|
}
|
|
64
83
|
catch (e) {
|
|
65
|
-
|
|
84
|
+
throw (`createVelaAvd: ${e.message}`);
|
|
66
85
|
}
|
|
67
86
|
}
|
|
87
|
+
/** 根据AVD名字获取模拟器的详细信息 */
|
|
68
88
|
getVelaAvdInfo(avdName) {
|
|
69
89
|
const avdInfo = {
|
|
70
90
|
avdName,
|
|
@@ -76,25 +96,32 @@ class VelaAvdCls {
|
|
|
76
96
|
};
|
|
77
97
|
const currAvdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
|
|
78
98
|
const configIni = path_1.default.resolve(currAvdDir, 'config.ini');
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
try {
|
|
100
|
+
const contents = fs_1.default.readFileSync(configIni, 'utf-8');
|
|
101
|
+
// 这里需要使用惰性匹配
|
|
102
|
+
const archRegex = /hw.cpu.arch = ([\d\D]+?)\n/;
|
|
103
|
+
const archMatcher = contents.match(archRegex);
|
|
104
|
+
const heightRegex = /hw.lcd.height = ([\d\D]+?)\n/;
|
|
105
|
+
const heightMatcher = contents.match(heightRegex);
|
|
106
|
+
const widthRegex = /hw.lcd.width = ([\d\D]+?)\n/;
|
|
107
|
+
const widthMatcher = contents.match(widthRegex);
|
|
108
|
+
const skinRegex = /skin.name = ([\d\D]+?)\n/;
|
|
109
|
+
const skinMatcher = contents.match(skinRegex);
|
|
110
|
+
const imagePathRegex = /image.sysdir.1 = ([\d\D]+?)\n/;
|
|
111
|
+
const imagePathMather = contents.match(imagePathRegex);
|
|
112
|
+
archMatcher && (avdInfo.avdArch = archMatcher[1]);
|
|
113
|
+
heightMatcher && (avdInfo.avdHeight = heightMatcher[1]);
|
|
114
|
+
widthMatcher && (avdInfo.avdWidth = widthMatcher[1]);
|
|
115
|
+
skinMatcher && (avdInfo.avdSkin = skinMatcher[1]);
|
|
116
|
+
imagePathMather && (avdInfo.avdImagePath = imagePathMather[1]);
|
|
117
|
+
return avdInfo;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
ColorConsole_1.default.log(`getVelaAvdInfo: ${err.message}`);
|
|
121
|
+
return avdInfo;
|
|
122
|
+
}
|
|
97
123
|
}
|
|
124
|
+
/** 根据名字删除AVD */
|
|
98
125
|
deleteVelaAvd(avdName) {
|
|
99
126
|
const avdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
|
|
100
127
|
const avdIni = path_1.default.resolve(this.avdHome, `${avdName}.ini`);
|
|
@@ -107,25 +134,22 @@ class VelaAvdCls {
|
|
|
107
134
|
return false;
|
|
108
135
|
}
|
|
109
136
|
}
|
|
137
|
+
/** 获取已经创建的模拟器列表 */
|
|
110
138
|
getVelaAvdList() {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
avdList.push(avdInfo);
|
|
121
|
-
}
|
|
139
|
+
const avdList = [];
|
|
140
|
+
const files = fs_1.default.readdirSync(this.avdHome);
|
|
141
|
+
const regex = /^(Vela[\d\D]*)\.avd$/;
|
|
142
|
+
for (const fileName of files) {
|
|
143
|
+
const matcher = fileName.match(regex);
|
|
144
|
+
if (matcher) {
|
|
145
|
+
const avdName = matcher[1];
|
|
146
|
+
const avdInfo = this.getVelaAvdInfo(avdName);
|
|
147
|
+
avdList.push(avdInfo);
|
|
122
148
|
}
|
|
123
|
-
return avdList;
|
|
124
|
-
}
|
|
125
|
-
catch (err) {
|
|
126
|
-
return [];
|
|
127
149
|
}
|
|
150
|
+
return avdList;
|
|
128
151
|
}
|
|
152
|
+
/** 获取模拟器皮肤列表 */
|
|
129
153
|
getVelaSkinList() {
|
|
130
154
|
try {
|
|
131
155
|
const skinList = [];
|
|
@@ -142,5 +166,3 @@ class VelaAvdCls {
|
|
|
142
166
|
}
|
|
143
167
|
}
|
|
144
168
|
exports.default = VelaAvdCls;
|
|
145
|
-
|
|
146
|
-
//# sourceMappingURL=index.js.map
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { default as VelaAvdCls } from './avd';
|
|
2
|
-
|
|
2
|
+
export * from './instance';
|
|
3
3
|
export * from './typing/Avd';
|
|
4
4
|
export * from './typing/Instance';
|
|
5
|
-
export {
|
|
5
|
+
export { VelaAvdCls };
|
package/lib/index.js
CHANGED
|
@@ -17,12 +17,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.VelaAvdCls =
|
|
20
|
+
exports.VelaAvdCls = void 0;
|
|
21
21
|
const avd_1 = __importDefault(require("./avd"));
|
|
22
22
|
Object.defineProperty(exports, "VelaAvdCls", { enumerable: true, get: function () { return avd_1.default; } });
|
|
23
|
-
|
|
24
|
-
Object.defineProperty(exports, "GoldfishInstance", { enumerable: true, get: function () { return goldfish_1.default; } });
|
|
23
|
+
__exportStar(require("./instance"), exports);
|
|
25
24
|
__exportStar(require("./typing/Avd"), exports);
|
|
26
25
|
__exportStar(require("./typing/Instance"), exports);
|
|
27
|
-
|
|
28
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import IManifest from '@aiot-toolkit/aiotpack/lib/compiler/javascript/vela/interface/IManifest';
|
|
3
|
+
import { ChildProcess } from 'child_process';
|
|
4
|
+
import VelaAvdCls from '../avd';
|
|
5
|
+
import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
|
|
6
|
+
/**
|
|
7
|
+
* CommonInstance
|
|
8
|
+
*/
|
|
9
|
+
declare class CommonInstance {
|
|
10
|
+
projectPath: string;
|
|
11
|
+
sdkHome: string;
|
|
12
|
+
avdHome: string;
|
|
13
|
+
velaAvdCls: VelaAvdCls;
|
|
14
|
+
goldfishProcess: ChildProcess | undefined;
|
|
15
|
+
isFirstStart: boolean;
|
|
16
|
+
startOptions: IStartOptions | undefined;
|
|
17
|
+
projectInfo: IManifest;
|
|
18
|
+
isRpk: boolean;
|
|
19
|
+
isDistributedApp: boolean;
|
|
20
|
+
sn?: string;
|
|
21
|
+
constructor(params: INewGoldfishInstanceParams);
|
|
22
|
+
/** 获取模拟器二进制文件所在位置 */
|
|
23
|
+
getEmulatorBinPath(): string;
|
|
24
|
+
/** 在goldfish模拟器中运行快应用 */
|
|
25
|
+
start(options: IStartOptions): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* 通过adb连接模拟器。
|
|
28
|
+
* 时间限制为 @param timeout 秒,超时则表示连接失败
|
|
29
|
+
* @returns
|
|
30
|
+
*/
|
|
31
|
+
connectGoldfish(timeout?: number): Promise<unknown>;
|
|
32
|
+
/** 杀死进程 */
|
|
33
|
+
killProcess(currProcess?: ChildProcess): void;
|
|
34
|
+
/** 停止模拟器并释放相关资源 */
|
|
35
|
+
stop(): Promise<void>;
|
|
36
|
+
/** 重启模拟器 */
|
|
37
|
+
restart(): Promise<void>;
|
|
38
|
+
/** 创建server */
|
|
39
|
+
createWebsockeServer(): Promise<void>;
|
|
40
|
+
connectDevice(): Promise<string | void>;
|
|
41
|
+
}
|
|
42
|
+
export default CommonInstance;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
const UxFileUtils_1 = __importDefault(require("@aiot-toolkit/aiotpack/lib/utils/ux/UxFileUtils"));
|
|
39
|
+
const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
|
|
40
|
+
const adbMiwt = __importStar(require("@miwt/adb"));
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const os_1 = __importDefault(require("os"));
|
|
43
|
+
const path_1 = __importDefault(require("path"));
|
|
44
|
+
const ws_1 = require("ws");
|
|
45
|
+
const avd_1 = __importDefault(require("../avd"));
|
|
46
|
+
const constants_1 = require("../static/constants");
|
|
47
|
+
const utils_1 = require("../utils");
|
|
48
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
49
|
+
/**
|
|
50
|
+
* CommonInstance
|
|
51
|
+
*/
|
|
52
|
+
class CommonInstance {
|
|
53
|
+
constructor(params) {
|
|
54
|
+
this.isFirstStart = true;
|
|
55
|
+
this.isDistributedApp = false;
|
|
56
|
+
this.projectPath = params.projectPath;
|
|
57
|
+
this.sdkHome = params.sdkHome || constants_1.defaultSDKHome;
|
|
58
|
+
this.avdHome = params.avdHome || constants_1.defaultAvdHome;
|
|
59
|
+
this.isRpk = params.sourceRoot === './';
|
|
60
|
+
const projectJsonFile = path_1.default.resolve(this.projectPath, '.project.json');
|
|
61
|
+
const projectJsonExist = fs_extra_1.default.existsSync(projectJsonFile);
|
|
62
|
+
if (projectJsonExist) {
|
|
63
|
+
const projectJsonInfo = fs_extra_1.default.readJSONSync(projectJsonFile);
|
|
64
|
+
this.isDistributedApp = projectJsonInfo.projectType === 'distributed';
|
|
65
|
+
}
|
|
66
|
+
this.velaAvdCls = new avd_1.default({
|
|
67
|
+
sdkHome: this.sdkHome,
|
|
68
|
+
avdHome: this.avdHome
|
|
69
|
+
});
|
|
70
|
+
this.projectInfo = UxFileUtils_1.default.getMainfestInfo(this.projectPath, params.sourceRoot);
|
|
71
|
+
}
|
|
72
|
+
/** 获取模拟器二进制文件所在位置 */
|
|
73
|
+
getEmulatorBinPath() {
|
|
74
|
+
const osPlatform = os_1.default.platform();
|
|
75
|
+
const arch = (0, utils_1.getSystemArch)();
|
|
76
|
+
const platform = osPlatform === 'win32' ? 'windows' : osPlatform;
|
|
77
|
+
const emulatorHome = path_1.default.resolve(this.sdkHome, 'emulator');
|
|
78
|
+
return path_1.default.resolve(emulatorHome, `${platform}-${arch}`, 'emulator');
|
|
79
|
+
}
|
|
80
|
+
/** 在goldfish模拟器中运行快应用 */
|
|
81
|
+
start(options) {
|
|
82
|
+
return __awaiter(this, void 0, void 0, function* () { });
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 通过adb连接模拟器。
|
|
86
|
+
* 时间限制为 @param timeout 秒,超时则表示连接失败
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
89
|
+
connectGoldfish(timeout = 10) {
|
|
90
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
let adbConnected = false; // adb是否连接成功
|
|
92
|
+
let enableLoop = true; // 是否允许循环,用于终止adb的连接
|
|
93
|
+
let needKill = false;
|
|
94
|
+
const connectFn = () => __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
while (enableLoop && !adbConnected) {
|
|
96
|
+
if (needKill) {
|
|
97
|
+
const adbKillCmd = `adb kill-server`;
|
|
98
|
+
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbKillCmd}`);
|
|
99
|
+
yield adbMiwt.execAdbCmdAsync(adbKillCmd);
|
|
100
|
+
}
|
|
101
|
+
const str = yield this.connectDevice();
|
|
102
|
+
ColorConsole_1.default.log(`### Emulator ### ${str}`);
|
|
103
|
+
// 查询模拟器的状态是否为“device”
|
|
104
|
+
const devices = yield adbMiwt.getAdbDevices();
|
|
105
|
+
ColorConsole_1.default.log(`### Emulator ### adb devices: ${JSON.stringify(devices)}`);
|
|
106
|
+
const curDev = devices.find((t) => t.sn === this.sn);
|
|
107
|
+
if ((curDev === null || curDev === void 0 ? void 0 : curDev.status) === 'offline')
|
|
108
|
+
needKill = true;
|
|
109
|
+
adbConnected = devices.some((item) => item.sn === this.sn && item.status === 'device');
|
|
110
|
+
}
|
|
111
|
+
return Promise.resolve(adbConnected);
|
|
112
|
+
});
|
|
113
|
+
const res = yield Promise.race([
|
|
114
|
+
connectFn(),
|
|
115
|
+
new Promise((resolve) => {
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
if (!adbConnected) {
|
|
118
|
+
enableLoop = false;
|
|
119
|
+
}
|
|
120
|
+
// 超时则认为adb没有连接成功
|
|
121
|
+
resolve(false);
|
|
122
|
+
}, timeout * 1000);
|
|
123
|
+
})
|
|
124
|
+
]);
|
|
125
|
+
return res;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/** 杀死进程 */
|
|
129
|
+
killProcess(currProcess) {
|
|
130
|
+
if (currProcess && currProcess.pid && currProcess.exitCode === null) {
|
|
131
|
+
console.log('process pid:', currProcess.pid);
|
|
132
|
+
try {
|
|
133
|
+
if (os_1.default.platform() === 'win32') {
|
|
134
|
+
(0, child_process_1.execSync)(`taskkill /pid ${currProcess.pid} /T /F`);
|
|
135
|
+
}
|
|
136
|
+
else if (os_1.default.platform() === 'darwin') {
|
|
137
|
+
process.kill(currProcess.pid);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
currProcess.kill();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
ColorConsole_1.default.log(`### Emulator ### kill process get error :\n${err.stack}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** 停止模拟器并释放相关资源 */
|
|
149
|
+
stop() {
|
|
150
|
+
var _a, _b;
|
|
151
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
if (this.goldfishProcess) {
|
|
153
|
+
// Linux删除goldfishProcess后,子进程仍存在,导致模拟器窗口未关闭
|
|
154
|
+
// 这里的作用是手动删除创建出来的子进程
|
|
155
|
+
// if (os.platform() === 'linux' && this.goldfishProcess.pid) {
|
|
156
|
+
// process.kill(this.goldfishProcess.pid + 1)
|
|
157
|
+
// }
|
|
158
|
+
this.killProcess(this.goldfishProcess);
|
|
159
|
+
if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.avdName) {
|
|
160
|
+
const emulatorProcessFlag = `-avd ${(_b = this.startOptions) === null || _b === void 0 ? void 0 : _b.avdName} -avd-arch`;
|
|
161
|
+
yield (0, utils_1.killProcessByCmd)(emulatorProcessFlag);
|
|
162
|
+
}
|
|
163
|
+
this.goldfishProcess = undefined;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/** 重启模拟器 */
|
|
168
|
+
restart() {
|
|
169
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
170
|
+
yield this.stop();
|
|
171
|
+
this.start(this.startOptions);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/** 创建server */
|
|
175
|
+
createWebsockeServer() {
|
|
176
|
+
var _a;
|
|
177
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
const wsServer = new ws_1.WebSocketServer({
|
|
179
|
+
port: (_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.serverPort
|
|
180
|
+
});
|
|
181
|
+
wsServer.on('connection', (socket) => {
|
|
182
|
+
ColorConsole_1.default.success(`### App Socket server ### Websocket connects to websocket server`);
|
|
183
|
+
socket.on('error', (err) => {
|
|
184
|
+
ColorConsole_1.default.error(`### App Socket server ### Websocket server error: ${err.message}`);
|
|
185
|
+
});
|
|
186
|
+
socket.on('message', (data) => {
|
|
187
|
+
const message = JSON.parse(data.toString());
|
|
188
|
+
ColorConsole_1.default.log(`### App Socket server ### Websocket server get data: ${data}`);
|
|
189
|
+
if (message.type === 'restart') {
|
|
190
|
+
this.restart();
|
|
191
|
+
}
|
|
192
|
+
else if (message.type === 'stop') {
|
|
193
|
+
this.stop();
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
connectDevice() {
|
|
200
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
201
|
+
const adbConnectCmd = `adb connect ${this.sn}`;
|
|
202
|
+
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbConnectCmd}`);
|
|
203
|
+
let pending = true;
|
|
204
|
+
const p1 = adbMiwt.execAdbCmdAsync(adbConnectCmd);
|
|
205
|
+
let timer;
|
|
206
|
+
// 超过一定时间还没有连接成功,则 kill-server 后再试
|
|
207
|
+
// 用于处理 adb connect 一直 pending 的情况
|
|
208
|
+
const p2 = new Promise((resolve, reject) => {
|
|
209
|
+
timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
210
|
+
if (pending) {
|
|
211
|
+
const adbKillCmd = `adb kill-server`;
|
|
212
|
+
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbKillCmd}`);
|
|
213
|
+
yield adbMiwt.execAdbCmdAsync(adbKillCmd);
|
|
214
|
+
}
|
|
215
|
+
resolve();
|
|
216
|
+
}), 10 * 1000);
|
|
217
|
+
});
|
|
218
|
+
p1.then((r) => {
|
|
219
|
+
pending = false;
|
|
220
|
+
clearTimeout(timer);
|
|
221
|
+
console.log(r);
|
|
222
|
+
});
|
|
223
|
+
return Promise.race([p1, p2]);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
exports.default = CommonInstance;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
|
|
2
|
+
import CommonInstance from './common';
|
|
3
|
+
declare class GoldfishInstance extends CommonInstance {
|
|
4
|
+
private appRunDir;
|
|
5
|
+
constructor(params: INewGoldfishInstanceParams);
|
|
6
|
+
/**
|
|
7
|
+
* 1. 启动模拟器
|
|
8
|
+
* 2. 启动成功后,adb连接模拟器
|
|
9
|
+
* 3. 连接成功后,在模拟器中启动快应用
|
|
10
|
+
*/
|
|
11
|
+
start(options: IStartOptions): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* 在模拟器中启动快应用
|
|
14
|
+
* 通过vapp命令启动,调试时需额外配置--jsdebugger参数
|
|
15
|
+
* @param options
|
|
16
|
+
*/
|
|
17
|
+
startupQuickApp(options: IStartOptions): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* 启动模拟器
|
|
20
|
+
* 1. 通过options生成模拟器的启动命令
|
|
21
|
+
* 2. 执行启动命令
|
|
22
|
+
* 3. 判断模拟器是否启动成功
|
|
23
|
+
* 3.1 若disableNSH=true,输出流中匹配到(NSH),认为模拟器启动成功了
|
|
24
|
+
* 3.2 若disableNSH=false,认为2s过后模拟器启动成功了
|
|
25
|
+
* @param options
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
startGoldfish(options: IStartOptions): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* 将目录通过adb push到模拟器中
|
|
31
|
+
* @param sourceRoot
|
|
32
|
+
*/
|
|
33
|
+
pushRpk(sourceRoot: string): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export default GoldfishInstance;
|