diff --git a/backend/config/channel.json b/backend/config/channel.json index 8b66336..4008c28 100644 --- a/backend/config/channel.json +++ b/backend/config/channel.json @@ -1,103 +1,5 @@ { "ai_channel": [ - { - "ch": 1, - "singal_type": "1-5v", - "line_no": 1, - "type": "UA", - "limit_low": 0.0, - "limit_high": 20.0 - }, - { - "ch": 2, - "singal_type": "1~5V", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 3, - "singal_type": "4~20mA", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 4, - "singal_type": "1~5V", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 5, - "singal_type": "4~20mA", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 6, - "singal_type": "1~5V", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 7, - "singal_type": "4~20mA", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 8, - "singal_type": "1~5V", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 9, - "singal_type": "4~20mA", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 10, - "singal_type": "1~5V", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 11, - "singal_type": "4~20mA", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - }, - { - "ch": 12, - "singal_type": "4~20mA", - "line_no": 1, - "type": "UA", - "limit_low": 1.0, - "limit_high": 8.0 - } - ], - "ao_channel": [ { "ch": 1, "singal_type": "1-5v", @@ -194,5 +96,103 @@ "limit_low": 1.0, "limit_high": 8.0 } + ], + "ao_channel": [ + { + "ch": 1, + "singal_type": "1-5v", + "line_no": 1, + "type": "UA", + "limit_low": 10.0, + "limit_high": 20.0 + }, + { + "ch": 2, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 3, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 4, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 5, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 6, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 7, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 8, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 9, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 10, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 11, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 12, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + } ] } \ No newline at end of file diff --git a/backend/config/device.json b/backend/config/device.json index 3185aaf..e8a696f 100644 --- a/backend/config/device.json +++ b/backend/config/device.json @@ -1,5 +1,5 @@ { - "password": "bcb15f821479b4d5772bd0ca866c00ad5f926e3580720659cc80d39c9d09802a", + "password": "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92", "hardware_version": { "board_version": "B001.001.001", "display_version": "S001.001.001", diff --git a/document/test.md b/document/test.md new file mode 100644 index 0000000..e69de29 diff --git a/document/在RK3568设备上系统部署及使用说明.md b/document/在RK3568设备上系统部署及使用说明.md index 024934b..006bc26 100644 --- a/document/在RK3568设备上系统部署及使用说明.md +++ b/document/在RK3568设备上系统部署及使用说明.md @@ -28,10 +28,16 @@ - 前端默认请求头中携带:`X-API-Token: admin123` - 默认安全校验密码:`admin123` - 后端默认启用模拟采集:`use_mock_device = True` +- 后端项目声明支持的 Python 版本:`>=3.8` +- 当前设备资源较紧张:根分区剩余约 `2.8G`,物理内存约 `1.9G`,无 `Swap` +- 最终部署方案:不创建虚拟环境,直接使用系统 `Python 3.10.12` 说明: - 当前前端代码默认调用本机 `127.0.0.1:8000` 的后端接口,因此推荐前后端部署在同一台 RK3568 设备上 +- 推荐发布方式为:前端在开发机执行 `npm run build`,服务器只上传并发布 `dist/` 静态文件 +- 当前设备属于嵌入式一体机,推荐采用“最小化部署”方案,避免在设备上保留前端源码、`node_modules`、开发工具和大体积缓存 +- 当前文档以下步骤均按“直接使用系统 `python3`,不创建 `.venv`”编写 - 如果后续需要将前后端拆分到不同设备,需要同步调整前端接口配置 --- @@ -41,17 +47,32 @@ 推荐目录规划如下: ```text -/opt/emcp/ +/userdata/emcp/ ├── backend/ ├── frontend/ -└── scripts/ +│ └── dist/ +├── scripts/ +└── data/ ``` 建议使用以下方式部署: - 后端:`systemd` 托管 `uvicorn` -- 前端:前端执行 `npm run build` 后,用轻量静态服务对 `dist/` 目录发布 +- 前端:开发机完成 `npm run build` 后,仅将 `dist/` 上传到设备,再用轻量静态服务对 `dist/` 目录发布 - 浏览器自启动:使用桌面自动启动项 `~/.config/autostart/*.desktop` +- 数据目录、报警数据库、备份目录统一放在 `/userdata`,减少根分区占用 +- 不在服务器安装 `Node.js/npm` +- 不在服务器安装前端源码依赖,不安装后端开发依赖 `.[dev]` + +### 3.1 低资源设备优化原则 + +- 优先使用 `/userdata` 分区存放程序和运行数据,尽量不占用根分区 +- 后端安装依赖时使用 `pip --no-cache-dir`,避免留下 `pip` 缓存 +- 前端只上传 `dist/`,不上传 `src/`、`node_modules/`、`.git/` +- 后端直接使用系统 `python3.10.12`,不创建虚拟环境,进一步减少磁盘占用 +- 生产环境只启动一个 `uvicorn` 进程,不开启 `--reload` +- 尽量复用系统已有浏览器,不额外安装多个浏览器 +- 日志通过 `journalctl` 查看,不另外输出到大日志文件 --- @@ -63,14 +84,18 @@ ```bash sudo apt update -sudo apt install -y python3.8 python3.8-venv python3-pip curl git -sudo apt install -y nodejs npm +sudo apt install -y python3.10-venv python3-pip ``` 说明: -- Ubuntu 20.04 默认可直接使用 `Python 3.8` -- 如果设备上的 `nodejs` 版本过低,建议升级到 `Node.js 18 LTS` 后再安装前端依赖 +- 当前服务器已安装 `Python 3.10.12`,本项目后端 `requires-python = ">=3.8"`,因此可直接使用 `Python 3.10.12` 部署 +- 建议先执行 `python3.10 --version`,确认输出为 `Python 3.10.12` +- 如果设备上的 `python3.10-venv` 尚未安装,可通过上面的命令补齐 +- 当前推荐方案下,RK3568 服务器无需安装 `Node.js` 或 `npm` +- `Node.js/npm` 仅在开发机或构建机执行前端打包时需要 +- 如果代码是通过 `scp`、`rsync` 或 U 盘拷贝,服务器也无需安装 `git` +- 若现场需要临时下载文件,再按需安装 `curl` 或 `git` ### 4.2 安装浏览器 @@ -86,63 +111,125 @@ sudo apt install -y chromium-browser sudo apt install -y firefox ``` +低资源建议: + +- 如果系统已经自带可用浏览器,则不要重复安装第二个浏览器 +- 通常只保留一个浏览器即可,减少磁盘占用 + ### 4.3 拷贝项目代码 将工程目录拷贝到设备,例如: ```bash -sudo mkdir -p /opt/emcp -sudo chown -R $USER:$USER /opt/emcp +sudo mkdir -p /userdata/emcp +sudo chown -R $USER:$USER /userdata/emcp ``` 然后将项目复制到: ```text -/opt/emcp/backend -/opt/emcp/frontend +/userdata/emcp/backend +/userdata/emcp/frontend/dist ``` -如果是通过 `git` 拉取,也可以在 `/opt/emcp` 下执行: +推荐目录示例: + +```text +/userdata/emcp/ +├── backend/ +├── frontend/ +│ └── dist/ +├── scripts/ +└── data/ +``` + +如果确实需要兼容历史脚本中的 `/opt/emcp` 路径,可以建立软链接: ```bash -git clone <你的仓库地址> /opt/emcp +sudo ln -sfn /userdata/emcp /opt/emcp ``` -如果项目目录本身已经包含 `backend` 和 `frontend`,则以实际目录结构为准。 +如果后端通过 `git` 拉取,也可以在 `/userdata` 下执行: + +```bash +git clone <你的仓库地址> /userdata/emcp +``` + +前端发布建议: + +- 开发机执行 `npm install`、`npm run build` +- 只将打包产物 `frontend/dist/` 复制到服务器 `/userdata/emcp/frontend/dist` +- 服务器无需保留前端源码、`node_modules`、`package.json` +- 如需进一步节省空间,可将 `backend/` 打包为压缩包后再上传,上传后立即删除压缩包 --- ## 5. 后端部署 -### 5.1 创建虚拟环境 +### 5.1 确认系统 Python 版本 ```bash -cd /opt/emcp/backend -python3.8 -m venv .venv -source .venv/bin/activate -python -m pip install --upgrade pip +python3 --version +``` + +期望输出: + +```text +Python 3.10.12 +安装pip3 +curl -sS https://bootstrap.pypa.io/get-pip.py -o get-pip.py && python3 get-pip.py && rm get-pip.py ``` ### 5.2 安装后端依赖 ```bash -cd /opt/emcp/backend -source .venv/bin/activate -pip install -e . +cd /userdata/emcp/backend + +python3 -m pip install --no-cache-dir -e . ``` -如需测试或调试工具,再安装: +生产环境不建议安装测试或调试工具,避免额外占用磁盘空间。 + +如确实需要测试或调试工具,再安装: ```bash -pip install -e .[dev] +python3 -m pip install --no-cache-dir -e .[dev] ``` -### 5.3 手动启动后端验证 +补充说明: + +- 当前项目在本地开发阶段使用过 `Python 3.8` 虚拟环境,但部署到 RK3568 服务器时直接使用系统 `Python 3.10.12` 即可 +- 当前部署方案不创建 `.venv`,Python 依赖直接安装到系统环境 +- `--no-cache-dir` 可以避免在 `~/.cache/pip` 中留下额外缓存文件 +- 安装完成后如需进一步清理,可执行 `rm -rf ~/.cache/pip` +- 如需保留当前依赖快照,可执行 `python3 -m pip freeze > /userdata/emcp/backend/requirements.lock.txt` + +### 5.3 配置运行数据目录 + +建议将报警数据库、备份目录等运行时数据统一放到 `/userdata/emcp/data`: ```bash -cd /opt/emcp/backend -source .venv/bin/activate -python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 +mkdir -p /userdata/emcp/data/backups +``` + +在 `/userdata/emcp/backend/.env` 中写入: + +```env +EMCP_DATA_DIR=/userdata/emcp/data +EMCP_ALARM_DB_PATH=/userdata/emcp/data/alarm.db +EMCP_BACKUP_DIR=/userdata/emcp/data/backups +``` + +说明: + +- 这样即使根分区空间较紧张,报警数据库和备份文件也不会继续占用 `/` +- 如果将来需要清理历史数据,也只需处理 `/userdata/emcp/data` + +### 5.4 手动启动后端验证 + +```bash +cd /userdata/emcp/backend +python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --no-access-log ``` 验证方式: @@ -156,32 +243,38 @@ python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 ## 6. 前端部署 -### 6.1 安装前端依赖 +### 6.1 在开发机打包前端 + +以下步骤在开发机执行,不在 RK3568 服务器执行: ```bash -cd /opt/emcp/frontend +cd /path/to/EMCP/frontend npm install -``` - -### 6.2 构建前端 - -```bash -cd /opt/emcp/frontend npm run build ``` 构建完成后会生成: ```text -/opt/emcp/frontend/dist +frontend/dist ``` +### 6.2 上传 dist 到 RK3568 服务器 + +将打包好的 `dist/` 目录复制到服务器: + +```text +/userdata/emcp/frontend/dist +``` + +可根据现场情况使用 `scp`、`rsync`、U 盘或其他方式拷贝。 + ### 6.3 手动启动前端验证 推荐使用 Python 内置静态服务快速发布: ```bash -cd /opt/emcp/frontend +cd /userdata/emcp/frontend python3 -m http.server 5173 --directory dist --bind 0.0.0.0 ``` @@ -193,6 +286,7 @@ python3 -m http.server 5173 --directory dist --bind 0.0.0.0 - 当前前端默认调用本机后端 `http://127.0.0.1:8000/api` - 因此前端页面只要在设备本机打开,即可直接访问后端服务 +- 当前发布方案中,服务器只负责托管静态文件,不参与前端构建 --- @@ -222,6 +316,7 @@ python3 -m http.server 5173 --directory dist --bind 0.0.0.0 - `报警历史` 页面支持分页和刷新 - 配置保存前会弹出安全校验密码输入框 - 当前默认安全校验密码为 `admin123` +- 交付到现场后,建议尽快修改 `device.json` 中的设备密码,避免继续使用默认密码 --- @@ -248,8 +343,11 @@ Wants=network-online.target [Service] Type=simple User=ubuntu -WorkingDirectory=/opt/emcp/backend -ExecStart=/opt/emcp/backend/.venv/bin/python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 +WorkingDirectory=/userdata/emcp/backend +Environment=PYTHONDONTWRITEBYTECODE=1 +Environment=PYTHONUNBUFFERED=1 +EnvironmentFile=-/userdata/emcp/backend/.env +ExecStart=/usr/bin/python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --no-access-log Restart=always RestartSec=3 @@ -261,6 +359,9 @@ WantedBy=multi-user.target - `User=ubuntu` 需要替换为设备实际登录用户名 - 如需切换真实采集设备,可结合 `.env` 或配置文件调整后端参数 +- 当前服务直接调用系统 `python3` +- `--no-access-log` 可减少日志输出和磁盘写入 +- `PYTHONDONTWRITEBYTECODE=1` 可避免生成额外的 `.pyc` 文件 ### 8.2 配置前端服务 @@ -281,8 +382,8 @@ Wants=network-online.target [Service] Type=simple User=ubuntu -WorkingDirectory=/opt/emcp/frontend -ExecStart=/usr/bin/python3 -m http.server 5173 --directory /opt/emcp/frontend/dist --bind 0.0.0.0 +WorkingDirectory=/userdata/emcp/frontend +ExecStart=/usr/bin/python3 -m http.server 5173 --directory /userdata/emcp/frontend/dist --bind 0.0.0.0 Restart=always RestartSec=3 @@ -292,6 +393,13 @@ WantedBy=multi-user.target ### 8.3 启用开机自启 +需要给linaro授权 + +sudo chown -R linaro:linaro /userdata/emcp/data +sudo chown -R linaro:linaro /userdata/emcp/backend/config + + + 执行: ```bash @@ -339,13 +447,13 @@ journalctl -u emcp-frontend.service -f 创建目录: ```bash -mkdir -p /opt/emcp/scripts +mkdir -p /userdata/emcp/scripts ``` 创建脚本文件: ```text -/opt/emcp/scripts/start-emcp-browser.sh +/userdata/emcp/scripts/start-emcp-browser.sh ``` 内容如下: @@ -357,7 +465,7 @@ sleep 8 URL="http://127.0.0.1:5173/" if command -v chromium-browser >/dev/null 2>&1; then - chromium-browser --start-fullscreen "$URL" >/dev/null 2>&1 & + chromium-browser --start-fullscreen --no-first-run --disk-cache-size=10485760 "$URL" >/dev/null 2>&1 & elif command -v firefox >/dev/null 2>&1; then firefox "$URL" >/dev/null 2>&1 & else @@ -368,7 +476,7 @@ fi 赋予执行权限: ```bash -chmod +x /opt/emcp/scripts/start-emcp-browser.sh +chmod +x /userdata/emcp/scripts/start-emcp-browser.sh ``` 说明: @@ -376,6 +484,7 @@ chmod +x /opt/emcp/scripts/start-emcp-browser.sh - `sleep 8` 用于等待前后端服务启动完成 - 如果设备性能较低,可改为 `sleep 10` 或 `sleep 15` - 如需全屏显示,优先使用 `chromium-browser --start-fullscreen` +- 对低资源设备,浏览器缓存建议尽量缩小 ### 9.3 配置桌面自动启动项 @@ -397,7 +506,7 @@ mkdir -p ~/.config/autostart [Desktop Entry] Type=Application Name=EMCP Browser Autostart -Exec=/opt/emcp/scripts/start-emcp-browser.sh +Exec=/userdata/emcp/scripts/start-emcp-browser.sh X-GNOME-Autostart-enabled=true Terminal=false ``` @@ -418,13 +527,23 @@ http://127.0.0.1:5173/ 按以下顺序操作即可: -1. 拷贝项目到 `/opt/emcp` -2. 创建后端虚拟环境并安装依赖 -3. 前端执行 `npm install` 和 `npm run build` -4. 创建两个 `systemd` 服务 -5. 创建浏览器自动启动脚本 -6. 创建 `~/.config/autostart/emcp-browser.desktop` -7. 执行 `systemctl enable` 完成开机自启 +1. 拷贝项目到 `/userdata/emcp` +2. 如需兼容旧路径,建立 `/opt/emcp -> /userdata/emcp` 软链接 +3. 使用系统 `python3` 安装后端依赖 +4. 在开发机执行前端 `npm install` 和 `npm run build` +5. 将 `dist/` 上传到服务器 `/userdata/emcp/frontend/dist` +6. 创建 `/userdata/emcp/backend/.env`,将运行数据目录指向 `/userdata/emcp/data` +7. 创建两个 `systemd` 服务 +8. 创建浏览器自动启动脚本 +9. 创建 `~/.config/autostart/emcp-browser.desktop` +10. 执行 `systemctl enable` 完成开机自启 + +资源紧张时,优先保证以下三点: + +- 服务器只保留 `backend/` 和 `frontend/dist/` +- 不安装 `Node.js/npm`,不安装 `.[dev]` +- 运行数据放在 `/userdata/emcp/data` +- 后端直接使用系统 `python3.10.12`,不创建虚拟环境 ### 10.2 最常用检查命令 @@ -455,16 +574,18 @@ sudo systemctl restart emcp-frontend.service 重新打开浏览器主页: ```bash -/opt/emcp/scripts/start-emcp-browser.sh +/userdata/emcp/scripts/start-emcp-browser.sh ``` ### 10.3 修改主页地址 如果前端端口改为其他端口,例如 `8080`,需要同步修改以下两处: -- `/opt/emcp/scripts/start-emcp-browser.sh` 中的 `URL` +- `/userdata/emcp/scripts/start-emcp-browser.sh` 中的 `URL` - 前端静态服务端口或访问地址 +如果修改了前端源码,需要在开发机重新构建并重新上传新的 `dist/` 目录,服务器本机不需要执行 `npm install` + ### 10.4 修改登录后自动打开行为 如果不希望每次登录都自动打开浏览器,可以删除或重命名: @@ -516,7 +637,7 @@ admin123 - 是否进入了图形桌面 - `~/.config/autostart/emcp-browser.desktop` 是否存在 -- `/opt/emcp/scripts/start-emcp-browser.sh` 是否有执行权限 +- `/userdata/emcp/scripts/start-emcp-browser.sh` 是否有执行权限 - 浏览器是否已安装 ### 12.2 浏览器打开了,但页面访问失败 @@ -525,7 +646,7 @@ admin123 - 前端服务是否启动成功 - `5173` 端口是否被其他程序占用 -- `/opt/emcp/frontend/dist` 是否已经构建生成 +- `/userdata/emcp/frontend/dist` 是否已经构建生成 ### 12.3 页面能打开,但接口不通 @@ -545,11 +666,16 @@ admin123 ### 12.5 修改代码后页面没有变化 -请检查是否重新执行了前端构建: +请检查是否已经在开发机重新执行前端构建并重新上传新的 `dist/`: ```bash -cd /opt/emcp/frontend +cd /path/to/EMCP/frontend npm run build +``` + +上传完成后,在服务器执行: + +```bash sudo systemctl restart emcp-frontend.service ``` @@ -569,6 +695,7 @@ sudo systemctl restart emcp-backend.service - 后端和前端均使用 `systemd` 自启动 - 浏览器自动全屏打开主页 - 保留一个终端窗口或 SSH 方式用于维护 +- 程序与运行数据优先放在 `/userdata/emcp` 如需进一步增强生产部署能力,后续可继续补充: diff --git a/frontend/src/api/http.ts b/frontend/src/api/http.ts index 8f85d72..2133dad 100644 --- a/frontend/src/api/http.ts +++ b/frontend/src/api/http.ts @@ -1,7 +1,7 @@ import axios from 'axios' export const http = axios.create({ - baseURL: '/api', + baseURL: 'http://192.168.1.135:8000/api', timeout: 5000, headers: { 'Content-Type': 'application/json', diff --git a/frontend/src/websocket/client.ts b/frontend/src/websocket/client.ts index fd117ad..c0fbf6b 100644 --- a/frontend/src/websocket/client.ts +++ b/frontend/src/websocket/client.ts @@ -1,9 +1,7 @@ import type { AlarmEvent, RealtimeData } from '../types/platform' function buildWsUrl(path: string): string { - const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws' - const host = window.location.host - return `${protocol}://${host}${path}` + return `ws://localhost:8000${path}` } export function connectRealtime(onMessage: (data: RealtimeData) => void): WebSocket { diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 8375fff..84f5a71 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -9,11 +9,11 @@ export default defineConfig({ port: 5173, proxy: { '/api': { - target: 'http://192.168.1.59:8000', + target: 'http://127.0.0.1:8000', changeOrigin: true, }, '/ws': { - target: 'ws://192.168.1.59:8000', + target: 'ws://127.0.0.1:8000', changeOrigin: true, ws: true, }, diff --git a/scripts/emcp-backend.service b/scripts/emcp-backend.service new file mode 100644 index 0000000..7dc8ffb --- /dev/null +++ b/scripts/emcp-backend.service @@ -0,0 +1,18 @@ +[Unit] +Description=EMCP Backend Service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=linaro +WorkingDirectory=/userdata/emcp/backend +Environment=PYTHONDONTWRITEBYTECODE=1 +Environment=PYTHONUNBUFFERED=1 +EnvironmentFile=-/userdata/emcp/backend/.env +ExecStart=/usr/bin/python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --no-access-log +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/scripts/emcp-browser.desktop b/scripts/emcp-browser.desktop new file mode 100644 index 0000000..0817afd --- /dev/null +++ b/scripts/emcp-browser.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=EMCP Browser Autostart +Exec=/userdata/emcp/scripts/start-emcp-browser.sh +X-GNOME-Autostart-enabled=true +Terminal=false \ No newline at end of file diff --git a/scripts/emcp-frontend.service b/scripts/emcp-frontend.service new file mode 100644 index 0000000..f7d4cb6 --- /dev/null +++ b/scripts/emcp-frontend.service @@ -0,0 +1,15 @@ +[Unit] +Description=EMCP Frontend Static Service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=linaro +WorkingDirectory=/userdata/emcp/frontend +ExecStart=/usr/bin/python3 -m http.server 5173 --directory /userdata/emcp/frontend/dist --bind 0.0.0.0 +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/scripts/start-emcp-browser.sh b/scripts/start-emcp-browser.sh new file mode 100644 index 0000000..89623f1 --- /dev/null +++ b/scripts/start-emcp-browser.sh @@ -0,0 +1,76 @@ +#!/bin/bash +LOG_FILE="/userdata/emcp/data/browser-autostart.log" +mkdir -p /userdata/emcp/data +exec >>"$LOG_FILE" 2>&1 + +echo "==== $(date '+%F %T') start-emcp-browser ====" +echo "USER=$(whoami)" +echo "DISPLAY=${DISPLAY:-}" +echo "XDG_SESSION_TYPE=${XDG_SESSION_TYPE:-}" +echo "DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS:-}" + +URL="${EMCP_BROWSER_URL:-http://127.0.0.1:5173/}" +BACKEND_HEALTH_URL="${EMCP_BACKEND_HEALTH_URL:-http://127.0.0.1:8000/}" +FRONTEND_HEALTH_URL="${EMCP_FRONTEND_HEALTH_URL:-$URL}" +BROWSER_PROFILE_DIR="${EMCP_BROWSER_PROFILE_DIR:-/userdata/emcp/data/browser-profile}" +WAIT_TIMEOUT_SECONDS="${EMCP_BROWSER_WAIT_TIMEOUT:-60}" +WAIT_INTERVAL_SECONDS="${EMCP_BROWSER_WAIT_INTERVAL:-2}" + +mkdir -p "$BROWSER_PROFILE_DIR" + +# 设备仅使用 chromium。找不到 chromium 时直接退出,不再尝试其他浏览器。 +# 避免使用 --kiosk,降低嵌入式设备启动后卡死风险。 +# 独立 profile 可避免浏览器复用旧窗口,导致 fullscreen 参数失效。 +# 禁用系统密钥环集成,避免自动登录桌面后启动浏览器时弹出密码解锁提示。 +if ! command -v chromium >/dev/null 2>&1; then + exit 1 +fi + +wait_for_url() { + local target_url="$1" + local elapsed=0 + + while [ "$elapsed" -lt "$WAIT_TIMEOUT_SECONDS" ]; do + if python3 - "$target_url" <<'PY' +import sys +import urllib.request + +url = sys.argv[1] +try: + with urllib.request.urlopen(url, timeout=3) as response: + sys.exit(0 if response.status < 500 else 1) +except Exception: + sys.exit(1) +PY + then + return 0 + fi + + sleep "$WAIT_INTERVAL_SECONDS" + elapsed=$((elapsed + WAIT_INTERVAL_SECONDS)) + done + + return 1 +} + +if ! wait_for_url "$BACKEND_HEALTH_URL"; then + exit 1 +fi + +if ! wait_for_url "$FRONTEND_HEALTH_URL"; then + exit 1 +fi + +chromium \ + --new-window \ + --start-fullscreen \ + --start-maximized \ + --no-first-run \ + --no-default-browser-check \ + --password-store=basic \ + --use-mock-keychain \ + --disable-session-crashed-bubble \ + --disable-infobars \ + --disk-cache-size=10485760 \ + --user-data-dir="$BROWSER_PROFILE_DIR/chromium" \ + "$URL" >/dev/null 2>&1 &