Compare commits
30 Commits
caebc6595e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
708bdc6bf0 | ||
|
|
44b02f4781 | ||
|
|
48ce7f35e1 | ||
|
|
9b80cf73d3 | ||
|
|
67a3a3bd78 | ||
|
|
e8de8f19c2 | ||
|
|
7d2076f165 | ||
|
|
10d5254a7e | ||
|
|
a67284afe5 | ||
|
|
4ac522ffc6 | ||
|
|
5456419bb3 | ||
|
|
9bcc49059e | ||
|
|
0c6199d8e4 | ||
|
|
c232332d69 | ||
|
|
cc2a2714c3 | ||
|
|
c32583e3f4 | ||
|
|
cc550ec5cc | ||
|
|
ed9264bd70 | ||
|
|
0fc1d9c439 | ||
|
|
ef98c5e89f | ||
|
|
ec4814c208 | ||
|
|
6c6661c6c0 | ||
|
|
7cd59ef596 | ||
|
|
e76956c4bc | ||
|
|
1e4d4247bf | ||
|
|
d776e24d2f | ||
|
|
c7d57d39a1 | ||
|
|
8b59808c17 | ||
|
|
aa85c7ef27 | ||
|
|
49d6ec88f0 |
110
README.md
110
README.md
@@ -64,73 +64,87 @@ pm2 startup
|
||||
|
||||
### 4.2 使用 Nginx 反代 (推荐)
|
||||
|
||||
#### A. 传统 Nginx 配置 (命令行)
|
||||
如果你使用原生 Nginx,请参考以下配置:
|
||||
#### 步骤 1: 安装 Nginx 和 Certbot
|
||||
```bash
|
||||
apt update && apt install -y nginx certbot python3-certbot-nginx
|
||||
```
|
||||
|
||||
```nginx
|
||||
#### 步骤 2: 创建 Nginx 配置文件
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/mixly << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name 你的域名;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name 你的域名;
|
||||
|
||||
ssl_certificate /path/to/your/cert.pem;
|
||||
ssl_certificate_key /path/to/your/key.pem;
|
||||
|
||||
# 核心配置:处理 WebSocket 和大文件上传
|
||||
# 主站点反代
|
||||
location / {
|
||||
proxy_pass https://127.0.0.1:7100;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_ssl_verify off;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
client_max_body_size 100M;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
|
||||
# 处理 Socket.io 特殊路径
|
||||
# WebSocket 反代 (关键配置)
|
||||
location /mixly-socket/ {
|
||||
proxy_pass https://127.0.0.1:7100;
|
||||
proxy_pass https://127.0.0.1:7100/socket.io/;
|
||||
proxy_ssl_verify off;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 86400s;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
> **注意**:将 `你的域名` 替换为实际域名,如 `mixly.example.com`
|
||||
|
||||
#### 步骤 3: 启用配置并测试
|
||||
```bash
|
||||
# 创建软链接启用配置
|
||||
ln -sf /etc/nginx/sites-available/mixly /etc/nginx/sites-enabled/
|
||||
|
||||
# 删除默认站点 (可选)
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# 测试配置语法
|
||||
nginx -t
|
||||
|
||||
# 重启 Nginx
|
||||
systemctl restart nginx
|
||||
```
|
||||
|
||||
#### B. Nginx Proxy Manager (NPM) 配置 (可视化)
|
||||
如果你使用的是 NPM,请按以下步骤配置:
|
||||
#### 步骤 4: 自动申请 SSL 证书 (Let's Encrypt)
|
||||
```bash
|
||||
# 将 your-email@example.com 替换为你的邮箱
|
||||
certbot --nginx -d 你的域名 --non-interactive --agree-tos -m your-email@example.com
|
||||
```
|
||||
|
||||
1. **Details 选项卡**:
|
||||
* **Domain Names**: `你的域名`
|
||||
* **Scheme**: `https`
|
||||
* **Forward Hostname/IP**: `127.0.0.1`
|
||||
* **Forward Port**: `7100`
|
||||
* **Websockets Support**: **必须勾选 (ON)**
|
||||
2. **Custom Locations 选项卡**:
|
||||
* 点击 **Add Location**:
|
||||
* **Define Location**: `/mixly-socket/`
|
||||
* **Forward Scheme**: `https`
|
||||
* **Forward Hostname/IP**: `127.0.0.1`
|
||||
* **Forward Port**: `7100`
|
||||
* 点击内置的齿轮图标或进入 **Advanced**,确保有以下配置(通常勾选 Websockets Support 后 NPM 会自动处理,但建议检查):
|
||||
```nginx
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
```
|
||||
3. **Advanced 选项卡** (可选):
|
||||
* 建议添加以下配置以支持大文件上传并忽略自签名证书错误(重要!):
|
||||
```nginx
|
||||
client_max_body_size 100M;
|
||||
# 忽略后端自签名证书错误 (必填,否则无法连接 wss)
|
||||
proxy_ssl_verify off;
|
||||
```
|
||||
Certbot 会自动修改 Nginx 配置并启用 HTTPS (443 端口)。
|
||||
|
||||
#### 步骤 5: 设置证书自动续期
|
||||
```bash
|
||||
# 测试自动续期
|
||||
certbot renew --dry-run
|
||||
|
||||
# 证书默认每 90 天过期,certbot 会自动配置定时任务续期
|
||||
```
|
||||
|
||||
#### Nginx 常用管理命令
|
||||
```bash
|
||||
# 查看状态
|
||||
systemctl status nginx
|
||||
|
||||
# 重载配置 (不中断服务)
|
||||
systemctl reload nginx
|
||||
|
||||
# 查看错误日志
|
||||
tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
## 5. 跨平台特性说明
|
||||
|
||||
@@ -163,3 +177,7 @@ pm2 reload mixly3
|
||||
- **反向代理配置**:如果使用 Nginx 反代,请务必处理好的 WebSocket (Upgrade) 头,否则页面无法连接。
|
||||
- **上传报错**:如果提示权限不足,请确认当前用户是否在 `dialout` 组,或尝试 `root` 运行(不推荐)。
|
||||
- **Python 命令**:系统必须能识别 `python3` 命令。
|
||||
- **没有“添加设备”按钮**:这是因为 WebSocket 连接失败。
|
||||
1. 检查 NPM 中是否勾选了 **Websockets Support**。
|
||||
2. 检查 NPM Advanced 配置中是否添加了 `proxy_ssl_verify off;` (关键)。
|
||||
3. 尝试重启服务端:`pm2 reload mixly3`。
|
||||
|
||||
@@ -34,15 +34,15 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:educore:educore": {
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"special": [
|
||||
{
|
||||
"name": "Firmware No Ble With SSL",
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
{
|
||||
"name": "Firmware With Ble No SSL",
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib_ble-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib_ble-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -54,7 +54,7 @@
|
||||
"{indexPath}/../micropython/build/lib",
|
||||
"{indexPath}/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -118,16 +118,16 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32:mixgo": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Mixgo_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Mixgo_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32:mixgo_pe": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Mixgo_PE_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Mixgo_PE_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32:generic": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Generic_ESP32_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Generic_ESP32_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32:mpython": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/mPython_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/mPython_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -137,7 +137,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -62,20 +62,20 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32c2:mixgo_mini": {
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"special": [
|
||||
{
|
||||
"name": "Firmware For General Application",
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
{
|
||||
"name": "Firmware Optimize For V2.x Board",
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_v2_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_v2_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
]
|
||||
},
|
||||
"micropython:esp32c2:generic_2M": {
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Generic_C2_lib-v1.25.0.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Generic_C2_lib-v1.25.0.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -85,7 +85,7 @@
|
||||
"{indexPath}/../micropython/build/lib",
|
||||
"{indexPath}/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -118,16 +118,16 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32c3:mixgo_cc": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_CC_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_CC_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32c3:mixgo_me": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_ME_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_ME_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32c3:mixgocar_c3": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_Car_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_Car_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32c3:generic": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Generic_C3_UART_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Generic_C3_UART_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -137,7 +137,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -62,10 +62,10 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32c5:mixgo_sowl": {
|
||||
"command": "\"{esptool}\" --chip esp32c5 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x2000 \"{indexPath}/build/Mixgo_Sowl_lib-v1.27.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
"command": "{esptool} --chip esp32c5 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x2000 \"{indexPath}/build/Mixgo_Sowl_lib-v1.27.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
},
|
||||
"micropython:esp32c5:generic": {
|
||||
"command": "\"{esptool}\" --chip esp32c5 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x2000 \"{indexPath}/build/Generic_C5_lib-v1.27.0.bin\" 0x3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c5 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x2000 \"{indexPath}/build/Generic_C5_lib-v1.27.0.bin\" 0x3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -75,7 +75,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -62,28 +62,28 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32s2:mixgo_ce": {
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Mixgo_CE_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Mixgo_CE_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"special": [
|
||||
{
|
||||
"name": "Default",
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Mixgo_CE_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Mixgo_CE_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
{
|
||||
"name": "ESP-AT-mode",
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0000 \"{indexPath}/build/MixGo-CE_AT-T17_R18.bin\""
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0000 \"{indexPath}/build/MixGo-CE_AT-T17_R18.bin\""
|
||||
}
|
||||
]
|
||||
},
|
||||
"micropython:esp32s2:generic": {
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Generic_S2_lib-v1.25.0.bin\"",
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Generic_S2_lib-v1.25.0.bin\"",
|
||||
"special": [
|
||||
{
|
||||
"name": "Default",
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Generic_S2_lib-v1.25.0.bin\""
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Generic_S2_lib-v1.25.0.bin\""
|
||||
},
|
||||
{
|
||||
"name": "ESP-AT-mode",
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0000 \"{indexPath}/build/MixGo-CE_AT-T17_R18.bin\""
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0000 \"{indexPath}/build/MixGo-CE_AT-T17_R18.bin\""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -95,7 +95,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -118,16 +118,16 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32s3:mixgo_sant": {
|
||||
"command": "\"{esptool}\" --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Sant_lib_DL-v1.25.0.bin\" 0xF00000 \"{indexPath}/../micropython/build/HZK16_GBK.bin\" 0xC00000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
"command": "{esptool} --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Sant_lib_DL-v1.25.0.bin\" 0xF00000 \"{indexPath}/../micropython/build/HZK16_GBK.bin\" 0xC00000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
},
|
||||
"micropython:esp32s3:mixgo_nova": {
|
||||
"command": "\"{esptool}\" --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Nova_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
"command": "{esptool} --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Nova_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
},
|
||||
"micropython:esp32s3:mixgo_soar": {
|
||||
"command": "\"{esptool}\" --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Soar_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK16_GBK.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
"command": "{esptool} --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Soar_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK16_GBK.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
},
|
||||
"micropython:esp32s3:generic": {
|
||||
"command": "\"{esptool}\" --chip esp32s3 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0 \"{indexPath}/build/Generic_S3_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32s3 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0 \"{indexPath}/build/Generic_S3_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -137,7 +137,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"upload": {
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": true,
|
||||
"reset": []
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"libPath": [
|
||||
"{indexPath}/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": true,
|
||||
"reset": []
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"libPath": [
|
||||
"{indexPath}/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": true,
|
||||
"reset": []
|
||||
|
||||
@@ -90,13 +90,13 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32c3:feiyi": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_FeiYi_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_FeiYi_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32:rm_e1": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/RM_E1_lib-v1.25.0.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/RM_E1_lib-v1.25.0.bin\""
|
||||
},
|
||||
"micropython:esp32:mixbot": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/MixBot_lib-v1.25.0.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/MixBot_lib-v1.25.0.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -106,7 +106,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": [],
|
||||
|
||||
@@ -34,15 +34,15 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:educore:educore": {
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"special": [
|
||||
{
|
||||
"name": "Firmware No Ble With SSL",
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
{
|
||||
"name": "Firmware With Ble No SSL",
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib_ble-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib_ble-v1.23.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -54,7 +54,7 @@
|
||||
"{indexPath}/../micropython/build/lib",
|
||||
"{indexPath}/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -118,16 +118,16 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32:mixgo": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Mixgo_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Mixgo_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32:mixgo_pe": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Mixgo_PE_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Mixgo_PE_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32:generic": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Generic_ESP32_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/Generic_ESP32_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32:mpython": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/mPython_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/mPython_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -137,7 +137,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -62,20 +62,20 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32c2:mixgo_mini": {
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"special": [
|
||||
{
|
||||
"name": "Firmware For General Application",
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
{
|
||||
"name": "Firmware Optimize For V2.x Board",
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_v2_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} --after=no_reset_stub write_flash -e 0x0 \"{indexPath}/build/Mixgo_Mini_v2_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
]
|
||||
},
|
||||
"micropython:esp32c2:generic_2M": {
|
||||
"command": "\"{esptool}\" --chip esp32c2 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Generic_C2_lib-v1.25.0.bin\""
|
||||
"command": "{esptool} --chip esp32c2 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Generic_C2_lib-v1.25.0.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -85,7 +85,7 @@
|
||||
"{indexPath}/../micropython/build/lib",
|
||||
"{indexPath}/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -118,16 +118,16 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32c3:mixgo_cc": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_CC_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_CC_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32c3:mixgo_me": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_ME_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_ME_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32c3:mixgocar_c3": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_Car_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_Car_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32c3:generic": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Generic_C3_UART_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Generic_C3_UART_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -137,7 +137,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -62,10 +62,10 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32c5:mixgo_sowl": {
|
||||
"command": "\"{esptool}\" --chip esp32c5 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x2000 \"{indexPath}/build/Mixgo_Sowl_lib-v1.27.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
"command": "{esptool} --chip esp32c5 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x2000 \"{indexPath}/build/Mixgo_Sowl_lib-v1.27.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
},
|
||||
"micropython:esp32c5:generic": {
|
||||
"command": "\"{esptool}\" --chip esp32c5 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x2000 \"{indexPath}/build/Generic_C5_lib-v1.27.0.bin\" 0x3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c5 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x2000 \"{indexPath}/build/Generic_C5_lib-v1.27.0.bin\" 0x3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -75,7 +75,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -62,28 +62,28 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32s2:mixgo_ce": {
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Mixgo_CE_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Mixgo_CE_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\"",
|
||||
"special": [
|
||||
{
|
||||
"name": "Default",
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Mixgo_CE_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Mixgo_CE_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
{
|
||||
"name": "ESP-AT-mode",
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0000 \"{indexPath}/build/MixGo-CE_AT-T17_R18.bin\""
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0000 \"{indexPath}/build/MixGo-CE_AT-T17_R18.bin\""
|
||||
}
|
||||
]
|
||||
},
|
||||
"micropython:esp32s2:generic": {
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Generic_S2_lib-v1.25.0.bin\"",
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Generic_S2_lib-v1.25.0.bin\"",
|
||||
"special": [
|
||||
{
|
||||
"name": "Default",
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Generic_S2_lib-v1.25.0.bin\""
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x1000 \"{indexPath}/build/Generic_S2_lib-v1.25.0.bin\""
|
||||
},
|
||||
{
|
||||
"name": "ESP-AT-mode",
|
||||
"command": "\"{esptool}\" --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0000 \"{indexPath}/build/MixGo-CE_AT-T17_R18.bin\""
|
||||
"command": "{esptool} --chip esp32s2 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0000 \"{indexPath}/build/MixGo-CE_AT-T17_R18.bin\""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -95,7 +95,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -118,16 +118,16 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32s3:mixgo_sant": {
|
||||
"command": "\"{esptool}\" --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Sant_lib_DL-v1.25.0.bin\" 0xF00000 \"{indexPath}/../micropython/build/HZK16_GBK.bin\" 0xC00000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
"command": "{esptool} --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Sant_lib_DL-v1.25.0.bin\" 0xF00000 \"{indexPath}/../micropython/build/HZK16_GBK.bin\" 0xC00000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
},
|
||||
"micropython:esp32s3:mixgo_nova": {
|
||||
"command": "\"{esptool}\" --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Nova_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
"command": "{esptool} --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Nova_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK12.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
},
|
||||
"micropython:esp32s3:mixgo_soar": {
|
||||
"command": "\"{esptool}\" --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Soar_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK16_GBK.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
"command": "{esptool} --chip esp32s3 --port {com} --baud {baudrate} --after hard_reset write_flash -e 0x0 \"{indexPath}/build/Mixgo_Soar_lib-v1.25.0.bin\" 0x700000 \"{indexPath}/../micropython/build/HZK16_GBK.bin\" 0x400000 \"{indexPath}/../micropython/build/esp_tts_voice_data_xiaole.dat\""
|
||||
},
|
||||
"micropython:esp32s3:generic": {
|
||||
"command": "\"{esptool}\" --chip esp32s3 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0 \"{indexPath}/build/Generic_S3_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32s3 --port {com} --baud {baudrate} --after=no_reset write_flash -e 0x0 \"{indexPath}/build/Generic_S3_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -137,7 +137,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": []
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"upload": {
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": true,
|
||||
"reset": []
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"libPath": [
|
||||
"{indexPath}/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": true,
|
||||
"reset": []
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"libPath": [
|
||||
"{indexPath}/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": true,
|
||||
"reset": []
|
||||
|
||||
@@ -90,13 +90,13 @@
|
||||
"type": "command",
|
||||
"portSelect": "all",
|
||||
"micropython:esp32c3:feiyi": {
|
||||
"command": "\"{esptool}\" --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_FeiYi_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
"command": "{esptool} --chip esp32c3 --port {com} --baud {baudrate} write_flash -e 0x0 \"{indexPath}/build/Mixgo_FeiYi_lib-v1.25.0.bin\" 0X3A0000 \"{indexPath}/../micropython/build/HZK12.bin\""
|
||||
},
|
||||
"micropython:esp32:rm_e1": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/RM_E1_lib-v1.25.0.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/RM_E1_lib-v1.25.0.bin\""
|
||||
},
|
||||
"micropython:esp32:mixbot": {
|
||||
"command": "\"{esptool}\" --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/MixBot_lib-v1.25.0.bin\""
|
||||
"command": "{esptool} --port {com} --baud {baudrate} write_flash -e 0x1000 \"{indexPath}/build/MixBot_lib-v1.25.0.bin\""
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
@@ -106,7 +106,7 @@
|
||||
"{indexPath}/build/lib",
|
||||
"{indexPath}/../micropython/build/lib"
|
||||
],
|
||||
"command": "\"{ampy}\" -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"command": "{ampy} -p {com} -d 1 -r \"{reset}\" put \"{indexPath}/build/upload\"",
|
||||
"filePath": "{indexPath}/build/upload/main.py",
|
||||
"copyLib": false,
|
||||
"reset": [],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,76 +1,76 @@
|
||||
goog.loadJs('common', () => {
|
||||
|
||||
goog.require('Mixly.Debug');
|
||||
goog.provide('Mixly.MString');
|
||||
goog.require('Mixly.Debug');
|
||||
goog.provide('Mixly.MString');
|
||||
|
||||
const { Debug, MString } = Mixly;
|
||||
const { Debug, MString } = Mixly;
|
||||
|
||||
/**
|
||||
* @function 使用传入值替换字符串中{xxx}
|
||||
* @param str {string} 传入字符串
|
||||
* @param obj {object}
|
||||
* obj = {
|
||||
* xxx: value1,
|
||||
* xxx: value2
|
||||
* }
|
||||
* 使用value替换{xxx}
|
||||
* @return {string} 返回处理后的字符串
|
||||
**/
|
||||
MString.tpl = (str, obj) => {
|
||||
if (typeof str !== 'string' || !(obj instanceof Object)) {
|
||||
/**
|
||||
* @function 使用传入值替换字符串中{xxx}
|
||||
* @param str {string} 传入字符串
|
||||
* @param obj {object}
|
||||
* obj = {
|
||||
* xxx: value1,
|
||||
* xxx: value2
|
||||
* }
|
||||
* 使用value替换{xxx}
|
||||
* @return {string} 返回处理后的字符串
|
||||
**/
|
||||
MString.tpl = (str, obj) => {
|
||||
if (typeof str !== 'string' || !(obj instanceof Object)) {
|
||||
return str;
|
||||
}
|
||||
for (let key in obj) {
|
||||
let re = new RegExp(`{${key}}`, 'gm');
|
||||
str = str.replace(re, obj[key]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
for (let key in obj) {
|
||||
let re = new RegExp("{[\s]*" + key + "[\s]*}", "gim");
|
||||
str = str.replace(re, obj[key]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
MString.decode = (str) => {
|
||||
try {
|
||||
str = unescape(str.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
|
||||
str = unescape(str.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
|
||||
} catch (error) {
|
||||
Debug.error(error);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
MString.strToByte = (str) => {
|
||||
var len, c;
|
||||
len = str.length;
|
||||
var bytes = [];
|
||||
for (var i = 0; i < len; i++) {
|
||||
c = str.charCodeAt(i);
|
||||
if (c >= 0x010000 && c <= 0x10FFFF) {
|
||||
bytes.push(((c >> 18) & 0x07) | 0xF0);
|
||||
bytes.push(((c >> 12) & 0x3F) | 0x80);
|
||||
bytes.push(((c >> 6) & 0x3F) | 0x80);
|
||||
bytes.push((c & 0x3F) | 0x80);
|
||||
} else if (c >= 0x000800 && c <= 0x00FFFF) {
|
||||
bytes.push(((c >> 12) & 0x0F) | 0xE0);
|
||||
bytes.push(((c >> 6) & 0x3F) | 0x80);
|
||||
bytes.push((c & 0x3F) | 0x80);
|
||||
} else if (c >= 0x000080 && c <= 0x0007FF) {
|
||||
bytes.push(((c >> 6) & 0x1F) | 0xC0);
|
||||
bytes.push((c & 0x3F) | 0x80);
|
||||
} else {
|
||||
bytes.push(c & 0xFF);
|
||||
MString.decode = (str) => {
|
||||
try {
|
||||
str = unescape(str.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
|
||||
str = unescape(str.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
|
||||
} catch (error) {
|
||||
Debug.error(error);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
return new Int8Array(bytes);
|
||||
}
|
||||
|
||||
MString.uint8ArrayToStr = (fileData) => {
|
||||
var dataString = "";
|
||||
for (var i = 0; i < fileData.length; i++) {
|
||||
var convert = (fileData[i]).toString(16);
|
||||
if (convert.length % 2 == 1)
|
||||
convert = "0" + convert;
|
||||
dataString = dataString + " " + convert.toUpperCase();
|
||||
MString.strToByte = (str) => {
|
||||
var len, c;
|
||||
len = str.length;
|
||||
var bytes = [];
|
||||
for (var i = 0; i < len; i++) {
|
||||
c = str.charCodeAt(i);
|
||||
if (c >= 0x010000 && c <= 0x10FFFF) {
|
||||
bytes.push(((c >> 18) & 0x07) | 0xF0);
|
||||
bytes.push(((c >> 12) & 0x3F) | 0x80);
|
||||
bytes.push(((c >> 6) & 0x3F) | 0x80);
|
||||
bytes.push((c & 0x3F) | 0x80);
|
||||
} else if (c >= 0x000800 && c <= 0x00FFFF) {
|
||||
bytes.push(((c >> 12) & 0x0F) | 0xE0);
|
||||
bytes.push(((c >> 6) & 0x3F) | 0x80);
|
||||
bytes.push((c & 0x3F) | 0x80);
|
||||
} else if (c >= 0x000080 && c <= 0x0007FF) {
|
||||
bytes.push(((c >> 6) & 0x1F) | 0xC0);
|
||||
bytes.push((c & 0x3F) | 0x80);
|
||||
} else {
|
||||
bytes.push(c & 0xFF);
|
||||
}
|
||||
}
|
||||
return new Int8Array(bytes);
|
||||
}
|
||||
|
||||
MString.uint8ArrayToStr = (fileData) => {
|
||||
var dataString = "";
|
||||
for (var i = 0; i < fileData.length; i++) {
|
||||
var convert = (fileData[i]).toString(16);
|
||||
if (convert.length % 2 == 1)
|
||||
convert = "0" + convert;
|
||||
dataString = dataString + " " + convert.toUpperCase();
|
||||
}
|
||||
return dataString;
|
||||
}
|
||||
return dataString;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,275 +1,302 @@
|
||||
goog.loadJs('web', () => {
|
||||
|
||||
goog.require('layui');
|
||||
goog.require('dayjs.duration');
|
||||
goog.require('Mixly.Boards');
|
||||
goog.require('Mixly.Debug');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Workspace');
|
||||
goog.require('Mixly.LayerProgress');
|
||||
goog.require('Mixly.WebSocket.Serial');
|
||||
goog.provide('Mixly.WebSocket.ArduShell');
|
||||
goog.require('layui');
|
||||
goog.require('dayjs.duration');
|
||||
goog.require('Mixly.Boards');
|
||||
goog.require('Mixly.Debug');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Workspace');
|
||||
goog.require('Mixly.LayerProgress');
|
||||
goog.require('Mixly.WebSocket.Serial');
|
||||
goog.require('Mixly.WebCompiler.ArduShell');
|
||||
goog.provide('Mixly.WebSocket.ArduShell');
|
||||
|
||||
const {
|
||||
Boards,
|
||||
Debug,
|
||||
LayerExt,
|
||||
Msg,
|
||||
Workspace,
|
||||
LayerProgress,
|
||||
WebSocket
|
||||
} = Mixly;
|
||||
const {
|
||||
Boards,
|
||||
Debug,
|
||||
LayerExt,
|
||||
Msg,
|
||||
Workspace,
|
||||
LayerProgress,
|
||||
WebSocket,
|
||||
WebCompiler = {}
|
||||
} = Mixly;
|
||||
|
||||
const { Serial } = WebSocket;
|
||||
// 动态获取 WebCompiler.ArduShell,用于本地上传
|
||||
const getWebCompilerArduShell = () => Mixly.WebCompiler?.ArduShell;
|
||||
|
||||
const { layer } = layui;
|
||||
const { Serial } = WebSocket;
|
||||
|
||||
const { layer } = layui;
|
||||
|
||||
|
||||
class WebSocketArduShell {
|
||||
static {
|
||||
this.mixlySocket = null;
|
||||
this.socket = null;
|
||||
this.shell = null;
|
||||
class WebSocketArduShell {
|
||||
static {
|
||||
this.mixlySocket = null;
|
||||
this.socket = null;
|
||||
this.shell = null;
|
||||
|
||||
this.getSocket = function () {
|
||||
return this.socket;
|
||||
}
|
||||
this.getSocket = function () {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
this.getMixlySocket = function () {
|
||||
return this.mixlySocket;
|
||||
}
|
||||
this.getMixlySocket = function () {
|
||||
return this.mixlySocket;
|
||||
}
|
||||
|
||||
this.init = function (mixlySocket) {
|
||||
this.mixlySocket = mixlySocket;
|
||||
this.socket = mixlySocket.getSocket();
|
||||
this.shell = new WebSocketArduShell();
|
||||
const socket = this.socket;
|
||||
this.init = function (mixlySocket) {
|
||||
this.mixlySocket = mixlySocket;
|
||||
this.socket = mixlySocket.getSocket();
|
||||
this.shell = new WebSocketArduShell();
|
||||
const socket = this.socket;
|
||||
|
||||
socket.on('arduino.dataEvent', (data) => {
|
||||
if (data.length > 1000) {
|
||||
return;
|
||||
// 同时初始化 WebCompiler.ArduShell,用于本地上传
|
||||
const WebCompilerArduShell = getWebCompilerArduShell();
|
||||
if (WebCompilerArduShell && !WebCompilerArduShell.getMixlySocket()) {
|
||||
WebCompilerArduShell.init(mixlySocket);
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
|
||||
socket.on('arduino.errorEvent', (data) => {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
try {
|
||||
data = unescape(data.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
|
||||
data = unescape(data.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
|
||||
} catch (error) {
|
||||
Debug.error(error);
|
||||
}
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
}
|
||||
|
||||
this.initCompile = function () {
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
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((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.compileFailed']}==\n`);
|
||||
});
|
||||
}
|
||||
|
||||
this.initUpload = function () {
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const port = Serial.getSelectedPortName();
|
||||
if (!port) {
|
||||
layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
|
||||
time: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
|
||||
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)) {
|
||||
socket.on('arduino.dataEvent', (data) => {
|
||||
if (data.length > 1000) {
|
||||
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((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
}
|
||||
|
||||
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(`\n==${message}==\n`);
|
||||
} else {
|
||||
message = (this.shell.isCompiling() ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']);
|
||||
statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${
|
||||
dayjs.duration(time).format('HH:mm:ss.SSS')
|
||||
})==\n`);
|
||||
socket.on('arduino.errorEvent', (data) => {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
try {
|
||||
data = unescape(data.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
|
||||
data = unescape(data.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
|
||||
} catch (error) {
|
||||
Debug.error(error);
|
||||
}
|
||||
|
||||
// 过滤掉无关紧要的端口错误(因为已经在本地完成上传了)
|
||||
if (typeof data === 'string' && (
|
||||
data.includes('cannot open serial1') ||
|
||||
data.includes('cannot open device "undefined"') ||
|
||||
data.includes('No such file or directory, cannot open')
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
}
|
||||
layer.msg(message, { time: 1000 });
|
||||
|
||||
this.initCompile = function () {
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
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((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.compileFailed']}==\n`);
|
||||
});
|
||||
}
|
||||
|
||||
this.initUpload = function () {
|
||||
// 委托给 WebCompiler.ArduShell 处理本地上传(使用 AVRUploader 或 esptool-js)
|
||||
// 服务器无法访问用户本地的串口设备,必须在浏览器端完成上传
|
||||
const WebCompilerArduShell = getWebCompilerArduShell();
|
||||
if (WebCompilerArduShell) {
|
||||
return WebCompilerArduShell.initUpload();
|
||||
}
|
||||
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const port = Serial.getSelectedPortName();
|
||||
if (!port) {
|
||||
layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
|
||||
time: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
|
||||
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((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
|
||||
});
|
||||
}
|
||||
|
||||
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(`\n==${message}==\n`);
|
||||
} else {
|
||||
message = (this.shell.isCompiling() ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']);
|
||||
statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${dayjs.duration(time).format('HH:mm:ss.SSS')
|
||||
})==\n`);
|
||||
}
|
||||
layer.msg(message, { time: 1000 });
|
||||
}
|
||||
}
|
||||
|
||||
#running_ = false;
|
||||
#upload_ = false;
|
||||
#killing_ = false;
|
||||
#layer_ = null;
|
||||
|
||||
constructor() {
|
||||
this.#layer_ = new LayerProgress({
|
||||
width: 200,
|
||||
cancelValue: Msg.Lang['nav.btn.stop'],
|
||||
skin: 'layui-anim layui-anim-scale',
|
||||
cancel: () => {
|
||||
if (this.#killing_) {
|
||||
return false;
|
||||
}
|
||||
this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
|
||||
this.#killing_ = true;
|
||||
this.kill().catch(Debug.error);
|
||||
return false;
|
||||
},
|
||||
cancelDisplay: false
|
||||
});
|
||||
}
|
||||
|
||||
async compile(code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = false;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const key = Boards.getSelectedBoardCommandParam();
|
||||
const config = { key, code };
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.compile', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async upload(port, code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = true;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const key = Boards.getSelectedBoardCommandParam();
|
||||
const config = { key, code, port };
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.upload', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async kill() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.kill', (response) => {
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showProgress() {
|
||||
const message = this.isCompiling() ? Msg.Lang['shell.compiling'] : Msg.Lang['shell.uploading'];
|
||||
this.#layer_.title(`${message}...`);
|
||||
this.#layer_.show();
|
||||
}
|
||||
|
||||
hideProgress() {
|
||||
this.#layer_.hide();
|
||||
}
|
||||
|
||||
isUploading() {
|
||||
return this.#running_ && this.#upload_;
|
||||
}
|
||||
|
||||
isCompiling() {
|
||||
return this.#running_ && !this.#upload_;
|
||||
}
|
||||
}
|
||||
|
||||
#running_ = false;
|
||||
#upload_ = false;
|
||||
#killing_ = false;
|
||||
#layer_ = null;
|
||||
|
||||
constructor() {
|
||||
this.#layer_ = new LayerProgress({
|
||||
width: 200,
|
||||
cancelValue: Msg.Lang['nav.btn.stop'],
|
||||
skin: 'layui-anim layui-anim-scale',
|
||||
cancel: () => {
|
||||
if (this.#killing_) {
|
||||
return false;
|
||||
}
|
||||
this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
|
||||
this.#killing_ = true;
|
||||
this.kill().catch(Debug.error);
|
||||
return false;
|
||||
},
|
||||
cancelDisplay: false
|
||||
});
|
||||
}
|
||||
|
||||
async compile(code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = false;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const key = Boards.getSelectedBoardCommandParam();
|
||||
const config = { key, code };
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.compile', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async upload(port, code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = true;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const key = Boards.getSelectedBoardCommandParam();
|
||||
const config = { key, code, port };
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.upload', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async kill() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.kill', (response) => {
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showProgress() {
|
||||
const message = this.isCompiling() ? Msg.Lang['shell.compiling'] : Msg.Lang['shell.uploading'];
|
||||
this.#layer_.title(`${message}...`);
|
||||
this.#layer_.show();
|
||||
}
|
||||
|
||||
hideProgress() {
|
||||
this.#layer_.hide();
|
||||
}
|
||||
|
||||
isUploading() {
|
||||
return this.#running_ && this.#upload_;
|
||||
}
|
||||
|
||||
isCompiling() {
|
||||
return this.#running_ && !this.#upload_;
|
||||
}
|
||||
}
|
||||
|
||||
WebSocket.ArduShell = WebSocketArduShell;
|
||||
WebSocket.ArduShell = WebSocketArduShell;
|
||||
|
||||
});
|
||||
@@ -1,350 +1,371 @@
|
||||
goog.loadJs('web', () => {
|
||||
|
||||
goog.require('path');
|
||||
goog.require('layui');
|
||||
goog.require('dayjs.duration');
|
||||
goog.require('Mixly.Debug');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.Config');
|
||||
goog.require('Mixly.Workspace');
|
||||
goog.require('Mixly.MString');
|
||||
goog.require('Mixly.LayerProgress');
|
||||
goog.require('Mixly.WebSocket.Serial');
|
||||
goog.provide('Mixly.WebSocket.BU');
|
||||
goog.require('path');
|
||||
goog.require('layui');
|
||||
goog.require('dayjs.duration');
|
||||
goog.require('Mixly.Debug');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.Config');
|
||||
goog.require('Mixly.Workspace');
|
||||
goog.require('Mixly.MString');
|
||||
goog.require('Mixly.LayerProgress');
|
||||
goog.require('Mixly.Boards');
|
||||
goog.require('Mixly.Web.BU');
|
||||
goog.require('Mixly.WebSocket.Serial');
|
||||
goog.provide('Mixly.WebSocket.BU');
|
||||
|
||||
const {
|
||||
Debug,
|
||||
LayerExt,
|
||||
Config,
|
||||
Msg,
|
||||
Env,
|
||||
Workspace,
|
||||
MString,
|
||||
LayerProgress,
|
||||
WebSocket
|
||||
} = Mixly;
|
||||
const {
|
||||
Debug,
|
||||
LayerExt,
|
||||
Config,
|
||||
Msg,
|
||||
Env,
|
||||
Workspace,
|
||||
MString,
|
||||
LayerProgress,
|
||||
Boards,
|
||||
WebSocket
|
||||
} = Mixly;
|
||||
|
||||
const { SELECTED_BOARD } = Config;
|
||||
const { SELECTED_BOARD, BOARD } = Config;
|
||||
|
||||
const { Serial } = WebSocket;
|
||||
// WebBU 需要在使用时动态获取,避免模块加载顺序问题
|
||||
const getWebBU = () => Mixly.Web?.BU;
|
||||
|
||||
const { layer } = layui;
|
||||
const { Serial } = WebSocket;
|
||||
|
||||
const { layer } = layui;
|
||||
|
||||
|
||||
class WebSocketBU {
|
||||
static {
|
||||
this.mixlySocket = null;
|
||||
this.socket = null;
|
||||
this.shell = null;
|
||||
class WebSocketBU {
|
||||
static {
|
||||
this.mixlySocket = null;
|
||||
this.socket = null;
|
||||
this.shell = null;
|
||||
|
||||
this.getSocket = function () {
|
||||
return this.socket;
|
||||
}
|
||||
this.getSocket = function () {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
this.getMixlySocket = function () {
|
||||
return this.mixlySocket;
|
||||
}
|
||||
this.getMixlySocket = function () {
|
||||
return this.mixlySocket;
|
||||
}
|
||||
|
||||
this.init = function (mixlySocket) {
|
||||
this.mixlySocket = mixlySocket;
|
||||
this.socket = mixlySocket.getSocket();
|
||||
this.shell = new WebSocketBU();
|
||||
const socket = this.socket;
|
||||
this.init = function (mixlySocket) {
|
||||
this.mixlySocket = mixlySocket;
|
||||
this.socket = mixlySocket.getSocket();
|
||||
this.shell = new WebSocketBU();
|
||||
const socket = this.socket;
|
||||
|
||||
socket.on('micropython.dataEvent', (data) => {
|
||||
socket.on('micropython.dataEvent', (data) => {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
|
||||
socket.on('micropython.errorEvent', (data) => {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
}
|
||||
|
||||
this.initBurn = function () {
|
||||
// MicroPython 板卡使用本地 Web Serial API 烧录,而非通过服务器
|
||||
const WebBU = getWebBU();
|
||||
if (SELECTED_BOARD?.language === 'MicroPython' && WebBU) {
|
||||
return WebBU.initBurn();
|
||||
}
|
||||
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const port = Serial.getSelectedPortName();
|
||||
if (!port) {
|
||||
layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
|
||||
time: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.burning']}...\n`);
|
||||
const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
|
||||
const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve();
|
||||
closePromise
|
||||
.then(() => {
|
||||
return this.shell.burn(port);
|
||||
})
|
||||
.then((info) => {
|
||||
this.endCallback(info.code, info.time);
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.burnFailed']}==\n`);
|
||||
});
|
||||
}
|
||||
|
||||
socket.on('micropython.errorEvent', (data) => {
|
||||
this.initUpload = function () {
|
||||
// MicroPython 板卡使用本地 Web Serial API 上传,而非通过服务器
|
||||
const WebBU = getWebBU();
|
||||
if (SELECTED_BOARD?.language === 'MicroPython' && WebBU) {
|
||||
return WebBU.initUpload();
|
||||
}
|
||||
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const port = Serial.getSelectedPortName();
|
||||
if (!port) {
|
||||
layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
|
||||
time: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
statusBarTerminal.addValue(data);
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
|
||||
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(() => {
|
||||
return statusBarSerial.setBaudRate(115200);
|
||||
})
|
||||
.catch(Debug.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
|
||||
});
|
||||
}
|
||||
|
||||
this.endCallback = function (code, time) {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
let message = '';
|
||||
if (code) {
|
||||
message = (this.shell.isBurning() ? Msg.Lang['shell.burnFailed'] : Msg.Lang['shell.uploadFailed']);
|
||||
statusBarTerminal.addValue(`\n==${message}==\n`);
|
||||
} else {
|
||||
message = (this.shell.isBurning() ? Msg.Lang['shell.burnSucc'] : Msg.Lang['shell.uploadSucc']);
|
||||
statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${dayjs.duration(time).format('HH:mm:ss.SSS')
|
||||
})==\n`);
|
||||
}
|
||||
layer.msg(message, { time: 1000 });
|
||||
}
|
||||
}
|
||||
|
||||
#running_ = false;
|
||||
#upload_ = false;
|
||||
#killing_ = false;
|
||||
#layer_ = null;
|
||||
|
||||
constructor() {
|
||||
this.#layer_ = new LayerProgress({
|
||||
width: 200,
|
||||
cancelValue: Msg.Lang['nav.btn.stop'],
|
||||
skin: 'layui-anim layui-anim-scale',
|
||||
cancel: () => {
|
||||
if (this.#killing_) {
|
||||
return false;
|
||||
}
|
||||
this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
|
||||
this.#killing_ = true;
|
||||
this.kill().catch(Debug.error);
|
||||
return false;
|
||||
},
|
||||
cancelDisplay: false
|
||||
});
|
||||
}
|
||||
|
||||
this.initBurn = function () {
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const port = Serial.getSelectedPortName();
|
||||
if (!port) {
|
||||
layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
|
||||
time: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.burning']}...\n`);
|
||||
const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
|
||||
const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve();
|
||||
closePromise
|
||||
.then(() => {
|
||||
return this.shell.burn(port);
|
||||
})
|
||||
.then((info) => {
|
||||
this.endCallback(info.code, info.time);
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.burnFailed']}==\n`);
|
||||
});
|
||||
}
|
||||
|
||||
this.initUpload = function () {
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const port = Serial.getSelectedPortName();
|
||||
if (!port) {
|
||||
layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
|
||||
time: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
|
||||
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)) {
|
||||
async burn(port) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = false;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const config = {
|
||||
boardDirPath: `.${Env.boardDirPath}`,
|
||||
port,
|
||||
command: SELECTED_BOARD.burn.command,
|
||||
baudrate: Boards.getSelectedBoardConfigParam('BurnSpeed'),
|
||||
reset: SELECTED_BOARD.burn.reset || []
|
||||
};
|
||||
const mixlySocket = WebSocketBU.getMixlySocket();
|
||||
mixlySocket.emit('micropython.burn', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
mainStatusBarTabs.add('serial', port);
|
||||
mainStatusBarTabs.changeTo(port);
|
||||
const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
|
||||
statusBarSerial.open()
|
||||
.then(() => {
|
||||
return statusBarSerial.setBaudRate(115200);
|
||||
})
|
||||
.catch(Debug.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.endCallback = function (code, time) {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
let message = '';
|
||||
if (code) {
|
||||
message = (this.shell.isBurning() ? Msg.Lang['shell.burnFailed'] : Msg.Lang['shell.uploadFailed']);
|
||||
statusBarTerminal.addValue(`\n==${message}==\n`);
|
||||
} else {
|
||||
message = (this.shell.isBurning() ? Msg.Lang['shell.burnSucc'] : Msg.Lang['shell.uploadSucc']);
|
||||
statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${
|
||||
dayjs.duration(time).format('HH:mm:ss.SSS')
|
||||
})==\n`);
|
||||
}
|
||||
layer.msg(message, { time: 1000 });
|
||||
}
|
||||
}
|
||||
|
||||
#running_ = false;
|
||||
#upload_ = false;
|
||||
#killing_ = false;
|
||||
#layer_ = null;
|
||||
|
||||
constructor() {
|
||||
this.#layer_ = new LayerProgress({
|
||||
width: 200,
|
||||
cancelValue: Msg.Lang['nav.btn.stop'],
|
||||
skin: 'layui-anim layui-anim-scale',
|
||||
cancel: () => {
|
||||
if (this.#killing_) {
|
||||
return false;
|
||||
}
|
||||
this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
|
||||
this.#killing_ = true;
|
||||
this.kill().catch(Debug.error);
|
||||
return false;
|
||||
},
|
||||
cancelDisplay: false
|
||||
});
|
||||
}
|
||||
|
||||
async burn(port) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = false;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const config = {
|
||||
boardDirPath: `.${Env.boardDirPath}`,
|
||||
port,
|
||||
command: SELECTED_BOARD.burn.command
|
||||
};
|
||||
const mixlySocket = WebSocketBU.getMixlySocket();
|
||||
mixlySocket.emit('micropython.burn', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async upload(port, code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = true;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const importsMap = this.getImportModules(code);
|
||||
let libraries = {};
|
||||
for (let key in importsMap) {
|
||||
const filename = importsMap[key]['__name__'];
|
||||
const data = goog.readFileSync(importsMap[key]['__path__']);
|
||||
libraries[filename] = data;
|
||||
}
|
||||
const config = {
|
||||
boardDirPath: `.${Env.boardDirPath}`,
|
||||
command: SELECTED_BOARD.upload.command,
|
||||
filePath: SELECTED_BOARD.upload.filePath,
|
||||
port, code, libraries
|
||||
};
|
||||
|
||||
const mixlySocket = WebSocketBU.getMixlySocket();
|
||||
mixlySocket.emit('micropython.upload', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
async upload(port, code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = true;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const importsMap = this.getImportModules(code);
|
||||
let libraries = {};
|
||||
for (let key in importsMap) {
|
||||
const filename = importsMap[key]['__name__'];
|
||||
const data = goog.readFileSync(importsMap[key]['__path__']);
|
||||
libraries[filename] = data;
|
||||
}
|
||||
const config = {
|
||||
boardDirPath: `.${Env.boardDirPath}`,
|
||||
command: SELECTED_BOARD.upload.command,
|
||||
filePath: SELECTED_BOARD.upload.filePath,
|
||||
baudrate: Boards.getSelectedBoardConfigParam('BurnSpeed'),
|
||||
reset: SELECTED_BOARD.upload.reset || [],
|
||||
port, code, libraries
|
||||
};
|
||||
|
||||
const mixlySocket = WebSocketBU.getMixlySocket();
|
||||
mixlySocket.emit('micropython.upload', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getImportModulesName(code) {
|
||||
// 正则表达式: 匹配 import 或 from 导入语句
|
||||
const importRegex = /(?:import\s+([a-zA-Z0-9_]+)|from\s+([a-zA-Z0-9_]+)\s+import)/g;
|
||||
|
||||
let imports = [];
|
||||
let match;
|
||||
while ((match = importRegex.exec(code)) !== null) {
|
||||
if (match[1]) {
|
||||
imports.push(match[1]); // 'import module'
|
||||
}
|
||||
if (match[2]) {
|
||||
imports.push(match[2]); // 'from module import ...'
|
||||
}
|
||||
}
|
||||
return imports;
|
||||
}
|
||||
|
||||
getImportModules(code) {
|
||||
let importsMap = {};
|
||||
const libPath = SELECTED_BOARD.upload.libPath;
|
||||
for (let i = libPath.length - 1; i >= 0; i--) {
|
||||
const dirname = MString.tpl(libPath[i], { indexPath: Env.boardDirPath });
|
||||
const map = goog.readJsonSync(path.join(dirname, 'map.json'));
|
||||
if (!(map && map instanceof Object)) {
|
||||
continue;
|
||||
}
|
||||
for (let key in map) {
|
||||
importsMap[key] = structuredClone(map[key]);
|
||||
importsMap[key]['__path__'] = path.join(dirname, map[key]['__name__']);
|
||||
}
|
||||
}
|
||||
|
||||
let usedMap = {};
|
||||
let currentImports = this.getImportModulesName(code);
|
||||
while (currentImports.length) {
|
||||
let temp = [];
|
||||
for (let moduleName of currentImports) {
|
||||
let moduleInfo = importsMap[moduleName];
|
||||
if (!moduleInfo) {
|
||||
getImportModulesName(code) {
|
||||
// 正则表达式: 匹配 import 或 from 导入语句
|
||||
const importRegex = /(?:import\s+([a-zA-Z0-9_]+)|from\s+([a-zA-Z0-9_]+)\s+import)/g;
|
||||
|
||||
let imports = [];
|
||||
let match;
|
||||
while ((match = importRegex.exec(code)) !== null) {
|
||||
if (match[1]) {
|
||||
imports.push(match[1]); // 'import module'
|
||||
}
|
||||
if (match[2]) {
|
||||
imports.push(match[2]); // 'from module import ...'
|
||||
}
|
||||
}
|
||||
return imports;
|
||||
}
|
||||
|
||||
getImportModules(code) {
|
||||
let importsMap = {};
|
||||
const libPath = SELECTED_BOARD.upload.libPath;
|
||||
for (let i = libPath.length - 1; i >= 0; i--) {
|
||||
const dirname = MString.tpl(libPath[i], { indexPath: Env.boardDirPath });
|
||||
const map = goog.readJsonSync(path.join(dirname, 'map.json'));
|
||||
if (!(map && map instanceof Object)) {
|
||||
continue;
|
||||
}
|
||||
usedMap[moduleName] = moduleInfo;
|
||||
const moduleImports = moduleInfo['__require__'];
|
||||
if (!moduleImports) {
|
||||
continue;
|
||||
for (let key in map) {
|
||||
importsMap[key] = structuredClone(map[key]);
|
||||
importsMap[key]['__path__'] = path.join(dirname, map[key]['__name__']);
|
||||
}
|
||||
for (let name of moduleImports) {
|
||||
if (usedMap[name] || !importsMap[name] || temp.includes(name)) {
|
||||
}
|
||||
|
||||
let usedMap = {};
|
||||
let currentImports = this.getImportModulesName(code);
|
||||
while (currentImports.length) {
|
||||
let temp = [];
|
||||
for (let moduleName of currentImports) {
|
||||
let moduleInfo = importsMap[moduleName];
|
||||
if (!moduleInfo) {
|
||||
continue;
|
||||
}
|
||||
temp.push(name);
|
||||
usedMap[moduleName] = moduleInfo;
|
||||
const moduleImports = moduleInfo['__require__'];
|
||||
if (!moduleImports) {
|
||||
continue;
|
||||
}
|
||||
for (let name of moduleImports) {
|
||||
if (usedMap[name] || !importsMap[name] || temp.includes(name)) {
|
||||
continue;
|
||||
}
|
||||
temp.push(name);
|
||||
}
|
||||
}
|
||||
currentImports = temp;
|
||||
}
|
||||
currentImports = temp;
|
||||
return usedMap;
|
||||
}
|
||||
return usedMap;
|
||||
}
|
||||
|
||||
async kill() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const mixlySocket = WebSocketBU.getMixlySocket();
|
||||
mixlySocket.emit('micropython.kill', (response) => {
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
async kill() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const mixlySocket = WebSocketBU.getMixlySocket();
|
||||
mixlySocket.emit('micropython.kill', (response) => {
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showProgress() {
|
||||
const message = this.isBurning() ? Msg.Lang['shell.burning'] : Msg.Lang['shell.uploading'];
|
||||
this.#layer_.title(`${message}...`);
|
||||
this.#layer_.show();
|
||||
}
|
||||
|
||||
hideProgress() {
|
||||
this.#layer_.hide();
|
||||
}
|
||||
|
||||
isUploading() {
|
||||
return this.#running_ && this.#upload_;
|
||||
}
|
||||
|
||||
isBurning() {
|
||||
return this.#running_ && !this.#upload_;
|
||||
}
|
||||
}
|
||||
|
||||
showProgress() {
|
||||
const message = this.isBurning() ? Msg.Lang['shell.burning'] : Msg.Lang['shell.uploading'];
|
||||
this.#layer_.title(`${message}...`);
|
||||
this.#layer_.show();
|
||||
}
|
||||
|
||||
hideProgress() {
|
||||
this.#layer_.hide();
|
||||
}
|
||||
|
||||
isUploading() {
|
||||
return this.#running_ && this.#upload_;
|
||||
}
|
||||
|
||||
isBurning() {
|
||||
return this.#running_ && !this.#upload_;
|
||||
}
|
||||
}
|
||||
|
||||
WebSocket.BU = WebSocketBU;
|
||||
WebSocket.BU = WebSocketBU;
|
||||
|
||||
});
|
||||
@@ -1,429 +1,434 @@
|
||||
goog.loadJs('web', () => {
|
||||
|
||||
goog.require('path');
|
||||
goog.require('Mustache');
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.Events');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Ampy');
|
||||
goog.require('Mixly.Web');
|
||||
goog.provide('Mixly.Web.Ampy');
|
||||
goog.require('path');
|
||||
goog.require('Mustache');
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.Events');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Ampy');
|
||||
goog.require('Mixly.Web');
|
||||
goog.provide('Mixly.Web.Ampy');
|
||||
|
||||
const {
|
||||
Env,
|
||||
Events,
|
||||
Msg,
|
||||
Ampy,
|
||||
Web
|
||||
} = Mixly;
|
||||
const {
|
||||
Env,
|
||||
Events,
|
||||
Msg,
|
||||
Ampy,
|
||||
Web
|
||||
} = Mixly;
|
||||
|
||||
|
||||
class AmpyExt extends Ampy {
|
||||
static {
|
||||
this.LS = goog.readFileSync(path.join(Env.templatePath, 'python/ls.py'));
|
||||
this.LS_RECURSIVE = goog.readFileSync(path.join(Env.templatePath, 'python/ls-recursive.py'));
|
||||
this.LS_LONG_FORMAT = goog.readFileSync(path.join(Env.templatePath, 'python/ls-long-format.py'));
|
||||
this.MKDIR = goog.readFileSync(path.join(Env.templatePath, 'python/mkdir.py'));
|
||||
this.MKFILE = goog.readFileSync(path.join(Env.templatePath, 'python/mkfile.py'));
|
||||
this.RENAME = goog.readFileSync(path.join(Env.templatePath, 'python/rename.py'));
|
||||
this.RM = goog.readFileSync(path.join(Env.templatePath, 'python/rm.py'));
|
||||
this.RMDIR = goog.readFileSync(path.join(Env.templatePath, 'python/rmdir.py'));
|
||||
this.GET = goog.readFileSync(path.join(Env.templatePath, 'python/get.py'));
|
||||
this.CWD = goog.readFileSync(path.join(Env.templatePath, 'python/cwd.py'));
|
||||
this.CPDIR = goog.readFileSync(path.join(Env.templatePath, 'python/cpdir.py'));
|
||||
this.CPFILE = goog.readFileSync(path.join(Env.templatePath, 'python/cpfile.py'));
|
||||
}
|
||||
class AmpyExt extends Ampy {
|
||||
static {
|
||||
this.LS = goog.readFileSync(path.join(Env.templatePath, 'python/ls.py'));
|
||||
this.LS_RECURSIVE = goog.readFileSync(path.join(Env.templatePath, 'python/ls-recursive.py'));
|
||||
this.LS_LONG_FORMAT = goog.readFileSync(path.join(Env.templatePath, 'python/ls-long-format.py'));
|
||||
this.MKDIR = goog.readFileSync(path.join(Env.templatePath, 'python/mkdir.py'));
|
||||
this.MKFILE = goog.readFileSync(path.join(Env.templatePath, 'python/mkfile.py'));
|
||||
this.RENAME = goog.readFileSync(path.join(Env.templatePath, 'python/rename.py'));
|
||||
this.RM = goog.readFileSync(path.join(Env.templatePath, 'python/rm.py'));
|
||||
this.RMDIR = goog.readFileSync(path.join(Env.templatePath, 'python/rmdir.py'));
|
||||
this.GET = goog.readFileSync(path.join(Env.templatePath, 'python/get.py'));
|
||||
this.CWD = goog.readFileSync(path.join(Env.templatePath, 'python/cwd.py'));
|
||||
this.CPDIR = goog.readFileSync(path.join(Env.templatePath, 'python/cpdir.py'));
|
||||
this.CPFILE = goog.readFileSync(path.join(Env.templatePath, 'python/cpfile.py'));
|
||||
}
|
||||
|
||||
#device_ = null;
|
||||
#receiveTemp_ = [];
|
||||
#writeBuffer_ = true;
|
||||
#active_ = false;
|
||||
#dataLength_ = 256;
|
||||
#events_ = new Events(['message', 'replaceMessage'])
|
||||
constructor(device, writeBuffer = true, dataLength = 256) {
|
||||
super();
|
||||
this.#device_ = device;
|
||||
this.#writeBuffer_ = writeBuffer;
|
||||
this.#dataLength_ = dataLength;
|
||||
this.#addEventsListener_();
|
||||
}
|
||||
#device_ = null;
|
||||
#receiveTemp_ = [];
|
||||
#writeBuffer_ = true;
|
||||
#active_ = false;
|
||||
#dataLength_ = 256;
|
||||
#events_ = new Events(['message', 'replaceMessage'])
|
||||
constructor(device, writeBuffer = true, dataLength = 256) {
|
||||
super();
|
||||
this.#device_ = device;
|
||||
this.#writeBuffer_ = writeBuffer;
|
||||
this.#dataLength_ = dataLength;
|
||||
this.#addEventsListener_();
|
||||
}
|
||||
|
||||
#addEventsListener_() {
|
||||
this.#device_.bind('onChar', (char) => {
|
||||
if (['\r', '\n'].includes(char)) {
|
||||
this.#receiveTemp_.push('');
|
||||
} else {
|
||||
let line = this.#receiveTemp_.pop() ?? '';
|
||||
this.#receiveTemp_.push(line + char);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bind(...args) {
|
||||
return this.#events_.bind(...args);
|
||||
}
|
||||
|
||||
message(message) {
|
||||
this.#events_.run('message', message);
|
||||
}
|
||||
|
||||
replaceMessage(lineNumber, message) {
|
||||
this.#events_.run('replaceMessage', lineNumber, message);
|
||||
}
|
||||
|
||||
getProgressMessage(name, percent) {
|
||||
const sended = parseInt(percent * 45);
|
||||
const left = percent === 0 ? '' : Array(sended).fill('=').join('');
|
||||
const right = percent === 100 ? '' : Array(45 - sended).fill('-').join('');
|
||||
return `${name} → |${left}${right}| ${(percent * 100).toFixed(1)}%`;
|
||||
}
|
||||
|
||||
isActive() {
|
||||
return this.#active_;
|
||||
}
|
||||
|
||||
async readUntil(ending, withEnding = true, timeout = 5000) {
|
||||
const startTime = Number(new Date());
|
||||
let nowTime = startTime;
|
||||
let readStr = '';
|
||||
while (nowTime - startTime < timeout) {
|
||||
const nowTime = Number(new Date());
|
||||
let len = this.#receiveTemp_.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const data = this.#receiveTemp_.shift();
|
||||
let index = data.toLowerCase().indexOf(ending);
|
||||
if (index !== -1) {
|
||||
if (withEnding) {
|
||||
index += ending.length;
|
||||
}
|
||||
this.#receiveTemp_.unshift(data.substring(index));
|
||||
readStr += data.substring(0, index);
|
||||
return readStr;
|
||||
#addEventsListener_() {
|
||||
this.#device_.bind('onChar', (char) => {
|
||||
if (['\r', '\n'].includes(char)) {
|
||||
this.#receiveTemp_.push('');
|
||||
} else {
|
||||
readStr += data;
|
||||
if (i !== len - 1) {
|
||||
readStr += '\n';
|
||||
let line = this.#receiveTemp_.pop() ?? '';
|
||||
this.#receiveTemp_.push(line + char);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bind(...args) {
|
||||
return this.#events_.bind(...args);
|
||||
}
|
||||
|
||||
message(message) {
|
||||
this.#events_.run('message', message);
|
||||
}
|
||||
|
||||
replaceMessage(lineNumber, message) {
|
||||
this.#events_.run('replaceMessage', lineNumber, message);
|
||||
}
|
||||
|
||||
getProgressMessage(name, percent) {
|
||||
const sended = parseInt(percent * 45);
|
||||
const left = percent === 0 ? '' : Array(sended).fill('=').join('');
|
||||
const right = percent === 100 ? '' : Array(45 - sended).fill('-').join('');
|
||||
return `${name} → |${left}${right}| ${(percent * 100).toFixed(1)}%`;
|
||||
}
|
||||
|
||||
isActive() {
|
||||
return this.#active_;
|
||||
}
|
||||
|
||||
async readUntil(ending, withEnding = true, timeout = 5000) {
|
||||
const startTime = Number(new Date());
|
||||
let nowTime = startTime;
|
||||
let readStr = '';
|
||||
while (nowTime - startTime < timeout) {
|
||||
const nowTime = Number(new Date());
|
||||
let len = this.#receiveTemp_.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const data = this.#receiveTemp_.shift();
|
||||
let index = data.toLowerCase().indexOf(ending);
|
||||
if (index !== -1) {
|
||||
if (withEnding) {
|
||||
index += ending.length;
|
||||
}
|
||||
this.#receiveTemp_.unshift(data.substring(index));
|
||||
readStr += data.substring(0, index);
|
||||
return readStr;
|
||||
} else {
|
||||
readStr += data;
|
||||
if (i !== len - 1) {
|
||||
readStr += '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nowTime - startTime >= timeout) {
|
||||
return '';
|
||||
}
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.dataReadInterrupt']);
|
||||
}
|
||||
await this.#device_.sleep(100);
|
||||
}
|
||||
if (nowTime - startTime >= timeout) {
|
||||
}
|
||||
|
||||
async interrupt(timeout = 1000) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
// 中断两次
|
||||
await this.#device_.sendBuffer([0x0D, 0x03]);
|
||||
await this.#device_.sleep(100);
|
||||
await this.#device_.sendBuffer([0x03]);
|
||||
await this.#device_.sleep(100);
|
||||
if (await this.readUntil('>>>', true, timeout)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async enterRawREPL(timeout = 1000) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await this.#device_.sendBuffer([0x01]);
|
||||
await this.#device_.sleep(100);
|
||||
if (await this.readUntil('raw repl; ctrl-b to exit', true, timeout)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async exitRawREPL(timeout = 5000) {
|
||||
await this.#device_.sendBuffer([0x02]);
|
||||
await this.#device_.sleep(100);
|
||||
let succeed = false;
|
||||
if (await this.readUntil('>>>', true, timeout)) {
|
||||
succeed = true;
|
||||
}
|
||||
return succeed;
|
||||
}
|
||||
|
||||
async exitREPL(timeout = 5000) {
|
||||
await this.#device_.sendBuffer([0x04]);
|
||||
await this.#device_.sleep(100);
|
||||
let succeed = false;
|
||||
if (await this.readUntil('soft reboot', false, timeout)) {
|
||||
succeed = true;
|
||||
}
|
||||
return succeed;
|
||||
}
|
||||
|
||||
async exec(str, timeout = 5000) {
|
||||
if (this.#writeBuffer_) {
|
||||
const buffer = this.#device_.encode(str);
|
||||
const len = Math.ceil(buffer.length / this.#dataLength_);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const start = i * this.#dataLength_;
|
||||
const end = Math.min((i + 1) * this.#dataLength_, buffer.length);
|
||||
const writeBuffer = buffer.slice(start, end);
|
||||
await this.#device_.sendBuffer(writeBuffer);
|
||||
await this.#device_.sleep(10);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < str.length / this.#dataLength_; i++) {
|
||||
const start = i * this.#dataLength_;
|
||||
const end = Math.min((i + 1) * this.#dataLength_, str.length);
|
||||
let data = str.substring(start, end);
|
||||
await this.#device_.sendString(data);
|
||||
await this.#device_.sleep(10);
|
||||
}
|
||||
}
|
||||
await this.#device_.sendBuffer([0x04]);
|
||||
return await this.follow(timeout);
|
||||
}
|
||||
|
||||
async follow(timeout = 1000) {
|
||||
let data = await this.readUntil('\x04', true, timeout);
|
||||
if (data.length < 1) {
|
||||
throw new Error(Msg.Lang['ampy.waitingFirstEOFTimeout']);
|
||||
}
|
||||
let start = data.toLowerCase().lastIndexOf('ok');
|
||||
if (start === -1) {
|
||||
start = 0;
|
||||
} else {
|
||||
start += 2;
|
||||
}
|
||||
data = data.substring(start, data.length - 1);
|
||||
let dataError = await this.readUntil('\x04', true, timeout);
|
||||
if (dataError.length < 1) {
|
||||
throw new Error(Msg.Lang['ampy.secondEOFTimeout']);
|
||||
}
|
||||
dataError = dataError.substring(0, dataError.length - 1);
|
||||
return { data, dataError };
|
||||
}
|
||||
|
||||
async enter() {
|
||||
if (this.isActive()) {
|
||||
return;
|
||||
}
|
||||
this.#active_ = true;
|
||||
await this.#device_.open(115200);
|
||||
await this.#device_.sleep(500);
|
||||
await this.#device_.sendBuffer([0x02]);
|
||||
if (!await this.interrupt()) {
|
||||
throw new Error(Msg.Lang['ampy.interruptFailed']);
|
||||
}
|
||||
if (!await this.enterRawREPL()) {
|
||||
throw new Error(Msg.Lang['ampy.enterRawREPLFailed']);
|
||||
}
|
||||
}
|
||||
|
||||
async exit() {
|
||||
if (!this.isActive()) {
|
||||
return;
|
||||
}
|
||||
if (!await this.exitRawREPL()) {
|
||||
throw new Error(Msg.Lang['ampy.exitRawREPLFailed']);
|
||||
}
|
||||
// 发送 Ctrl+D 触发软复位,让设备执行上传的 main.py
|
||||
if (!await this.exitREPL()) {
|
||||
throw new Error(Msg.Lang['ampy.exitREPLFailed']);
|
||||
}
|
||||
await this.#device_.close();
|
||||
this.#active_ = false;
|
||||
}
|
||||
|
||||
async get(filename, encoding = 'utf8', timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.GET, {
|
||||
path: filename
|
||||
});
|
||||
const { data, dataError } = await this.exec(code, timeout);
|
||||
if (dataError) {
|
||||
return '';
|
||||
}
|
||||
if (encoding === 'utf8') {
|
||||
return this.#device_.decode(this.unhexlify(data));
|
||||
} else {
|
||||
return this.unhexlify(data);
|
||||
}
|
||||
}
|
||||
|
||||
async put(filename, data, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.dataReadInterrupt']);
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
await this.#device_.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
async interrupt(timeout = 1000) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
// 中断两次
|
||||
await this.#device_.sendBuffer([0x0D, 0x03]);
|
||||
await this.#device_.sleep(100);
|
||||
await this.#device_.sendBuffer([0x03]);
|
||||
await this.#device_.sleep(100);
|
||||
if (await this.readUntil('>>>', true, timeout)) {
|
||||
return true;
|
||||
this.message(`Writing ${filename}...\n`);
|
||||
this.message(this.getProgressMessage('', 0));
|
||||
await this.exec(`file = open('${filename}', 'wb')`, timeout);
|
||||
let buffer = null;
|
||||
if (data.constructor === String) {
|
||||
buffer = this.#device_.encode(data);
|
||||
} else if (data.constructor === ArrayBuffer) {
|
||||
buffer = new Uint8Array(data);
|
||||
} else {
|
||||
buffer = data;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async enterRawREPL(timeout = 1000) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await this.#device_.sendBuffer([0x01]);
|
||||
await this.#device_.sleep(100);
|
||||
if (await this.readUntil('raw repl; ctrl-b to exit', true, timeout)) {
|
||||
return true;
|
||||
const len = Math.ceil(buffer.length / 64);
|
||||
if (!len) {
|
||||
this.replaceMessage(-1, this.getProgressMessage('', 1));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async exitRawREPL(timeout = 5000) {
|
||||
await this.#device_.sendBuffer([0x02]);
|
||||
await this.#device_.sleep(100);
|
||||
let succeed = false;
|
||||
if (await this.readUntil('>>>', true, timeout)) {
|
||||
succeed = true;
|
||||
}
|
||||
return succeed;
|
||||
}
|
||||
|
||||
async exitREPL(timeout = 5000) {
|
||||
await this.#device_.sendBuffer([0x04]);
|
||||
await this.#device_.sleep(100);
|
||||
let succeed = false;
|
||||
if (await this.readUntil('soft reboot', false, timeout)) {
|
||||
succeed = true;
|
||||
}
|
||||
return succeed;
|
||||
}
|
||||
|
||||
async exec(str, timeout = 5000) {
|
||||
if (this.#writeBuffer_) {
|
||||
const buffer = this.#device_.encode(str);
|
||||
const len = Math.ceil(buffer.length / this.#dataLength_);
|
||||
let sendedLength = 0;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const start = i * this.#dataLength_;
|
||||
const end = Math.min((i + 1) * this.#dataLength_, buffer.length);
|
||||
const writeBuffer = buffer.slice(start, end);
|
||||
await this.#device_.sendBuffer(writeBuffer);
|
||||
await this.#device_.sleep(10);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < str.length / this.#dataLength_; i++) {
|
||||
const start = i * this.#dataLength_;
|
||||
const end = Math.min((i + 1) * this.#dataLength_, str.length);
|
||||
let data = str.substring(start, end);
|
||||
await this.#device_.sendString(data);
|
||||
await this.#device_.sleep(10);
|
||||
}
|
||||
}
|
||||
await this.#device_.sendBuffer([0x04]);
|
||||
return await this.follow(timeout);
|
||||
}
|
||||
|
||||
async follow(timeout = 1000) {
|
||||
let data = await this.readUntil('\x04', true, timeout);
|
||||
if (data.length < 1) {
|
||||
throw new Error(Msg.Lang['ampy.waitingFirstEOFTimeout']);
|
||||
}
|
||||
let start = data.toLowerCase().lastIndexOf('ok');
|
||||
if (start === -1) {
|
||||
start = 0;
|
||||
} else {
|
||||
start += 2;
|
||||
}
|
||||
data = data.substring(start, data.length - 1);
|
||||
let dataError = await this.readUntil('\x04', true, timeout);
|
||||
if (dataError.length < 1) {
|
||||
throw new Error(Msg.Lang['ampy.secondEOFTimeout']);
|
||||
}
|
||||
dataError = dataError.substring(0, dataError.length - 1);
|
||||
return { data, dataError };
|
||||
}
|
||||
|
||||
async enter() {
|
||||
if (this.isActive()) {
|
||||
return;
|
||||
}
|
||||
this.#active_ = true;
|
||||
await this.#device_.open(115200);
|
||||
await this.#device_.sleep(500);
|
||||
await this.#device_.sendBuffer([0x02]);
|
||||
if (!await this.interrupt()) {
|
||||
throw new Error(Msg.Lang['ampy.interruptFailed']);
|
||||
}
|
||||
if (!await this.enterRawREPL()) {
|
||||
throw new Error(Msg.Lang['ampy.enterRawREPLFailed']);
|
||||
}
|
||||
}
|
||||
|
||||
async exit() {
|
||||
if (!this.isActive()) {
|
||||
return;
|
||||
}
|
||||
if (!await this.exitRawREPL()) {
|
||||
throw new Error(Msg.Lang['ampy.exitRawREPLFailed']);
|
||||
}
|
||||
/*if (!await this.exitREPL()) {
|
||||
throw new Error(Msg.Lang['ampy.exitREPLFailed']);
|
||||
}*/
|
||||
await this.#device_.close();
|
||||
this.#active_ = false;
|
||||
}
|
||||
|
||||
async get(filename, encoding = 'utf8', timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.GET, {
|
||||
path: filename
|
||||
});
|
||||
const { data, dataError } = await this.exec(code, timeout);
|
||||
if (dataError) {
|
||||
return '';
|
||||
}
|
||||
if (encoding === 'utf8') {
|
||||
return this.#device_.decode(this.unhexlify(data));
|
||||
} else {
|
||||
return this.unhexlify(data);
|
||||
}
|
||||
}
|
||||
|
||||
async put(filename, data, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
this.message(`Writing ${filename}...\n`);
|
||||
this.message(this.getProgressMessage('', 0));
|
||||
await this.exec(`file = open('${filename}', 'wb')`, timeout);
|
||||
let buffer = null;
|
||||
if (data.constructor === String) {
|
||||
buffer = this.#device_.encode(data);
|
||||
} else if (data.constructor === ArrayBuffer) {
|
||||
buffer = new Uint8Array(data);
|
||||
} else {
|
||||
buffer = data;
|
||||
}
|
||||
const len = Math.ceil(buffer.length / 64);
|
||||
if (!len) {
|
||||
this.replaceMessage(-1, this.getProgressMessage('', 1));
|
||||
}
|
||||
let sendedLength = 0;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const writeBuffer = buffer.slice(i * 64, Math.min((i + 1) * 64, buffer.length));
|
||||
sendedLength += writeBuffer.length;
|
||||
const percent = sendedLength / buffer.length;
|
||||
this.replaceMessage(-1, this.getProgressMessage('', percent));
|
||||
let writeStr = '';
|
||||
for (let num of writeBuffer) {
|
||||
let numStr = num.toString(16);
|
||||
if (numStr.length === 1) {
|
||||
numStr = '0' + numStr;
|
||||
const writeBuffer = buffer.slice(i * 64, Math.min((i + 1) * 64, buffer.length));
|
||||
sendedLength += writeBuffer.length;
|
||||
const percent = sendedLength / buffer.length;
|
||||
this.replaceMessage(-1, this.getProgressMessage('', percent));
|
||||
let writeStr = '';
|
||||
for (let num of writeBuffer) {
|
||||
let numStr = num.toString(16);
|
||||
if (numStr.length === 1) {
|
||||
numStr = '0' + numStr;
|
||||
}
|
||||
writeStr += '\\x' + numStr;
|
||||
}
|
||||
writeStr += '\\x' + numStr;
|
||||
await this.exec(`file.write(b'${writeStr}')`, timeout);
|
||||
}
|
||||
await this.exec(`file.write(b'${writeStr}')`, timeout);
|
||||
await this.exec('file.close()', timeout);
|
||||
this.message('\n');
|
||||
}
|
||||
await this.exec('file.close()', timeout);
|
||||
this.message('\n');
|
||||
}
|
||||
|
||||
async ls(directory = '/', longFormat = true, recursive = false, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
async ls(directory = '/', longFormat = true, recursive = false, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
let code = '';
|
||||
if (longFormat) {
|
||||
code = Mustache.render(AmpyExt.LS_LONG_FORMAT, {
|
||||
path: directory
|
||||
});
|
||||
} else if (recursive) {
|
||||
code = Mustache.render(AmpyExt.LS_RECURSIVE, {
|
||||
path: directory
|
||||
});
|
||||
} else {
|
||||
code = Mustache.render(AmpyExt.LS, {
|
||||
path: directory
|
||||
});
|
||||
}
|
||||
const { data, dataError } = await this.exec(code, timeout);
|
||||
if (dataError) {
|
||||
return [];
|
||||
}
|
||||
return JSON.parse(data.replaceAll('\'', '\"'));
|
||||
}
|
||||
let code = '';
|
||||
if (longFormat) {
|
||||
code = Mustache.render(AmpyExt.LS_LONG_FORMAT, {
|
||||
|
||||
async mkdir(directory, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.MKDIR, {
|
||||
path: directory
|
||||
});
|
||||
} else if (recursive) {
|
||||
code = Mustache.render(AmpyExt.LS_RECURSIVE, {
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async mkfile(file, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.MKFILE, {
|
||||
path: file
|
||||
});
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async rename(oldname, newname, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.RENAME, {
|
||||
oldPath: oldname,
|
||||
newPath: newname
|
||||
});
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async rm(filename, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.RM, {
|
||||
path: filename
|
||||
});
|
||||
await this.exec(code);
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async rmdir(directory, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.RMDIR, {
|
||||
path: directory
|
||||
});
|
||||
} else {
|
||||
code = Mustache.render(AmpyExt.LS, {
|
||||
path: directory
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async cpdir(oldname, newname, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.CPDIR, {
|
||||
oldPath: oldname,
|
||||
newPath: newname
|
||||
});
|
||||
const { data, dataError } = await this.exec(code, timeout);
|
||||
console.log(data, dataError)
|
||||
return !dataError;
|
||||
}
|
||||
const { data, dataError } = await this.exec(code, timeout);
|
||||
if (dataError) {
|
||||
return [];
|
||||
|
||||
async cpfile(oldname, newname, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.CPFILE, {
|
||||
oldPath: oldname,
|
||||
newPath: newname
|
||||
});
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async cwd(timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.CWD, {});
|
||||
const { data, dataError } = await this.exec(code, timeout);
|
||||
if (dataError) {
|
||||
return '/';
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
getDevice() {
|
||||
return this.#device_;
|
||||
}
|
||||
|
||||
async dispose() {
|
||||
this.#active_ = false;
|
||||
if (this.#device_) {
|
||||
await this.#device_.dispose();
|
||||
this.#device_ = null;
|
||||
}
|
||||
if (this.#events_) {
|
||||
this.#events_.reset();
|
||||
this.#events_ = null;
|
||||
}
|
||||
}
|
||||
return JSON.parse(data.replaceAll('\'', '\"'));
|
||||
}
|
||||
|
||||
async mkdir(directory, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.MKDIR, {
|
||||
path: directory
|
||||
});
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async mkfile(file, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.MKFILE, {
|
||||
path: file
|
||||
});
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async rename(oldname, newname, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.RENAME, {
|
||||
oldPath: oldname,
|
||||
newPath: newname
|
||||
});
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async rm(filename, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.RM, {
|
||||
path: filename
|
||||
});
|
||||
await this.exec(code);
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async rmdir(directory, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.RMDIR, {
|
||||
path: directory
|
||||
});
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async cpdir(oldname, newname, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.CPDIR, {
|
||||
oldPath: oldname,
|
||||
newPath: newname
|
||||
});
|
||||
const { data, dataError } = await this.exec(code, timeout);
|
||||
console.log(data, dataError)
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async cpfile(oldname, newname, timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.CPFILE, {
|
||||
oldPath: oldname,
|
||||
newPath: newname
|
||||
});
|
||||
const { dataError } = await this.exec(code, timeout);
|
||||
return !dataError;
|
||||
}
|
||||
|
||||
async cwd(timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
const code = Mustache.render(AmpyExt.CWD, {});
|
||||
const { data, dataError } = await this.exec(code, timeout);
|
||||
if (dataError) {
|
||||
return '/';
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
getDevice() {
|
||||
return this.#device_;
|
||||
}
|
||||
|
||||
async dispose() {
|
||||
this.#active_ = false;
|
||||
await this.#device_.dispose();
|
||||
this.#device_ = null;
|
||||
this.#events_.reset();
|
||||
this.#events_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
Web.Ampy = AmpyExt;
|
||||
Web.Ampy = AmpyExt;
|
||||
|
||||
});
|
||||
@@ -1,186 +1,174 @@
|
||||
goog.loadJs('web', () => {
|
||||
|
||||
goog.require('path');
|
||||
goog.require('Mixly.Config');
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Registry');
|
||||
goog.require('Mixly.Serial');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.HTMLTemplate');
|
||||
goog.require('Mixly.Web.SerialPort');
|
||||
goog.require('Mixly.Web.USB');
|
||||
goog.require('Mixly.Web.USBMini');
|
||||
goog.require('Mixly.Web.HID');
|
||||
goog.provide('Mixly.Web.Serial');
|
||||
goog.require('path');
|
||||
goog.require('Mixly.Config');
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Registry');
|
||||
goog.require('Mixly.Serial');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.HTMLTemplate');
|
||||
goog.require('Mixly.Web.SerialPort');
|
||||
goog.require('Mixly.Web.USB');
|
||||
goog.require('Mixly.Web.USBMini');
|
||||
goog.require('Mixly.Web.HID');
|
||||
goog.provide('Mixly.Web.Serial');
|
||||
|
||||
const {
|
||||
Config,
|
||||
Env,
|
||||
Msg,
|
||||
Registry,
|
||||
Serial,
|
||||
LayerExt,
|
||||
HTMLTemplate,
|
||||
Web
|
||||
} = Mixly;
|
||||
const {
|
||||
Config,
|
||||
Env,
|
||||
Msg,
|
||||
Registry,
|
||||
Serial,
|
||||
LayerExt,
|
||||
HTMLTemplate,
|
||||
Web
|
||||
} = Mixly;
|
||||
|
||||
const {
|
||||
SerialPort,
|
||||
USB,
|
||||
USBMini,
|
||||
HID
|
||||
} = Web;
|
||||
const {
|
||||
SerialPort,
|
||||
USB,
|
||||
USBMini,
|
||||
HID
|
||||
} = Web;
|
||||
|
||||
const { BOARD } = Config;
|
||||
const { BOARD } = Config;
|
||||
|
||||
const platform = goog.platform();
|
||||
const fullPlatform = goog.fullPlatform();
|
||||
const platform = goog.platform();
|
||||
const fullPlatform = goog.fullPlatform();
|
||||
|
||||
|
||||
class WebSerial extends Serial {
|
||||
static {
|
||||
this.devicesRegistry = new Registry();
|
||||
this.type = Serial.type;
|
||||
this.DEVICES_SELECT_LAYER = new HTMLTemplate(
|
||||
goog.readFileSync(path.join(Env.templatePath, 'html/devices-select-layer.html'))
|
||||
);
|
||||
class WebSerial extends Serial {
|
||||
static {
|
||||
this.devicesRegistry = new Registry();
|
||||
this.type = Serial.type;
|
||||
this.DEVICES_SELECT_LAYER = new HTMLTemplate(
|
||||
goog.readFileSync(path.join(Env.templatePath, 'html/devices-select-layer.html'))
|
||||
);
|
||||
|
||||
this.getConfig = function () {
|
||||
return Serial.getConfig();
|
||||
}
|
||||
|
||||
this.getSelectedPortName = function () {
|
||||
return Serial.getSelectedPortName();
|
||||
}
|
||||
|
||||
this.getCurrentPortsName = function () {
|
||||
return Serial.getCurrentPortsName();
|
||||
}
|
||||
|
||||
this.refreshPorts = function () {
|
||||
Serial.refreshPorts();
|
||||
}
|
||||
|
||||
this.requestPort = function () {
|
||||
if (this.devicesRegistry.length() < 1) {
|
||||
throw Error('can not find any device handler');
|
||||
} else if (this.devicesRegistry.length() === 1) {
|
||||
const keys = this.devicesRegistry.keys();
|
||||
return this.devicesRegistry.getItem(keys[0]).requestPort();
|
||||
this.getConfig = function () {
|
||||
return Serial.getConfig();
|
||||
}
|
||||
const msg = {
|
||||
serialMsg: Msg.Lang['layer.devices.serial'],
|
||||
serialStatus: this.devicesRegistry.hasKey('serial') ? '' : 'disabled',
|
||||
hidMsg: Msg.Lang['layer.devices.hid'],
|
||||
hidStatus: this.devicesRegistry.hasKey('hid') ? '' : 'disabled',
|
||||
usbMsg: Msg.Lang['layer.devices.usb'],
|
||||
usbStatus: (
|
||||
this.devicesRegistry.hasKey('usb') || this.devicesRegistry.hasKey('usbmini')
|
||||
) ? '' : 'disabled'
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
let selected = false;
|
||||
const layerNum = LayerExt.open({
|
||||
title: [Msg.Lang['layer.devices.select'], '36px'],
|
||||
area: ['400px', '150px'],
|
||||
max: false,
|
||||
min: false,
|
||||
content: this.DEVICES_SELECT_LAYER.render(msg),
|
||||
shade: LayerExt.SHADE_ALL,
|
||||
resize: false,
|
||||
success: function (layero, index) {
|
||||
$(layero).on('click', 'button', (event) => {
|
||||
selected = true;
|
||||
layer.close(layerNum);
|
||||
const $btn = $(event.currentTarget);
|
||||
let mId = $btn.attr('m-id');
|
||||
if (mId === 'usb' && WebSerial.devicesRegistry.hasKey('usbmini')) {
|
||||
mId = 'usbmini';
|
||||
|
||||
this.getSelectedPortName = function () {
|
||||
return Serial.getSelectedPortName();
|
||||
}
|
||||
|
||||
this.getCurrentPortsName = function () {
|
||||
return Serial.getCurrentPortsName();
|
||||
}
|
||||
|
||||
this.refreshPorts = function () {
|
||||
Serial.refreshPorts();
|
||||
}
|
||||
|
||||
this.requestPort = function () {
|
||||
if (this.devicesRegistry.length() < 1) {
|
||||
throw Error('can not find any device handler');
|
||||
} else if (this.devicesRegistry.length() === 1) {
|
||||
const keys = this.devicesRegistry.keys();
|
||||
return this.devicesRegistry.getItem(keys[0]).requestPort();
|
||||
}
|
||||
const msg = {
|
||||
serialMsg: Msg.Lang['layer.devices.serial'],
|
||||
serialStatus: this.devicesRegistry.hasKey('serial') ? '' : 'disabled',
|
||||
hidMsg: Msg.Lang['layer.devices.hid'],
|
||||
hidStatus: this.devicesRegistry.hasKey('hid') ? '' : 'disabled',
|
||||
usbMsg: Msg.Lang['layer.devices.usb'],
|
||||
usbStatus: (
|
||||
this.devicesRegistry.hasKey('usb') || this.devicesRegistry.hasKey('usbmini')
|
||||
) ? '' : 'disabled'
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
let selected = false;
|
||||
const layerNum = LayerExt.open({
|
||||
title: [Msg.Lang['layer.devices.select'], '36px'],
|
||||
area: ['400px', '150px'],
|
||||
max: false,
|
||||
min: false,
|
||||
content: this.DEVICES_SELECT_LAYER.render(msg),
|
||||
shade: LayerExt.SHADE_ALL,
|
||||
resize: false,
|
||||
success: function (layero, index) {
|
||||
$(layero).on('click', 'button', (event) => {
|
||||
selected = true;
|
||||
layer.close(layerNum);
|
||||
const $btn = $(event.currentTarget);
|
||||
let mId = $btn.attr('m-id');
|
||||
if (mId === 'usb' && WebSerial.devicesRegistry.hasKey('usbmini')) {
|
||||
mId = 'usbmini';
|
||||
}
|
||||
const Device = WebSerial.devicesRegistry.getItem(mId);
|
||||
Device.requestPort().then(resolve).catch(reject);
|
||||
});
|
||||
},
|
||||
end: function () {
|
||||
if (!selected) {
|
||||
reject('user not select any device');
|
||||
}
|
||||
const Device = WebSerial.devicesRegistry.getItem(mId);
|
||||
Device.requestPort().then(resolve).catch(reject);
|
||||
});
|
||||
},
|
||||
end: function () {
|
||||
if (!selected) {
|
||||
reject('user not select any device');
|
||||
$(`#layui-layer-shade${layerNum}`).remove();
|
||||
}
|
||||
$(`#layui-layer-shade${layerNum}`).remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.getHandler = function (device) {
|
||||
if (device.constructor.name === 'SerialPort') {
|
||||
return SerialPort;
|
||||
} else if (device.constructor.name === 'HIDDevice') {
|
||||
return HID;
|
||||
} else if (device.constructor.name === 'USBDevice') {
|
||||
if (this.devicesRegistry.hasKey('usbmini')) {
|
||||
return USBMini;
|
||||
} else {
|
||||
return USB;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
this.getPort = function (name) {
|
||||
return Serial.nameToPortRegistry.getItem(name);
|
||||
}
|
||||
|
||||
this.addPort = function (device) {
|
||||
const handler = this.getHandler(device);
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
handler.addPort(device);
|
||||
}
|
||||
|
||||
this.removePort = function (device) {
|
||||
const handler = this.getHandler(device);
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
handler.removePort(device);
|
||||
}
|
||||
|
||||
this.addEventsListener = function () {}
|
||||
|
||||
this.init = function () {
|
||||
if (Env.hasSocketServer) {
|
||||
return;
|
||||
}
|
||||
if (platform === 'win32' && fullPlatform !== 'win10') {
|
||||
if (BOARD?.web?.devices?.hid) {
|
||||
this.devicesRegistry.register('hid', HID);
|
||||
HID.init();
|
||||
}
|
||||
if (BOARD?.web?.devices?.serial) {
|
||||
this.devicesRegistry.register('serial', SerialPort);
|
||||
SerialPort.init();
|
||||
}
|
||||
if (BOARD?.web?.devices?.usb) {
|
||||
if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
|
||||
this.devicesRegistry.register('usb', USB);
|
||||
USB.init();
|
||||
this.getHandler = function (device) {
|
||||
if (device.constructor.name === 'SerialPort') {
|
||||
return SerialPort;
|
||||
} else if (device.constructor.name === 'HIDDevice') {
|
||||
return HID;
|
||||
} else if (device.constructor.name === 'USBDevice') {
|
||||
if (this.devicesRegistry.hasKey('usbmini')) {
|
||||
return USBMini;
|
||||
} else {
|
||||
return USB;
|
||||
}
|
||||
}
|
||||
} else if (platform === 'mobile') {
|
||||
if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
|
||||
this.devicesRegistry.register('usb', USB);
|
||||
USB.init();
|
||||
} else {
|
||||
this.devicesRegistry.register('usbmini', USBMini);
|
||||
USBMini.init();
|
||||
return null;
|
||||
}
|
||||
|
||||
this.getPort = function (name) {
|
||||
return Serial.nameToPortRegistry.getItem(name);
|
||||
}
|
||||
|
||||
this.addPort = function (device) {
|
||||
const handler = this.getHandler(device);
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (BOARD?.web?.devices?.serial) {
|
||||
this.devicesRegistry.register('serial', SerialPort);
|
||||
SerialPort.init();
|
||||
} else if (BOARD?.web?.devices?.usb) {
|
||||
handler.addPort(device);
|
||||
}
|
||||
|
||||
this.removePort = function (device) {
|
||||
const handler = this.getHandler(device);
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
handler.removePort(device);
|
||||
}
|
||||
|
||||
this.addEventsListener = function () { }
|
||||
|
||||
this.init = function () {
|
||||
// if (Env.hasSocketServer) {
|
||||
// return;
|
||||
// }
|
||||
if (platform === 'win32' && fullPlatform !== 'win10') {
|
||||
if (BOARD?.web?.devices?.hid) {
|
||||
this.devicesRegistry.register('hid', HID);
|
||||
HID.init();
|
||||
}
|
||||
if (BOARD?.web?.devices?.serial) {
|
||||
this.devicesRegistry.register('serial', SerialPort);
|
||||
SerialPort.init();
|
||||
}
|
||||
if (BOARD?.web?.devices?.usb) {
|
||||
if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
|
||||
this.devicesRegistry.register('usb', USB);
|
||||
USB.init();
|
||||
}
|
||||
}
|
||||
} else if (platform === 'mobile') {
|
||||
if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
|
||||
this.devicesRegistry.register('usb', USB);
|
||||
USB.init();
|
||||
@@ -188,28 +176,40 @@ class WebSerial extends Serial {
|
||||
this.devicesRegistry.register('usbmini', USBMini);
|
||||
USBMini.init();
|
||||
}
|
||||
} else if (BOARD?.web?.devices?.hid) {
|
||||
this.devicesRegistry.register('hid', HID);
|
||||
HID.init();
|
||||
} else {
|
||||
if (BOARD?.web?.devices?.serial) {
|
||||
this.devicesRegistry.register('serial', SerialPort);
|
||||
SerialPort.init();
|
||||
} else if (BOARD?.web?.devices?.usb) {
|
||||
if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
|
||||
this.devicesRegistry.register('usb', USB);
|
||||
USB.init();
|
||||
} else {
|
||||
this.devicesRegistry.register('usbmini', USBMini);
|
||||
USBMini.init();
|
||||
}
|
||||
} else if (BOARD?.web?.devices?.hid) {
|
||||
this.devicesRegistry.register('hid', HID);
|
||||
HID.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
constructor(port) {
|
||||
super(port);
|
||||
const device = WebSerial.getPort(port);
|
||||
const handler = WebSerial.getHandler(device);
|
||||
if (!handler) {
|
||||
return;
|
||||
constructor(port) {
|
||||
super(port);
|
||||
const device = WebSerial.getPort(port);
|
||||
const handler = WebSerial.getHandler(device);
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
return new handler(port);
|
||||
}
|
||||
return new handler(port);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Web.Serial = WebSerial;
|
||||
Web.Serial = WebSerial;
|
||||
|
||||
});
|
||||
@@ -129,6 +129,7 @@
|
||||
{
|
||||
"path": "/web-socket/socket.js",
|
||||
"require": [
|
||||
"io",
|
||||
"Mixly.Env",
|
||||
"Mixly.Config",
|
||||
"Mixly.MJson",
|
||||
@@ -140,4 +141,4 @@
|
||||
"Mixly.WebSocket.Socket"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -1,244 +1,253 @@
|
||||
(() => {
|
||||
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.Config');
|
||||
goog.require('Mixly.MJson');
|
||||
goog.require('Mixly.WebSocket');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.Command');
|
||||
goog.provide('Mixly.WebSocket.Socket');
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.Config');
|
||||
goog.require('Mixly.MJson');
|
||||
goog.require('Mixly.WebSocket');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.Command');
|
||||
goog.provide('Mixly.WebSocket.Socket');
|
||||
|
||||
const {
|
||||
Env,
|
||||
Config,
|
||||
MJson,
|
||||
LayerExt,
|
||||
Command
|
||||
} = Mixly;
|
||||
const {
|
||||
Env,
|
||||
Config,
|
||||
MJson,
|
||||
LayerExt,
|
||||
Command
|
||||
} = Mixly;
|
||||
|
||||
const { SOFTWARE } = Config;
|
||||
const { SOFTWARE } = Config;
|
||||
|
||||
const { Socket } = Mixly.WebSocket;
|
||||
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;
|
||||
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;
|
||||
Socket.disconnectTimes = 0;
|
||||
Socket.updating = false;
|
||||
Socket.obj = null;
|
||||
Socket.url = '';
|
||||
Socket.jsonArr = [];
|
||||
Socket.connected = false;
|
||||
Socket.initFunc = null;
|
||||
Socket.debug = SOFTWARE.debug;
|
||||
Socket.disconnectTimes = 0;
|
||||
Socket.updating = false;
|
||||
|
||||
|
||||
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.io 连接 URL (和后端统一使用 Socket.io)
|
||||
let { hostname, protocol, port } = window.location;
|
||||
if (protocol === 'http:') {
|
||||
Socket.protocol = 'ws:';
|
||||
} else {
|
||||
Socket.protocol = 'wss:';
|
||||
}
|
||||
}
|
||||
Socket.url = Socket.protocol + '//' + hostname + (port ? ':' + port : '');
|
||||
Socket.IPAddress = hostname;
|
||||
|
||||
Socket.init = (onopenFunc = (data) => {}, doFunc = () => {}) => {
|
||||
if (Socket.connected) {
|
||||
if (Socket.initFunc) {
|
||||
Socket.initFunc();
|
||||
Socket.initFunc = null;
|
||||
}
|
||||
doFunc();
|
||||
return;
|
||||
}
|
||||
let lockReconnect = false;
|
||||
let timeoutFlag = true;
|
||||
let timeoutSet = null;
|
||||
let reconectNum = 0;
|
||||
const timeout = 5000;
|
||||
|
||||
timeoutSet = setTimeout(() => {
|
||||
if (timeoutFlag && reconectNum < 3) {
|
||||
console.info(`重连`);
|
||||
reconectNum++;
|
||||
function reconnect() {
|
||||
if (lockReconnect) return;
|
||||
lockReconnect = true;
|
||||
setTimeout(function () {
|
||||
timeoutFlag = true;
|
||||
Socket.init();
|
||||
console.info(`正在重连第${reconectNum + 1}次`);
|
||||
reconectNum++;
|
||||
lockReconnect = false;
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
// 心跳检测
|
||||
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;
|
||||
this.timeoutObj = setInterval(() => {
|
||||
if (count < 3) {
|
||||
if (Socket.connected) {
|
||||
// Socket.io 自带心跳,这里可以发送自定义心跳
|
||||
console.info(`HeartBeat第${count + 1}次`);
|
||||
}
|
||||
count++;
|
||||
} else {
|
||||
clearInterval(this.timeoutObj);
|
||||
count = 0;
|
||||
if (Socket.obj && !Socket.connected) {
|
||||
Socket.obj.disconnect();
|
||||
}
|
||||
}
|
||||
}, self.timeout);
|
||||
}
|
||||
}, timeout);
|
||||
|
||||
let WS = Socket;
|
||||
WS.obj = new WebSocket(WS.url);
|
||||
WS.obj.onopen = () => {
|
||||
console.log('已连接' + WS.url);
|
||||
WS.connected = true;
|
||||
Socket.initFunc = doFunc;
|
||||
reconectNum = 0;
|
||||
timeoutFlag = false;
|
||||
clearTimeout(timeoutSet);
|
||||
heartCheck.reset().start();
|
||||
onopenFunc(WS);
|
||||
Socket.reload();
|
||||
if (Socket.updating) {
|
||||
Socket.updating = false;
|
||||
}
|
||||
|
||||
Socket.init = (onopenFunc = (data) => { }, doFunc = () => { }) => {
|
||||
if (Socket.connected) {
|
||||
if (Socket.initFunc) {
|
||||
Socket.initFunc();
|
||||
Socket.initFunc = null;
|
||||
}
|
||||
doFunc();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
Command.run(command);
|
||||
};
|
||||
timeoutSet = setTimeout(() => {
|
||||
if (timeoutFlag && reconectNum < 3) {
|
||||
console.info(`重连`);
|
||||
reconectNum++;
|
||||
Socket.init();
|
||||
}
|
||||
}, timeout);
|
||||
|
||||
WS.obj.onerror = (event) => {
|
||||
console.log('WebSocket error: ', event);
|
||||
reconnect();
|
||||
};
|
||||
// 使用 Socket.io 客户端连接(和后端统一)
|
||||
Socket.obj = io(`${Socket.url}/all`, {
|
||||
path: '/mixly-socket/',
|
||||
reconnection: true,
|
||||
reconnectionDelayMax: 10000,
|
||||
transports: ['websocket']
|
||||
});
|
||||
|
||||
WS.obj.onclose = (event) => {
|
||||
WS.connected = false;
|
||||
WS.disconnectTimes += 1;
|
||||
if (WS.disconnectTimes > 255) {
|
||||
WS.disconnectTimes = 1;
|
||||
}
|
||||
console.log('已断开' + WS.url);
|
||||
|
||||
console.info(`关闭`, event.code);
|
||||
if (event.code !== 1000) {
|
||||
Socket.obj.on('connect', () => {
|
||||
console.log('已连接' + Socket.url);
|
||||
Socket.connected = true;
|
||||
Socket.initFunc = doFunc;
|
||||
reconectNum = 0;
|
||||
timeoutFlag = false;
|
||||
clearTimeout(timeoutSet);
|
||||
heartCheck.reset().start();
|
||||
onopenFunc(Socket);
|
||||
Socket.reload();
|
||||
if (Socket.updating) {
|
||||
Socket.updating = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Socket.io 消息接收
|
||||
Socket.obj.onAny((eventName, ...args) => {
|
||||
heartCheck.reset().start();
|
||||
// 构造兼容原有 Command 格式的消息
|
||||
const command = { event: eventName, data: args };
|
||||
if (Socket.debug) {
|
||||
console.log('receive -> ', eventName, args);
|
||||
}
|
||||
// 尝试使用原有 Command 系统处理
|
||||
try {
|
||||
Command.run(MJson.decode(command));
|
||||
} catch (e) {
|
||||
// 如果 Command 系统不能处理,忽略
|
||||
}
|
||||
});
|
||||
|
||||
Socket.obj.on('connect_error', (error) => {
|
||||
console.log('WebSocket error: ', error);
|
||||
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.obj.on('disconnect', (reason) => {
|
||||
Socket.connected = false;
|
||||
Socket.disconnectTimes += 1;
|
||||
if (Socket.disconnectTimes > 255) {
|
||||
Socket.disconnectTimes = 1;
|
||||
}
|
||||
console.log('已断开' + Socket.url);
|
||||
console.info(`关闭`, reason);
|
||||
|
||||
Socket.clickConnect = () => {
|
||||
if (Socket.connected) {
|
||||
Socket.disconnect();
|
||||
} else {
|
||||
Socket.connect((WS) => {
|
||||
layer.closeAll();
|
||||
layer.msg(WS.url + '连接成功', { time: 1000 });
|
||||
if (reason !== 'io client disconnect') {
|
||||
timeoutFlag = false;
|
||||
clearTimeout(timeoutSet);
|
||||
reconnect();
|
||||
} else {
|
||||
clearInterval(heartCheck.timeoutObj);
|
||||
clearTimeout(heartCheck.serverTimeoutObj);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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.sendCommand = (command) => {
|
||||
if (!Socket.connected) {
|
||||
layer.msg('未连接' + Socket.url, { time: 1000 });
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Socket.connect = (onopenFunc = (data) => {}, doFunc = () => {}) => {
|
||||
if (Socket.connected) {
|
||||
doFunc();
|
||||
return;
|
||||
try {
|
||||
const encodedCommand = MJson.encode(command);
|
||||
if (Socket.debug) {
|
||||
console.log('send -> ', encodedCommand);
|
||||
}
|
||||
// 使用 Socket.io emit 发送命令
|
||||
Socket.obj.emit('command', encodedCommand);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
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.reload = () => {
|
||||
if (!Socket.updating && Socket.disconnectTimes) {
|
||||
window.location.reload();
|
||||
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.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.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
Socket.reload = () => {
|
||||
if (!Socket.updating && Socket.disconnectTimes) {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -17,7 +17,7 @@ MString.tpl = (str, obj) => {
|
||||
return str;
|
||||
}
|
||||
for (let key in obj) {
|
||||
let re = new RegExp(`{*${key}*}`, 'gim');
|
||||
let re = new RegExp(`{${key}}`, 'gm');
|
||||
str = str.replace(re, obj[key]);
|
||||
}
|
||||
return str;
|
||||
|
||||
@@ -11,38 +11,60 @@ export default class ShellArduino extends Shell {
|
||||
async compile(config) {
|
||||
let arduino = _.merge({}, ARDUINO);
|
||||
arduino = _.merge(arduino, config);
|
||||
const command = [
|
||||
`"${arduino.path.cli}"`,
|
||||
|
||||
// 构建参数数组,避免 shell 解析引号问题
|
||||
const args = [
|
||||
'compile',
|
||||
'-b', arduino.key,
|
||||
'--config-file', `"${arduino.path.config}"`,
|
||||
'--verbose',
|
||||
'--libraries', `"${arduino.path.libraries.join('","')}"`,
|
||||
'--build-path', `"${arduino.path.build}"`,
|
||||
'--output-dir', `"${arduino.path.output}"`,
|
||||
`"${arduino.path.code}"`,
|
||||
'--config-file', arduino.path.config,
|
||||
'--verbose'
|
||||
];
|
||||
|
||||
// 为每个 library 路径添加参数
|
||||
// 注意:config.json 中的路径是指向包含多个库的集合目录,所以必须用 --libraries
|
||||
// 如果用 --library,arduino-cli 会尝试把该目录当作单个库处理,导致找不到头文件报错
|
||||
for (const lib of arduino.path.libraries) {
|
||||
args.push('--libraries', lib);
|
||||
}
|
||||
|
||||
args.push(
|
||||
'--build-path', arduino.path.build,
|
||||
'--output-dir', arduino.path.output,
|
||||
arduino.path.code,
|
||||
'--no-color'
|
||||
].join(' ');
|
||||
return this.execUntilClosed(command, { maxBuffer: 4096 * 1000000 });
|
||||
);
|
||||
|
||||
return this.execFileUntilClosed(arduino.path.cli, args, { maxBuffer: 4096 * 1000000 });
|
||||
}
|
||||
|
||||
async upload(config) {
|
||||
let arduino = _.merge({}, ARDUINO);
|
||||
arduino = _.merge(arduino, config);
|
||||
const command = [
|
||||
`"${arduino.path.cli}"`,
|
||||
|
||||
// 构建参数数组,避免 shell 解析引号问题
|
||||
const args = [
|
||||
'compile',
|
||||
'--upload',
|
||||
'-p', arduino.port,
|
||||
'-b', arduino.key,
|
||||
'--config-file', `"${arduino.path.config}"`,
|
||||
'--verbose',
|
||||
'--libraries', `"${arduino.path.libraries.join('","')}"`,
|
||||
'--build-path', `"${arduino.path.build}"`,
|
||||
'--output-dir', `"${arduino.path.output}"`,
|
||||
`"${arduino.path.code}"`,
|
||||
'--config-file', arduino.path.config,
|
||||
'--verbose'
|
||||
];
|
||||
|
||||
// 为每个 library 路径添加参数
|
||||
// 注意:config.json 中的路径是指向包含多个库的集合目录,所以必须用 --libraries
|
||||
// 如果用 --library,arduino-cli 会尝试把该目录当作单个库处理,导致找不到头文件报错
|
||||
for (const lib of arduino.path.libraries) {
|
||||
args.push('--libraries', lib);
|
||||
}
|
||||
|
||||
args.push(
|
||||
'--build-path', arduino.path.build,
|
||||
'--output-dir', arduino.path.output,
|
||||
arduino.path.code,
|
||||
'--no-color'
|
||||
].join(' ');
|
||||
return this.execUntilClosed(command, { maxBuffer: 4096 * 1000000 });
|
||||
);
|
||||
|
||||
return this.execFileUntilClosed(arduino.path.cli, args, { maxBuffer: 4096 * 1000000 });
|
||||
}
|
||||
}
|
||||
@@ -10,22 +10,65 @@ export default class ShellMicroPython extends Shell {
|
||||
}
|
||||
|
||||
async burn(config) {
|
||||
// 对系统命令(不含路径分隔符)不加引号,只对路径加引号
|
||||
const pythonCmd = PYTHON.path.cli.includes('/') || PYTHON.path.cli.includes('\\')
|
||||
? `"${PYTHON.path.cli}"`
|
||||
: PYTHON.path.cli;
|
||||
|
||||
const info = {
|
||||
indexPath: path.resolve(CLIENT_PATH, config.boardDirPath),
|
||||
esptool: `"${PYTHON.path.cli}" "${MICROPYTHON.path.esptool}`,
|
||||
com: config.port
|
||||
esptool: `${pythonCmd} "${MICROPYTHON.path.esptool}"`,
|
||||
com: config.port,
|
||||
baudrate: config.baudrate || "460800"
|
||||
};
|
||||
const command = MString.tpl(config.command, info);
|
||||
// 兼容性处理:移除模板中可能存在的冗余引号
|
||||
let cmdTemplate = config.command || "";
|
||||
cmdTemplate = cmdTemplate.replace(/"{esptool}"/g, '{esptool}');
|
||||
const command = MString.tpl(cmdTemplate, info);
|
||||
console.log('DEBUG CMD BURN:', command);
|
||||
return this.execUntilClosed(command);
|
||||
}
|
||||
|
||||
async upload(config) {
|
||||
// 调试:打印接收到的原始配置
|
||||
console.log('=== UPLOAD DEBUG START ===');
|
||||
console.log('Received config.command:', JSON.stringify(config.command));
|
||||
console.log('Received config.reset:', JSON.stringify(config.reset));
|
||||
console.log('Received config.port:', config.port);
|
||||
console.log('Received config.boardDirPath:', config.boardDirPath);
|
||||
|
||||
// 正确处理 reset 参数:如果是数组则序列化为 JSON 字符串
|
||||
let resetValue = config.reset;
|
||||
if (Array.isArray(resetValue)) {
|
||||
resetValue = JSON.stringify(resetValue);
|
||||
} else if (resetValue === undefined || resetValue === null) {
|
||||
resetValue = '[]';
|
||||
}
|
||||
console.log('Processed resetValue:', resetValue);
|
||||
|
||||
// 对系统命令(不含路径分隔符)不加引号,只对路径加引号
|
||||
const pythonCmd = PYTHON.path.cli.includes('/') || PYTHON.path.cli.includes('\\')
|
||||
? `"${PYTHON.path.cli}"`
|
||||
: PYTHON.path.cli;
|
||||
|
||||
const info = {
|
||||
indexPath: path.resolve(CLIENT_PATH, config.boardDirPath),
|
||||
ampy: `"${PYTHON.path.cli}" "${MICROPYTHON.path.ampy}`,
|
||||
com: config.port
|
||||
ampy: `${pythonCmd} "${MICROPYTHON.path.ampy}"`,
|
||||
com: config.port,
|
||||
reset: resetValue
|
||||
};
|
||||
const command = MString.tpl(config.command, info);
|
||||
console.log('Info object:', JSON.stringify(info));
|
||||
|
||||
// 兼容性处理:移除模板中可能存在的冗余引号
|
||||
let cmdTemplate = config.command || "";
|
||||
console.log('Original cmdTemplate:', cmdTemplate);
|
||||
cmdTemplate = cmdTemplate.replace(/"{ampy}"/g, '{ampy}');
|
||||
console.log('After quote cleanup:', cmdTemplate);
|
||||
|
||||
const command = MString.tpl(cmdTemplate, info);
|
||||
console.log('Final command:', command);
|
||||
console.log('=== UPLOAD DEBUG END ===');
|
||||
|
||||
return this.execUntilClosed(command);
|
||||
}
|
||||
}
|
||||
@@ -173,6 +173,12 @@ export default class Socket {
|
||||
});
|
||||
|
||||
socket.on('micropython.burn', async (config, callback) => {
|
||||
// 检查端口是否是有效的系统设备路径(而非前端内部标识符如 serial1)
|
||||
if (config.port && !config.port.startsWith('/dev/') && !config.port.includes(':')) {
|
||||
console.log(`[MicroPython] 忽略无效端口请求: ${config.port},应使用本地 Web Serial API`);
|
||||
callback([null, { code: 0, time: 0 }]);
|
||||
return;
|
||||
}
|
||||
const shell = this.#shellMicroPython_.getItem(socket.id);
|
||||
const [error, result] = await to(shell.burn(config));
|
||||
error && Debug.error(error);
|
||||
@@ -180,6 +186,12 @@ export default class Socket {
|
||||
});
|
||||
|
||||
socket.on('micropython.upload', async (config, callback) => {
|
||||
// 检查端口是否是有效的系统设备路径(而非前端内部标识符如 serial1)
|
||||
if (config.port && !config.port.startsWith('/dev/') && !config.port.includes(':')) {
|
||||
console.log(`[MicroPython] 忽略无效端口请求: ${config.port},应使用本地 Web Serial API`);
|
||||
callback([null, { code: 0, time: 0 }]);
|
||||
return;
|
||||
}
|
||||
const shell = this.#shellMicroPython_.getItem(socket.id);
|
||||
let { filePath = '', libraries = {} } = config;
|
||||
filePath = MString.tpl(filePath, {
|
||||
@@ -250,8 +262,16 @@ export default class Socket {
|
||||
error2 && Debug.error(error2);
|
||||
let [error3,] = await to(fsExtra.outputFile(config.path.code, config.code));
|
||||
error3 && Debug.error(error3);
|
||||
const [error, result] = await to(shell.upload(config));
|
||||
error && Debug.error(error);
|
||||
const [error, result] = await to(shell.compile(config));
|
||||
if (error) {
|
||||
Debug.error(error);
|
||||
callback([error, result]);
|
||||
return;
|
||||
}
|
||||
if (result.code === 0) {
|
||||
const buildPath = config.path.build;
|
||||
result.files = await Boards.getFiles(config.key, buildPath);
|
||||
}
|
||||
callback([error, result]);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user