From 43e4c71a8e395c13729693073f715686fbef7e3a Mon Sep 17 00:00:00 2001 From: Xuwznln <18435084+Xuwznln@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:31:24 +0800 Subject: [PATCH] Update to ROS2 Humble 0.7 --- .conda/base/recipe.yaml | 60 ++++++ .conda/full/recipe.yaml | 42 ++++ .conda/recipe.yaml | 91 --------- .github/workflows/ci-check.yml | 40 +--- .github/workflows/conda-pack-build.yml | 39 +++- .github/workflows/deploy-docs.yml | 31 ++- .github/workflows/unilabos-conda-build.yml | 50 ++++- MANIFEST.in | 1 + README.md | 38 +++- README_zh.md | 38 +++- docs/user_guide/best_practice.md | 75 +++++++- docs/user_guide/installation.md | 200 ++++++++++++++----- scripts/create_readme.py | 4 +- scripts/dev_install.py | 214 +++++++++++++++++++++ unilabos/app/main.py | 4 +- unilabos/app/utils.py | 34 +++- unilabos/utils/requirements.txt | 17 ++ 17 files changed, 759 insertions(+), 219 deletions(-) create mode 100644 .conda/base/recipe.yaml create mode 100644 .conda/full/recipe.yaml delete mode 100644 .conda/recipe.yaml create mode 100644 scripts/dev_install.py create mode 100644 unilabos/utils/requirements.txt diff --git a/.conda/base/recipe.yaml b/.conda/base/recipe.yaml new file mode 100644 index 0000000..40c29fc --- /dev/null +++ b/.conda/base/recipe.yaml @@ -0,0 +1,60 @@ +# unilabos: Production package (depends on unilabos-env + pip unilabos) +# For production deployment + +package: + name: unilabos + version: 0.10.16 + +source: + path: ../../unilabos + target_directory: unilabos + +build: + python: + entry_points: + - unilab = unilabos.app.main:main + script: + - set PIP_NO_INDEX= + - if: win + then: + - copy %RECIPE_DIR%\..\..\MANIFEST.in %SRC_DIR% + - copy %RECIPE_DIR%\..\..\setup.cfg %SRC_DIR% + - copy %RECIPE_DIR%\..\..\setup.py %SRC_DIR% + - pip install %SRC_DIR% + - if: unix + then: + - cp $RECIPE_DIR/../../MANIFEST.in $SRC_DIR + - cp $RECIPE_DIR/../../setup.cfg $SRC_DIR + - cp $RECIPE_DIR/../../setup.py $SRC_DIR + - uv pip install $SRC_DIR + +requirements: + host: + - python ==3.11.14 + - pip + - setuptools + - zstd + - zstandard + run: + - zstd + - zstandard + - networkx + - typing_extensions + - websockets + - opentrons_shared_data + - pint + - fastapi + - jinja2 + - requests + - uvicorn + - opcua + - pyserial + - pandas + - pymodbus + - matplotlib + - uni-lab::unilabos-env ==0.10.16 + +about: + repository: https://github.com/deepmodeling/Uni-Lab-OS + license: GPL-3.0-only + description: "UniLabOS - Production package with minimal ROS2 dependencies" diff --git a/.conda/full/recipe.yaml b/.conda/full/recipe.yaml new file mode 100644 index 0000000..137d9db --- /dev/null +++ b/.conda/full/recipe.yaml @@ -0,0 +1,42 @@ +# unilabos-full: Full package with all features +# Depends on unilabos + complete ROS2 desktop + dev tools + +package: + name: unilabos-full + version: 0.10.16 + +build: + noarch: generic + +requirements: + run: + # Base unilabos package (includes unilabos-env) + - uni-lab::unilabos ==0.10.16 + # Documentation tools + - sphinx + - sphinx_rtd_theme + # Web UI + - gradio + - flask + # Interactive development + - ipython + - jupyter + - jupyros + - colcon-common-extensions + # ROS2 full desktop (includes rviz2, gazebo, etc.) + - robostack-staging::ros-humble-desktop-full + # Navigation and motion control + - ros-humble-navigation2 + - ros-humble-ros2-control + - ros-humble-robot-state-publisher + - ros-humble-joint-state-publisher + # MoveIt motion planning + - ros-humble-moveit + - ros-humble-moveit-servo + # Simulation + - ros-humble-simulation + +about: + repository: https://github.com/deepmodeling/Uni-Lab-OS + license: GPL-3.0-only + description: "UniLabOS Full - Complete package with ROS2 Desktop, MoveIt, Navigation2, Gazebo, Jupyter" diff --git a/.conda/recipe.yaml b/.conda/recipe.yaml deleted file mode 100644 index 2b041c8..0000000 --- a/.conda/recipe.yaml +++ /dev/null @@ -1,91 +0,0 @@ -package: - name: unilabos - version: 0.10.15 - -source: - path: ../unilabos - target_directory: unilabos - -build: - python: - entry_points: - - unilab = unilabos.app.main:main - script: - - set PIP_NO_INDEX= - - if: win - then: - - copy %RECIPE_DIR%\..\MANIFEST.in %SRC_DIR% - - copy %RECIPE_DIR%\..\setup.cfg %SRC_DIR% - - copy %RECIPE_DIR%\..\setup.py %SRC_DIR% - - call %PYTHON% -m pip install %SRC_DIR% - - if: unix - then: - - cp $RECIPE_DIR/../MANIFEST.in $SRC_DIR - - cp $RECIPE_DIR/../setup.cfg $SRC_DIR - - cp $RECIPE_DIR/../setup.py $SRC_DIR - - $PYTHON -m pip install $SRC_DIR - -requirements: - host: - - python ==3.11.11 - - pip - - setuptools - - zstd - - zstandard - run: - - conda-forge::python ==3.11.11 - - compilers - - cmake - - zstd - - zstandard - - ninja - - if: unix - then: - - make - - sphinx - - sphinx_rtd_theme - - numpy - - scipy - - pandas - - networkx - - matplotlib - - pint - - pyserial - - pyusb - - pylibftdi - - pymodbus - - python-can - - pyvisa - - opencv - - pydantic - - fastapi - - uvicorn - - gradio - - flask - - websockets - - ipython - - jupyter - - jupyros - - colcon-common-extensions - - robostack-staging::ros-humble-desktop-full - - robostack-staging::ros-humble-control-msgs - - robostack-staging::ros-humble-sensor-msgs - - robostack-staging::ros-humble-trajectory-msgs - - ros-humble-navigation2 - - ros-humble-ros2-control - - ros-humble-robot-state-publisher - - ros-humble-joint-state-publisher - - ros-humble-rosbridge-server - - ros-humble-cv-bridge - - ros-humble-tf2 - - ros-humble-moveit - - ros-humble-moveit-servo - - ros-humble-simulation - - ros-humble-tf-transformations - - transforms3d - - uni-lab::ros-humble-unilabos-msgs - -about: - repository: https://github.com/deepmodeling/Uni-Lab-OS - license: GPL-3.0-only - description: "Uni-Lab-OS" diff --git a/.github/workflows/ci-check.yml b/.github/workflows/ci-check.yml index 6341c27..2151a66 100644 --- a/.github/workflows/ci-check.yml +++ b/.github/workflows/ci-check.yml @@ -31,12 +31,14 @@ jobs: auto-update-conda: false show-channel-urls: true - - name: Install ROS dependencies and unilabos-msgs + - name: Install ROS dependencies, uv and unilabos-msgs run: | # Install all packages together for proper dependency resolution # Use mamba for faster and more reliable solving mamba install -n check-env \ python=3.11.14 \ + conda-forge::uv \ + conda-forge::opencv \ robostack-staging::ros-humble-ros-core \ robostack-staging::ros-humble-action-msgs \ robostack-staging::ros-humble-std-msgs \ @@ -57,36 +59,14 @@ jobs: run: | # Activate the environment conda activate check-env - - # Core dependencies for devices - pip install uv - uv pip install networkx \ - typing_extensions \ - websockets \ - msgcenterpy \ - opentrons_shared_data \ - pint \ - fastapi \ - jinja2 \ - requests \ - uvicorn \ - git+https://github.com/Xuwznln/pylabrobot.git \ - opencv-python \ - pyautogui \ - opcua \ - pyserial \ - pandas \ - crcmod-plus \ - pymodbus \ - pywinauto_recorder \ - matplotlib \ - - - # PyLabRobot (custom fork) - pip install - + # Install pip dependencies from requirements.txt (uv already installed via conda) + uv pip install -r unilabos/utils/requirements.txt + # Install special packages (git-based) + uv pip install pywinauto git+https://github.com/Xuwznln/pylabrobot.git + # Remove conflicting package + uv pip uninstall enum34 || true # Install unilabos in editable mode - pip install -e . + uv pip install -e . - name: Run check mode (complete_registry) run: | diff --git a/.github/workflows/conda-pack-build.yml b/.github/workflows/conda-pack-build.yml index 3a379fa..6476be9 100644 --- a/.github/workflows/conda-pack-build.yml +++ b/.github/workflows/conda-pack-build.yml @@ -13,6 +13,11 @@ on: required: false default: 'win-64' type: string + build_full: + description: '是否构建完整版 unilabos-full (默认构建轻量版 unilabos)' + required: false + default: false + type: boolean jobs: build-conda-pack: @@ -69,7 +74,7 @@ jobs: with: miniforge-version: latest use-mamba: true - python-version: '3.11.11' + python-version: '3.11.14' channels: conda-forge,robostack-staging,uni-lab,defaults channel-priority: flexible activate-environment: unilab @@ -81,7 +86,14 @@ jobs: run: | echo Installing unilabos and dependencies to unilab environment... echo Using mamba for faster and more reliable dependency resolution... - mamba install -n unilab uni-lab::unilabos conda-pack -c uni-lab -c robostack-staging -c conda-forge -y + echo Build full: ${{ github.event.inputs.build_full }} + if "${{ github.event.inputs.build_full }}"=="true" ( + echo Installing unilabos-full ^(complete package^)... + mamba install -n unilab uni-lab::unilabos-full conda-pack -c uni-lab -c robostack-staging -c conda-forge -y + ) else ( + echo Installing unilabos ^(minimal package^)... + mamba install -n unilab uni-lab::unilabos conda-pack -c uni-lab -c robostack-staging -c conda-forge -y + ) - name: Install conda-pack, unilabos and dependencies (Unix) if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64' @@ -89,7 +101,14 @@ jobs: run: | echo "Installing unilabos and dependencies to unilab environment..." echo "Using mamba for faster and more reliable dependency resolution..." - mamba install -n unilab uni-lab::unilabos conda-pack -c uni-lab -c robostack-staging -c conda-forge -y + echo "Build full: ${{ github.event.inputs.build_full }}" + if [[ "${{ github.event.inputs.build_full }}" == "true" ]]; then + echo "Installing unilabos-full (complete package)..." + mamba install -n unilab uni-lab::unilabos-full conda-pack -c uni-lab -c robostack-staging -c conda-forge -y + else + echo "Installing unilabos (minimal package)..." + mamba install -n unilab uni-lab::unilabos conda-pack -c uni-lab -c robostack-staging -c conda-forge -y + fi - name: Get latest ros-humble-unilabos-msgs version (Windows) if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64' @@ -308,7 +327,12 @@ jobs: echo ========================================== echo Platform: ${{ matrix.platform }} echo Branch: ${{ github.event.inputs.branch }} - echo Python version: 3.11.11 + echo Python version: 3.11.14 + if "${{ github.event.inputs.build_full }}"=="true" ( + echo Package: unilabos-full ^(complete^) + ) else ( + echo Package: unilabos ^(minimal^) + ) echo. echo Distribution package contents: dir dist-package @@ -328,7 +352,12 @@ jobs: echo "==========================================" echo "Platform: ${{ matrix.platform }}" echo "Branch: ${{ github.event.inputs.branch }}" - echo "Python version: 3.11.11" + echo "Python version: 3.11.14" + if [[ "${{ github.event.inputs.build_full }}" == "true" ]]; then + echo "Package: unilabos-full (complete)" + else + echo "Package: unilabos (minimal)" + fi echo "" echo "Distribution package contents:" ls -lh dist-package/ diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 66aef8d..cf2d338 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,10 +1,12 @@ name: Deploy Docs on: - push: - branches: [main] - pull_request: + # 在 CI Check 成功后自动触发(仅 main 分支) + workflow_run: + workflows: ["CI Check"] + types: [completed] branches: [main] + # 手动触发 workflow_dispatch: inputs: branch: @@ -33,12 +35,19 @@ concurrency: jobs: # Build documentation build: + # 只在以下情况运行: + # 1. workflow_run 触发且 CI Check 成功 + # 2. 手动触发 + if: | + github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: - ref: ${{ github.event.inputs.branch || github.ref }} + # workflow_run 时使用触发工作流的分支,手动触发时使用输入的分支 + ref: ${{ github.event.workflow_run.head_branch || github.event.inputs.branch || github.ref }} fetch-depth: 0 - name: Setup Miniforge (with mamba) @@ -46,7 +55,7 @@ jobs: with: miniforge-version: latest use-mamba: true - python-version: '3.11.11' + python-version: '3.11.14' channels: conda-forge,robostack-staging,uni-lab,defaults channel-priority: flexible activate-environment: unilab @@ -76,7 +85,9 @@ jobs: - name: Setup Pages id: pages uses: actions/configure-pages@v4 - if: github.ref == 'refs/heads/main' || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_pages == 'true') + if: | + github.event.workflow_run.head_branch == 'main' || + (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_pages == 'true') - name: Build Sphinx documentation run: | @@ -95,13 +106,17 @@ jobs: - name: Upload build artifacts uses: actions/upload-pages-artifact@v3 - if: github.ref == 'refs/heads/main' || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_pages == 'true') + if: | + github.event.workflow_run.head_branch == 'main' || + (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_pages == 'true') with: path: docs/_build/html # Deploy to GitHub Pages deploy: - if: github.ref == 'refs/heads/main' || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_pages == 'true') + if: | + github.event.workflow_run.head_branch == 'main' || + (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_pages == 'true') environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} diff --git a/.github/workflows/unilabos-conda-build.yml b/.github/workflows/unilabos-conda-build.yml index 214f9bf..2ace422 100644 --- a/.github/workflows/unilabos-conda-build.yml +++ b/.github/workflows/unilabos-conda-build.yml @@ -1,17 +1,26 @@ name: UniLabOS Conda Build on: + # 在 CI Check 成功后自动触发 + workflow_run: + workflows: ["CI Check"] + types: [completed] + branches: [main, dev] + # 标签推送时直接触发(发布版本) push: - branches: [main, dev] tags: ['v*'] - pull_request: - branches: [main, dev] + # 手动触发 workflow_dispatch: inputs: platforms: description: '选择构建平台 (逗号分隔): linux-64, osx-64, osx-arm64, win-64' required: false default: 'linux-64' + build_full: + description: '是否构建 unilabos-full 完整包 (默认只构建 unilabos 基础包)' + required: false + default: false + type: boolean upload_to_anaconda: description: '是否上传到Anaconda.org' required: false @@ -20,6 +29,14 @@ on: jobs: build: + # 只在以下情况运行: + # 1. workflow_run 触发且 CI Check 成功 + # 2. 标签推送(发布版本) + # 3. 手动触发 + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'push' || + (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') strategy: fail-fast: false matrix: @@ -81,12 +98,33 @@ jobs: conda list | grep -E "(rattler-build|anaconda-client)" echo "Platform: ${{ matrix.platform }}" echo "OS: ${{ matrix.os }}" - echo "Building UniLabOS package" + echo "Build full package: ${{ github.event.inputs.build_full || 'false' }}" + echo "Building packages:" + echo " - unilabos-env (environment dependencies)" + echo " - unilabos (with pip package)" + if [[ "${{ github.event.inputs.build_full }}" == "true" ]]; then + echo " - unilabos-full (complete package)" + fi - - name: Build conda package + - name: Build unilabos-env (conda environment only, noarch) if: steps.should_build.outputs.should_build == 'true' run: | - rattler-build build -r .conda/recipe.yaml -c uni-lab -c robostack-staging -c conda-forge + echo "Building unilabos-env (conda environment dependencies)..." + rattler-build build -r .conda/env/recipe.yaml -c uni-lab -c robostack-staging -c conda-forge + + - name: Build unilabos (with pip package) + if: steps.should_build.outputs.should_build == 'true' + run: | + echo "Building unilabos package..." + rattler-build build -r .conda/base/recipe.yaml -c uni-lab -c robostack-staging -c conda-forge --channel ./output + + - name: Build unilabos-full - Only when explicitly requested + if: | + steps.should_build.outputs.should_build == 'true' && + github.event.inputs.build_full == 'true' + run: | + echo "Building unilabos-full package on ${{ matrix.platform }}..." + rattler-build build -r .conda/full/recipe.yaml -c uni-lab -c robostack-staging -c conda-forge --channel ./output - name: List built packages if: steps.should_build.outputs.should_build == 'true' diff --git a/MANIFEST.in b/MANIFEST.in index d81945e..156ca52 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ recursive-include unilabos/test * +recursive-include unilabos/utils * recursive-include unilabos/registry *.yaml recursive-include unilabos/app/web/static * recursive-include unilabos/app/web/templates * diff --git a/README.md b/README.md index f10cc0f..fa0d9dd 100644 --- a/README.md +++ b/README.md @@ -31,26 +31,46 @@ Detailed documentation can be found at: ## Quick Start -1. Setup Conda Environment +### 1. Setup Conda Environment -Uni-Lab-OS recommends using `mamba` for environment management: +Uni-Lab-OS recommends using `mamba` for environment management. Choose the package that fits your needs: + +| Package | Use Case | Contents | +|---------|----------|----------| +| `unilabos` | **Recommended for most users** | Complete package, ready to use | +| `unilabos-env` | Developers (editable install) | Environment only, install unilabos via pip | +| `unilabos-full` | Simulation/Visualization | unilabos + ROS2 Desktop + Gazebo + MoveIt | ```bash # Create new environment -mamba create -n unilab python=3.11.11 +mamba create -n unilab python=3.11.14 mamba activate unilab -mamba install -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge + +# Option A: Standard installation (recommended for most users) +mamba install uni-lab::unilabos -c robostack-staging -c conda-forge + +# Option B: For developers (editable mode development) +mamba install uni-lab::unilabos-env -c robostack-staging -c conda-forge +# Then install unilabos and dependencies: +git clone https://github.com/deepmodeling/Uni-Lab-OS.git && cd Uni-Lab-OS +pip install -e . +uv pip install -r unilabos/utils/requirements.txt + +# Option C: Full installation (simulation/visualization) +mamba install uni-lab::unilabos-full -c robostack-staging -c conda-forge ``` -2. Install Dev Uni-Lab-OS +**When to use which?** +- **unilabos**: Standard installation for production deployment and general usage (recommended) +- **unilabos-env**: For developers who need `pip install -e .` editable mode, modify source code +- **unilabos-full**: For simulation (Gazebo), visualization (rviz2), and Jupyter notebooks + +### 2. Clone Repository (Optional, for developers) ```bash -# Clone the repository +# Clone the repository (only needed for development or examples) git clone https://github.com/deepmodeling/Uni-Lab-OS.git cd Uni-Lab-OS - -# Install Uni-Lab-OS -pip install . ``` 3. Start Uni-Lab System diff --git a/README_zh.md b/README_zh.md index c4dba7d..20b8f53 100644 --- a/README_zh.md +++ b/README_zh.md @@ -31,26 +31,46 @@ Uni-Lab-OS 是一个用于实验室自动化的综合平台,旨在连接和控 ## 快速开始 -1. 配置 Conda 环境 +### 1. 配置 Conda 环境 -Uni-Lab-OS 建议使用 `mamba` 管理环境。根据您的操作系统选择适当的环境文件: +Uni-Lab-OS 建议使用 `mamba` 管理环境。根据您的需求选择合适的安装包: + +| 安装包 | 适用场景 | 包含内容 | +|--------|----------|----------| +| `unilabos` | **推荐大多数用户** | 完整安装包,开箱即用 | +| `unilabos-env` | 开发者(可编辑安装) | 仅环境依赖,通过 pip 安装 unilabos | +| `unilabos-full` | 仿真/可视化 | unilabos + ROS2 桌面版 + Gazebo + MoveIt | ```bash # 创建新环境 -mamba create -n unilab python=3.11.11 +mamba create -n unilab python=3.11.14 mamba activate unilab -mamba install -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge + +# 方案 A:标准安装(推荐大多数用户) +mamba install uni-lab::unilabos -c robostack-staging -c conda-forge + +# 方案 B:开发者环境(可编辑模式开发) +mamba install uni-lab::unilabos-env -c robostack-staging -c conda-forge +# 然后安装 unilabos 和依赖: +git clone https://github.com/deepmodeling/Uni-Lab-OS.git && cd Uni-Lab-OS +pip install -e . +uv pip install -r unilabos/utils/requirements.txt + +# 方案 C:完整安装(仿真/可视化) +mamba install uni-lab::unilabos-full -c robostack-staging -c conda-forge ``` -2. 安装开发版 Uni-Lab-OS: +**如何选择?** +- **unilabos**:标准安装,适用于生产部署和日常使用(推荐) +- **unilabos-env**:开发者使用,支持 `pip install -e .` 可编辑模式,可修改源代码 +- **unilabos-full**:需要仿真(Gazebo)、可视化(rviz2)或 Jupyter Notebook + +### 2. 克隆仓库(可选,供开发者使用) ```bash -# 克隆仓库 +# 克隆仓库(仅开发或查看示例时需要) git clone https://github.com/deepmodeling/Uni-Lab-OS.git cd Uni-Lab-OS - -# 安装 Uni-Lab-OS -pip install . ``` 3. 启动 Uni-Lab 系统 diff --git a/docs/user_guide/best_practice.md b/docs/user_guide/best_practice.md index e1ffc24..0fa4d1e 100644 --- a/docs/user_guide/best_practice.md +++ b/docs/user_guide/best_practice.md @@ -31,6 +31,14 @@ 详细的安装步骤请参考 [安装指南](installation.md)。 +**选择合适的安装包:** + +| 安装包 | 适用场景 | 包含组件 | +|--------|----------|----------| +| `unilabos` | **推荐大多数用户**,生产部署 | 完整安装包,开箱即用 | +| `unilabos-env` | 开发者(可编辑安装) | 仅环境依赖,通过 pip 安装 unilabos | +| `unilabos-full` | 仿真/可视化 | unilabos + 完整 ROS2 桌面版 + Gazebo + MoveIt | + **关键步骤:** ```bash @@ -38,15 +46,30 @@ # 下载 Miniforge: https://github.com/conda-forge/miniforge/releases # 2. 创建 Conda 环境 -mamba create -n unilab python=3.11.11 +mamba create -n unilab python=3.11.14 # 3. 激活环境 mamba activate unilab -# 4. 安装 Uni-Lab-OS +# 4. 安装 Uni-Lab-OS(选择其一) + +# 方案 A:标准安装(推荐大多数用户) mamba install uni-lab::unilabos -c robostack-staging -c conda-forge + +# 方案 B:开发者环境(可编辑模式开发) +mamba install uni-lab::unilabos-env -c robostack-staging -c conda-forge +pip install -e /path/to/Uni-Lab-OS # 可编辑安装 +uv pip install -r unilabos/utils/requirements.txt # 安装 pip 依赖 + +# 方案 C:完整版(仿真/可视化) +mamba install uni-lab::unilabos-full -c robostack-staging -c conda-forge ``` +**选择建议:** +- **日常使用/生产部署**:使用 `unilabos`(推荐),完整功能,开箱即用 +- **开发者**:使用 `unilabos-env` + `pip install -e .` + `uv pip install -r unilabos/utils/requirements.txt`,代码修改立即生效 +- **仿真/可视化**:使用 `unilabos-full`,含 Gazebo、rviz2、MoveIt + #### 1.2 验证安装 ```bash @@ -768,7 +791,43 @@ Waiting for host service... 详细的设备驱动编写指南请参考 [添加设备驱动](../developer_guide/add_device.md)。 -#### 9.1 为什么需要自定义设备? +#### 9.1 开发环境准备 + +**推荐使用 `unilabos-env` + `pip install -e .` + `uv pip install`** 进行设备开发: + +```bash +# 1. 创建环境并安装 unilabos-env(ROS2 + conda 依赖 + uv) +mamba create -n unilab python=3.11.14 +conda activate unilab +mamba install uni-lab::unilabos-env -c robostack-staging -c conda-forge + +# 2. 克隆代码 +git clone https://github.com/deepmodeling/Uni-Lab-OS.git +cd Uni-Lab-OS + +# 3. 以可编辑模式安装(推荐使用脚本,自动检测中文环境) +python scripts/dev_install.py + +# 或手动安装: +pip install -e . +uv pip install -r unilabos/utils/requirements.txt +``` + +**为什么使用这种方式?** +- `unilabos-env` 提供 ROS2 核心组件和 uv(通过 conda 安装,避免编译) +- `unilabos/utils/requirements.txt` 包含所有运行时需要的 pip 依赖 +- `dev_install.py` 自动检测中文环境,中文系统自动使用清华镜像 +- 使用 `uv` 替代 `pip`,安装速度更快 +- 可编辑模式:代码修改**立即生效**,无需重新安装 + +**如果安装失败或速度太慢**,可以手动执行(使用清华镜像): + +```bash +pip install -e . -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +uv pip install -r unilabos/utils/requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +``` + +#### 9.2 为什么需要自定义设备? Uni-Lab-OS 内置了常见设备,但您的实验室可能有特殊设备需要集成: @@ -777,7 +836,7 @@ Uni-Lab-OS 内置了常见设备,但您的实验室可能有特殊设备需要 - 特殊的实验流程 - 第三方设备集成 -#### 9.2 创建 Python 包 +#### 9.3 创建 Python 包 为了方便开发和管理,建议为您的实验室创建独立的 Python 包。 @@ -814,7 +873,7 @@ touch my_lab_devices/my_lab_devices/__init__.py touch my_lab_devices/my_lab_devices/devices/__init__.py ``` -#### 9.3 创建 setup.py +#### 9.4 创建 setup.py ```python # my_lab_devices/setup.py @@ -845,7 +904,7 @@ setup( ) ``` -#### 9.4 开发安装 +#### 9.5 开发安装 使用 `-e` 参数进行可编辑安装,这样代码修改后立即生效: @@ -860,7 +919,7 @@ pip install -e . -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple - 方便调试和测试 - 支持版本控制(git) -#### 9.5 编写设备驱动 +#### 9.6 编写设备驱动 创建设备驱动文件: @@ -1001,7 +1060,7 @@ class MyPump: - **返回 Dict**:所有动作方法返回字典类型 - **文档字符串**:详细说明参数和功能 -#### 9.6 测试设备驱动 +#### 9.7 测试设备驱动 创建简单的测试脚本: diff --git a/docs/user_guide/installation.md b/docs/user_guide/installation.md index 3f94f2f..acf8fb6 100644 --- a/docs/user_guide/installation.md +++ b/docs/user_guide/installation.md @@ -13,15 +13,26 @@ - 开发者需要 Git 和基本的 Python 开发知识 - 自定义 msgs 需要 GitHub 账号 +## 安装包选择 + +Uni-Lab-OS 提供三个安装包版本,根据您的需求选择: + +| 安装包 | 适用场景 | 包含组件 | 磁盘占用 | +|--------|----------|----------|----------| +| **unilabos** | **推荐大多数用户**,生产部署 | 完整安装包,开箱即用 | ~2-3 GB | +| **unilabos-env** | 开发者环境(可编辑安装) | 仅环境依赖,通过 pip 安装 unilabos | ~2 GB | +| **unilabos-full** | 仿真可视化、完整功能体验 | unilabos + 完整 ROS2 桌面版 + Gazebo + MoveIt | ~8-10 GB | + ## 安装方式选择 根据您的使用场景,选择合适的安装方式: -| 安装方式 | 适用人群 | 特点 | 安装时间 | -| ---------------------- | -------------------- | ------------------------------ | ---------------------------- | -| **方式一:一键安装** | 实验室用户、快速体验 | 预打包环境,离线可用,无需配置 | 5-10 分钟 (网络良好的情况下) | -| **方式二:手动安装** | 标准用户、生产环境 | 灵活配置,版本可控 | 10-20 分钟 | -| **方式三:开发者安装** | 开发者、需要修改源码 | 可编辑模式,支持自定义 msgs | 20-30 分钟 | +| 安装方式 | 适用人群 | 推荐安装包 | 特点 | 安装时间 | +| ---------------------- | -------------------- | ----------------- | ------------------------------ | ---------------------------- | +| **方式一:一键安装** | 快速体验、演示 | 预打包环境 | 离线可用,无需配置 | 5-10 分钟 (网络良好的情况下) | +| **方式二:手动安装** | **大多数用户** | `unilabos` | 完整功能,开箱即用 | 10-20 分钟 | +| **方式三:开发者安装** | 开发者、需要修改源码 | `unilabos-env` | 可编辑模式,支持自定义开发 | 20-30 分钟 | +| **仿真/可视化** | 仿真测试、可视化调试 | `unilabos-full` | 含 Gazebo、rviz2、MoveIt | 30-60 分钟 | --- @@ -144,17 +155,38 @@ bash Miniforge3-$(uname)-$(uname -m).sh 使用以下命令创建 Uni-Lab 专用环境: ```bash -mamba create -n unilab python=3.11.11 # 目前ros2组件依赖版本大多为3.11.11 +mamba create -n unilab python=3.11.14 # 目前ros2组件依赖版本大多为3.11.14 mamba activate unilab -mamba install -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge + +# 选择安装包(三选一): + +# 方案 A:标准安装(推荐大多数用户) +mamba install uni-lab::unilabos -c robostack-staging -c conda-forge + +# 方案 B:开发者环境(可编辑模式开发) +mamba install uni-lab::unilabos-env -c robostack-staging -c conda-forge +# 然后安装 unilabos 和 pip 依赖: +git clone https://github.com/deepmodeling/Uni-Lab-OS.git && cd Uni-Lab-OS +pip install -e . +uv pip install -r unilabos/utils/requirements.txt + +# 方案 C:完整版(含仿真和可视化工具) +mamba install uni-lab::unilabos-full -c robostack-staging -c conda-forge ``` **参数说明**: - `-n unilab`: 创建名为 "unilab" 的环境 -- `uni-lab::unilabos`: 从 uni-lab channel 安装 unilabos 包 +- `uni-lab::unilabos`: 安装 unilabos 完整包,开箱即用(推荐) +- `uni-lab::unilabos-env`: 仅安装环境依赖,适合开发者使用 `pip install -e .` +- `uni-lab::unilabos-full`: 安装完整包(含 ROS2 Desktop、Gazebo、MoveIt 等) - `-c robostack-staging -c conda-forge`: 添加额外的软件源 +**包选择建议**: +- **日常使用/生产部署**:安装 `unilabos`(推荐,完整功能,开箱即用) +- **开发者**:安装 `unilabos-env`,然后使用 `uv pip install -r unilabos/utils/requirements.txt` 安装依赖,再 `pip install -e .` 进行可编辑安装 +- **仿真/可视化**:安装 `unilabos-full`(Gazebo、rviz2、MoveIt) + **如果遇到网络问题**,可以使用清华镜像源加速下载: ```bash @@ -163,8 +195,14 @@ mamba config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/m mamba config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ mamba config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ -# 然后重新执行安装命令 +# 然后重新执行安装命令(推荐标准安装) mamba create -n unilab uni-lab::unilabos -c robostack-staging + +# 或完整版(仿真/可视化) +mamba create -n unilab uni-lab::unilabos-full -c robostack-staging + +# pip 安装时使用清华镜像(开发者安装时使用) +uv pip install -r unilabos/utils/requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple ``` ### 第三步:激活环境 @@ -203,58 +241,87 @@ cd Uni-Lab-OS cd Uni-Lab-OS ``` -### 第二步:安装基础环境 +### 第二步:安装开发环境(unilabos-env) -**推荐方式**:先通过**方式一(一键安装)**或**方式二(手动安装)**完成基础环境的安装,这将包含所有必需的依赖项(ROS2、msgs 等)。 - -#### 选项 A:通过一键安装(推荐) - -参考上文"方式一:一键安装",完成基础环境的安装后,激活环境: +**重要**:开发者请使用 `unilabos-env` 包,它专为开发者设计: +- 包含 ROS2 核心组件和消息包(ros-humble-ros-core、std-msgs、geometry-msgs 等) +- 包含 transforms3d、cv-bridge、tf2 等 conda 依赖 +- 包含 `uv` 工具,用于快速安装 pip 依赖 +- **不包含** pip 依赖和 unilabos 包(由 `pip install -e .` 和 `uv pip install` 安装) ```bash +# 创建并激活环境 +mamba create -n unilab python=3.11.14 conda activate unilab + +# 安装开发者环境包(ROS2 + conda 依赖 + uv) +mamba install uni-lab::unilabos-env -c robostack-staging -c conda-forge ``` -#### 选项 B:通过手动安装 +### 第三步:安装 pip 依赖和可编辑模式安装 -参考上文"方式二:手动安装",创建并安装环境: - -```bash -mamba create -n unilab python=3.11.11 -conda activate unilab -mamba install -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge -``` - -**说明**:这会安装包括 Python 3.11.11、ROS2 Humble、ros-humble-unilabos-msgs 和所有必需依赖 - -### 第三步:切换到开发版本 - -现在你已经有了一个完整可用的 Uni-Lab 环境,接下来将 unilabos 包切换为开发版本: +克隆代码并安装依赖: ```bash # 确保环境已激活 conda activate unilab -# 卸载 pip 安装的 unilabos(保留所有 conda 依赖) -pip uninstall unilabos -y - -# 克隆 dev 分支(如果还未克隆) -cd /path/to/your/workspace -git clone -b dev https://github.com/deepmodeling/Uni-Lab-OS.git -# 或者如果已经克隆,切换到 dev 分支 +# 克隆仓库(如果还未克隆) +git clone https://github.com/deepmodeling/Uni-Lab-OS.git cd Uni-Lab-OS + +# 切换到 dev 分支(可选) git checkout dev git pull - -# 以可编辑模式安装开发版 unilabos -pip install -e . -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple ``` -**参数说明**: +**推荐:使用安装脚本**(自动检测中文环境,使用 uv 加速): -- `-e`: editable mode(可编辑模式),代码修改立即生效,无需重新安装 -- `-i`: 使用清华镜像源加速下载 -- `pip uninstall unilabos`: 只卸载 pip 安装的 unilabos 包,不影响 conda 安装的其他依赖(如 ROS2、msgs 等) +```bash +# 自动检测中文环境,如果是中文系统则使用清华镜像 +python scripts/dev_install.py + +# 或者手动指定: +python scripts/dev_install.py --china # 强制使用清华镜像 +python scripts/dev_install.py --no-mirror # 强制使用 PyPI +python scripts/dev_install.py --skip-deps # 跳过 pip 依赖安装 +python scripts/dev_install.py --use-pip # 使用 pip 而非 uv +``` + +**手动安装**(如果脚本安装失败或速度太慢): + +```bash +# 1. 安装 unilabos(可编辑模式) +pip install -e . + +# 2. 使用 uv 安装 pip 依赖(推荐,速度更快) +uv pip install -r unilabos/utils/requirements.txt + +# 国内用户使用清华镜像: +pip install -e . -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +uv pip install -r unilabos/utils/requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +``` + +**注意**: +- `uv` 已包含在 `unilabos-env` 中,无需单独安装 +- `unilabos/utils/requirements.txt` 包含运行 unilabos 所需的所有 pip 依赖 +- 部分特殊包(如 pylabrobot)会在运行时由 unilabos 自动检测并安装 + +**为什么使用可编辑模式?** + +- `-e` (editable mode):代码修改**立即生效**,无需重新安装 +- 适合开发调试:修改代码后直接运行测试 +- 与 `unilabos-env` 配合:环境依赖由 conda 管理,unilabos 代码由 pip 管理 + +**验证安装**: + +```bash +# 检查 unilabos 版本 +python -c "import unilabos; print(unilabos.__version__)" + +# 检查安装位置(应该指向你的代码目录) +pip show unilabos | grep Location +``` ### 第四步:安装或自定义 ros-humble-unilabos-msgs(可选) @@ -464,7 +531,45 @@ cd $CONDA_PREFIX/envs/unilab ### 问题 8: 环境很大,有办法减小吗? -**解决方案**: 预打包的环境包含所有依赖,通常较大(压缩后 2-5GB)。这是为了确保离线安装和完整功能。如果空间有限,考虑使用方式二手动安装,只安装需要的组件。 +**解决方案**: + +1. **使用 `unilabos` 标准版**(推荐大多数用户): + ```bash + mamba install uni-lab::unilabos -c robostack-staging -c conda-forge + ``` + 标准版包含完整功能,环境大小约 2-3GB(相比完整版的 8-10GB)。 + +2. **使用 `unilabos-env` 开发者版**(最小化): + ```bash + mamba install uni-lab::unilabos-env -c robostack-staging -c conda-forge + # 然后手动安装依赖 + pip install -e . + uv pip install -r unilabos/utils/requirements.txt + ``` + 开发者版只包含环境依赖,体积最小约 2GB。 + +3. **按需安装额外组件**: + 如果后续需要特定功能,可以单独安装: + ```bash + # 需要 Jupyter + mamba install jupyter jupyros + + # 需要可视化 + mamba install matplotlib opencv + + # 需要仿真(注意:这会安装大量依赖) + mamba install ros-humble-gazebo-ros + ``` + +4. **预打包环境问题**: + 预打包环境(方式一)包含所有依赖,通常较大(压缩后 2-5GB)。这是为了确保离线安装和完整功能。 + +**包选择建议**: +| 需求 | 推荐包 | 预估大小 | +|------|--------|----------| +| 日常使用/生产部署 | `unilabos` | ~2-3 GB | +| 开发调试(可编辑模式) | `unilabos-env` | ~2 GB | +| 仿真/可视化 | `unilabos-full` | ~8-10 GB | ### 问题 9: 如何更新到最新版本? @@ -511,6 +616,7 @@ mamba update ros-humble-unilabos-msgs -c uni-lab -c robostack-staging -c conda-f **提示**: -- 生产环境推荐使用方式二(手动安装)的稳定版本 -- 开发和测试推荐使用方式三(开发者安装) -- 快速体验和演示推荐使用方式一(一键安装) +- **大多数用户**推荐使用方式二(手动安装)的 `unilabos` 标准版 +- **开发者**推荐使用方式三(开发者安装),安装 `unilabos-env` 后使用 `uv pip install -r unilabos/utils/requirements.txt` 安装依赖 +- **仿真/可视化**推荐安装 `unilabos-full` 完整版 +- **快速体验和演示**推荐使用方式一(一键安装) diff --git a/scripts/create_readme.py b/scripts/create_readme.py index c4f3933..e87c1d8 100644 --- a/scripts/create_readme.py +++ b/scripts/create_readme.py @@ -85,7 +85,7 @@ Verification: ------------- The verify_installation.py script will check: - - Python version (3.11.11) + - Python version (3.11.14) - ROS2 rclpy installation - UniLabOS installation and dependencies @@ -104,7 +104,7 @@ Build Information: Branch: {branch} Platform: {platform} - Python: 3.11.11 + Python: 3.11.14 Date: {build_date} Troubleshooting: diff --git a/scripts/dev_install.py b/scripts/dev_install.py new file mode 100644 index 0000000..002db24 --- /dev/null +++ b/scripts/dev_install.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +""" +Development installation script for UniLabOS. +Auto-detects Chinese locale and uses appropriate mirror. + +Usage: + python scripts/dev_install.py + python scripts/dev_install.py --no-mirror # Force no mirror + python scripts/dev_install.py --china # Force China mirror + python scripts/dev_install.py --skip-deps # Skip pip dependencies installation + +Flow: + 1. pip install -e . (install unilabos in editable mode) + 2. Detect Chinese locale + 3. Use uv to install pip dependencies from requirements.txt + 4. Special packages (like pylabrobot) are handled by environment_check.py at runtime +""" + +import locale +import subprocess +import sys +import argparse +from pathlib import Path + +# Tsinghua mirror URL +TSINGHUA_MIRROR = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" + + +def is_chinese_locale() -> bool: + """ + Detect if system is in Chinese locale. + Same logic as EnvironmentChecker._is_chinese_locale() + """ + try: + lang = locale.getdefaultlocale()[0] + if lang and ("zh" in lang.lower() or "chinese" in lang.lower()): + return True + except Exception: + pass + return False + + +def run_command(cmd: list, description: str, retry: int = 2) -> bool: + """Run command with retry support.""" + print(f"[INFO] {description}") + print(f"[CMD] {' '.join(cmd)}") + + for attempt in range(retry + 1): + try: + result = subprocess.run(cmd, check=True, timeout=600) + print(f"[OK] {description}") + return True + except subprocess.CalledProcessError as e: + if attempt < retry: + print(f"[WARN] Attempt {attempt + 1} failed, retrying...") + else: + print(f"[ERROR] {description} failed: {e}") + return False + except subprocess.TimeoutExpired: + print(f"[ERROR] {description} timed out") + return False + return False + + +def install_editable(project_root: Path, use_mirror: bool) -> bool: + """Install unilabos in editable mode using pip.""" + cmd = [sys.executable, "-m", "pip", "install", "-e", str(project_root)] + if use_mirror: + cmd.extend(["-i", TSINGHUA_MIRROR]) + + return run_command(cmd, "Installing unilabos in editable mode") + + +def install_requirements_uv(requirements_file: Path, use_mirror: bool) -> bool: + """Install pip dependencies using uv (installed via conda-forge::uv).""" + cmd = ["uv", "pip", "install", "-r", str(requirements_file)] + if use_mirror: + cmd.extend(["-i", TSINGHUA_MIRROR]) + + return run_command(cmd, "Installing pip dependencies with uv", retry=2) + + +def install_requirements_pip(requirements_file: Path, use_mirror: bool) -> bool: + """Fallback: Install pip dependencies using pip.""" + cmd = [sys.executable, "-m", "pip", "install", "-r", str(requirements_file)] + if use_mirror: + cmd.extend(["-i", TSINGHUA_MIRROR]) + + return run_command(cmd, "Installing pip dependencies with pip", retry=2) + + +def check_uv_available() -> bool: + """Check if uv is available (installed via conda-forge::uv).""" + try: + subprocess.run(["uv", "--version"], capture_output=True, check=True) + return True + except (subprocess.CalledProcessError, FileNotFoundError): + return False + + +def main(): + parser = argparse.ArgumentParser(description="Development installation script for UniLabOS") + parser.add_argument("--china", action="store_true", help="Force use China mirror (Tsinghua)") + parser.add_argument("--no-mirror", action="store_true", help="Force use default PyPI (no mirror)") + parser.add_argument( + "--skip-deps", action="store_true", help="Skip pip dependencies installation (only install unilabos)" + ) + parser.add_argument("--use-pip", action="store_true", help="Use pip instead of uv for dependencies") + args = parser.parse_args() + + # Determine project root + script_dir = Path(__file__).parent + project_root = script_dir.parent + requirements_file = project_root / "unilabos" / "utils" / "requirements.txt" + + if not (project_root / "setup.py").exists(): + print(f"[ERROR] setup.py not found in {project_root}") + sys.exit(1) + + print("=" * 60) + print("UniLabOS Development Installation") + print("=" * 60) + print(f"Project root: {project_root}") + print() + + # Determine mirror usage based on locale + if args.no_mirror: + use_mirror = False + print("[INFO] Mirror disabled by --no-mirror flag") + elif args.china: + use_mirror = True + print("[INFO] China mirror enabled by --china flag") + else: + use_mirror = is_chinese_locale() + if use_mirror: + print("[INFO] Chinese locale detected, using Tsinghua mirror") + else: + print("[INFO] Non-Chinese locale detected, using default PyPI") + + print() + + # Step 1: Install unilabos in editable mode + print("[STEP 1] Installing unilabos in editable mode...") + if not install_editable(project_root, use_mirror): + print("[ERROR] Failed to install unilabos") + print() + print("Manual fallback:") + if use_mirror: + print(f" pip install -e {project_root} -i {TSINGHUA_MIRROR}") + else: + print(f" pip install -e {project_root}") + sys.exit(1) + + print() + + # Step 2: Install pip dependencies + if args.skip_deps: + print("[INFO] Skipping pip dependencies installation (--skip-deps)") + else: + print("[STEP 2] Installing pip dependencies...") + + if not requirements_file.exists(): + print(f"[WARN] Requirements file not found: {requirements_file}") + print("[INFO] Skipping dependencies installation") + else: + # Try uv first (faster), fallback to pip + if args.use_pip: + print("[INFO] Using pip (--use-pip flag)") + success = install_requirements_pip(requirements_file, use_mirror) + elif check_uv_available(): + print("[INFO] Using uv (installed via conda-forge::uv)") + success = install_requirements_uv(requirements_file, use_mirror) + if not success: + print("[WARN] uv failed, falling back to pip...") + success = install_requirements_pip(requirements_file, use_mirror) + else: + print("[WARN] uv not available (should be installed via: mamba install conda-forge::uv)") + print("[INFO] Falling back to pip...") + success = install_requirements_pip(requirements_file, use_mirror) + + if not success: + print() + print("[WARN] Failed to install some dependencies automatically.") + print("You can manually install them:") + if use_mirror: + print(f" uv pip install -r {requirements_file} -i {TSINGHUA_MIRROR}") + print(" or:") + print(f" pip install -r {requirements_file} -i {TSINGHUA_MIRROR}") + else: + print(f" uv pip install -r {requirements_file}") + print(" or:") + print(f" pip install -r {requirements_file}") + + print() + print("=" * 60) + print("Installation complete!") + print("=" * 60) + print() + print("Note: Some special packages (like pylabrobot) are installed") + print("automatically at runtime by unilabos if needed.") + print() + print("Verify installation:") + print(' python -c "import unilabos; print(unilabos.__version__)"') + print() + print("If you encounter issues, you can manually install dependencies:") + if use_mirror: + print(f" uv pip install -r unilabos/utils/requirements.txt -i {TSINGHUA_MIRROR}") + else: + print(" uv pip install -r unilabos/utils/requirements.txt") + print() + + +if __name__ == "__main__": + main() diff --git a/unilabos/app/main.py b/unilabos/app/main.py index 8cea5f6..063bf7a 100644 --- a/unilabos/app/main.py +++ b/unilabos/app/main.py @@ -1,13 +1,11 @@ import argparse import asyncio import os -import shutil import signal import sys import threading import time from typing import Dict, Any, List - import networkx as nx import yaml @@ -17,9 +15,9 @@ unilabos_dir = os.path.dirname(os.path.dirname(current_dir)) if unilabos_dir not in sys.path: sys.path.append(unilabos_dir) +from unilabos.app.utils import cleanup_for_restart from unilabos.utils.banner_print import print_status, print_unilab_banner from unilabos.config.config import load_config, BasicConfig, HTTPConfig -from unilabos.app.utils import cleanup_for_restart # Global restart flags (used by ws_client and web/server) _restart_requested: bool = False diff --git a/unilabos/app/utils.py b/unilabos/app/utils.py index d10c2e0..f6114a1 100644 --- a/unilabos/app/utils.py +++ b/unilabos/app/utils.py @@ -4,8 +4,40 @@ UniLabOS 应用工具函数 提供清理、重启等工具函数 """ -import gc +import glob import os +import shutil +import sys + + +def patch_rclpy_dll_windows(): + """在 Windows + conda 环境下为 rclpy 打 DLL 加载补丁""" + if sys.platform != "win32" or not os.environ.get("CONDA_PREFIX"): + return + try: + import rclpy + + return + except ImportError as e: + if not str(e).startswith("DLL load failed"): + return + cp = os.environ["CONDA_PREFIX"] + impl = os.path.join(cp, "Lib", "site-packages", "rclpy", "impl", "implementation_singleton.py") + pyd = glob.glob(os.path.join(cp, "Lib", "site-packages", "rclpy", "_rclpy_pybind11*.pyd")) + if not os.path.exists(impl) or not pyd: + return + with open(impl, "r", encoding="utf-8") as f: + content = f.read() + lib_bin = os.path.join(cp, "Library", "bin").replace("\\", "/") + patch = f'# UniLabOS DLL Patch\nimport os,ctypes\nos.add_dll_directory("{lib_bin}") if hasattr(os,"add_dll_directory") else None\ntry: ctypes.CDLL("{pyd[0].replace(chr(92),"/")}")\nexcept: pass\n# End Patch\n' + shutil.copy2(impl, impl + ".bak") + with open(impl, "w", encoding="utf-8") as f: + f.write(patch + content) + + +patch_rclpy_dll_windows() + +import gc import threading import time diff --git a/unilabos/utils/requirements.txt b/unilabos/utils/requirements.txt new file mode 100644 index 0000000..86fbef3 --- /dev/null +++ b/unilabos/utils/requirements.txt @@ -0,0 +1,17 @@ +networkx +typing_extensions +websockets +msgcenterpy>=0.1.5 +opentrons_shared_data +pint +fastapi +jinja2 +requests +uvicorn +pyautogui +opcua +pyserial +pandas +crcmod-plus +pymodbus +matplotlib \ No newline at end of file