aiot-toolkit 2.0.2-dev.8 → 2.0.3-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.
Files changed (38) hide show
  1. package/README.md +0 -1
  2. package/lib/bin.js +43 -46
  3. package/lib/builder/AndroidUxBuilder.d.ts +10 -0
  4. package/lib/builder/AndroidUxBuilder.js +20 -0
  5. package/lib/builder/UxBuilderBase.d.ts +35 -0
  6. package/lib/builder/{UxBuilder.js → UxBuilderBase.js} +51 -59
  7. package/lib/builder/VelaUxBuilder.d.ts +29 -0
  8. package/lib/builder/VelaUxBuilder.js +66 -0
  9. package/lib/builder/XtsBuilder.js +15 -18
  10. package/lib/index.d.ts +6 -0
  11. package/lib/index.js +14 -0
  12. package/lib/interface/VelaEmulatorInterface.d.ts +7 -6
  13. package/lib/starter/AndroidUxStart.d.ts +40 -0
  14. package/lib/starter/AndroidUxStart.js +171 -0
  15. package/lib/starter/IStarter.d.ts +2 -1
  16. package/lib/starter/IStarter.js +5 -1
  17. package/lib/starter/VelaUxStarter.d.ts +23 -0
  18. package/lib/starter/VelaUxStarter.js +198 -0
  19. package/lib/starter/XtsStarter.d.ts +2 -2
  20. package/lib/starter/androidRouter/LinkMode.d.ts +9 -0
  21. package/lib/starter/androidRouter/LinkMode.js +12 -0
  22. package/lib/starter/androidRouter/PackageRouter.d.ts +55 -0
  23. package/lib/starter/androidRouter/PackageRouter.js +152 -0
  24. package/lib/starter/androidRouter/h5/index.css +167 -0
  25. package/lib/starter/androidRouter/h5/index.html +58 -0
  26. package/lib/starter/androidRouter/h5/index.js +66 -0
  27. package/lib/utils/AdbUtils.d.ts +0 -1
  28. package/lib/utils/AdbUtils.js +3 -14
  29. package/lib/utils/DeviceUtil.js +42 -29
  30. package/lib/utils/RequestUtils.js +5 -5
  31. package/lib/utils/VelaAvdUtils.d.ts +25 -9
  32. package/lib/utils/VelaAvdUtils.js +262 -100
  33. package/package.json +21 -12
  34. package/lib/builder/UxBuilder.d.ts +0 -27
  35. package/lib/starter/UxStarter.d.ts +0 -10
  36. package/lib/starter/UxStarter.js +0 -139
  37. package/lib/utils/UxBuilderUtils.d.ts +0 -8
  38. package/lib/utils/UxBuilderUtils.js +0 -51
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const aiotpack_1 = require("@aiot-toolkit/aiotpack");
16
+ const commander_1 = require("@aiot-toolkit/commander");
17
+ const shared_utils_1 = require("@aiot-toolkit/shared-utils");
18
+ const fs_extra_1 = __importDefault(require("fs-extra"));
19
+ const http_1 = __importDefault(require("http"));
20
+ const path_1 = __importDefault(require("path"));
21
+ const AndroidUxBuilder_1 = __importDefault(require("../builder/AndroidUxBuilder"));
22
+ const IStarter_1 = __importDefault(require("./IStarter"));
23
+ const PackageRouter_1 = __importDefault(require("./androidRouter/PackageRouter"));
24
+ /**
25
+ * AndroidUxStart
26
+ *
27
+ * 1. 启动本机开发者http服务器,并提供二维码,以下载 rpk
28
+ * 2. 打包 rpk,并监听文件变化,重新打包
29
+ */
30
+ class AndroidUxStart extends IStarter_1.default {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.builder = new AndroidUxBuilder_1.default();
34
+ this.projectPath = '';
35
+ this.params = [...this.builder.params];
36
+ }
37
+ get waiter() {
38
+ return new commander_1.PersistentCommand({
39
+ description: 'you can press follow keys to do something',
40
+ options: [
41
+ {
42
+ key: 'q',
43
+ description: 'show qrcode',
44
+ action: () => {
45
+ this.showAddress();
46
+ }
47
+ },
48
+ {
49
+ key: '?',
50
+ description: 'show waiter desc',
51
+ action: () => {
52
+ this.waiter.clearLog();
53
+ this.waiter.describe();
54
+ }
55
+ }
56
+ ]
57
+ });
58
+ }
59
+ /**
60
+ * 启动
61
+ * 1. build 项目
62
+ * 2. build 成功,则创建http服务器
63
+ */
64
+ start(projectPath, options) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ this.projectPath = projectPath;
67
+ const buildOption = Object.assign(Object.assign({}, aiotpack_1.JavascriptDefaultCompileOption), options);
68
+ yield this.build(projectPath, buildOption);
69
+ yield this.createServer(buildOption);
70
+ });
71
+ }
72
+ /**
73
+ * 创建服务器
74
+ *
75
+ * 1. 使用 koa 创建服务器
76
+ * 2. 显示服务器地址和二维码
77
+ * @returns
78
+ */
79
+ createServer(options) {
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
82
+ const routeConfigList = [
83
+ new PackageRouter_1.default({
84
+ projectPath: this.projectPath,
85
+ options
86
+ })
87
+ ];
88
+ const data = yield shared_utils_1.NetworkUtil.createHttpServer({
89
+ wantPort: options.server.port,
90
+ routeConfigList,
91
+ staticFolder: path_1.default.join(__dirname, './androidRouter/h5'),
92
+ events: {
93
+ onSuccess: () => {
94
+ this.server = data.server;
95
+ this.port = data.port;
96
+ this.showAddress();
97
+ resolve(data);
98
+ }
99
+ }
100
+ });
101
+ }));
102
+ });
103
+ }
104
+ showAddress() {
105
+ const ip = shared_utils_1.NetworkUtil.getIPv4IPAddress();
106
+ const { port } = this;
107
+ if (!ip) {
108
+ const localUrl = `http://localhost:${port}`;
109
+ shared_utils_1.ColorConsole.warn(`devServer`, localUrl);
110
+ }
111
+ else {
112
+ const lanUrl = `http://${ip}:${port}`;
113
+ // 显示二维码
114
+ shared_utils_1.ColorConsole.info(`devServer`, lanUrl);
115
+ shared_utils_1.CommonUtil.outputQRCodeOnTerminal(lanUrl);
116
+ }
117
+ }
118
+ build(projectPath, options) {
119
+ return __awaiter(this, void 0, void 0, function* () {
120
+ this.builder.events = {
121
+ onBuildSuccess: (data) => {
122
+ shared_utils_1.ColorConsole.info(`build time: ${data.costTime}ms`);
123
+ this.noticeDeviceListUpdate(projectPath, options);
124
+ }
125
+ };
126
+ yield this.builder.build(projectPath, Object.assign(Object.assign({}, options), { watch: true }));
127
+ });
128
+ }
129
+ dispose() {
130
+ var _a;
131
+ (_a = this.server) === null || _a === void 0 ? void 0 : _a.close();
132
+ }
133
+ /**
134
+ * 通知已知的设备,应用有更新
135
+ * @param projectPath
136
+ * @param options
137
+ */
138
+ noticeDeviceListUpdate(projectPath, options) {
139
+ var _a;
140
+ const { clientRecordPath } = options;
141
+ const json = fs_extra_1.default.readJSONSync(clientRecordPath, { throws: false });
142
+ const deviceList = (_a = json === null || json === void 0 ? void 0 : json.records) === null || _a === void 0 ? void 0 : _a[projectPath];
143
+ if (deviceList) {
144
+ deviceList.forEach((item) => {
145
+ const { ip, port } = item;
146
+ if (!ip) {
147
+ return;
148
+ }
149
+ const url = `http://${ip}:${port}/update`;
150
+ const requestOption = { host: ip, port, path: '/update', timeout: 3000 };
151
+ http_1.default
152
+ .request(requestOption, () => {
153
+ shared_utils_1.ColorConsole.success(`Notify the phone to update the rpk file success`, {
154
+ word: url
155
+ });
156
+ })
157
+ .on('error', (error) => {
158
+ shared_utils_1.ColorConsole.warn(`Notify the phone to update the rpk file`, { word: 'error: ' }, {
159
+ word: error.message
160
+ });
161
+ })
162
+ .on('timeout', () => {
163
+ shared_utils_1.ColorConsole.warn(`Notify the phone to update the rpk file`, { word: 'timeout: ' }, {
164
+ word: url
165
+ });
166
+ });
167
+ });
168
+ }
169
+ }
170
+ }
171
+ exports.default = AndroidUxStart;
@@ -1,4 +1,4 @@
1
- import { ICommand, IParam } from '@aiot-toolkit/commander';
1
+ import { ICommand, IParam, PersistentCommand } from '@aiot-toolkit/commander';
2
2
  /**
3
3
  * IStarter
4
4
  */
@@ -10,6 +10,7 @@ export default abstract class IStarter<O = any> {
10
10
  */
11
11
  readonly params: IParam[];
12
12
  constructor(name: string, description: string);
13
+ get waiter(): PersistentCommand | undefined;
13
14
  /**
14
15
  * start 的命令
15
16
  */
@@ -21,6 +21,9 @@ class IStarter {
21
21
  */
22
22
  this.params = [];
23
23
  }
24
+ get waiter() {
25
+ return;
26
+ }
24
27
  /**
25
28
  * start 的命令
26
29
  */
@@ -29,9 +32,10 @@ class IStarter {
29
32
  name: this.name,
30
33
  description: this.description,
31
34
  paramList: this.params,
35
+ waiter: this.waiter,
32
36
  action: (option) => __awaiter(this, void 0, void 0, function* () {
33
37
  const projectPath = process.cwd();
34
- this.start(projectPath, option);
38
+ return this.start(projectPath, option);
35
39
  })
36
40
  };
37
41
  }
@@ -0,0 +1,23 @@
1
+ import { IParam } from '@aiot-toolkit/commander';
2
+ import { IStartOptions, GoldfishInstance, MiwearInstance, OldGoldfishInstance } from '@aiot-toolkit/emulator';
3
+ import VelaUxBuilder from '../builder/VelaUxBuilder';
4
+ import IStarter from './IStarter';
5
+ /**
6
+ * VelaUxStarter
7
+ * ux快应用启动器
8
+ */
9
+ declare class VelaUxStarter extends IStarter<IStartOptions> {
10
+ private ws;
11
+ currentGoldfishInstance: GoldfishInstance | MiwearInstance | OldGoldfishInstance | undefined;
12
+ builder: VelaUxBuilder;
13
+ params: IParam[];
14
+ start(projectPath: string, options: any): Promise<void>;
15
+ /**
16
+ * 检查选择的模拟器在当前环境下是否可以使用
17
+ * 下面的几种情况会导致模拟器不可用:1. 模拟器绑定的Vela镜像不存在;2. Vela4.0缺少coredump.core或者vela_data.bin文件
18
+ * @param avdName 模拟器名称
19
+ * @returns {boolean}
20
+ */
21
+ isAvailableEmulator(avdName: string): boolean;
22
+ }
23
+ export default VelaUxStarter;
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const aiotpack_1 = require("@aiot-toolkit/aiotpack");
16
+ const emulator_1 = require("@aiot-toolkit/emulator");
17
+ const constants_1 = require("@aiot-toolkit/emulator/lib/emulatorutil/constants");
18
+ const index_1 = require("@aiot-toolkit/emulator/lib/utils/index");
19
+ const shared_utils_1 = require("@aiot-toolkit/shared-utils");
20
+ const prompts_1 = require("@inquirer/prompts");
21
+ const os_1 = __importDefault(require("os"));
22
+ const path_1 = __importDefault(require("path"));
23
+ const portfinder_1 = __importDefault(require("portfinder"));
24
+ const VelaUxBuilder_1 = __importDefault(require("../builder/VelaUxBuilder"));
25
+ const VelaAvdUtils_1 = __importDefault(require("../utils/VelaAvdUtils"));
26
+ const IStarter_1 = __importDefault(require("./IStarter"));
27
+ const fs_extra_1 = __importDefault(require("fs-extra"));
28
+ const ws_1 = __importDefault(require("ws"));
29
+ /**
30
+ * VelaUxStarter
31
+ * ux快应用启动器
32
+ */
33
+ class VelaUxStarter extends IStarter_1.default {
34
+ constructor() {
35
+ super(...arguments);
36
+ this.builder = new VelaUxBuilder_1.default({
37
+ onBuildSuccess: (data) => {
38
+ var _a;
39
+ shared_utils_1.ColorConsole.info(`build time: ${data.costTime}ms`);
40
+ if (this.ws) {
41
+ this.ws.send(JSON.stringify({
42
+ type: 'restart'
43
+ }));
44
+ }
45
+ else {
46
+ (_a = this.currentGoldfishInstance) === null || _a === void 0 ? void 0 : _a.restart();
47
+ }
48
+ }
49
+ });
50
+ this.params = [
51
+ {
52
+ name: 'disableNSH',
53
+ description: 'disable goldfish NSH terminal',
54
+ defaultValue: false,
55
+ type: 'confirm'
56
+ },
57
+ {
58
+ name: 'watch',
59
+ description: 'recompile project while file changes',
60
+ defaultValue: false,
61
+ type: 'confirm'
62
+ },
63
+ {
64
+ name: 'openVNC',
65
+ description: 'open vnc',
66
+ defaultValue: false,
67
+ type: 'confirm'
68
+ },
69
+ ...this.builder.params
70
+ ];
71
+ }
72
+ start(projectPath, options) {
73
+ return __awaiter(this, void 0, void 0, function* () {
74
+ // 获取已经创建的模拟器列表
75
+ const avdList = VelaAvdUtils_1.default.velaAvdCls.getVelaAvdList();
76
+ let avdName;
77
+ if (avdList.length === 0) {
78
+ shared_utils_1.ColorConsole.error('no vela emulator available, please create it first.');
79
+ VelaAvdUtils_1.default.createVelaAvdByInquire();
80
+ return;
81
+ }
82
+ else if (avdList.length === 1) {
83
+ const needtoRun = yield (0, prompts_1.confirm)({
84
+ message: 'there is only one emulator, need to Run?',
85
+ default: true
86
+ });
87
+ if (!needtoRun) {
88
+ VelaAvdUtils_1.default.createVelaAvdByInquire();
89
+ return;
90
+ }
91
+ avdName = avdList[0].avdName;
92
+ shared_utils_1.ColorConsole.warn(`Start run by :${avdName} `);
93
+ }
94
+ else {
95
+ avdName = yield (0, prompts_1.select)({
96
+ message: 'name of the avd to start',
97
+ choices: avdList.map((item) => {
98
+ return { value: item.avdName };
99
+ })
100
+ });
101
+ }
102
+ // 检查选择的模拟器是否符合要求
103
+ if (avdName && !this.isAvailableEmulator(avdName)) {
104
+ shared_utils_1.ColorConsole.throw(`this emulator is unavailable, please create a new emulator.`);
105
+ return;
106
+ }
107
+ let serverPort;
108
+ // watch模型下开启server
109
+ serverPort = yield portfinder_1.default.getPortPromise({
110
+ port: aiotpack_1.JavascriptDefaultCompileOption.serverPort
111
+ });
112
+ (0, aiotpack_1.setServerPort)(serverPort);
113
+ // build
114
+ const compilerOption = this.builder.getCompilerOption(projectPath, options);
115
+ yield this.builder.build(projectPath, options);
116
+ // start
117
+ const params = {
118
+ sdkHome: path_1.default.resolve(os_1.default.homedir(), '.export'),
119
+ avdHome: path_1.default.resolve(os_1.default.homedir(), '.android/avd'),
120
+ projectPath,
121
+ compilerOption
122
+ };
123
+ // 寻找对应的模拟器instance
124
+ this.currentGoldfishInstance = (0, emulator_1.findInstance)(avdName, params);
125
+ if (!this.currentGoldfishInstance) {
126
+ shared_utils_1.ColorConsole.throw('no corresponding emulator found, please recreate it');
127
+ return;
128
+ }
129
+ let grpcPort;
130
+ // 设置vncPort
131
+ if (options.openVNC) {
132
+ grpcPort = yield portfinder_1.default.getPortPromise({
133
+ port: constants_1.defaultVncPort,
134
+ stopPort: constants_1.defaultVncPort + 100
135
+ });
136
+ }
137
+ // 设置adbPort
138
+ const adbPort = yield (0, index_1.getEvenPort)();
139
+ if (!adbPort) {
140
+ shared_utils_1.ColorConsole.throw(`${adbPort},the port numbers between 5555 and 5585 are all occupied. please resolve the port conflict before starting the emulator`);
141
+ return;
142
+ }
143
+ // 设置debugPort
144
+ let debugPort;
145
+ if (options.devtool) {
146
+ debugPort = yield portfinder_1.default.getPortPromise({ port: constants_1.defaultDebugPort });
147
+ }
148
+ const startOptions = {
149
+ avdName,
150
+ devtool: options.devtool,
151
+ disableNSH: options.disableNSH,
152
+ serverPort,
153
+ grpcPort,
154
+ adbPort,
155
+ debugPort
156
+ };
157
+ yield this.currentGoldfishInstance.start(startOptions);
158
+ this.ws = new ws_1.default(`ws://localhost:${serverPort}`);
159
+ // waiter
160
+ // const startWaiter: PersistentCommand = new PersistentCommand({
161
+ // description: 'aiot-toolkit start 的常驻命令',
162
+ // options: [
163
+ // {
164
+ // key: '?',
165
+ // description: '显示所有命令',
166
+ // action() {
167
+ // startWaiter.clearLog()
168
+ // startWaiter.describe()
169
+ // }
170
+ // }
171
+ // ]
172
+ // })
173
+ // startWaiter.describe()
174
+ // startWaiter.start()
175
+ });
176
+ }
177
+ /**
178
+ * 检查选择的模拟器在当前环境下是否可以使用
179
+ * 下面的几种情况会导致模拟器不可用:1. 模拟器绑定的Vela镜像不存在;2. Vela4.0缺少coredump.core或者vela_data.bin文件
180
+ * @param avdName 模拟器名称
181
+ * @returns {boolean}
182
+ */
183
+ isAvailableEmulator(avdName) {
184
+ const { avdImagePath } = VelaAvdUtils_1.default.velaAvdCls.getVelaAvdInfo(avdName);
185
+ // 没有avdImagePath,即对应的configIni里没有 image.sysdir.1 字段
186
+ // 有avdImagePath,但是这个目录下没有nuttx
187
+ if (!avdImagePath || !fs_extra_1.default.existsSync(path_1.default.resolve(avdImagePath, 'nuttx'))) {
188
+ return false;
189
+ }
190
+ if (avdImagePath.includes('vela-release') &&
191
+ (!fs_extra_1.default.existsSync(path_1.default.resolve(avdImagePath, 'coredump.core')) ||
192
+ !fs_extra_1.default.existsSync(path_1.default.resolve(avdImagePath, 'vela_data.bin')))) {
193
+ return false;
194
+ }
195
+ return true;
196
+ }
197
+ }
198
+ exports.default = VelaUxStarter;
@@ -1,9 +1,9 @@
1
- import ParamType from '@aiot-toolkit/commander/lib/interface/IParam';
1
+ import { IParam } from '@aiot-toolkit/commander';
2
2
  import XtsBuilder from '../builder/XtsBuilder';
3
3
  import IStarter from './IStarter';
4
4
  declare class XtsStarter extends IStarter {
5
5
  builder: XtsBuilder;
6
- params: ParamType[];
6
+ params: IParam[];
7
7
  start(projectPath: string, options: any): void;
8
8
  }
9
9
  export default XtsStarter;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 连接类型
3
+ */
4
+ declare enum LinkMode {
5
+ NULL = 0,
6
+ WIFI = 1,
7
+ ADB = 2
8
+ }
9
+ export default LinkMode;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * 连接类型
5
+ */
6
+ var LinkMode;
7
+ (function (LinkMode) {
8
+ LinkMode[LinkMode["NULL"] = 0] = "NULL";
9
+ LinkMode[LinkMode["WIFI"] = 1] = "WIFI";
10
+ LinkMode[LinkMode["ADB"] = 2] = "ADB";
11
+ })(LinkMode || (LinkMode = {}));
12
+ exports.default = LinkMode;
@@ -0,0 +1,55 @@
1
+ import { IJavascriptCompileOption } from '@aiot-toolkit/aiotpack';
2
+ import { IRouteItem, IRouter } from '@aiot-toolkit/shared-utils';
3
+ interface IPackageRouter {
4
+ projectPath: string;
5
+ options: IJavascriptCompileOption;
6
+ }
7
+ /**
8
+ * 打包相关的路由配置
9
+ * 用于开发机的httpServer
10
+ */
11
+ declare class PackageRouter implements IRouter<IPackageRouter> {
12
+ readonly param: IPackageRouter;
13
+ constructor(param: IPackageRouter);
14
+ routeList: IRouteItem<any, {}>[];
15
+ /**
16
+ * 下载 rpk 包
17
+ * @param ctx
18
+ * @param next
19
+ */
20
+ private handlerDownLoadRpk;
21
+ private handlerQrCode;
22
+ /**
23
+ * 记录设备信息
24
+ *
25
+ * 获取请求的 ip 和端口,记录到本机中,最多记录5个
26
+ *
27
+ * 文件内容的格式为:
28
+ * ```
29
+ * records: {
30
+ * "快应用项目路径":[
31
+ * {ip:"", port:""},
32
+ * {ip:"", port:""},
33
+ * {ip:"", port:""}
34
+ * ]
35
+ * }
36
+ * ```
37
+ *
38
+ *
39
+ * 此记录的作用是:当重新打包后,获取已连接的设备列表
40
+ * @param ctx
41
+ * @param next
42
+ */
43
+ private recordDevice;
44
+ private getClientFromRequest;
45
+ /**
46
+ * http 请求中,用于返回下载文件
47
+ *
48
+ * 调用此方法后,仍需调用 next()
49
+ *
50
+ * @param filePath 文件绝对路径
51
+ * @param ctx 请求上下文
52
+ */
53
+ private download;
54
+ }
55
+ export default PackageRouter;
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const shared_utils_1 = require("@aiot-toolkit/shared-utils");
16
+ const fs_extra_1 = __importDefault(require("fs-extra"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const qr_image_1 = __importDefault(require("qr-image"));
19
+ const LinkMode_1 = __importDefault(require("./LinkMode"));
20
+ const CLIENT_PORT = 39517;
21
+ /**
22
+ * 打包相关的路由配置
23
+ * 用于开发机的httpServer
24
+ */
25
+ class PackageRouter {
26
+ constructor(param) {
27
+ this.param = param;
28
+ this.routeList = [
29
+ {
30
+ path: '/qrcode', // 获取二维码图片
31
+ handler: (ctx, next) => {
32
+ return this.handlerQrCode(ctx, next);
33
+ }
34
+ },
35
+ {
36
+ path: '/bundle', // 获取 rpk 包
37
+ handler: (ctx, next) => {
38
+ this.recordDevice(ctx);
39
+ return this.handlerDownLoadRpk(ctx, next);
40
+ }
41
+ }
42
+ ];
43
+ /**
44
+ * 下载 rpk 包
45
+ * @param ctx
46
+ * @param next
47
+ */
48
+ this.handlerDownLoadRpk = (ctx, next) => {
49
+ const { projectPath, options } = this.param;
50
+ const dist = path_1.default.join(projectPath, options.releasePath);
51
+ const files = fs_extra_1.default.readdirSync(dist).filter((item) => path_1.default.extname(item) === '.rpk');
52
+ const filePath = files.length ? path_1.default.join(dist, files[0]) : '';
53
+ this.download(filePath, ctx);
54
+ return next();
55
+ };
56
+ this.handlerQrCode = (ctx, next) => {
57
+ const qrText = ctx.origin;
58
+ const image = qr_image_1.default.image(qrText, {
59
+ size: 9
60
+ });
61
+ ctx.type = 'image/png';
62
+ ctx.body = image;
63
+ return next();
64
+ };
65
+ /**
66
+ * 记录设备信息
67
+ *
68
+ * 获取请求的 ip 和端口,记录到本机中,最多记录5个
69
+ *
70
+ * 文件内容的格式为:
71
+ * ```
72
+ * records: {
73
+ * "快应用项目路径":[
74
+ * {ip:"", port:""},
75
+ * {ip:"", port:""},
76
+ * {ip:"", port:""}
77
+ * ]
78
+ * }
79
+ * ```
80
+ *
81
+ *
82
+ * 此记录的作用是:当重新打包后,获取已连接的设备列表
83
+ * @param ctx
84
+ * @param next
85
+ */
86
+ this.recordDevice = (ctx) => __awaiter(this, void 0, void 0, function* () {
87
+ const clientInfo = this.getClientFromRequest(ctx);
88
+ if (!(clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientIp)) {
89
+ return;
90
+ }
91
+ if (clientInfo.linkMode === LinkMode_1.default.WIFI) {
92
+ const max = 5;
93
+ const filePath = this.param.options.clientRecordPath;
94
+ const projectPath = this.param.projectPath;
95
+ const data = {
96
+ sn: clientInfo.sn,
97
+ ip: clientInfo.clientIp,
98
+ port: CLIENT_PORT
99
+ };
100
+ const json = fs_extra_1.default.readJSONSync(filePath, { throws: false }) || {};
101
+ if (!json.records) {
102
+ json.records = {};
103
+ }
104
+ if (!json.records[projectPath]) {
105
+ json.records[projectPath] = [];
106
+ }
107
+ const deviceList = json.records[projectPath];
108
+ if (deviceList.findIndex((item) => item.ip === data.ip) < 0) {
109
+ deviceList.push(data);
110
+ if (deviceList.length > max) {
111
+ deviceList.splice(0, deviceList.length - max);
112
+ }
113
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(filePath));
114
+ fs_extra_1.default.writeJSONSync(filePath, json, { spaces: 2 });
115
+ }
116
+ }
117
+ });
118
+ }
119
+ getClientFromRequest(ctx) {
120
+ const clientIp = shared_utils_1.NetworkUtil.getClientIp(ctx);
121
+ const serverIp = shared_utils_1.NetworkUtil.getIPv4IPAddress();
122
+ const sn = ctx.request.header['device-serial-number'];
123
+ let linkMode = LinkMode_1.default.NULL;
124
+ if (clientIp === '127.0.0.1' && sn) {
125
+ linkMode = LinkMode_1.default.ADB;
126
+ }
127
+ else if (clientIp !== '127.0.0.1' && clientIp !== serverIp) {
128
+ linkMode = LinkMode_1.default.WIFI;
129
+ }
130
+ return { clientIp, sn, linkMode };
131
+ }
132
+ /**
133
+ * http 请求中,用于返回下载文件
134
+ *
135
+ * 调用此方法后,仍需调用 next()
136
+ *
137
+ * @param filePath 文件绝对路径
138
+ * @param ctx 请求上下文
139
+ */
140
+ download(filePath, ctx) {
141
+ if (filePath && fs_extra_1.default.existsSync(filePath)) {
142
+ ctx.set('Content-Type', 'application/octet-stream');
143
+ ctx.set('Content-Transfer-Encoding', 'binary');
144
+ ctx.set('Content-Disposition', `attachment; filename=${path_1.default.basename(filePath)}`);
145
+ ctx.body = fs_extra_1.default.createReadStream(filePath);
146
+ }
147
+ else {
148
+ ctx.throw('404', 404);
149
+ }
150
+ }
151
+ }
152
+ exports.default = PackageRouter;