Compare commits
19 Commits
v0.10.7
...
4b30b8021f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b30b8021f | ||
|
|
344e91bce3 | ||
|
|
7b8638aa03 | ||
|
|
1ca7d4912b | ||
|
|
c04202c6e0 | ||
|
|
93c40e236f | ||
|
|
460f7dd322 | ||
|
|
e5d977e884 | ||
|
|
02425f89e9 | ||
|
|
139fc2158c | ||
|
|
32b50c2680 | ||
|
|
9d2d70c804 | ||
|
|
6eac72ed27 | ||
|
|
9b4daf8e82 | ||
|
|
6795b49e6d | ||
|
|
1461e61648 | ||
|
|
4355135c80 | ||
|
|
63c6fe8427 | ||
|
|
83c765f0ab |
@@ -1,92 +0,0 @@
|
||||
package:
|
||||
name: unilabos
|
||||
version: 0.10.7
|
||||
|
||||
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/dptech-corp/Uni-Lab-OS
|
||||
license: GPL-3.0-only
|
||||
description: "Uni-Lab-OS"
|
||||
@@ -1,9 +0,0 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM upgrade pip
|
||||
"%PREFIX%\python.exe" -m pip install --upgrade pip
|
||||
|
||||
REM install extra deps
|
||||
"%PREFIX%\python.exe" -m pip install paho-mqtt opentrons_shared_data
|
||||
"%PREFIX%\python.exe" -m pip install git+https://github.com/Xuwznln/pylabrobot.git
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euxo pipefail
|
||||
|
||||
# make sure pip is available
|
||||
"$PREFIX/bin/python" -m pip install --upgrade pip
|
||||
|
||||
# install extra deps
|
||||
"$PREFIX/bin/python" -m pip install paho-mqtt opentrons_shared_data
|
||||
"$PREFIX/bin/python" -m pip install git+https://github.com/Xuwznln/pylabrobot.git
|
||||
376
.github/workflows/conda-pack-build.yml
vendored
@@ -1,376 +0,0 @@
|
||||
name: Build Conda-Pack Environment
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: '选择要构建的分支'
|
||||
required: true
|
||||
default: 'dev'
|
||||
type: string
|
||||
platforms:
|
||||
description: '选择构建平台 (逗号分隔): linux-64, osx-64, osx-arm64, win-64'
|
||||
required: false
|
||||
default: 'win-64'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build-conda-pack:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux-64
|
||||
env_file: unilabos-linux-64.yaml
|
||||
script_ext: sh
|
||||
- os: macos-13 # Intel
|
||||
platform: osx-64
|
||||
env_file: unilabos-osx-64.yaml
|
||||
script_ext: sh
|
||||
- os: macos-latest # ARM64
|
||||
platform: osx-arm64
|
||||
env_file: unilabos-osx-arm64.yaml
|
||||
script_ext: sh
|
||||
- os: windows-latest
|
||||
platform: win-64
|
||||
env_file: unilabos-win64.yaml
|
||||
script_ext: bat
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
# Windows uses cmd for better conda/mamba compatibility, Unix uses bash
|
||||
shell: ${{ matrix.platform == 'win-64' && 'cmd /C CALL {0}' || 'bash -el {0}' }}
|
||||
|
||||
steps:
|
||||
- name: Check if platform should be built
|
||||
id: should_build
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ -z "${{ github.event.inputs.platforms }}" ]]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.event.inputs.platforms }}" == *"${{ matrix.platform }}"* ]]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Miniforge (with mamba)
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
with:
|
||||
miniforge-version: latest
|
||||
use-mamba: true
|
||||
python-version: '3.11.11'
|
||||
channels: conda-forge,robostack-staging,uni-lab,defaults
|
||||
channel-priority: flexible
|
||||
activate-environment: unilab
|
||||
auto-activate-base: true
|
||||
auto-update-conda: false
|
||||
show-channel-urls: true
|
||||
|
||||
- name: Install conda-pack, unilabos and dependencies (Windows)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
run: |
|
||||
echo Installing unilabos and dependencies to unilab environment...
|
||||
echo Using mamba for faster and more reliable dependency resolution...
|
||||
mamba install 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'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Installing unilabos and dependencies to unilab environment..."
|
||||
echo "Using mamba for faster and more reliable dependency resolution..."
|
||||
mamba install uni-lab::unilabos conda-pack -c uni-lab -c robostack-staging -c conda-forge -y
|
||||
|
||||
- name: Get latest ros-humble-unilabos-msgs version (Windows)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
id: msgs_version_win
|
||||
run: |
|
||||
echo Checking installed ros-humble-unilabos-msgs version...
|
||||
conda list ros-humble-unilabos-msgs
|
||||
for /f "tokens=2" %%i in ('conda list ros-humble-unilabos-msgs --json ^| python -c "import sys, json; pkgs=json.load(sys.stdin); print(pkgs[0]['version'] if pkgs else 'not-found')"') do set VERSION=%%i
|
||||
echo installed_version=%VERSION% >> %GITHUB_OUTPUT%
|
||||
echo Installed ros-humble-unilabos-msgs version: %VERSION%
|
||||
|
||||
- name: Get latest ros-humble-unilabos-msgs version (Unix)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64'
|
||||
id: msgs_version_unix
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Checking installed ros-humble-unilabos-msgs version..."
|
||||
VERSION=$(conda list ros-humble-unilabos-msgs --json | python -c "import sys, json; pkgs=json.load(sys.stdin); print(pkgs[0]['version'] if pkgs else 'not-found')")
|
||||
echo "installed_version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Installed ros-humble-unilabos-msgs version: $VERSION"
|
||||
|
||||
- name: Check for newer ros-humble-unilabos-msgs (Windows)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
run: |
|
||||
echo Checking for available ros-humble-unilabos-msgs versions...
|
||||
mamba search ros-humble-unilabos-msgs -c uni-lab -c robostack-staging -c conda-forge || echo Search completed
|
||||
echo.
|
||||
echo Updating ros-humble-unilabos-msgs to latest version...
|
||||
mamba update ros-humble-unilabos-msgs -c uni-lab -c robostack-staging -c conda-forge -y || echo Already at latest version
|
||||
|
||||
- name: Check for newer ros-humble-unilabos-msgs (Unix)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Checking for available ros-humble-unilabos-msgs versions..."
|
||||
mamba search ros-humble-unilabos-msgs -c uni-lab -c robostack-staging -c conda-forge || echo "Search completed"
|
||||
echo ""
|
||||
echo "Updating ros-humble-unilabos-msgs to latest version..."
|
||||
mamba update ros-humble-unilabos-msgs -c uni-lab -c robostack-staging -c conda-forge -y || echo "Already at latest version"
|
||||
|
||||
- name: Install latest unilabos from source (Windows)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
run: |
|
||||
echo Uninstalling existing unilabos...
|
||||
pip uninstall unilabos -y || echo unilabos not installed via pip
|
||||
echo Installing unilabos from source (branch: ${{ github.event.inputs.branch }})...
|
||||
pip install .
|
||||
echo Verifying installation...
|
||||
pip show unilabos
|
||||
|
||||
- name: Install latest unilabos from source (Unix)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Uninstalling existing unilabos..."
|
||||
pip uninstall unilabos -y || echo "unilabos not installed via pip"
|
||||
echo "Installing unilabos from source (branch: ${{ github.event.inputs.branch }})..."
|
||||
pip install .
|
||||
echo "Verifying installation..."
|
||||
pip show unilabos
|
||||
|
||||
- name: Display environment info (Windows)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
run: |
|
||||
echo === Environment Information ===
|
||||
conda env list
|
||||
echo.
|
||||
echo === Installed Packages ===
|
||||
conda list | findstr /C:"unilabos" /C:"ros-humble-unilabos-msgs" || conda list
|
||||
echo.
|
||||
echo === Python Packages ===
|
||||
pip list | findstr unilabos || pip list
|
||||
|
||||
- name: Display environment info (Unix)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "=== Environment Information ==="
|
||||
conda env list
|
||||
echo ""
|
||||
echo "=== Installed Packages ==="
|
||||
conda list | grep -E "(unilabos|ros-humble-unilabos-msgs)" || conda list
|
||||
echo ""
|
||||
echo "=== Python Packages ==="
|
||||
pip list | grep unilabos || pip list
|
||||
|
||||
- name: Verify environment integrity (Windows)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
run: |
|
||||
echo Verifying Python version...
|
||||
python -c "import sys; print(f'Python version: {sys.version}')"
|
||||
echo Verifying unilabos import...
|
||||
python -c "import unilabos; print(f'UniLabOS version: {unilabos.__version__}')" || echo Warning: Could not import unilabos
|
||||
echo Checking critical packages...
|
||||
python -c "import rclpy; print('ROS2 rclpy: OK')"
|
||||
echo Running comprehensive verification script...
|
||||
python scripts\verify_installation.py || echo Warning: Verification script reported issues
|
||||
echo Environment verification complete!
|
||||
|
||||
- name: Verify environment integrity (Unix)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Verifying Python version..."
|
||||
python -c "import sys; print(f'Python version: {sys.version}')"
|
||||
echo "Verifying unilabos import..."
|
||||
python -c "import unilabos; print(f'UniLabOS version: {unilabos.__version__}')" || echo "Warning: Could not import unilabos"
|
||||
echo "Checking critical packages..."
|
||||
python -c "import rclpy; print('ROS2 rclpy: OK')"
|
||||
echo "Running comprehensive verification script..."
|
||||
python scripts/verify_installation.py || echo "Warning: Verification script reported issues"
|
||||
echo "Environment verification complete!"
|
||||
|
||||
- name: Pack conda environment (Windows)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
run: |
|
||||
echo Packing unilab environment with conda-pack...
|
||||
conda pack -n unilab -o unilab-env-${{ matrix.platform }}.tar.gz --ignore-missing-files
|
||||
echo Pack file created:
|
||||
dir unilab-env-${{ matrix.platform }}.tar.gz
|
||||
|
||||
- name: Pack conda environment (Unix)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Packing unilab environment with conda-pack..."
|
||||
conda pack -n unilab -o unilab-env-${{ matrix.platform }}.tar.gz --ignore-missing-files
|
||||
echo "Pack file created:"
|
||||
ls -lh unilab-env-${{ matrix.platform }}.tar.gz
|
||||
|
||||
- name: Prepare Windows distribution package
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
run: |
|
||||
echo ==========================================
|
||||
echo Creating distribution package...
|
||||
echo Platform: ${{ matrix.platform }}
|
||||
echo ==========================================
|
||||
mkdir dist-package 2>nul || cd .
|
||||
|
||||
rem Copy packed environment
|
||||
echo Adding: unilab-env-${{ matrix.platform }}.tar.gz
|
||||
copy unilab-env-${{ matrix.platform }}.tar.gz dist-package\
|
||||
|
||||
rem Copy installation script
|
||||
echo Adding: install_unilab.bat
|
||||
copy scripts\install_unilab.bat dist-package\
|
||||
|
||||
rem Copy verification script
|
||||
echo Adding: verify_installation.py
|
||||
copy scripts\verify_installation.py dist-package\
|
||||
|
||||
rem Create README using Python script
|
||||
echo Creating: README.txt
|
||||
python scripts\create_readme.py ${{ matrix.platform }} ${{ github.event.inputs.branch }} dist-package\README.txt
|
||||
|
||||
echo.
|
||||
echo Distribution package contents:
|
||||
dir /b dist-package
|
||||
echo.
|
||||
|
||||
- name: Prepare Unix/Linux distribution package
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "=========================================="
|
||||
echo "Creating distribution package..."
|
||||
echo "Platform: ${{ matrix.platform }}"
|
||||
echo "=========================================="
|
||||
mkdir -p dist-package
|
||||
|
||||
# Copy packed environment
|
||||
echo "Adding: unilab-env-${{ matrix.platform }}.tar.gz"
|
||||
cp unilab-env-${{ matrix.platform }}.tar.gz dist-package/
|
||||
|
||||
# Copy installation script
|
||||
echo "Adding: install_unilab.sh"
|
||||
cp scripts/install_unilab.sh dist-package/
|
||||
chmod +x dist-package/install_unilab.sh
|
||||
|
||||
# Copy verification script
|
||||
echo "Adding: verify_installation.py"
|
||||
cp scripts/verify_installation.py dist-package/
|
||||
|
||||
# Create README using Python script
|
||||
echo "Creating: README.txt"
|
||||
python scripts/create_readme.py ${{ matrix.platform }} ${{ github.event.inputs.branch }} dist-package/README.txt
|
||||
|
||||
echo ""
|
||||
echo "Distribution package contents:"
|
||||
ls -lh dist-package/
|
||||
echo ""
|
||||
|
||||
- name: Finalize Windows distribution package
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
run: |
|
||||
echo ==========================================
|
||||
echo Windows distribution package ready
|
||||
echo.
|
||||
echo Package will be uploaded as artifact
|
||||
echo GitHub Actions will automatically create ZIP
|
||||
echo.
|
||||
echo Contents:
|
||||
dir /b dist-package
|
||||
echo.
|
||||
echo Users will download a ZIP containing:
|
||||
echo - install_unilab.bat
|
||||
echo - unilab-env-${{ matrix.platform }}.tar.gz
|
||||
echo - verify_installation.py
|
||||
echo - README.txt
|
||||
echo ==========================================
|
||||
|
||||
- name: Create Unix/Linux TAR.GZ archive
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "=========================================="
|
||||
echo "Creating Unix/Linux TAR.GZ archive..."
|
||||
echo "Archive: unilab-pack-${{ matrix.platform }}.tar.gz"
|
||||
echo "Contents: install_unilab.sh + unilab-env-${{ matrix.platform }}.tar.gz + extras"
|
||||
tar -czf unilab-pack-${{ matrix.platform }}.tar.gz -C dist-package .
|
||||
echo "=========================================="
|
||||
|
||||
echo ""
|
||||
echo "Final package created:"
|
||||
ls -lh unilab-pack-*
|
||||
echo ""
|
||||
echo "Users can now:"
|
||||
echo " 1. Download unilab-pack-${{ matrix.platform }}.tar.gz"
|
||||
echo " 2. Extract it: tar -xzf unilab-pack-${{ matrix.platform }}.tar.gz"
|
||||
echo " 3. Run: bash install_unilab.sh"
|
||||
echo ""
|
||||
|
||||
- name: Upload distribution package
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: unilab-pack-${{ matrix.platform }}-${{ github.event.inputs.branch }}
|
||||
path: dist-package/
|
||||
retention-days: 90
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Display package info (Windows)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform == 'win-64'
|
||||
run: |
|
||||
echo ==========================================
|
||||
echo Build Summary
|
||||
echo ==========================================
|
||||
echo Platform: ${{ matrix.platform }}
|
||||
echo Branch: ${{ github.event.inputs.branch }}
|
||||
echo Python version: 3.11.11
|
||||
echo.
|
||||
echo Distribution package contents:
|
||||
dir dist-package
|
||||
echo.
|
||||
echo Artifact name: unilab-pack-${{ matrix.platform }}-${{ github.event.inputs.branch }}
|
||||
echo.
|
||||
echo After download, extract the ZIP and run:
|
||||
echo install_unilab.bat
|
||||
echo ==========================================
|
||||
|
||||
- name: Display package info (Unix)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.platform != 'win-64'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "=========================================="
|
||||
echo "Build Summary"
|
||||
echo "=========================================="
|
||||
echo "Platform: ${{ matrix.platform }}"
|
||||
echo "Branch: ${{ github.event.inputs.branch }}"
|
||||
echo "Python version: 3.11.11"
|
||||
echo ""
|
||||
echo "Distribution package contents:"
|
||||
ls -lh dist-package/
|
||||
echo ""
|
||||
echo "Package size (tar.gz):"
|
||||
ls -lh unilab-pack-*.tar.gz
|
||||
echo ""
|
||||
echo "Artifact name: unilab-pack-${{ matrix.platform }}-${{ github.event.inputs.branch }}"
|
||||
echo ""
|
||||
echo "After download:"
|
||||
echo " - Windows/macOS: Extract ZIP, then: tar -xzf unilab-pack-${{ matrix.platform }}.tar.gz"
|
||||
echo " - Linux: Extract ZIP (or download tar.gz directly), run install_unilab.sh"
|
||||
echo "=========================================="
|
||||
98
.github/workflows/deploy-docs.yml
vendored
@@ -1,98 +0,0 @@
|
||||
name: Deploy Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: '要部署文档的分支'
|
||||
required: false
|
||||
default: 'main'
|
||||
type: string
|
||||
deploy_to_pages:
|
||||
description: '是否部署到 GitHub Pages'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
# 设置 GITHUB_TOKEN 权限以部署到 GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# 只允许一个并发部署,跳过正在进行和最新排队之间的运行
|
||||
# 但是不取消正在进行的运行,因为我们希望允许这些生产部署完成
|
||||
concurrency:
|
||||
group: 'pages'
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Build documentation
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch || github.ref }}
|
||||
|
||||
- name: Setup Python environment
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y pandoc
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
# Install package in development mode to get version info
|
||||
pip install -e .
|
||||
# Install documentation dependencies
|
||||
pip install -r docs/requirements.txt
|
||||
|
||||
- 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')
|
||||
|
||||
- name: Build Sphinx documentation
|
||||
run: |
|
||||
cd docs
|
||||
# Clean previous builds
|
||||
rm -rf _build
|
||||
# Build HTML documentation
|
||||
python -m sphinx -b html . _build/html -v
|
||||
|
||||
- name: Check build results
|
||||
run: |
|
||||
echo "Documentation build completed, checking output directory:"
|
||||
ls -la docs/_build/html/
|
||||
echo "Checking for index.html:"
|
||||
test -f docs/_build/html/index.html && echo "✓ index.html exists" || echo "✗ index.html missing"
|
||||
|
||||
- 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')
|
||||
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')
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
131
.github/workflows/multi-platform-build.yml
vendored
@@ -1,131 +0,0 @@
|
||||
name: Multi-Platform Conda Build
|
||||
|
||||
on:
|
||||
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: 'osx-arm64'
|
||||
upload_to_anaconda:
|
||||
description: '是否上传到Anaconda.org'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux-64
|
||||
env_file: unilabos-linux-64.yaml
|
||||
- os: macos-13 # Intel
|
||||
platform: osx-64
|
||||
env_file: unilabos-osx-64.yaml
|
||||
- os: macos-latest # ARM64
|
||||
platform: osx-arm64
|
||||
env_file: unilabos-osx-arm64.yaml
|
||||
- os: windows-latest
|
||||
platform: win-64
|
||||
env_file: unilabos-win64.yaml
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -l {0}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check if platform should be built
|
||||
id: should_build
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" != "workflow_dispatch" ]]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
elif [[ -z "${{ github.event.inputs.platforms }}" ]]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.event.inputs.platforms }}" == *"${{ matrix.platform }}"* ]]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Setup Miniconda
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
with:
|
||||
miniconda-version: 'latest'
|
||||
channels: conda-forge,robostack-staging,defaults
|
||||
channel-priority: strict
|
||||
activate-environment: build-env
|
||||
auto-activate-base: false
|
||||
auto-update-conda: false
|
||||
show-channel-urls: true
|
||||
|
||||
- name: Install rattler-build and anaconda-client
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
conda install -c conda-forge rattler-build anaconda-client
|
||||
|
||||
- name: Show environment info
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
conda info
|
||||
conda list | grep -E "(rattler-build|anaconda-client)"
|
||||
echo "Platform: ${{ matrix.platform }}"
|
||||
echo "OS: ${{ matrix.os }}"
|
||||
|
||||
- name: Build conda package
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
if [[ "${{ matrix.platform }}" == "osx-arm64" ]]; then
|
||||
rattler-build build -r ./recipes/msgs/recipe.yaml -c robostack -c robostack-staging -c conda-forge
|
||||
else
|
||||
rattler-build build -r ./recipes/msgs/recipe.yaml -c robostack -c robostack-staging -c conda-forge
|
||||
fi
|
||||
|
||||
- name: List built packages
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
echo "Built packages in output directory:"
|
||||
find ./output -name "*.conda" | head -10
|
||||
ls -la ./output/${{ matrix.platform }}/ || echo "${{ matrix.platform }} directory not found"
|
||||
ls -la ./output/noarch/ || echo "noarch directory not found"
|
||||
echo "Output directory structure:"
|
||||
find ./output -type f -name "*.conda"
|
||||
|
||||
- name: Prepare artifacts for upload
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
mkdir -p conda-packages-temp
|
||||
find ./output -name "*.conda" -exec cp {} conda-packages-temp/ \;
|
||||
echo "Copied files to temp directory:"
|
||||
ls -la conda-packages-temp/
|
||||
|
||||
- name: Upload conda package artifacts
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: conda-package-${{ matrix.platform }}
|
||||
path: conda-packages-temp
|
||||
if-no-files-found: warn
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload to Anaconda.org (unilab organization)
|
||||
if: steps.should_build.outputs.should_build == 'true' && github.event.inputs.upload_to_anaconda == 'true'
|
||||
run: |
|
||||
for package in $(find ./output -name "*.conda"); do
|
||||
echo "Uploading $package to unilab organization..."
|
||||
anaconda -t ${{ secrets.ANACONDA_API_TOKEN }} upload --user uni-lab --force "$package"
|
||||
done
|
||||
124
.github/workflows/unilabos-conda-build.yml
vendored
@@ -1,124 +0,0 @@
|
||||
name: UniLabOS Conda Build
|
||||
|
||||
on:
|
||||
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'
|
||||
upload_to_anaconda:
|
||||
description: '是否上传到Anaconda.org'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux-64
|
||||
- os: macos-13 # Intel
|
||||
platform: osx-64
|
||||
- os: macos-latest # ARM64
|
||||
platform: osx-arm64
|
||||
- os: windows-latest
|
||||
platform: win-64
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -l {0}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check if platform should be built
|
||||
id: should_build
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" != "workflow_dispatch" ]]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
elif [[ -z "${{ github.event.inputs.platforms }}" ]]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.event.inputs.platforms }}" == *"${{ matrix.platform }}"* ]]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Setup Miniconda
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
with:
|
||||
miniconda-version: 'latest'
|
||||
channels: conda-forge,robostack-staging,uni-lab,defaults
|
||||
channel-priority: strict
|
||||
activate-environment: build-env
|
||||
auto-activate-base: false
|
||||
auto-update-conda: false
|
||||
show-channel-urls: true
|
||||
|
||||
- name: Install rattler-build and anaconda-client
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
conda install -c conda-forge rattler-build anaconda-client
|
||||
|
||||
- name: Show environment info
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
conda info
|
||||
conda list | grep -E "(rattler-build|anaconda-client)"
|
||||
echo "Platform: ${{ matrix.platform }}"
|
||||
echo "OS: ${{ matrix.os }}"
|
||||
echo "Building UniLabOS package"
|
||||
|
||||
- name: Build conda package
|
||||
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
|
||||
|
||||
- name: List built packages
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
echo "Built packages in output directory:"
|
||||
find ./output -name "*.conda" | head -10
|
||||
ls -la ./output/${{ matrix.platform }}/ || echo "${{ matrix.platform }} directory not found"
|
||||
ls -la ./output/noarch/ || echo "noarch directory not found"
|
||||
echo "Output directory structure:"
|
||||
find ./output -type f -name "*.conda"
|
||||
|
||||
- name: Prepare artifacts for upload
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
mkdir -p conda-packages-temp
|
||||
find ./output -name "*.conda" -exec cp {} conda-packages-temp/ \;
|
||||
echo "Copied files to temp directory:"
|
||||
ls -la conda-packages-temp/
|
||||
|
||||
- name: Upload conda package artifacts
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: conda-package-unilabos-${{ matrix.platform }}
|
||||
path: conda-packages-temp
|
||||
if-no-files-found: warn
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload to Anaconda.org (uni-lab organization)
|
||||
if: github.event.inputs.upload_to_anaconda == 'true'
|
||||
run: |
|
||||
for package in $(find ./output -name "*.conda"); do
|
||||
echo "Uploading $package to uni-lab organization..."
|
||||
anaconda -t ${{ secrets.ANACONDA_API_TOKEN }} upload --user uni-lab --force "$package"
|
||||
done
|
||||
19
.gitignore
vendored
@@ -1,8 +1,3 @@
|
||||
configs/
|
||||
temp/
|
||||
output/
|
||||
unilabos_data/
|
||||
pyrightconfig.json
|
||||
## Python
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
@@ -11,7 +6,6 @@ __pycache__/
|
||||
.vscode
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
service
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
@@ -237,15 +231,4 @@ CATKIN_IGNORE
|
||||
|
||||
/**/local_config.py
|
||||
|
||||
*.graphml
|
||||
unilabos/device_mesh/view_robot.rviz
|
||||
|
||||
|
||||
# Certs
|
||||
**/.certs
|
||||
local_test2.py
|
||||
ros-humble-unilabos-msgs-0.9.13-h6403a04_5.tar.bz2
|
||||
*.bz2
|
||||
test_config.py
|
||||
|
||||
|
||||
*.graphml
|
||||
15
CONTRIBUTORS
@@ -1,15 +0,0 @@
|
||||
156 Xuwznln <18435084+Xuwznln@users.noreply.github.com>
|
||||
39 Junhan Chang <changjh@dp.tech>
|
||||
9 wznln <18435084+Xuwznln@users.noreply.github.com>
|
||||
8 Guangxin Zhang <guangxin.zhang.bio@gmail.com>
|
||||
5 ZiWei <131428629+ZiWei09@users.noreply.github.com>
|
||||
2 Junhan Chang <changjh@pku.edu.cn>
|
||||
2 Xie Qiming <97236197+Andy6M@users.noreply.github.com>
|
||||
1 Harvey Que <103566763+Mile-Away@users.noreply.github.com>
|
||||
1 Junhan Chang <1700011741@pku.edu.cn>
|
||||
1 LccLink <1951855008@qq.com>
|
||||
1 h840473807 <47357934+h840473807@users.noreply.github.com>
|
||||
1 lixinyu1011 <61094742+lixinyu1011@users.noreply.github.com>
|
||||
1 shiyubo0410 <shiyubo@dp.tech>
|
||||
1 王俊杰 <1800011822@pku.edu.cn>
|
||||
1 王俊杰 <43375851+wjjxxx@users.noreply.github.com>
|
||||
@@ -1,5 +1,3 @@
|
||||
recursive-include unilabos/registry *.yaml
|
||||
recursive-include unilabos/app/web/static *
|
||||
recursive-include unilabos/app/web/templates *
|
||||
recursive-include unilabos/device_mesh/devices *
|
||||
recursive-include unilabos/device_mesh/resources *
|
||||
recursive-include unilabos/app/web *.html
|
||||
recursive-include unilabos/app/web *.css
|
||||
|
||||
76
README.md
@@ -4,75 +4,83 @@
|
||||
|
||||
# Uni-Lab-OS
|
||||
|
||||
<!-- Language switcher -->
|
||||
|
||||
**English** | [中文](README_zh.md)
|
||||
|
||||
[](https://github.com/dptech-corp/Uni-Lab-OS/stargazers)
|
||||
[](https://github.com/dptech-corp/Uni-Lab-OS/network/members)
|
||||
[](https://github.com/dptech-corp/Uni-Lab-OS/issues)
|
||||
[](https://github.com/dptech-corp/Uni-Lab-OS/blob/main/LICENSE)
|
||||
|
||||
Uni-Lab-OS is a platform for laboratory automation, designed to connect and control various experimental equipment, enabling automation and standardization of experimental workflows.
|
||||
Uni-Lab 操作系统是一个用于实验室自动化的综合平台,旨在连接和控制各种实验设备,实现实验流程的自动化和标准化。
|
||||
|
||||
## 🏆 Competition
|
||||
## 核心特点
|
||||
|
||||
Join the [Intelligent Organic Chemistry Synthesis Competition](https://bohrium.dp.tech/competitions/1451645258) to explore automated synthesis with Uni-Lab-OS!
|
||||
- 多设备集成管理
|
||||
- 自动化实验流程
|
||||
- 云端连接能力
|
||||
- 灵活的配置系统
|
||||
- 支持多种实验协议
|
||||
|
||||
## Key Features
|
||||
## 文档
|
||||
|
||||
- Multi-device integration management
|
||||
- Automated experimental workflows
|
||||
- Cloud connectivity capabilities
|
||||
- Flexible configuration system
|
||||
- Support for multiple experimental protocols
|
||||
详细文档可在以下位置找到:
|
||||
|
||||
## Documentation
|
||||
- [在线文档](https://readthedocs.dp.tech/Uni-Lab/v0.8.0/)
|
||||
|
||||
Detailed documentation can be found at:
|
||||
## 快速开始
|
||||
|
||||
- [Online Documentation](https://dptech-corp.github.io/Uni-Lab-OS/)
|
||||
1. 配置Conda环境
|
||||
|
||||
## Quick Start
|
||||
|
||||
Uni-Lab-OS recommends using `mamba` for environment management. Choose the appropriate environment file for your operating system:
|
||||
Uni-Lab-OS 建议使用 `mamba` 管理环境。根据您的操作系统选择适当的环境文件:
|
||||
|
||||
```bash
|
||||
# Create new environment
|
||||
mamba create -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge
|
||||
# 创建新环境
|
||||
mamba env create -f unilabos-[YOUR_OS].yaml
|
||||
mamba activate unilab
|
||||
|
||||
# 或更新现有环境
|
||||
# 其中 `[YOUR_OS]` 可以是 `win64`, `linux-64`, `osx-64`, 或 `osx-arm64`。
|
||||
conda env update --file unilabos-[YOUR_OS].yml -n 环境名
|
||||
|
||||
# 现阶段,需要安装 `unilabos_msgs` 包
|
||||
# 可以前往 Release 页面下载系统对应的包进行安装
|
||||
conda install ros-humble-unilabos-msgs-0.9.0-xxxxx.tar.bz2
|
||||
|
||||
# 安装PyLabRobot等前置
|
||||
git clone https://github.com/PyLabRobot/pylabrobot plr_repo
|
||||
cd plr_repo
|
||||
pip install .[opentrons]
|
||||
```
|
||||
|
||||
## Install Dev Uni-Lab-OS
|
||||
2. 安装 Uni-Lab-OS:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
# 克隆仓库
|
||||
git clone https://github.com/dptech-corp/Uni-Lab-OS.git
|
||||
cd Uni-Lab-OS
|
||||
|
||||
# Install Uni-Lab-OS
|
||||
# 安装 Uni-Lab-OS
|
||||
pip install .
|
||||
```
|
||||
|
||||
3. Start Uni-Lab System:
|
||||
3. 启动 Uni-Lab 系统:
|
||||
|
||||
Please refer to [Documentation - Boot Examples](https://dptech-corp.github.io/Uni-Lab-OS/boot_examples/index.html)
|
||||
请见[文档-启动样例](https://readthedocs.dp.tech/Uni-Lab/v0.8.0/boot_examples/index.html)
|
||||
|
||||
## Message Format
|
||||
## 消息格式
|
||||
|
||||
Uni-Lab-OS uses pre-built `unilabos_msgs` for system communication. You can find the built versions on the [GitHub Releases](https://github.com/dptech-corp/Uni-Lab-OS/releases) page.
|
||||
Uni-Lab-OS 使用预构建的 `unilabos_msgs` 进行系统通信。您可以在 [GitHub Releases](https://github.com/dptech-corp/Uni-Lab-OS/releases) 页面找到已构建的版本。
|
||||
|
||||
## License
|
||||
## 许可证
|
||||
|
||||
This project is licensed under GPL-3.0 - see the [LICENSE](LICENSE) file for details.
|
||||
此项目采用 GPL-3.0 许可 - 详情请参阅 [LICENSE](LICENSE) 文件。
|
||||
|
||||
## Project Statistics
|
||||
## 项目统计
|
||||
|
||||
### Stars Trend
|
||||
### Stars 趋势
|
||||
|
||||
<a href="https://star-history.com/#dptech-corp/Uni-Lab-OS&Date">
|
||||
<img src="https://api.star-history.com/svg?repos=dptech-corp/Uni-Lab-OS&type=Date" alt="Star History Chart" width="600">
|
||||
</a>
|
||||
|
||||
## Contact Us
|
||||
## 联系我们
|
||||
|
||||
- GitHub Issues: [https://github.com/dptech-corp/Uni-Lab-OS/issues](https://github.com/dptech-corp/Uni-Lab-OS/issues)
|
||||
- GitHub Issues: [https://github.com/dptech-corp/Uni-Lab-OS/issues](https://github.com/dptech-corp/Uni-Lab-OS/issues)
|
||||
80
README_zh.md
@@ -1,80 +0,0 @@
|
||||
<div align="center">
|
||||
<img src="docs/logo.png" alt="Uni-Lab Logo" width="200"/>
|
||||
</div>
|
||||
|
||||
# Uni-Lab-OS
|
||||
|
||||
<!-- Language switcher -->
|
||||
|
||||
[English](README.md) | **中文**
|
||||
|
||||
[](https://github.com/dptech-corp/Uni-Lab-OS/stargazers)
|
||||
[](https://github.com/dptech-corp/Uni-Lab-OS/network/members)
|
||||
[](https://github.com/dptech-corp/Uni-Lab-OS/issues)
|
||||
[](https://github.com/dptech-corp/Uni-Lab-OS/blob/main/LICENSE)
|
||||
|
||||
Uni-Lab-OS 是一个用于实验室自动化的综合平台,旨在连接和控制各种实验设备,实现实验流程的自动化和标准化。
|
||||
|
||||
## 🏆 比赛
|
||||
|
||||
欢迎参加[有机化学合成智能实验大赛](https://bohrium.dp.tech/competitions/1451645258),使用 Uni-Lab-OS 探索自动化合成!
|
||||
|
||||
## 核心特点
|
||||
|
||||
- 多设备集成管理
|
||||
- 自动化实验流程
|
||||
- 云端连接能力
|
||||
- 灵活的配置系统
|
||||
- 支持多种实验协议
|
||||
|
||||
## 文档
|
||||
|
||||
详细文档可在以下位置找到:
|
||||
|
||||
- [在线文档](https://dptech-corp.github.io/Uni-Lab-OS/)
|
||||
|
||||
## 快速开始
|
||||
|
||||
1. 配置 Conda 环境
|
||||
|
||||
Uni-Lab-OS 建议使用 `mamba` 管理环境。根据您的操作系统选择适当的环境文件:
|
||||
|
||||
```bash
|
||||
# 创建新环境
|
||||
mamba create -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge
|
||||
```
|
||||
|
||||
2. 安装开发版 Uni-Lab-OS:
|
||||
|
||||
```bash
|
||||
# 克隆仓库
|
||||
git clone https://github.com/dptech-corp/Uni-Lab-OS.git
|
||||
cd Uni-Lab-OS
|
||||
|
||||
# 安装 Uni-Lab-OS
|
||||
pip install .
|
||||
```
|
||||
|
||||
3. 启动 Uni-Lab 系统:
|
||||
|
||||
请见[文档-启动样例](https://dptech-corp.github.io/Uni-Lab-OS/boot_examples/index.html)
|
||||
|
||||
## 消息格式
|
||||
|
||||
Uni-Lab-OS 使用预构建的 `unilabos_msgs` 进行系统通信。您可以在 [GitHub Releases](https://github.com/dptech-corp/Uni-Lab-OS/releases) 页面找到已构建的版本。
|
||||
|
||||
## 许可证
|
||||
|
||||
此项目采用 GPL-3.0 许可 - 详情请参阅 [LICENSE](LICENSE) 文件。
|
||||
|
||||
## 项目统计
|
||||
|
||||
### Stars 趋势
|
||||
|
||||
<a href="https://star-history.com/#dptech-corp/Uni-Lab-OS&Date">
|
||||
<img src="https://api.star-history.com/svg?repos=dptech-corp/Uni-Lab-OS&type=Date" alt="Star History Chart" width="600">
|
||||
</a>
|
||||
|
||||
## 联系我们
|
||||
|
||||
- GitHub Issues: [https://github.com/dptech-corp/Uni-Lab-OS/issues](https://github.com/dptech-corp/Uni-Lab-OS/issues)
|
||||
@@ -13,16 +13,18 @@
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "PLR_STATION",
|
||||
"name": "PLR_LH_TEST",
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "liquid_handler",
|
||||
"config": {},
|
||||
"data": {},
|
||||
"children": ["deck"]
|
||||
},
|
||||
{
|
||||
"id": "PLR_STATION",
|
||||
"name": "PLR_LH_TEST",
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "liquid_handler",
|
||||
"config": {},
|
||||
"data": {},
|
||||
"children": [
|
||||
"deck"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deck",
|
||||
"name": "deck",
|
||||
@@ -30,12 +32,12 @@
|
||||
"class": null,
|
||||
"parent": "PLR_STATION",
|
||||
"children": [
|
||||
"trash",
|
||||
"trash_core96",
|
||||
"teaching_carrier",
|
||||
"tip_rack",
|
||||
"plate"
|
||||
]
|
||||
"trash",
|
||||
"trash_core96",
|
||||
"teaching_carrier",
|
||||
"tip_rack",
|
||||
"plate"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
@@ -43,7 +45,6 @@
|
||||
```
|
||||
|
||||
配置文件定义了移液站的组成部分,主要包括:
|
||||
|
||||
- 移液站本体(LiquidHandler)- 设备类型
|
||||
- 移液站携带物料实例(deck)- 物料类型
|
||||
|
||||
@@ -54,7 +55,7 @@
|
||||
使用以下命令启动移液站设备:
|
||||
|
||||
```bash
|
||||
unilab -g test/experiments/plr_test.json --ak [通过网页获取的ak值] --sk [通过网页获取的sk值]
|
||||
unilab -g test/experiments/plr_test.json --app_bridges ""
|
||||
```
|
||||
|
||||
### 2. 执行枪头插入操作
|
||||
@@ -65,50 +66,35 @@ unilab -g test/experiments/plr_test.json --ak [通过网页获取的ak值] --sk
|
||||
ros2 action send_goal /devices/PLR_STATION/pick_up_tips unilabos_msgs/action/_liquid_handler_pick_up_tips/LiquidHandlerPickUpTips "{ tip_spots: [ { id: 'tip_rack_tipspot_0_0', name: 'tip_rack_tipspot_0_0', sample_id: null, children: [], parent: 'tip_rack', type: 'device', config: { position: { x: 7.2, y: 68.3, z: -83.5 }, size_x: 9.0, size_y: 9.0, size_z: 0, rotation: { x: 0, y: 0, z: 0, type: 'Rotation' }, category: 'tip_spot', model: null, type: 'TipSpot', prototype_tip: { type: 'HamiltonTip', total_tip_length: 95.1, has_filter: true, maximal_volume: 1065, pickup_method: 'OUT_OF_RACK', tip_size: 'HIGH_VOLUME' } }, data: { tip: { type: 'HamiltonTip', total_tip_length: 95.1, has_filter: true, maximal_volume: 1065, pickup_method: 'OUT_OF_RACK', tip_size: 'HIGH_VOLUME' }, tip_state: { liquids: [], pending_liquids: [], liquid_history: [] }, pending_tip: { type: 'HamiltonTip', total_tip_length: 95.1, has_filter: true, maximal_volume: 1065, pickup_method: 'OUT_OF_RACK', tip_size: 'HIGH_VOLUME' } } } ], use_channels: [ 0 ], offsets: [ { x: 0.0, y: 0.0, z: 0.0 } ] }"
|
||||
```
|
||||
|
||||
此命令会通过 ros 通信触发移液站执行枪头插入操作,得到如下的 PyLabRobot 的输出日志。
|
||||
此命令会通过ros通信触发移液站执行枪头插入操作,得到如下的PyLabRobot的输出日志。
|
||||
|
||||
```log
|
||||
Picking up tips:
|
||||
pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter
|
||||
p0: tip_rack_tipspot_0_0 0.0,0.0,0.0 HamiltonTip 1065 8 95.1 Yes
|
||||
pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter
|
||||
p0: tip_rack_tipspot_0_0 0.0,0.0,0.0 HamiltonTip 1065 8 95.1 Yes
|
||||
```
|
||||
|
||||
也可以登陆网页,给`tip_spots`选择`tip_rack_tipspot_0_0`,`use_channels`为`0`,`offsets`均填写`0`,同样可观察到上面的日志
|
||||
|
||||
## 常见问题
|
||||
|
||||
1. **重复插入枪头不成功**:操作编排应该符合实际操作顺序,可自行通过 PyLabRobot 进行测试
|
||||
1. **重复插入枪头不成功**:操作编排应该符合实际操作顺序,可自行通过PyLabRobot进行测试
|
||||
|
||||
## 移液站支持的操作
|
||||
|
||||
移液站支持多种操作,以下是当前系统支持的操作列表:
|
||||
|
||||
1. **LiquidHandlerProtocolCreation** - 协议创建
|
||||
2. **LiquidHandlerAspirate** - 吸液操作
|
||||
3. **LiquidHandlerDispense** - 排液操作
|
||||
4. **LiquidHandlerDiscardTips** - 丢弃枪头
|
||||
5. **LiquidHandlerDropTips** - 卸下枪头
|
||||
6. **LiquidHandlerDropTips96** - 卸下 96 通道枪头
|
||||
7. **LiquidHandlerMoveLid** - 移动盖子
|
||||
8. **LiquidHandlerMovePlate** - 移动板子
|
||||
9. **LiquidHandlerMoveResource** - 移动资源
|
||||
10. **LiquidHandlerPickUpTips** - 插入枪头
|
||||
11. **LiquidHandlerPickUpTips96** - 插入 96 通道枪头
|
||||
12. **LiquidHandlerReturnTips** - 归还枪头
|
||||
13. **LiquidHandlerReturnTips96** - 归还 96 通道枪头
|
||||
14. **LiquidHandlerSetLiquid** - 设置液体
|
||||
15. **LiquidHandlerSetTipRack** - 设置枪头架
|
||||
16. **LiquidHandlerStamp** - 打印标记
|
||||
17. **LiquidHandlerTransfer** - 液体转移
|
||||
18. **LiquidHandlerSetGroup** - 设置分组
|
||||
19. **LiquidHandlerTransferBiomek** - Biomek 液体转移
|
||||
20. **LiquidHandlerIncubateBiomek** - Biomek 孵育
|
||||
21. **LiquidHandlerMoveBiomek** - Biomek 移动
|
||||
22. **LiquidHandlerOscillateBiomek** - Biomek 振荡
|
||||
23. **LiquidHandlerTransferGroup** - 分组转移
|
||||
24. **LiquidHandlerAdd** - 添加操作
|
||||
25. **LiquidHandlerMix** - 混合操作
|
||||
26. **LiquidHandlerMoveTo** - 移动到指定位置
|
||||
27. **LiquidHandlerRemove** - 移除操作
|
||||
1. **LiquidHandlerAspirate** - 吸液操作
|
||||
2. **LiquidHandlerDispense** - 排液操作
|
||||
3. **LiquidHandlerDiscardTips** - 丢弃枪头
|
||||
4. **LiquidHandlerDropTips** - 卸下枪头
|
||||
5. **LiquidHandlerDropTips96** - 卸下96通道枪头
|
||||
6. **LiquidHandlerMoveLid** - 移动盖子
|
||||
7. **LiquidHandlerMovePlate** - 移动板子
|
||||
8. **LiquidHandlerMoveResource** - 移动资源
|
||||
9. **LiquidHandlerPickUpTips** - 插入枪头
|
||||
10. **LiquidHandlerPickUpTips96** - 插入96通道枪头
|
||||
11. **LiquidHandlerReturnTips** - 归还枪头
|
||||
12. **LiquidHandlerReturnTips96** - 归还96通道枪头
|
||||
13. **LiquidHandlerStamp** - 打印标记
|
||||
14. **LiquidHandlerTransfer** - 液体转移
|
||||
|
||||
这些操作可通过 ROS2 Action 接口进行调用,以实现复杂的移液流程。
|
||||
这些操作可通过ROS2 Action接口进行调用,以实现复杂的移液流程。
|
||||
@@ -19,7 +19,7 @@ Uni-Lab 的组态图当前支持 node-link json 和 graphml 格式,其中包
|
||||
对用户来说,“直接操作设备执行单个指令”不是个真实需求,真正的需求是**“执行对实验有意义的单个完整动作”——加入某种液体多少量;萃取分液;洗涤仪器等等。就像实验步骤文字书写的那样。**
|
||||
|
||||
而这些对实验有意义的单个完整动作,**一般需要多个设备的协同**,还依赖于他们的**物理连接关系(管道相连;机械臂可转运)**。
|
||||
于是 Uni-Lab 实现了抽象的“工作站”,即注册表中的 `workstation` 设备(`WorkstationNode`类)来处理编译、规划操作。以泵骨架组成的自动有机实验室为例,设备管道连接关系如下:
|
||||
于是 Uni-Lab 实现了抽象的“工作站”,即注册表中的 `workstation` 设备(`ProtocolNode`类)来处理编译、规划操作。以泵骨架组成的自动有机实验室为例,设备管道连接关系如下:
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,64 +1,26 @@
|
||||
## 简单单变量动作函数
|
||||
|
||||
|
||||
### `SendCmd`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/SendCmd.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `StrSingleInput`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/StrSingleInput.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `IntSingleInput`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/IntSingleInput.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `FloatSingleInput`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/FloatSingleInput.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Point3DSeparateInput`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Point3DSeparateInput.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Wait`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Wait.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
## 常量有机化学操作
|
||||
|
||||
Uni-Lab 常量有机化学指令集多数来自 [XDL](https://croningroup.gitlab.io/chemputer/xdl/standard/full_steps_specification.html#),包含有机合成实验中常见的操作,如加热、搅拌、冷却等。
|
||||
|
||||
|
||||
|
||||
### `Clean`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Clean.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `EvacuateAndRefill`
|
||||
|
||||
@@ -66,7 +28,7 @@ Uni-Lab 常量有机化学指令集多数来自 [XDL](https://croningroup.gitlab
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `Evaporate`
|
||||
|
||||
@@ -74,7 +36,7 @@ Uni-Lab 常量有机化学指令集多数来自 [XDL](https://croningroup.gitlab
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `HeatChill`
|
||||
|
||||
@@ -82,7 +44,7 @@ Uni-Lab 常量有机化学指令集多数来自 [XDL](https://croningroup.gitlab
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `HeatChillStart`
|
||||
|
||||
@@ -90,7 +52,7 @@ Uni-Lab 常量有机化学指令集多数来自 [XDL](https://croningroup.gitlab
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `HeatChillStop`
|
||||
|
||||
@@ -98,7 +60,7 @@ Uni-Lab 常量有机化学指令集多数来自 [XDL](https://croningroup.gitlab
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `PumpTransfer`
|
||||
|
||||
@@ -106,7 +68,7 @@ Uni-Lab 常量有机化学指令集多数来自 [XDL](https://croningroup.gitlab
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `Separate`
|
||||
|
||||
@@ -114,7 +76,7 @@ Uni-Lab 常量有机化学指令集多数来自 [XDL](https://croningroup.gitlab
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `Stir`
|
||||
|
||||
@@ -122,179 +84,20 @@ Uni-Lab 常量有机化学指令集多数来自 [XDL](https://croningroup.gitlab
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Add`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Add.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `AddSolid`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/AddSolid.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `AdjustPH`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/AdjustPH.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Centrifuge`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Centrifuge.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `CleanVessel`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/CleanVessel.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Crystallize`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Crystallize.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Dissolve`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Dissolve.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Dry`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Dry.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Filter`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Filter.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `FilterThrough`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/FilterThrough.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Hydrogenate`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Hydrogenate.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Purge`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Purge.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Recrystallize`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Recrystallize.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `RunColumn`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/RunColumn.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `StartPurge`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/StartPurge.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `StartStir`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/StartStir.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `StopPurge`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/StopPurge.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `StopStir`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/StopStir.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `Transfer`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/Transfer.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `WashSolid`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/WashSolid.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
## 移液工作站及相关生物自动化设备操作
|
||||
|
||||
Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.org/user_guide/index.html),包含生物实验中常见的操作,如移液、混匀、离心等。
|
||||
|
||||
|
||||
|
||||
### `LiquidHandlerAspirate`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerAspirate.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerDiscardTips`
|
||||
|
||||
@@ -302,7 +105,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerDispense`
|
||||
|
||||
@@ -310,7 +113,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerDropTips`
|
||||
|
||||
@@ -318,7 +121,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerDropTips96`
|
||||
|
||||
@@ -326,7 +129,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerMoveLid`
|
||||
|
||||
@@ -334,7 +137,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerMovePlate`
|
||||
|
||||
@@ -342,7 +145,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerMoveResource`
|
||||
|
||||
@@ -350,7 +153,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerPickUpTips`
|
||||
|
||||
@@ -358,7 +161,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerPickUpTips96`
|
||||
|
||||
@@ -366,7 +169,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerReturnTips`
|
||||
|
||||
@@ -374,7 +177,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerReturnTips96`
|
||||
|
||||
@@ -382,7 +185,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerStamp`
|
||||
|
||||
@@ -390,7 +193,7 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `LiquidHandlerTransfer`
|
||||
|
||||
@@ -398,121 +201,17 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerAdd`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerAdd.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerIncubateBiomek`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerIncubateBiomek.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerMix`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerMix.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerMoveBiomek`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerMoveBiomek.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerMoveTo`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerMoveTo.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerOscillateBiomek`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerOscillateBiomek.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerProtocolCreation`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerProtocolCreation.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerRemove`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerRemove.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerSetGroup`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerSetGroup.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerSetLiquid`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerSetLiquid.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerSetTipRack`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerSetTipRack.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerTransferBiomek`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerTransferBiomek.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `LiquidHandlerTransferGroup`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/LiquidHandlerTransferGroup.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
## 多工作站及小车运行、物料转移
|
||||
|
||||
|
||||
### `AGVTransfer`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/AGVTransfer.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
### `WorkStationRun`
|
||||
|
||||
@@ -520,64 +219,12 @@ Uni-Lab 生物操作指令集多数来自 [PyLabRobot](https://docs.pylabrobot.o
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `ResetHandling`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/ResetHandling.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `ResourceCreateFromOuter`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/ResourceCreateFromOuter.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `ResourceCreateFromOuterEasy`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/ResourceCreateFromOuterEasy.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `SetPumpPosition`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/SetPumpPosition.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 固体分配与处理设备操作
|
||||
|
||||
### `SolidDispenseAddPowderTube`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/SolidDispenseAddPowderTube.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 其他设备操作
|
||||
|
||||
### `EmptyIn`
|
||||
|
||||
```{literalinclude} ../../unilabos_msgs/action/EmptyIn.action
|
||||
:language: yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
## 机械臂、夹爪等机器人设备
|
||||
|
||||
Uni-Lab 机械臂、机器人、夹爪和导航指令集沿用 ROS2 的 `control_msgs` 和 `nav2_msgs`:
|
||||
|
||||
|
||||
### `FollowJointTrajectory`
|
||||
|
||||
```yaml
|
||||
@@ -645,8 +292,7 @@ trajectory_msgs/MultiDOFJointTrajectoryPoint multi_dof_error
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `GripperCommand`
|
||||
|
||||
```yaml
|
||||
@@ -664,19 +310,17 @@ bool reached_goal # True iff the gripper position has reached the commanded setp
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `JointTrajectory`
|
||||
|
||||
```yaml
|
||||
trajectory_msgs/JointTrajectory trajectory
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `PointHead`
|
||||
|
||||
```yaml
|
||||
@@ -686,13 +330,12 @@ string pointing_frame
|
||||
builtin_interfaces/Duration min_duration
|
||||
float64 max_velocity
|
||||
---
|
||||
|
||||
---
|
||||
float64 pointing_angle_error
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `SingleJointPosition`
|
||||
|
||||
```yaml
|
||||
@@ -700,16 +343,15 @@ float64 position
|
||||
builtin_interfaces/Duration min_duration
|
||||
float64 max_velocity
|
||||
---
|
||||
|
||||
---
|
||||
std_msgs/Header header
|
||||
float64 position
|
||||
float64 velocity
|
||||
float64 error
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `AssistedTeleop`
|
||||
|
||||
```yaml
|
||||
@@ -721,10 +363,10 @@ builtin_interfaces/Duration total_elapsed_time
|
||||
---
|
||||
#feedback
|
||||
builtin_interfaces/Duration current_teleop_duration
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `BackUp`
|
||||
|
||||
```yaml
|
||||
@@ -738,10 +380,10 @@ builtin_interfaces/Duration total_elapsed_time
|
||||
---
|
||||
#feedback definition
|
||||
float32 distance_traveled
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `ComputePathThroughPoses`
|
||||
|
||||
```yaml
|
||||
@@ -756,10 +398,10 @@ nav_msgs/Path path
|
||||
builtin_interfaces/Duration planning_time
|
||||
---
|
||||
#feedback definition
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `ComputePathToPose`
|
||||
|
||||
```yaml
|
||||
@@ -774,10 +416,10 @@ nav_msgs/Path path
|
||||
builtin_interfaces/Duration planning_time
|
||||
---
|
||||
#feedback definition
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `DriveOnHeading`
|
||||
|
||||
```yaml
|
||||
@@ -791,10 +433,10 @@ builtin_interfaces/Duration total_elapsed_time
|
||||
---
|
||||
#feedback definition
|
||||
float32 distance_traveled
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `DummyBehavior`
|
||||
|
||||
```yaml
|
||||
@@ -805,10 +447,10 @@ std_msgs/String command
|
||||
builtin_interfaces/Duration total_elapsed_time
|
||||
---
|
||||
#feedback definition
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `FollowPath`
|
||||
|
||||
```yaml
|
||||
@@ -823,10 +465,10 @@ std_msgs/Empty result
|
||||
#feedback definition
|
||||
float32 distance_to_goal
|
||||
float32 speed
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `FollowWaypoints`
|
||||
|
||||
```yaml
|
||||
@@ -838,10 +480,10 @@ int32[] missed_waypoints
|
||||
---
|
||||
#feedback definition
|
||||
uint32 current_waypoint
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `NavigateThroughPoses`
|
||||
|
||||
```yaml
|
||||
@@ -859,10 +501,10 @@ builtin_interfaces/Duration estimated_time_remaining
|
||||
int16 number_of_recoveries
|
||||
float32 distance_remaining
|
||||
int16 number_of_poses_remaining
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `NavigateToPose`
|
||||
|
||||
```yaml
|
||||
@@ -879,10 +521,10 @@ builtin_interfaces/Duration navigation_time
|
||||
builtin_interfaces/Duration estimated_time_remaining
|
||||
int16 number_of_recoveries
|
||||
float32 distance_remaining
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `SmoothPath`
|
||||
|
||||
```yaml
|
||||
@@ -898,10 +540,10 @@ builtin_interfaces/Duration smoothing_duration
|
||||
bool was_completed
|
||||
---
|
||||
#feedback definition
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `Spin`
|
||||
|
||||
```yaml
|
||||
@@ -914,10 +556,10 @@ builtin_interfaces/Duration total_elapsed_time
|
||||
---
|
||||
#feedback definition
|
||||
float32 angular_distance_traveled
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
### `Wait`
|
||||
|
||||
```yaml
|
||||
@@ -929,6 +571,7 @@ builtin_interfaces/Duration total_elapsed_time
|
||||
---
|
||||
#feedback definition
|
||||
builtin_interfaces/Duration time_left
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
----
|
||||
|
||||
@@ -1,142 +1,37 @@
|
||||
# 添加新动作指令(Action)
|
||||
|
||||
本指南将引导你完成添加新动作指令的整个流程,包括编写、在线构建和测试。
|
||||
|
||||
## 1. 编写新的 Action
|
||||
|
||||
### 1.1 创建 Action 文件
|
||||
|
||||
在 `unilabos_msgs/action` 目录中新建实验操作文件,如 `MyDeviceCmd.action`。一个 Action 定义由三个部分组成,分别是目标(Goal)、结果(Result)和反馈(Feedback),之间使用 `---` 分隔:
|
||||
1. 在 `unilabos_msgs/action` 中新建实验操作名和参数列表,如 `MyDeviceCmd.action`。一个 Action 定义由三个部分组成,分别是目标(Goal)、结果(Result)和反馈(Feedback),之间使用 `---` 分隔:
|
||||
|
||||
```action
|
||||
# 目标(Goal)- 定义动作执行所需的参数
|
||||
# 目标(Goal)
|
||||
string command
|
||||
float64 timeout
|
||||
---
|
||||
# 结果(Result)- 定义动作完成后返回的结果
|
||||
bool success # 要求必须包含success,以便回传执行结果
|
||||
string return_info # 要求必须包含return_info,以便回传执行结果
|
||||
... # 其他
|
||||
# 结果(Result)
|
||||
bool success
|
||||
---
|
||||
# 反馈(Feedback)- 定义动作执行过程中的反馈信息
|
||||
float64 progress
|
||||
string status
|
||||
# 反馈(Feedback)
|
||||
```
|
||||
|
||||
### 1.2 更新 CMakeLists.txt
|
||||
|
||||
在 `unilabos_msgs/CMakeLists.txt` 中的 `add_action_files()` 部分添加新定义的 action:
|
||||
2. 在 `unilabos_msgs/CMakeLists.txt` 中添加新定义的 action
|
||||
|
||||
```cmake
|
||||
add_action_files(
|
||||
FILES
|
||||
MyDeviceCmd.action
|
||||
# 其他已有的 action 文件...
|
||||
)
|
||||
```
|
||||
|
||||
## 2. 在线构建和测试
|
||||
|
||||
为了简化开发流程并确保构建环境的一致性,我们使用 GitHub Actions 进行在线构建。
|
||||
|
||||
### 2.1 Fork 仓库并创建分支
|
||||
|
||||
1. **Fork 仓库**:在 GitHub 上 fork `Uni-Lab-OS` 仓库到你的个人账户
|
||||
|
||||
2. **Clone 你的 fork**:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/Uni-Lab-OS.git
|
||||
cd Uni-Lab-OS
|
||||
```
|
||||
|
||||
3. **创建功能分支**:
|
||||
|
||||
```bash
|
||||
git checkout -b add-my-device-action
|
||||
```
|
||||
|
||||
4. **提交你的更改**:
|
||||
```bash
|
||||
git add unilabos_msgs/action/MyDeviceCmd.action
|
||||
git add unilabos_msgs/CMakeLists.txt
|
||||
git commit -m "Add MyDeviceCmd action for device control"
|
||||
git push origin add-my-device-action
|
||||
```
|
||||
|
||||
### 2.2 触发在线构建
|
||||
|
||||
1. **访问你的 fork 仓库**:在浏览器中打开你的 fork 仓库页面
|
||||
|
||||
2. **手动触发构建**:
|
||||
|
||||
- 点击 "Actions" 标签
|
||||
- 选择 "Multi-Platform Conda Build" 工作流
|
||||
- 点击 "Run workflow" 按钮
|
||||
|
||||
3. **监控构建状态**:
|
||||
- 构建过程大约需要 5-10 分钟
|
||||
- 在 Actions 页面可以实时查看构建日志
|
||||
- 构建完成后,可以下载生成的 conda 包进行测试
|
||||
|
||||
### 2.3 下载和测试构建包
|
||||
|
||||
1. **下载构建产物**:
|
||||
|
||||
- 在构建完成的 Action 页面,找到 "Artifacts" 部分
|
||||
- 下载对应平台的 `conda-package-*` 文件
|
||||
|
||||
2. **本地测试安装**:
|
||||
|
||||
```bash
|
||||
# 解压下载的构建产物
|
||||
unzip conda-package-linux-64.zip # 或其他平台
|
||||
|
||||
# 安装测试包
|
||||
mamba install ./ros-humble-unilabos-msgs-*.conda
|
||||
```
|
||||
|
||||
3. **验证 Action 是否正确添加**:
|
||||
```bash
|
||||
# 检查 action 是否可用
|
||||
ros2 interface show unilabos_msgs/action/MyDeviceCmd
|
||||
```
|
||||
|
||||
## 3. 提交 Pull Request
|
||||
|
||||
测试成功后,向主仓库提交 Pull Request:
|
||||
|
||||
1. **创建 Pull Request**:
|
||||
|
||||
- 在你的 fork 仓库页面,点击 "New Pull Request"
|
||||
- 选择你的功能分支作为源分支
|
||||
- 填写详细的 PR 描述,包括:
|
||||
- 添加的 Action 功能说明
|
||||
- 测试结果
|
||||
- 相关的设备或用例
|
||||
|
||||
2. **等待审核和合并**:
|
||||
- 维护者会审核你的代码
|
||||
- CI/CD 系统会自动运行完整的测试套件
|
||||
- 合并后,新的指令集会自动发布到官方 conda 仓库
|
||||
|
||||
## 4. 使用新的 Action
|
||||
|
||||
如果采用自己构建的action包,可以通过以下命令更新安装:
|
||||
3. 因为在指令集中新建了指令,因此调试时需要编译,并在终端环境中加载临时路径:
|
||||
|
||||
```bash
|
||||
mamba remove --force ros-humble-unilabos-msgs
|
||||
mamba config set safety_checks disabled # 如果没有提升版本号,会触发md5与网络上md5不一致,是正常现象,因此通过本指令关闭md5检查
|
||||
mamba install xxx.conda --offline
|
||||
cd unilabos_msgs
|
||||
colcon build
|
||||
source ./install/local_setup.sh
|
||||
cd ..
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
调试成功后,发起 pull request,Uni-Lab 的 CI/CD 系统会自动将新的指令集编译打包,mamba执行升级即可永久生效:
|
||||
|
||||
**Q: 构建失败怎么办?**
|
||||
A: 检查 Actions 日志中的错误信息,通常是语法错误或依赖问题。修复后重新推送代码即可自动触发新的构建。
|
||||
|
||||
**Q: 如何测试特定平台?**
|
||||
A: 在手动触发构建时,在平台选择中只填写你需要的平台,如 `linux-64` 或 `win-64`。
|
||||
|
||||
**Q: 构建包在哪里下载?**
|
||||
A: 在 Actions 页面的构建结果中,查找 "Artifacts" 部分,每个平台都有对应的构建包可供下载。
|
||||
```bash
|
||||
mamba update ros-humble-unilabos-msgs -c http://quetz.dp.tech:8088/get/unilab -c robostack-humble -c robostack-staging
|
||||
```
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
# 电池装配工站接入(PLC)
|
||||
|
||||
本指南将引导你完成电池装配工站(以 PLC 控制为例)的接入流程,包括新建工站文件、编写驱动与寄存器读写、生成注册表、上传及注意事项。
|
||||
|
||||
## 1. 新建工站文件
|
||||
|
||||
### 1.1 创建工站文件
|
||||
|
||||
在 `unilabos/devices/workstation/coin_cell_assembly` 目录下新建工站文件,如 `coin_cell_assembly.py`。工站类需继承 `WorkstationBase`,并在构造函数中初始化通信客户端与寄存器映射。
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
# 工站基类
|
||||
from unilabos.devices.workstation.workstation_base import WorkstationBase
|
||||
# Modbus 通讯与寄存器 CSV 支持
|
||||
from unilabos.device_comms.modbus_plc.client import TCPClient, BaseClient
|
||||
|
||||
class CoinCellAssemblyWorkstation(WorkstationBase):
|
||||
def __init__(
|
||||
self,
|
||||
station_resource,
|
||||
address: str = "192.168.1.20",
|
||||
port: str = "502",
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(station_resource=station_resource, *args, **kwargs)
|
||||
self.station_resource = station_resource # 物料台面(Deck)
|
||||
self.success: bool = False
|
||||
self.allow_data_read: bool = False
|
||||
self.csv_export_thread = None
|
||||
self.csv_export_running = False
|
||||
self.csv_export_file: Optional[str] = None
|
||||
|
||||
# 连接 PLC,并注册寄存器节点
|
||||
tcp = TCPClient(addr=address, port=port)
|
||||
tcp.client.connect()
|
||||
self.nodes = BaseClient.load_csv(".../PLC_register.csv")
|
||||
self.client = tcp.register_node_list(self.nodes)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 2. 编写驱动与寄存器读写
|
||||
|
||||
### 2.1 寄存器示例
|
||||
|
||||
- `COIL_SYS_START_CMD`(BOOL,地址 8010):启动命令(脉冲式)
|
||||
- `COIL_SYS_START_STATUS`(BOOL,地址 8210):启动状态
|
||||
- `REG_DATA_OPEN_CIRCUIT_VOLTAGE`(FLOAT32,地址 10002):开路电压
|
||||
- `REG_DATA_ASSEMBLY_PRESSURE`(INT16,地址 10014):压制扣电压力
|
||||
|
||||
### 2.2 最小驱动示例
|
||||
|
||||
```python
|
||||
from unilabos.device_comms.modbus_plc.modbus import WorderOrder
|
||||
|
||||
def start_and_read_metrics(self):
|
||||
# 1) 下发启动(置 True 再复位 False)
|
||||
self.client.use_node('COIL_SYS_START_CMD').write(True)
|
||||
self.client.use_node('COIL_SYS_START_CMD').write(False)
|
||||
|
||||
# 2) 等待进入启动状态
|
||||
while True:
|
||||
status, _ = self.client.use_node('COIL_SYS_START_STATUS').read(1)
|
||||
if bool(status[0]):
|
||||
break
|
||||
|
||||
# 3) 读取关键数据(FLOAT32 需读 2 个寄存器并指定字节序)
|
||||
voltage, _ = self.client.use_node('REG_DATA_OPEN_CIRCUIT_VOLTAGE').read(
|
||||
2, word_order=WorderOrder.LITTLE
|
||||
)
|
||||
pressure, _ = self.client.use_node('REG_DATA_ASSEMBLY_PRESSURE').read(1)
|
||||
|
||||
return {
|
||||
'open_circuit_voltage': voltage,
|
||||
'assembly_pressure': pressure,
|
||||
}
|
||||
```
|
||||
|
||||
> 提示:若需参数下发,可在 PLC 端设置标志寄存器并完成握手复位,避免粘连与竞争。
|
||||
|
||||
## 3. 本地生成注册表并校验
|
||||
|
||||
完成工站类与驱动后,需要生成(或更新)工站注册表供系统识别。
|
||||
|
||||
|
||||
### 3.1 新增工站设备(或资源)首次生成注册表
|
||||
首先通过以下命令启动unilab。进入unilab系统状态检查页面
|
||||
|
||||
```bash
|
||||
python unilabos\app\main.py -g celljson.json --ak <user的AK> --sk <user的SK>
|
||||
```
|
||||
|
||||
点击注册表编辑,进入注册表编辑页面
|
||||

|
||||
|
||||
按照图示步骤填写自动生成注册表信息:
|
||||

|
||||
|
||||
步骤说明:
|
||||
1. 选择新增的工站`coin_cell_assembly.py`文件
|
||||
2. 点击分析按钮,分析`coin_cell_assembly.py`文件
|
||||
3. 选择`coin_cell_assembly.py`文件中继承`WorkstationBase`类
|
||||
4. 填写新增的工站.py文件与`unilabos`目录的距离。例如,新增的工站文件`coin_cell_assembly.py`路径为`unilabos\devices\workstation\coin_cell_assembly\coin_cell_assembly.py`,则此处填写`unilabos.devices.workstation.coin_cell_assembly`。
|
||||
5. 此处填写新定义工站的类的名字(名称可以自拟)
|
||||
6. 填写新的工站注册表备注信息
|
||||
7. 生成注册表
|
||||
|
||||
以上操作步骤完成,则会生成的新的注册表ymal文件,如下图:
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 3.2 添加新生成注册表
|
||||
在`unilabos\registry\devices`目录下新建一个yaml文件,此处新建文件命名为`coincellassemblyworkstation_device.yaml`,将上面生成的新的注册表信息粘贴到`coincellassemblyworkstation_device.yaml`文件中。
|
||||
|
||||
在终端输入以下命令进行注册表补全操作。
|
||||
```bash
|
||||
python unilabos\app\register.py --complete_registry
|
||||
```
|
||||
|
||||
|
||||
### 3.3 启动并上传注册表
|
||||
|
||||
新增设备之后,启动unilab需要增加`--upload_registry`参数,来上传注册表信息。
|
||||
|
||||
```bash
|
||||
python unilabos\app\main.py -g celljson.json --ak <user的AK> --sk <user的SK> --upload_registry
|
||||
```
|
||||
|
||||
## 4. 注意事项
|
||||
|
||||
- 在新生成的 YAML 中,确认 `module` 指向新工站类,本例中需检查`coincellassemblyworkstation_device.yaml`文件中是否指向了`coin_cell_assembly.py`文件中定义的`CoinCellAssemblyWorkstation`类文件:
|
||||
|
||||
```
|
||||
module: unilabos.devices.workstation.coin_cell_assembly.coin_cell_assembly:CoinCellAssemblyWorkstation
|
||||
```
|
||||
|
||||
- 首次新增设备(或资源)需要在网页端新增注册表信息,`--complete_registry`补全注册表,`--upload_registry`上传注册表信息。
|
||||
|
||||
- 如果不是新增设备(或资源),仅对工站驱动的.py文件进行了修改,则不需要在网页端新增注册表信息。只需要运行补全注册表信息之后,上传注册表即可。
|
||||
|
||||
|
||||
@@ -13,36 +13,36 @@ class MockGripper:
|
||||
self._velocity: float = 2.0
|
||||
self._torque: float = 0.0
|
||||
self._status = "Idle"
|
||||
|
||||
|
||||
@property
|
||||
def position(self) -> float:
|
||||
return self._position
|
||||
|
||||
|
||||
@property
|
||||
def velocity(self) -> float:
|
||||
return self._velocity
|
||||
|
||||
|
||||
@property
|
||||
def torque(self) -> float:
|
||||
return self._torque
|
||||
|
||||
|
||||
# 会被自动识别的设备属性,接入 Uni-Lab 时会定时对外广播
|
||||
@property
|
||||
def status(self) -> str:
|
||||
return self._status
|
||||
|
||||
|
||||
# 会被自动识别的设备动作,接入 Uni-Lab 时会作为 ActionServer 接受任意控制者的指令
|
||||
@status.setter
|
||||
def status(self, target):
|
||||
self._status = target
|
||||
|
||||
|
||||
# 需要在注册表添加的设备动作,接入 Uni-Lab 时会作为 ActionServer 接受任意控制者的指令
|
||||
def push_to(self, position: float, torque: float, velocity: float = 0.0):
|
||||
self._status = "Running"
|
||||
current_pos = self.position
|
||||
if velocity == 0.0:
|
||||
velocity = self.velocity
|
||||
|
||||
|
||||
move_time = abs(position - current_pos) / velocity
|
||||
for i in range(20):
|
||||
self._position = current_pos + (position - current_pos) / 20 * (i+1)
|
||||
@@ -68,7 +68,7 @@ public class MockGripper
|
||||
public double velocity { get; private set; } = 2.0;
|
||||
public double torque { get; private set; } = 0.0;
|
||||
public string status { get; private set; } = "Idle";
|
||||
|
||||
|
||||
// 需要在注册表添加的设备动作,接入 Uni-Lab 时会作为 ActionServer 接受任意控制者的指令
|
||||
public async Task PushToAsync(double Position, double Torque, double Velocity = 0.0)
|
||||
{
|
||||
@@ -94,61 +94,107 @@ public class MockGripper
|
||||
|
||||
C# 驱动设备在完成注册表后,需要调用 Uni-Lab C# 编译后才能使用,但只需一次。
|
||||
|
||||
## 快速开始:使用注册表编辑器(推荐)
|
||||
## 注册表文件位置
|
||||
|
||||
推荐使用 Uni-Lab-OS 自带的可视化编辑器,它能自动分析您的设备驱动并生成大部分配置:
|
||||
Uni-Lab 启动时会自动读取默认注册表路径 `unilabos/registry/devices` 下的所有注册设备。您也可以任意维护自己的注册表路径,只需要在 Uni-Lab 启动时使用 `--registry` 参数将路径添加即可。
|
||||
|
||||
1. 启动 Uni-Lab-OS
|
||||
2. 在浏览器中打开"注册表编辑器"页面
|
||||
3. 选择您的 Python 设备驱动文件
|
||||
4. 点击"分析文件",让系统读取类信息
|
||||
5. 填写基本信息(设备描述、图标等)
|
||||
6. 点击"生成注册表",复制生成的内容
|
||||
7. 保存到 `devices/` 目录下
|
||||
在 `<path-to-registry>/devices` 中新建一个 yaml 文件,即可开始撰写。您可以将多个设备写到同一个 yaml 文件中。
|
||||
|
||||
---
|
||||
## 注册表的结构
|
||||
|
||||
## 手动编写注册表(简化版)
|
||||
1. 顶层名称:每个设备的注册表以设备名称开头,例如 `new_device`, `gripper.mock`。
|
||||
1. `class` 字段:定义设备的模块路径和驱动程序语言。
|
||||
1. `status_types` 字段:定义设备定时对 Uni-Lab 实验室内发送的属性名及其类型。
|
||||
1. `action_value_mappings` 字段:定义设备支持的动作及其目标、反馈和结果。
|
||||
1. `schema` 字段:定义设备定时对 Uni-Lab 云端监控发送的属性名及其类型、描述(非必须)
|
||||
|
||||
如果需要手动编写,只需要提供两个必需字段,系统会自动补全其余内容:
|
||||
## 创建新的注册表教程
|
||||
|
||||
### 最小配置示例
|
||||
1. 创建文件
|
||||
在 devices 文件夹中创建一个新的 YAML 文件,例如 `new_device.yaml`。
|
||||
2. 定义设备名称
|
||||
在文件中定义设备的顶层名称,例如:`new_device` 或 `gripper.mock`
|
||||
3. 定义设备的类信息
|
||||
添加设备的模块路径和类型:
|
||||
|
||||
```yaml
|
||||
my_device: # 设备唯一标识符
|
||||
class:
|
||||
module: unilabos.devices.your_module.my_device:MyDevice # Python 类路径
|
||||
type: python # 驱动类型
|
||||
gripper.mock:
|
||||
class: # 定义设备的类信息
|
||||
module: unilabos.devices.gripper.mock:MockGripper
|
||||
type: python # 指定驱动语言为 Python
|
||||
status_types:
|
||||
position: Float64
|
||||
torque: Float64
|
||||
status: String
|
||||
```
|
||||
|
||||
### 注册表文件位置
|
||||
|
||||
- 默认路径:`unilabos/registry/devices`
|
||||
- 自定义路径:启动时使用 `--registry` 参数指定
|
||||
- 可将多个设备写在同一个 yaml 文件中
|
||||
|
||||
### 系统自动生成的内容
|
||||
|
||||
系统会自动分析您的 Python 驱动类并生成:
|
||||
|
||||
- `status_types`:从 `get_*` 方法自动识别状态属性
|
||||
- `action_value_mappings`:从类方法自动生成动作映射
|
||||
- `init_param_schema`:从 `__init__` 方法分析初始化参数
|
||||
- `schema`:前端显示用的属性类型定义
|
||||
|
||||
### 完整结构概览
|
||||
4. 定义设备的定时发布属性。注意,对于 Python Class 来说,PROP 是 class 的 `property`,或满足能被 `getattr(cls, PROP)` 或 `cls.get_PROP` 读取到的属性值的对象。
|
||||
|
||||
```yaml
|
||||
my_device:
|
||||
class:
|
||||
module: unilabos.devices.your_module.my_device:MyDevice
|
||||
type: python
|
||||
status_types: {} # 自动生成
|
||||
action_value_mappings: {} # 自动生成
|
||||
description: '' # 可选:设备描述
|
||||
icon: '' # 可选:设备图标
|
||||
init_param_schema: {} # 自动生成
|
||||
schema: {} # 自动生成
|
||||
status_types:
|
||||
PROP: TYPE
|
||||
```
|
||||
5. 定义设备支持的动作
|
||||
添加设备支持的动作及其目标、反馈和结果:
|
||||
|
||||
```yaml
|
||||
action_value_mappings:
|
||||
set_speed:
|
||||
type: SendCmd
|
||||
goal:
|
||||
command: speed
|
||||
feedback: {}
|
||||
result:
|
||||
success: success
|
||||
```
|
||||
|
||||
详细的注册表编写指南和高级配置,请参考{doc}`yaml 注册表编写指南 <add_yaml>`。
|
||||
在 devices 文件夹中的 YAML 文件中,action_value_mappings 是用来将驱动内的动作函数,映射到 Uni-Lab 标准动作(actions)及其目标参数值(goal)、反馈值(feedback)和结果值(result)的映射规则。若在 Uni-Lab 指令集内找不到符合心意的,请【创建新指令】。
|
||||
|
||||
```yaml
|
||||
action_value_mappings:
|
||||
<action_name>: # <action_name>:动作的名称
|
||||
# start:启动设备或某个功能。
|
||||
# stop:停止设备或某个功能。
|
||||
# set_speed:设置设备的速度。
|
||||
# set_temperature:设置设备的温度。
|
||||
# move_to_position:移动设备到指定位置。
|
||||
# stir:执行搅拌操作。
|
||||
# heatchill:执行加热或冷却操作。
|
||||
# send_nav_task:发送导航任务(例如机器人导航)。
|
||||
# set_timer:设置设备的计时器。
|
||||
# valve_open_cmd:打开阀门。
|
||||
# valve_close_cmd:关闭阀门。
|
||||
# execute_command_from_outer:执行外部命令。
|
||||
# push_to:控制设备推送到某个位置(例如机械爪)。
|
||||
# move_through_points:导航设备通过多个点。
|
||||
|
||||
type: <ActionType> # 动作的类型,表示动作的功能
|
||||
# 根据动作的功能选择合适的类型,请查阅 Uni-Lab 已支持的指令集。
|
||||
|
||||
goal: # 定义动作的目标值映射,表示需要传递给设备的参数。
|
||||
<goal_key>: <mapped_value> #确定设备需要的输入参数,并将其映射到设备的字段。
|
||||
|
||||
feedback: # 定义动作的反馈值映射,表示设备执行动作时返回的实时状态。
|
||||
<feedback_key>: <mapped_value>
|
||||
result: # 定义动作的结果值映射,表示动作完成后返回的最终结果。
|
||||
<result_key>: <mapped_value>
|
||||
```
|
||||
|
||||
6. 定义设备的网页展示属性类型,这部分会被用于在 Uni-Lab 网页端渲染成状态监控
|
||||
添加设备的属性模式,包括属性类型和描述:
|
||||
|
||||
```yaml
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
description: The status of the device
|
||||
speed:
|
||||
type: number
|
||||
description: The speed of the device
|
||||
required:
|
||||
- status
|
||||
- speed
|
||||
additionalProperties: false
|
||||
```
|
||||
|
||||
@@ -1,610 +1,95 @@
|
||||
# yaml 注册表编写指南
|
||||
# yaml注册表编写指南
|
||||
|
||||
## 快速开始:使用注册表编辑器
|
||||
`注册表的结构`
|
||||
|
||||
推荐使用 UniLabOS 自带的可视化编辑器,它能帮你自动生成大部分配置,省去手写的麻烦。
|
||||
1. 顶层名称:每个设备的注册表以设备名称开头,例如 new_device。
|
||||
2. class 字段:定义设备的模块路径和类型。
|
||||
3. schema 字段:定义设备的属性模式,包括属性类型、描述和必需字段。
|
||||
4. action_value_mappings 字段:定义设备支持的动作及其目标、反馈和结果。
|
||||
|
||||
### 怎么用编辑器
|
||||
`创建新的注册表教程`
|
||||
1. 创建文件
|
||||
在 devices 文件夹中创建一个新的 YAML 文件,例如 new_device.yaml。
|
||||
|
||||
1. 启动 UniLabOS
|
||||
2. 在浏览器中打开"注册表编辑器"页面
|
||||
3. 选择你的 Python 设备驱动文件
|
||||
4. 点击"分析文件",让系统读取你的类信息
|
||||
5. 填写一些基本信息(设备描述、图标啥的)
|
||||
6. 点击"生成注册表",复制生成的内容
|
||||
7. 把内容保存到 `devices/` 目录下
|
||||
2. 定义设备名称
|
||||
在文件中定义设备的顶层名称,例如:new_device
|
||||
|
||||
我们为你准备了一个测试驱动,用于在界面上尝试注册表生成,参见目录:test\registry\example_devices.py
|
||||
|
||||
---
|
||||
|
||||
## 手动编写指南
|
||||
|
||||
如果你想自己写 yaml 文件,或者想深入了解结构,查阅下方说明。
|
||||
|
||||
## 注册表的基本结构
|
||||
|
||||
yaml 注册表就是设备的配置文件,里面定义了设备怎么用、有什么功能。好消息是系统会自动帮你填大部分内容,你只需要写两个必需的东西:设备名和 class 信息。
|
||||
|
||||
### 各字段用途
|
||||
|
||||
| 字段名 | 类型 | 需要手写 | 说明 |
|
||||
| ----------------- | ------ | -------- | ----------------------------------- |
|
||||
| 设备标识符 | string | 是 | 设备的唯一名字,比如 `mock_chiller` |
|
||||
| class | object | 部分 | 设备的核心信息,必须写 |
|
||||
| description | string | 否 | 设备描述,系统默认给空字符串 |
|
||||
| handles | array | 否 | 连接关系,默认是空的 |
|
||||
| icon | string | 否 | 图标路径,默认为空 |
|
||||
| init_param_schema | object | 否 | 初始化参数,系统自动分析生成 |
|
||||
| version | string | 否 | 版本号,默认 "1.0.0" |
|
||||
| category | array | 否 | 设备分类,默认用文件名 |
|
||||
| config_info | array | 否 | 嵌套配置,默认为空 |
|
||||
| file_path | string | 否 | 文件路径,系统自动设置 |
|
||||
| registry_type | string | 否 | 注册表类型,自动设为 "device" |
|
||||
|
||||
### class 字段里有啥
|
||||
|
||||
class 是核心部分,包含这些内容:
|
||||
|
||||
| 字段名 | 类型 | 需要手写 | 说明 |
|
||||
| --------------------- | ------ | -------- | ---------------------------------- |
|
||||
| module | string | 是 | Python 类的路径,必须写 |
|
||||
| type | string | 是 | 驱动类型,一般写 "python" |
|
||||
| status_types | object | 否 | 状态类型,系统自动分析生成 |
|
||||
| action_value_mappings | object | 部分 | 动作配置,系统会自动生成一些基础的 |
|
||||
|
||||
## 怎么创建新的注册表
|
||||
|
||||
### 创建文件
|
||||
|
||||
在 devices 文件夹里新建一个 yaml 文件,比如 `new_device.yaml`。
|
||||
|
||||
### 完整结构是什么样的
|
||||
|
||||
```yaml
|
||||
new_device: # 设备名,要唯一
|
||||
class: # 核心配置
|
||||
action_value_mappings: # 动作配置(后面会详细说)
|
||||
action_name:
|
||||
# 具体的动作设置
|
||||
module: unilabos.devices.your_module.new_device:NewDeviceClass # 你的 Python 类
|
||||
status_types: # 状态类型(系统会自动生成)
|
||||
status: str
|
||||
temperature: float
|
||||
# 其他状态
|
||||
type: python # 驱动类型,一般就是 python
|
||||
|
||||
description: New Device Description # 设备描述
|
||||
handles: [] # 连接关系,通常是空的
|
||||
icon: '' # 图标路径
|
||||
init_param_schema: # 初始化参数(系统会自动生成)
|
||||
config: # 初始化时需要的参数
|
||||
properties:
|
||||
port:
|
||||
default: DEFAULT_PORT
|
||||
type: string
|
||||
required: []
|
||||
type: object
|
||||
data: # 前端显示用的数据类型
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
temperature:
|
||||
type: number
|
||||
required:
|
||||
- status
|
||||
type: object
|
||||
|
||||
version: 0.0.1 # 版本号
|
||||
category:
|
||||
- device_category # 设备类别
|
||||
config_info: [] # 嵌套配置,通常为空
|
||||
```
|
||||
|
||||
## action_value_mappings 怎么写
|
||||
|
||||
这个部分定义设备能做哪些动作。好消息是系统会自动生成大部分动作,你通常只需要添加一些特殊的自定义动作。
|
||||
|
||||
### 系统自动生成哪些动作
|
||||
|
||||
系统会帮你生成这些:
|
||||
|
||||
1. 以 `auto-` 开头的动作:从你 Python 类的方法自动生成
|
||||
2. 通用的驱动动作:
|
||||
- `_execute_driver_command`:同步执行驱动命令(仅本地可用)
|
||||
- `_execute_driver_command_async`:异步执行驱动命令(仅本地可用)
|
||||
|
||||
### 如果要手动定义动作
|
||||
|
||||
如果你需要自定义一些特殊动作,需要这些字段:
|
||||
|
||||
| 字段名 | 需要手写 | 说明 |
|
||||
| ---------------- | -------- | -------------------------------- |
|
||||
| type | 是 | 动作类型,必须指定 |
|
||||
| goal | 是 | 输入参数怎么映射 |
|
||||
| feedback | 否 | 实时反馈,通常为空 |
|
||||
| result | 是 | 结果怎么返回 |
|
||||
| goal_default | 部分 | 参数默认值,ROS 动作会自动生成 |
|
||||
| schema | 部分 | 前端表单配置,ROS 动作会自动生成 |
|
||||
| handles | 否 | 连接关系,默认为空 |
|
||||
| placeholder_keys | 否 | 特殊输入字段配置 |
|
||||
|
||||
### 动作类型有哪些
|
||||
|
||||
| 类型 | 什么时候用 | 系统会自动生成什么 |
|
||||
| ---------------------- | -------------------- | ---------------------- |
|
||||
| UniLabJsonCommand | 自定义同步 JSON 命令 | 啥都不生成 |
|
||||
| UniLabJsonCommandAsync | 自定义异步 JSON 命令 | 啥都不生成 |
|
||||
| ROS 动作类型 | 标准 ROS 动作 | goal_default 和 schema |
|
||||
|
||||
常用的 ROS 动作类型:
|
||||
|
||||
- `SendCmd`:发送简单命令
|
||||
- `NavigateThroughPoses`:导航动作
|
||||
- `SingleJointPosition`:单关节位置控制
|
||||
- `Stir`:搅拌动作
|
||||
- `HeatChill`、`HeatChillStart`:加热冷却动作
|
||||
|
||||
### 复杂一点的例子
|
||||
|
||||
```yaml
|
||||
heat_chill_start:
|
||||
type: HeatChillStart
|
||||
goal:
|
||||
purpose: purpose
|
||||
temp: temp
|
||||
goal_default: # ROS动作会自动生成,你也可以手动覆盖
|
||||
purpose: ''
|
||||
temp: 0.0
|
||||
handles:
|
||||
output:
|
||||
- handler_key: labware
|
||||
label: Labware
|
||||
data_type: resource
|
||||
data_source: handle
|
||||
data_key: liquid
|
||||
placeholder_keys:
|
||||
purpose: unilabos_resources
|
||||
result:
|
||||
status: status
|
||||
success: success
|
||||
# schema 系统会自动生成,不用写
|
||||
```
|
||||
|
||||
### 动作名字怎么起
|
||||
|
||||
根据设备用途来起名字:
|
||||
|
||||
- 启动停止类:`start`、`stop`、`pause`、`resume`
|
||||
- 设置参数类:`set_speed`、`set_temperature`、`set_timer`
|
||||
- 移动控制类:`move_to_position`、`move_through_points`
|
||||
- 功能操作类:`stir`、`heat_chill_start`、`heat_chill_stop`
|
||||
- 开关控制类:`valve_open_cmd`、`valve_close_cmd`、`push_to`
|
||||
- 命令执行类:`send_nav_task`、`execute_command_from_outer`
|
||||
|
||||
### 常用的动作类型
|
||||
|
||||
- `UniLabJsonCommand`:自定义 JSON 命令(不走 ROS)
|
||||
- `UniLabJsonCommandAsync`:异步 JSON 命令(不走 ROS)
|
||||
- `SendCmd`:发送简单命令
|
||||
- `NavigateThroughPoses`:导航相关
|
||||
- `SingleJointPosition`:单关节控制
|
||||
- `Stir`:搅拌
|
||||
- `HeatChill`、`HeatChillStart`:加热冷却
|
||||
- 其他的 ROS 动作类型:看具体的 ROS 服务
|
||||
|
||||
### 示例:完整的动作配置
|
||||
|
||||
```yaml
|
||||
heat_chill_start:
|
||||
type: HeatChillStart
|
||||
goal:
|
||||
purpose: purpose
|
||||
temp: temp
|
||||
goal_default:
|
||||
purpose: ''
|
||||
temp: 0.0
|
||||
handles:
|
||||
output:
|
||||
- handler_key: labware
|
||||
label: Labware
|
||||
data_type: resource
|
||||
data_source: handle
|
||||
data_key: liquid
|
||||
placeholder_keys:
|
||||
purpose: unilabos_resources
|
||||
result:
|
||||
status: status
|
||||
success: success
|
||||
schema:
|
||||
description: '启动加热冷却功能'
|
||||
properties:
|
||||
goal:
|
||||
properties:
|
||||
purpose:
|
||||
type: string
|
||||
description: '用途说明'
|
||||
temp:
|
||||
type: number
|
||||
description: '目标温度'
|
||||
required:
|
||||
- purpose
|
||||
- temp
|
||||
title: HeatChillStart_Goal
|
||||
type: object
|
||||
required:
|
||||
- goal
|
||||
title: HeatChillStart
|
||||
type: object
|
||||
feedback: {}
|
||||
```
|
||||
|
||||
## 系统自动生成的字段
|
||||
|
||||
### status_types
|
||||
|
||||
系统会扫描你的 Python 类,从状态方法自动生成这部分:
|
||||
|
||||
```yaml
|
||||
status_types:
|
||||
current_temperature: float # 从 get_current_temperature() 方法来的
|
||||
is_heating: bool # 从 get_is_heating() 方法来的
|
||||
status: str # 从 get_status() 方法来的
|
||||
```
|
||||
|
||||
注意几点:
|
||||
|
||||
- 系统会找所有 `get_` 开头的方法
|
||||
- 类型会自动转成 ROS 类型(比如 `str` 变成 `String`)
|
||||
- 如果类型是 `Any`、`None` 或者不知道的,就默认用 `String`
|
||||
|
||||
### init_param_schema
|
||||
|
||||
这个完全是系统自动生成的,你不用管:
|
||||
|
||||
```yaml
|
||||
init_param_schema:
|
||||
config: # 从你类的 __init__ 方法分析出来的
|
||||
properties:
|
||||
port:
|
||||
type: string
|
||||
default: '/dev/ttyUSB0'
|
||||
baudrate:
|
||||
type: integer
|
||||
default: 9600
|
||||
required: []
|
||||
type: object
|
||||
|
||||
data: # 根据 status_types 生成的前端用的类型
|
||||
properties:
|
||||
current_temperature:
|
||||
type: number
|
||||
is_heating:
|
||||
type: boolean
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
type: object
|
||||
```
|
||||
|
||||
生成规则很简单:
|
||||
|
||||
- `config` 部分:看你类的 `__init__` 方法有什么参数,类型和默认值是啥
|
||||
- `data` 部分:根据 `status_types` 生成前端显示用的类型定义
|
||||
|
||||
### 其他自动填充的字段
|
||||
|
||||
```yaml
|
||||
version: '1.0.0' # 默认版本
|
||||
category: ['文件名'] # 用你的 yaml 文件名当类别
|
||||
description: '' # 默认为空,你可以手动改
|
||||
icon: '' # 默认为空,你可以加图标
|
||||
handles: [] # 默认空数组
|
||||
config_info: [] # 默认空数组
|
||||
file_path: '/path/to/file' # 系统自动填文件路径
|
||||
registry_type: 'device' # 自动设为设备类型
|
||||
```
|
||||
|
||||
### handles 字段
|
||||
|
||||
这个是定义设备连接关系的,类似动作里的 handles 一样:
|
||||
|
||||
```yaml
|
||||
handles: # 大多数时候都是空的,除非设备本身需要连接啥
|
||||
- handler_key: device_output
|
||||
label: Device Output
|
||||
data_type: resource
|
||||
data_source: value
|
||||
data_key: default_value
|
||||
```
|
||||
|
||||
### 其他可以配置的字段
|
||||
|
||||
```yaml
|
||||
description: '设备的详细描述' # 写清楚设备是干啥的
|
||||
|
||||
icon: 'device_icon.webp' # 设备图标,文件名(会上传到OSS)
|
||||
|
||||
version: '0.0.1' # 版本号
|
||||
|
||||
category: # 设备分类,前端会用这个分组
|
||||
- 'heating'
|
||||
- 'cooling'
|
||||
- 'temperature_control'
|
||||
|
||||
config_info: # 嵌套配置,如果设备包含子设备
|
||||
- children:
|
||||
- opentrons_24_tuberack_nest_1point5ml_snapcap_A1
|
||||
- other_nested_component
|
||||
```
|
||||
|
||||
## 完整的例子
|
||||
|
||||
这里是一个比较完整的设备配置示例:
|
||||
|
||||
```yaml
|
||||
my_temperature_controller:
|
||||
class:
|
||||
action_value_mappings:
|
||||
heat_start:
|
||||
type: HeatChillStart
|
||||
goal:
|
||||
target_temp: temp
|
||||
vessel: vessel
|
||||
goal_default:
|
||||
target_temp: 25.0
|
||||
vessel: ''
|
||||
handles:
|
||||
output:
|
||||
- handler_key: heated_sample
|
||||
label: Heated Sample
|
||||
data_type: resource
|
||||
data_source: handle
|
||||
data_key: sample
|
||||
placeholder_keys:
|
||||
vessel: unilabos_resources
|
||||
result:
|
||||
status: status
|
||||
success: success
|
||||
schema:
|
||||
description: '启动加热功能'
|
||||
properties:
|
||||
goal:
|
||||
properties:
|
||||
target_temp:
|
||||
type: number
|
||||
description: '目标温度'
|
||||
vessel:
|
||||
type: string
|
||||
description: '容器标识'
|
||||
required:
|
||||
- target_temp
|
||||
- vessel
|
||||
title: HeatStart_Goal
|
||||
type: object
|
||||
required:
|
||||
- goal
|
||||
title: HeatStart
|
||||
type: object
|
||||
feedback: {}
|
||||
|
||||
stop:
|
||||
type: UniLabJsonCommand
|
||||
goal: {}
|
||||
goal_default: {}
|
||||
handles: {}
|
||||
result:
|
||||
status: status
|
||||
schema:
|
||||
description: '停止设备'
|
||||
properties:
|
||||
goal:
|
||||
type: object
|
||||
title: Stop_Goal
|
||||
title: Stop
|
||||
type: object
|
||||
feedback: {}
|
||||
|
||||
module: unilabos.devices.temperature.my_controller:MyTemperatureController
|
||||
status_types:
|
||||
current_temperature: float
|
||||
target_temperature: float
|
||||
is_heating: bool
|
||||
is_cooling: bool
|
||||
status: str
|
||||
vessel: str
|
||||
type: python
|
||||
|
||||
description: '我的温度控制器设备'
|
||||
handles: []
|
||||
icon: 'temperature_controller.webp'
|
||||
init_param_schema:
|
||||
config:
|
||||
properties:
|
||||
port:
|
||||
default: '/dev/ttyUSB0'
|
||||
type: string
|
||||
baudrate:
|
||||
default: 9600
|
||||
type: number
|
||||
required: []
|
||||
type: object
|
||||
data:
|
||||
properties:
|
||||
current_temperature:
|
||||
type: number
|
||||
target_temperature:
|
||||
type: number
|
||||
is_heating:
|
||||
type: boolean
|
||||
is_cooling:
|
||||
type: boolean
|
||||
status:
|
||||
type: string
|
||||
vessel:
|
||||
type: string
|
||||
required:
|
||||
- current_temperature
|
||||
- target_temperature
|
||||
- status
|
||||
type: object
|
||||
|
||||
version: '1.0.0'
|
||||
category:
|
||||
- 'temperature_control'
|
||||
- 'heating'
|
||||
config_info: []
|
||||
```
|
||||
|
||||
## 怎么部署和使用
|
||||
|
||||
### 方法一:用编辑器(推荐)
|
||||
|
||||
1. 先写好你的 Python 驱动类
|
||||
2. 用注册表编辑器自动生成 yaml 配置
|
||||
3. 把生成的文件保存到 `devices/` 目录
|
||||
4. 重启 UniLabOS 就能用了
|
||||
|
||||
### 方法二:手动写(简化版)
|
||||
|
||||
1. 创建最简配置:
|
||||
|
||||
```yaml
|
||||
# devices/my_device.yaml
|
||||
my_device:
|
||||
class:
|
||||
module: unilabos.devices.my_module.my_device:MyDevice
|
||||
type: python
|
||||
```
|
||||
|
||||
2. 启动系统时用 `complete_registry=True` 参数,让系统自动补全
|
||||
|
||||
3. 检查一下生成的配置是不是你想要的
|
||||
|
||||
### Python 驱动类要怎么写
|
||||
|
||||
你的设备类要符合这些要求:
|
||||
3. 定义设备的类信息
|
||||
添加设备的模块路径和类型:
|
||||
|
||||
```python
|
||||
from unilabos.common.device_base import DeviceBase
|
||||
new_device: # 定义一个名为 linear_motion.grbl 的设备
|
||||
|
||||
class MyDevice(DeviceBase):
|
||||
def __init__(self, config):
|
||||
"""初始化,参数会自动分析到 init_param_schema.config"""
|
||||
super().__init__(config)
|
||||
self.port = config.get('port', '/dev/ttyUSB0')
|
||||
|
||||
# 状态方法(会自动生成到 status_types)
|
||||
def get_status(self):
|
||||
"""返回设备状态"""
|
||||
return "idle"
|
||||
class: # 定义设备的类信息
|
||||
module: unilabos.devices_names.new_device:NewDeviceClass # 指定模块路径和类名
|
||||
type: python # 指定类型为 Python 类
|
||||
status_types:
|
||||
```
|
||||
4. 定义设备支持的动作
|
||||
添加设备支持的动作及其目标、反馈和结果:
|
||||
```python
|
||||
action_value_mappings:
|
||||
set_speed:
|
||||
type: SendCmd
|
||||
goal:
|
||||
command: speed
|
||||
feedback: {}
|
||||
result:
|
||||
success: success
|
||||
```
|
||||
`如何编写action_valve_mappings`
|
||||
1. 在 devices 文件夹中的 YAML 文件中,action_value_mappings 是用来定义设备支持的动作(actions)及其目标值(goal)、反馈值(feedback)和结果值(result)的映射规则。以下是规则和编写方法:
|
||||
```python
|
||||
action_value_mappings:
|
||||
<action_name>: # <action_name>:动作的名称
|
||||
# start:启动设备或某个功能。
|
||||
# stop:停止设备或某个功能。
|
||||
# set_speed:设置设备的速度。
|
||||
# set_temperature:设置设备的温度。
|
||||
# move_to_position:移动设备到指定位置。
|
||||
# stir:执行搅拌操作。
|
||||
# heatchill:执行加热或冷却操作。
|
||||
# send_nav_task:发送导航任务(例如机器人导航)。
|
||||
# set_timer:设置设备的计时器。
|
||||
# valve_open_cmd:打开阀门。
|
||||
# valve_close_cmd:关闭阀门。
|
||||
# execute_command_from_outer:执行外部命令。
|
||||
# push_to:控制设备推送到某个位置(例如机械爪)。
|
||||
# move_through_points:导航设备通过多个点。
|
||||
|
||||
def get_temperature(self):
|
||||
"""返回当前温度"""
|
||||
return 25.0
|
||||
type: <ActionType> # 动作的类型,表示动作的功能
|
||||
# 根据动作的功能选择合适的类型:
|
||||
# SendCmd:发送简单命令。
|
||||
# NavigateThroughPoses:导航动作。
|
||||
# SingleJointPosition:设置单一关节的位置。
|
||||
# Stir:搅拌动作。
|
||||
# HeatChill:加热或冷却动作。
|
||||
|
||||
# 动作方法(会自动生成 auto- 开头的动作)
|
||||
async def start_heating(self, temperature: float):
|
||||
"""开始加热到指定温度"""
|
||||
pass
|
||||
goal: # 定义动作的目标值映射,表示需要传递给设备的参数。
|
||||
<goal_key>: <mapped_value> #确定设备需要的输入参数,并将其映射到设备的字段。
|
||||
|
||||
def stop(self):
|
||||
"""停止操作"""
|
||||
pass
|
||||
feedback: # 定义动作的反馈值映射,表示设备执行动作时返回的实时状态。
|
||||
<feedback_key>: <mapped_value>
|
||||
result: # 定义动作的结果值映射,表示动作完成后返回的最终结果。
|
||||
<result_key>: <mapped_value>
|
||||
```
|
||||
|
||||
### 系统集成
|
||||
|
||||
1. 把 yaml 文件放到 `devices/` 目录下
|
||||
2. 系统启动时会自动扫描并加载设备
|
||||
3. 系统会自动补全所有缺失的字段
|
||||
4. 设备马上就能在前端界面中使用
|
||||
|
||||
### 高级配置
|
||||
|
||||
如果需要特殊设置,可以手动加:
|
||||
|
||||
```yaml
|
||||
my_device:
|
||||
class:
|
||||
module: unilabos.devices.my_module.my_device:MyDevice
|
||||
type: python
|
||||
action_value_mappings:
|
||||
# 自定义动作
|
||||
special_command:
|
||||
type: UniLabJsonCommand
|
||||
goal: {}
|
||||
result: {}
|
||||
|
||||
# 可选的自定义配置
|
||||
description: '我的特殊设备'
|
||||
icon: 'my_device.webp'
|
||||
category: ['temperature', 'heating']
|
||||
6. 定义设备的属性模式
|
||||
添加设备的属性模式,包括属性类型和描述:
|
||||
```python
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
description: The status of the device
|
||||
speed:
|
||||
type: number
|
||||
description: The speed of the device
|
||||
required:
|
||||
- status
|
||||
- speed
|
||||
additionalProperties: false
|
||||
```
|
||||
|
||||
## 常见问题怎么排查
|
||||
|
||||
### 设备加载不了
|
||||
|
||||
1. 检查模块路径:确认 `class.module` 路径写对了
|
||||
2. 确认类能导入:看看你的 Python 驱动类能不能正常导入
|
||||
3. 检查语法:用 yaml 验证器看看文件格式对不对
|
||||
4. 查看日志:看 UniLabOS 启动时有没有报错信息
|
||||
|
||||
### 自动生成失败了
|
||||
|
||||
1. 类分析出问题:确认你的类继承了正确的基类
|
||||
2. 方法类型不明确:确保状态方法的返回类型写清楚了
|
||||
3. 导入有问题:检查类能不能被动态导入
|
||||
4. 没开完整注册:确认启用了 `complete_registry=True`
|
||||
|
||||
### 前端显示有问题
|
||||
|
||||
1. 重新生成:删掉旧的 yaml 文件,用编辑器重新生成
|
||||
2. 清除缓存:清除浏览器缓存,重新加载页面
|
||||
3. 检查字段:确认必需的字段(比如 `schema`)都有
|
||||
4. 验证数据:检查 `goal_default` 和 `schema` 的数据类型是不是一致
|
||||
|
||||
### 动作执行出错
|
||||
|
||||
1. 方法名不对:确认动作方法名符合规范(比如 `execute_<action_name>`)
|
||||
2. 参数映射错误:检查 `goal` 字段的参数映射是否正确
|
||||
3. 返回格式不对:确认方法返回值格式符合 `result` 映射
|
||||
4. 没异常处理:在驱动类里加上异常处理
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 开发流程
|
||||
|
||||
1. **优先使用编辑器**:除非有特殊需求,否则优先使用注册表编辑器
|
||||
2. **最小化配置**:手动配置时只定义必要字段,让系统自动生成其他内容
|
||||
3. **增量开发**:先创建基本配置,后续根据需要添加特殊动作
|
||||
|
||||
### 代码规范
|
||||
|
||||
1. **方法命名**:状态方法使用 `get_` 前缀,动作方法使用动词开头
|
||||
2. **类型注解**:为方法参数和返回值添加类型注解
|
||||
3. **文档字符串**:为类和方法添加详细的文档字符串
|
||||
4. **异常处理**:实现完善的错误处理和日志记录
|
||||
|
||||
### 配置管理
|
||||
|
||||
1. **版本控制**:所有 yaml 文件纳入版本控制
|
||||
2. **命名一致性**:设备 ID、文件名、类名保持一致的命名风格
|
||||
3. **定期更新**:定期运行完整注册以更新自动生成的字段
|
||||
4. **备份配置**:在修改前备份重要的手动配置
|
||||
|
||||
### 测试验证
|
||||
|
||||
1. **本地测试**:在本地环境充分测试后再部署
|
||||
2. **渐进部署**:先部署到测试环境,验证无误后再上生产环境
|
||||
3. **监控日志**:密切监控设备加载和运行日志
|
||||
4. **回滚准备**:准备快速回滚机制,以应对紧急情况
|
||||
|
||||
### 性能优化
|
||||
|
||||
1. **按需加载**:只加载实际使用的设备类型
|
||||
2. **缓存利用**:充分利用系统的注册表缓存机制
|
||||
3. **资源管理**:合理管理设备连接和资源占用
|
||||
4. **监控指标**:设置关键性能指标的监控和告警
|
||||
# 写完yaml注册表后需要添加到哪些其他文件?
|
||||
|
||||
|
Before Width: | Height: | Size: 428 KiB |
|
Before Width: | Height: | Size: 310 KiB |
|
Before Width: | Height: | Size: 66 KiB |
@@ -1,409 +0,0 @@
|
||||
# 物料教程(Resource)
|
||||
|
||||
本教程面向 Uni-Lab-OS 的开发者,讲解“物料”的核心概念、3种物料格式(UniLab、PyLabRobot、奔耀Bioyond)及其相互转换方法,并说明4种 children 结构表现形式及使用场景。
|
||||
|
||||
---
|
||||
|
||||
## 1. 物料是什么
|
||||
|
||||
- **物料(Resource)**:指实验工作站中的实体对象,包括设备(device)、操作甲板 (deck)、试剂、实验耗材,也包括设备上承载的具体物料或者包含的容器(如container/plate/well/瓶/孔/片等)。
|
||||
- **物料基本信息**(以 UniLab list格式为例):
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"id": "plate", // 某一类物料的唯一名称
|
||||
"name": "50ml瓶装试剂托盘", // 在云端显示的名称
|
||||
"sample_id": null, // 同类物料的不同样品
|
||||
"children": [
|
||||
"50ml试剂瓶" // 表示托盘上有一个 50ml 试剂瓶
|
||||
],
|
||||
"parent": "deck", // 此物料放置在 deck 上
|
||||
"type": "plate", // 物料类型
|
||||
"class": "plate", // 物料对应的注册/类名
|
||||
"position": {
|
||||
"x": 0, // 初始放置位置
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"config": { // 固有配置(尺寸、旋转等)
|
||||
"size_x": 400.0,
|
||||
"size_y": 400.0,
|
||||
"size_z": 400.0,
|
||||
"rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"type": "Rotation"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"bottle_number": 1 // 动态数据(可变化)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 3种物料格式概览(UniLab、PyLabRobot、奔耀Bioyond)
|
||||
|
||||
### 2.1 UniLab 物料格式(云端/项目内通用)
|
||||
|
||||
- 结构特征:顶层通常是 `nodes` 列表;每个节点是扁平字典,`children` 是子节点 `id` 列表;`parent` 为父节点 `id` 或 `null`。
|
||||
- 用途:
|
||||
- 云端数据存储、前端可视化、与图结构算法互操作
|
||||
- 在上传/下载/部署配置时作为标准交换格式
|
||||
|
||||
示例片段(UniLab 物料格式):
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"nodes": [
|
||||
|
||||
{
|
||||
"id": "a",
|
||||
"name": "name_a",
|
||||
"sample_id": 1,
|
||||
"type": "deck",
|
||||
"class": "deck",
|
||||
"parent": null,
|
||||
"children": ["b1"],
|
||||
"position": {"x": 0, "y": 0, "z": 0},
|
||||
"config": {},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
|
||||
"id": "b1",
|
||||
"name": "name_b1",
|
||||
"sample_id": 1,
|
||||
"type": "plate",
|
||||
"class": "plate",
|
||||
"parent": "a1",
|
||||
"children": [],
|
||||
"position": {"x": 0, "y": 0, "z": 0},
|
||||
"config": {},
|
||||
"data": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 PyLabRobot(PLR)物料格式(实验流程运行时)
|
||||
|
||||
- 结构特征:严格的层级树,`children` 为“子资源字典列表”(每个子节点本身是完整对象)。
|
||||
- 用途:
|
||||
- 实验流程执行与调度,PLR 运行时期望的资源对象格式
|
||||
- 通过 `Resource.deserialize/serialize`、`load_all_state/serialize_all_state` 与对象交互
|
||||
|
||||
示例片段(PRL 物料格式)::
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "deck",
|
||||
"type": "Deck",
|
||||
"category": "deck",
|
||||
"location": {"x": 0, "y": 0, "z": 0, "type": "Coordinate"},
|
||||
"rotation": {"x": 0, "y": 0, "z": 0, "type": "Rotation"},
|
||||
"parent_name": null,
|
||||
"children": [
|
||||
{
|
||||
"name": "plate_1",
|
||||
"type": "Plate",
|
||||
"category": "plate_96",
|
||||
"location": {"x": 100, "y": 0, "z": 0, "type": "Coordinate"},
|
||||
"rotation": {"x": 0, "y": 0, "z": 0, "type": "Rotation"},
|
||||
"parent_name": "deck",
|
||||
"children": [
|
||||
{
|
||||
"name": "A1",
|
||||
"type": "Well",
|
||||
"category": "well",
|
||||
"location": {"x": 0, "y": 0, "z": 0, "type": "Coordinate"},
|
||||
"rotation": {"x": 0, "y": 0, "z": 0, "type": "Rotation"},
|
||||
"parent_name": "plate_1",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 2.3 奔耀 Bioyond 物料格式(第三方来源)
|
||||
一般是厂商自己定义的json格式和字段,信息需要提取和对应。以下为示例说明。
|
||||
|
||||
- 结构特征:顶层 `data` 列表,每项包含 `typeName`、`code`、`barCode`、`name`、`quantity`、`unit`、`locations`(仓位 `whName`、`x/y/z`)、`detail`(细粒度内容,如瓶内液体或孔位物料)。
|
||||
- 用途:
|
||||
- 第三方 WMS/设备的物料清单输入
|
||||
- 需要自定义映射表将 `typeName` → PLR 类名,对 `locations`/`detail` 进行落位/赋值
|
||||
|
||||
示例片段(奔耀Bioyond 物料格式):
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "3a1b5c10-d4f3-01ac-1e64-5b4be2add4b1",
|
||||
"typeName": "液",
|
||||
"code": "0006-00014",
|
||||
"barCode": "",
|
||||
"name": "EMC",
|
||||
"quantity": 50,
|
||||
"lockQuantity": 2.057,
|
||||
"unit": "瓶",
|
||||
"status": 1,
|
||||
"isUse": false,
|
||||
"locations": [
|
||||
{
|
||||
"id": "3a19da43-57b5-5e75-552f-8dbd0ad1075f",
|
||||
"whid": "3a19da43-57b4-a2a8-3f52-91dbbeb836db",
|
||||
"whName": "配液站内试剂仓库",
|
||||
"code": "0003-0003",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"z": 1,
|
||||
"quantity": 0
|
||||
}
|
||||
],
|
||||
"detail": [
|
||||
{
|
||||
"code": "0006-00014-01",
|
||||
"name": "EMC-瓶-1",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"z": 1,
|
||||
"quantity": 500.0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"code": 1,
|
||||
"message": "",
|
||||
"timestamp": 0
|
||||
}
|
||||
```
|
||||
### 2.4 3种物料格式关键字段对应(UniLab、PyLabRobot、奔耀Bioyond)
|
||||
|
||||
| 含义 | UniLab | PyLabRobot (PLR) | 奔耀 Bioyond |
|
||||
| - | - | - | - |
|
||||
| 节点唯一名 | `id` | `name` | `name` |
|
||||
| 父节点引用 | `parent` | `parent_name` | `locations` 坐标(无直接父名,需映射坐标下的物料) |
|
||||
| 子节点集合 | `children`(id 列表或对象列表,视结构而定) | `children`(对象列表) | `detail`(明细,非严格树结构,需要自定义映射) |
|
||||
| 类型(抽象类别) | `type`(device/container/plate/deck/…) | `category`(plate/well/…),以及类名 `type` | `typeName`(厂商自定义,如“液”、“加样头(大)”) |
|
||||
| 运行/业务数据 | `data` | 通过 `serialize_all_state()`/`load_all_state()` 管理的状态 | `quantity`、`lockQuantity` 等业务数值 |
|
||||
| 固有配置 | `config`(size_x/size_y/size_z/model/ordering…) | 资源字典中的同名键(反序列化时按构造签名取用) | 厂商自定义字段(需映射入 PLR/UniLab 的 `config` 或 `data`) |
|
||||
| 空间位置 | `position`(x/y/z) | `location`(Coordinate) + `rotation`(Rotation) | `locations`(whName、x/y/z),不含旋转 |
|
||||
| 条码/标识 | `config.barcode`(可选) | 常放在配置键中(如 `barcode`) | `barCode` |
|
||||
| 数量单位 | 无固定键,通常在 `data` | 无固定键,通常在配置或状态中 | `unit` |
|
||||
| 物料编码 | 通常在 `config` 或 `data` 自定义 | 通常在配置中自定义 | `code` |
|
||||
|
||||
说明:
|
||||
- Bioyond 不提供显式的树形父子关系,通常通过 `locations` 将物料落位到某仓位/坐标。用 `detail` 表示子级明细。
|
||||
|
||||
---
|
||||
|
||||
## 3. children 的四种结构表示
|
||||
|
||||
- **list(扁平列表)**:每个节点是扁平字典,`children` 为子节点 `id` 数组。示例:UniLab `nodes` 中的单个节点。
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{ "id": "root", "parent": null, "children": ["child1"] },
|
||||
{ "id": "child1", "parent": "root", "children": [] }
|
||||
]
|
||||
}
|
||||
```
|
||||
- **dict(嵌套字典)**:节点的 `children` 是 `{ child_id: child_node_dict }` 字典。
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "root",
|
||||
"parent": null,
|
||||
"children": {
|
||||
"child1": { "id": "child1", "parent": "root", "children": {} }
|
||||
}
|
||||
}
|
||||
```
|
||||
- **tree(树形列表)**:顶层是 `[root_node, ...]`,每个 `node.children` 是“子节点对象列表”(而非 id 列表)。
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "root",
|
||||
"parent": null,
|
||||
"children": [
|
||||
{ "id": "child1", "parent": "root", "children": [] }
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
- **nestdict(顶层嵌套字典)**:顶层是 `{root_id: root_node, ...}`,或者根节点自身带 `children: {id: node}` 形态。
|
||||
|
||||
```json
|
||||
{
|
||||
"root": {
|
||||
"id": "root",
|
||||
"parent": null,
|
||||
"children": {
|
||||
"child1": { "id": "child1", "parent": "root", "children": {} }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这些结构之间可使用 `graphio.py` 中的工具函数互转(见下一节)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 转换函数及调用
|
||||
|
||||
核心代码文件:`unilabos/resources/graphio.py`
|
||||
|
||||
### 4.1 结构互转(list/dict/tree/nestdict)
|
||||
|
||||
代码引用:
|
||||
|
||||
```217:239:unilabos/resources/graphio.py
|
||||
def dict_to_tree(nodes: dict, devices_only: bool = False) -> list[dict]:
|
||||
# ... 由扁平 dict(id->node)生成树(children 为对象列表)
|
||||
```
|
||||
|
||||
```241:267:unilabos/resources/graphio.py
|
||||
def dict_to_nested_dict(nodes: dict, devices_only: bool = False) -> dict:
|
||||
# ... 由扁平 dict 生成嵌套字典(children 为 {id:node})
|
||||
```
|
||||
|
||||
```270:273:unilabos/resources/graphio.py
|
||||
def list_to_nested_dict(nodes: list[dict]) -> dict:
|
||||
# ... 由扁平列表(children 为 id 列表)转嵌套字典
|
||||
```
|
||||
|
||||
```275:286:unilabos/resources/graphio.py
|
||||
def tree_to_list(tree: list[dict]) -> list[dict]:
|
||||
# ... 由树形列表转回扁平列表(children 还原为 id 列表)
|
||||
```
|
||||
|
||||
```289:337:unilabos/resources/graphio.py
|
||||
def nested_dict_to_list(nested_dict: dict) -> list[dict]:
|
||||
# ... 由嵌套字典转回扁平列表
|
||||
```
|
||||
|
||||
常见路径:
|
||||
|
||||
- UniLab 扁平列表 → 树:`dict_to_tree({r["id"]: r for r in resources})`
|
||||
- 树 → UniLab 扁平列表:`tree_to_list(resources_tree)`
|
||||
- 扁平列表 ↔ 嵌套字典:`list_to_nested_dict` / `nested_dict_to_list`
|
||||
|
||||
### 4.2 UniLab ↔ PyLabRobot(PLR)
|
||||
|
||||
高层封装:
|
||||
|
||||
```339:368:unilabos/resources/graphio.py
|
||||
def convert_resources_to_type(resources_list: list[dict], resource_type: Union[type, list[type]], *, plr_model: bool = False):
|
||||
# UniLab -> (NestedDict or PLR)
|
||||
```
|
||||
|
||||
```371:395:unilabos/resources/graphio.py
|
||||
def convert_resources_from_type(resources_list, resource_type: Union[type, list[type]], *, is_plr: bool = False):
|
||||
# (NestedDict or PLR) -> UniLab 扁平列表
|
||||
```
|
||||
|
||||
底层转换:
|
||||
|
||||
```398:441:unilabos/resources/graphio.py
|
||||
def resource_ulab_to_plr(resource: dict, plr_model=False) -> "ResourcePLR":
|
||||
# UniLab 单节点(树根) -> PLR Resource 对象
|
||||
```
|
||||
|
||||
```443:481:unilabos/resources/graphio.py
|
||||
def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None, with_children=True):
|
||||
# PLR Resource -> UniLab 单节点(dict)
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```python
|
||||
from unilabos.resources.graphio import convert_resources_to_type, convert_resources_from_type
|
||||
from pylabrobot.resources.resource import Resource as ResourcePLR
|
||||
|
||||
# UniLab 扁平列表 -> PLR 根资源对象
|
||||
plr_root = convert_resources_to_type(resources_list=ulab_list, resource_type=ResourcePLR)
|
||||
|
||||
# PLR 资源对象 -> UniLab 扁平列表(用于保存/上传)
|
||||
ulab_flat = convert_resources_from_type(resources_list=plr_root, resource_type=ResourcePLR)
|
||||
```
|
||||
|
||||
可选项:
|
||||
|
||||
- `plr_model=True`:保留 `model` 字段(默认会移除)。
|
||||
- `with_children=False`:`resource_plr_to_ulab` 仅转换当前节点。
|
||||
|
||||
### 4.3 奔耀(Bioyond)→ PLR(及进一步到 UniLab)
|
||||
|
||||
转换入口:
|
||||
|
||||
```483:527:unilabos/resources/graphio.py
|
||||
def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: dict = {}, deck: Any = None) -> list[dict]:
|
||||
# Bioyond 列表 -> PLR 资源列表,并可根据 deck.warehouses 将资源落位
|
||||
```
|
||||
|
||||
使用示例:
|
||||
|
||||
```python
|
||||
import json
|
||||
from unilabos.resources.graphio import resource_bioyond_to_plr, convert_resources_from_type
|
||||
from pylabrobot.resources.resource import Resource as ResourcePLR
|
||||
|
||||
resp = json.load(open("unilabos/devices/workstation/bioyond_cell/bioyond_test_yibin.json", encoding="utf-8"))
|
||||
materials = resp["data"]
|
||||
|
||||
# 将第三方类型name映射到 PLR 资源类名(需根据现场定义)
|
||||
type_mapping = {
|
||||
"液": "RegularContainer",
|
||||
"加样头(大)": "RegularContainer"
|
||||
}
|
||||
|
||||
plr_list = resource_bioyond_to_plr(materials, type_mapping=type_mapping, deck=None)
|
||||
|
||||
# 如需上传云端(UniLab 扁平格式):
|
||||
ulab_flat = convert_resources_from_type(plr_list, [ResourcePLR])
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `type_mapping` 必须由开发者根据设备/物料种类人工维护。
|
||||
- 如传入 `deck`,且 `deck.warehouses` 命名与 `whName` 对应,可将物料安放到仓库坐标(x/y/z)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 何时使用哪种格式
|
||||
|
||||
- **云端/持久化**:使用 UniLab 物料格式(扁平 `nodes` 列表,children 为 id 列表)。便于版本化、可视化与网络传输。
|
||||
- **实验工作流执行**:使用 PyLabRobot(PLR)格式。PLR 运行时依赖严格的树形资源结构与对象 API。
|
||||
- **第三方设备/系统(Bioyond)输入**:保持来源格式不变,使用 `resource_bioyond_to_plr` + 人工 `type_mapping` 将其转换为 PLR(必要时再转 UniLab)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 常见问题与注意事项
|
||||
|
||||
- **children 形态不一致**:不同函数期望不同 children 形态,注意在进入转换前先用“结构互转”工具函数标准化形态。
|
||||
- **devices_only**:`dict_to_tree/dict_to_nested_dict` 支持仅保留 `type == device` 的节点。
|
||||
- **模型/类型字段**:PLR 对象序列化参数有所差异,`resource_ulab_to_plr` 内部会根据构造签名移除不兼容字段(如 `category`)。
|
||||
- **驱动初始化**:`initialize_resource(s)` 支持从注册表/类路径创建 PLR/UniLab 资源或列表。
|
||||
|
||||
参考代码:
|
||||
|
||||
```530:577:unilabos/resources/graphio.py
|
||||
def initialize_resource(resource_config: dict, resource_type: Any = None) -> Union[list[dict], ResourcePLR]:
|
||||
# 从注册类/模块反射创建资源,或将 UniLab 字典包装为列表
|
||||
```
|
||||
|
||||
```580:597:unilabos/resources/graphio.py
|
||||
def initialize_resources(resources_config) -> list[dict]:
|
||||
# 批量初始化
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
# 工作站基础架构设计文档
|
||||
|
||||
## 1. 整体架构图
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "工作站基础架构"
|
||||
WB[WorkstationBase]
|
||||
WB --> |继承| RPN[ROS2WorkstationNode]
|
||||
WB --> |组合| WCB[WorkstationCommunicationBase]
|
||||
WB --> |组合| MMB[MaterialManagementBase]
|
||||
WB --> |组合| WHS[WorkstationHTTPService]
|
||||
end
|
||||
|
||||
subgraph "通信层实现"
|
||||
WCB --> |实现| PLC[PLCCommunication]
|
||||
WCB --> |实现| SER[SerialCommunication]
|
||||
WCB --> |实现| ETH[EthernetCommunication]
|
||||
end
|
||||
|
||||
subgraph "物料管理实现"
|
||||
MMB --> |实现| PLR[PyLabRobotMaterialManager]
|
||||
MMB --> |实现| BIO[BioyondMaterialManager]
|
||||
MMB --> |实现| SIM[SimpleMaterialManager]
|
||||
end
|
||||
|
||||
subgraph "HTTP服务"
|
||||
WHS --> |处理| LIMS[LIMS协议报送]
|
||||
WHS --> |处理| MAT[物料变更报送]
|
||||
WHS --> |处理| ERR[错误处理报送]
|
||||
end
|
||||
|
||||
subgraph "具体工作站实现"
|
||||
WB --> |继承| WS1[PLCWorkstation]
|
||||
WB --> |继承| WS2[ReportingWorkstation]
|
||||
WB --> |继承| WS3[HybridWorkstation]
|
||||
end
|
||||
|
||||
subgraph "外部系统"
|
||||
EXT1[PLC设备] --> |通信| PLC
|
||||
EXT2[外部工作站] --> |HTTP报送| WHS
|
||||
EXT3[LIMS系统] --> |HTTP报送| WHS
|
||||
EXT4[Bioyond物料系统] --> |查询| BIO
|
||||
end
|
||||
```
|
||||
|
||||
## 2. 类关系图
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class WorkstationBase {
|
||||
<<abstract>>
|
||||
+device_id: str
|
||||
+communication: WorkstationCommunicationBase
|
||||
+material_management: MaterialManagementBase
|
||||
+http_service: WorkstationHTTPService
|
||||
+workflow_status: WorkflowStatus
|
||||
+supported_workflows: Dict
|
||||
|
||||
+_create_communication_module()*
|
||||
+_create_material_management_module()*
|
||||
+_register_supported_workflows()*
|
||||
|
||||
+process_step_finish_report()
|
||||
+process_sample_finish_report()
|
||||
+process_order_finish_report()
|
||||
+process_material_change_report()
|
||||
+handle_external_error()
|
||||
|
||||
+start_workflow()
|
||||
+stop_workflow()
|
||||
+get_workflow_status()
|
||||
+get_device_status()
|
||||
}
|
||||
|
||||
class ROS2WorkstationNode {
|
||||
+sub_devices: Dict
|
||||
+protocol_names: List
|
||||
+execute_single_action()
|
||||
+create_ros_action_server()
|
||||
+initialize_device()
|
||||
}
|
||||
|
||||
class WorkstationCommunicationBase {
|
||||
<<abstract>>
|
||||
+config: CommunicationConfig
|
||||
+is_connected: bool
|
||||
+connect()
|
||||
+disconnect()
|
||||
+start_workflow()*
|
||||
+stop_workflow()*
|
||||
+get_device_status()*
|
||||
+write_register()
|
||||
+read_register()
|
||||
}
|
||||
|
||||
class MaterialManagementBase {
|
||||
<<abstract>>
|
||||
+device_id: str
|
||||
+deck_config: Dict
|
||||
+resource_tracker: DeviceNodeResourceTracker
|
||||
+plr_deck: Deck
|
||||
+find_materials_by_type()
|
||||
+update_material_location()
|
||||
+convert_to_unilab_format()
|
||||
+_create_resource_by_type()*
|
||||
}
|
||||
|
||||
class WorkstationHTTPService {
|
||||
+workstation_instance: WorkstationBase
|
||||
+host: str
|
||||
+port: int
|
||||
+start()
|
||||
+stop()
|
||||
+_handle_step_finish_report()
|
||||
+_handle_material_change_report()
|
||||
}
|
||||
|
||||
class PLCWorkstation {
|
||||
+plc_config: Dict
|
||||
+modbus_client: ModbusTCPClient
|
||||
+_create_communication_module()
|
||||
+_create_material_management_module()
|
||||
+_register_supported_workflows()
|
||||
}
|
||||
|
||||
class ReportingWorkstation {
|
||||
+report_handlers: Dict
|
||||
+_create_communication_module()
|
||||
+_create_material_management_module()
|
||||
+_register_supported_workflows()
|
||||
}
|
||||
|
||||
WorkstationBase --|> ROS2WorkstationNode
|
||||
WorkstationBase *-- WorkstationCommunicationBase
|
||||
WorkstationBase *-- MaterialManagementBase
|
||||
WorkstationBase *-- WorkstationHTTPService
|
||||
|
||||
PLCWorkstation --|> WorkstationBase
|
||||
ReportingWorkstation --|> WorkstationBase
|
||||
|
||||
WorkstationCommunicationBase <|-- PLCCommunication
|
||||
WorkstationCommunicationBase <|-- DummyCommunication
|
||||
|
||||
MaterialManagementBase <|-- PyLabRobotMaterialManager
|
||||
MaterialManagementBase <|-- SimpleMaterialManager
|
||||
```
|
||||
|
||||
## 3. 工作站启动时序图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant APP as Application
|
||||
participant WS as WorkstationBase
|
||||
participant COMM as CommunicationModule
|
||||
participant MAT as MaterialManager
|
||||
participant HTTP as HTTPService
|
||||
participant ROS as ROS2WorkstationNode
|
||||
|
||||
APP->>WS: 创建工作站实例
|
||||
WS->>ROS: 初始化ROS2WorkstationNode
|
||||
ROS->>ROS: 初始化子设备
|
||||
ROS->>ROS: 设置硬件接口代理
|
||||
|
||||
WS->>COMM: _create_communication_module()
|
||||
COMM->>COMM: 初始化通信配置
|
||||
COMM->>COMM: 建立PLC/串口连接
|
||||
COMM-->>WS: 返回通信模块实例
|
||||
|
||||
WS->>MAT: _create_material_management_module()
|
||||
MAT->>MAT: 创建PyLabRobot Deck
|
||||
MAT->>MAT: 初始化物料资源
|
||||
MAT->>MAT: 注册到ResourceTracker
|
||||
MAT-->>WS: 返回物料管理实例
|
||||
|
||||
WS->>WS: _register_supported_workflows()
|
||||
WS->>WS: _create_workstation_services()
|
||||
WS->>HTTP: _start_http_service()
|
||||
HTTP->>HTTP: 创建HTTP服务器
|
||||
HTTP->>HTTP: 启动监听线程
|
||||
HTTP-->>WS: HTTP服务启动完成
|
||||
|
||||
WS-->>APP: 工作站初始化完成
|
||||
```
|
||||
|
||||
## 4. 工作流执行时序图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant EXT as ExternalSystem
|
||||
participant WS as WorkstationBase
|
||||
participant COMM as CommunicationModule
|
||||
participant MAT as MaterialManager
|
||||
participant ROS as ROS2WorkstationNode
|
||||
participant DEV as SubDevice
|
||||
|
||||
EXT->>WS: start_workflow(type, params)
|
||||
WS->>WS: 验证工作流类型
|
||||
WS->>COMM: start_workflow(type, params)
|
||||
COMM->>COMM: 发送启动命令到PLC
|
||||
COMM-->>WS: 启动成功
|
||||
|
||||
WS->>WS: 更新workflow_status = RUNNING
|
||||
|
||||
loop 工作流步骤执行
|
||||
WS->>ROS: execute_single_action(device_id, action, params)
|
||||
ROS->>DEV: 发送ROS Action请求
|
||||
DEV->>DEV: 执行设备动作
|
||||
DEV-->>ROS: 返回执行结果
|
||||
ROS-->>WS: 返回动作结果
|
||||
|
||||
WS->>MAT: update_material_location(material_id, location)
|
||||
MAT->>MAT: 更新PyLabRobot资源状态
|
||||
MAT-->>WS: 更新完成
|
||||
end
|
||||
|
||||
WS->>COMM: get_workflow_status()
|
||||
COMM->>COMM: 查询PLC状态寄存器
|
||||
COMM-->>WS: 返回状态信息
|
||||
|
||||
WS->>WS: 更新workflow_status = COMPLETED
|
||||
WS-->>EXT: 工作流执行完成
|
||||
```
|
||||
|
||||
## 5. HTTP报送处理时序图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant EXT as ExternalWorkstation
|
||||
participant HTTP as HTTPService
|
||||
participant WS as WorkstationBase
|
||||
participant MAT as MaterialManager
|
||||
participant DB as DataStorage
|
||||
|
||||
EXT->>HTTP: POST /report/step_finish
|
||||
HTTP->>HTTP: 解析请求数据
|
||||
HTTP->>HTTP: 验证LIMS协议字段
|
||||
HTTP->>WS: process_step_finish_report(request)
|
||||
|
||||
WS->>WS: 增加接收计数
|
||||
WS->>WS: 记录步骤完成事件
|
||||
WS->>MAT: 更新相关物料状态
|
||||
MAT->>MAT: 更新PyLabRobot资源
|
||||
MAT-->>WS: 更新完成
|
||||
|
||||
WS->>DB: 保存报送记录
|
||||
DB-->>WS: 保存完成
|
||||
|
||||
WS-->>HTTP: 返回处理结果
|
||||
HTTP->>HTTP: 构造HTTP响应
|
||||
HTTP-->>EXT: 200 OK + acknowledgment_id
|
||||
|
||||
Note over EXT,DB: 类似处理sample_finish, order_finish, material_change等报送
|
||||
```
|
||||
|
||||
## 6. 错误处理时序图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant DEV as Device
|
||||
participant WS as WorkstationBase
|
||||
participant COMM as CommunicationModule
|
||||
participant HTTP as HTTPService
|
||||
participant EXT as ExternalSystem
|
||||
|
||||
DEV->>WS: 设备错误事件
|
||||
WS->>WS: handle_external_error(error_data)
|
||||
WS->>WS: 记录错误历史
|
||||
|
||||
alt 关键错误
|
||||
WS->>COMM: emergency_stop()
|
||||
COMM->>COMM: 发送紧急停止命令
|
||||
WS->>WS: 更新workflow_status = ERROR
|
||||
else 普通错误
|
||||
WS->>WS: 标记动作失败
|
||||
WS->>WS: 触发重试逻辑
|
||||
end
|
||||
|
||||
WS->>HTTP: 记录错误报送
|
||||
HTTP->>EXT: 主动通知错误状态
|
||||
|
||||
WS-->>DEV: 错误处理完成
|
||||
```
|
||||
|
||||
## 7. 典型工作站实现示例
|
||||
|
||||
### 7.1 PLC工作站实现
|
||||
|
||||
```python
|
||||
class PLCWorkstation(WorkstationBase):
|
||||
def _create_communication_module(self):
|
||||
return PLCCommunication(self.communication_config)
|
||||
|
||||
def _create_material_management_module(self):
|
||||
return PyLabRobotMaterialManager(
|
||||
self.device_id,
|
||||
self.deck_config,
|
||||
self.resource_tracker
|
||||
)
|
||||
|
||||
def _register_supported_workflows(self):
|
||||
self.supported_workflows = {
|
||||
"battery_assembly": WorkflowInfo(...),
|
||||
"quality_check": WorkflowInfo(...)
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 报送接收工作站实现
|
||||
|
||||
```python
|
||||
class ReportingWorkstation(WorkstationBase):
|
||||
def _create_communication_module(self):
|
||||
return DummyCommunication(self.communication_config)
|
||||
|
||||
def _create_material_management_module(self):
|
||||
return SimpleMaterialManager(
|
||||
self.device_id,
|
||||
self.deck_config,
|
||||
self.resource_tracker
|
||||
)
|
||||
|
||||
def _register_supported_workflows(self):
|
||||
self.supported_workflows = {
|
||||
"data_collection": WorkflowInfo(...),
|
||||
"report_processing": WorkflowInfo(...)
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 核心接口说明
|
||||
|
||||
### 8.1 必须实现的抽象方法
|
||||
- `_create_communication_module()`: 创建通信模块
|
||||
- `_create_material_management_module()`: 创建物料管理模块
|
||||
- `_register_supported_workflows()`: 注册支持的工作流
|
||||
|
||||
### 8.2 可重写的报送处理方法
|
||||
- `process_step_finish_report()`: 步骤完成处理
|
||||
- `process_sample_finish_report()`: 样本完成处理
|
||||
- `process_order_finish_report()`: 订单完成处理
|
||||
- `process_material_change_report()`: 物料变更处理
|
||||
- `handle_external_error()`: 错误处理
|
||||
|
||||
### 8.3 工作流控制接口
|
||||
- `start_workflow()`: 启动工作流
|
||||
- `stop_workflow()`: 停止工作流
|
||||
- `get_workflow_status()`: 获取状态
|
||||
|
||||
## 9. 配置参数说明
|
||||
|
||||
```python
|
||||
workstation_config = {
|
||||
"communication_config": {
|
||||
"protocol": "modbus_tcp",
|
||||
"host": "192.168.1.100",
|
||||
"port": 502
|
||||
},
|
||||
"deck_config": {
|
||||
"size_x": 1000.0,
|
||||
"size_y": 1000.0,
|
||||
"size_z": 500.0
|
||||
},
|
||||
"http_service_config": {
|
||||
"enabled": True,
|
||||
"host": "127.0.0.1",
|
||||
"port": 8081
|
||||
},
|
||||
"communication_interfaces": {
|
||||
"logical_device_1": CommunicationInterface(...)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个架构设计支持:
|
||||
1. **灵活的通信方式**: 通过CommunicationBase支持PLC、串口、以太网等
|
||||
2. **多样的物料管理**: 支持PyLabRobot、Bioyond、简单物料系统
|
||||
3. **统一的HTTP报送**: 基于LIMS协议的标准化报送接口
|
||||
4. **完整的工作流控制**: 支持动态和静态工作流
|
||||
5. **强大的错误处理**: 多层次的错误处理和恢复机制
|
||||
@@ -33,8 +33,6 @@ developer_guide/add_device
|
||||
developer_guide/add_action
|
||||
developer_guide/actions
|
||||
developer_guide/add_protocol
|
||||
developer_guide/add_batteryPLC
|
||||
developer_guide/materials_tutorial.md
|
||||
```
|
||||
|
||||
## 接口文档
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Sphinx文档构建依赖
|
||||
sphinx>=7.0.0
|
||||
sphinx-rtd-theme>=2.0.0
|
||||
myst-parser>=2.0.0
|
||||
|
||||
# 用于支持Jupyter notebook文档
|
||||
myst-nb>=1.0.0
|
||||
|
||||
# 用于代码复制按钮
|
||||
sphinx-copybutton>=0.5.0
|
||||
|
||||
# 用于自动摘要生成
|
||||
sphinx-autobuild>=2024.2.4
|
||||
@@ -1,75 +1,82 @@
|
||||
# Uni-Lab 配置指南
|
||||
|
||||
Uni-Lab 支持通过 Python 配置文件进行灵活的系统配置。本指南将帮助您理解配置选项并设置您的 Uni-Lab 环境。
|
||||
Uni-Lab支持通过Python配置文件进行灵活的系统配置。本指南将帮助您理解配置选项并设置您的Uni-Lab环境。
|
||||
|
||||
## 配置文件格式
|
||||
|
||||
Uni-Lab 支持 Python 格式的配置文件,它比 YAML 或 JSON 提供更多的灵活性,包括支持注释、条件逻辑和复杂数据结构。
|
||||
Uni-Lab支持Python格式的配置文件,它比YAML或JSON提供更多的灵活性,包括支持注释、条件逻辑和复杂数据结构。
|
||||
|
||||
### 默认配置示例
|
||||
### 基本配置示例
|
||||
|
||||
首次使用时,系统会自动创建一个基础配置文件 `local_config.py`:
|
||||
|
||||
```python
|
||||
# unilabos的配置文件
|
||||
|
||||
class BasicConfig:
|
||||
ak = "" # 实验室网页给您提供的ak代码,您可以在配置文件中指定,也可以通过运行unilabos时以 --ak 传入,优先按照传入参数解析
|
||||
sk = "" # 实验室网页给您提供的sk代码,您可以在配置文件中指定,也可以通过运行unilabos时以 --sk 传入,优先按照传入参数解析
|
||||
|
||||
|
||||
# WebSocket配置,一般无需调整
|
||||
class WSConfig:
|
||||
reconnect_interval = 5 # 重连间隔(秒)
|
||||
max_reconnect_attempts = 999 # 最大重连次数
|
||||
ping_interval = 30 # ping间隔(秒)
|
||||
```
|
||||
您可以进入实验室,点击左下角的头像在实验室详情中获取所在实验室的ak sk
|
||||

|
||||
|
||||
### 完整配置示例
|
||||
|
||||
您可以根据需要添加更多配置选项:
|
||||
一个典型的配置文件包含以下部分:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
"""Uni-Lab 配置文件"""
|
||||
|
||||
# 基础配置
|
||||
class BasicConfig:
|
||||
ak = "your_access_key" # 实验室访问密钥
|
||||
sk = "your_secret_key" # 实验室私钥
|
||||
working_dir = "" # 工作目录(通常自动设置)
|
||||
config_path = "" # 配置文件路径(自动设置)
|
||||
is_host_mode = True # 是否为主站模式
|
||||
slave_no_host = False # 从站模式下是否跳过等待主机服务
|
||||
upload_registry = False # 是否上传注册表
|
||||
machine_name = "undefined" # 机器名称(自动获取)
|
||||
vis_2d_enable = False # 是否启用2D可视化
|
||||
enable_resource_load = True # 是否启用资源加载
|
||||
communication_protocol = "websocket" # 通信协议
|
||||
from dataclasses import dataclass
|
||||
|
||||
# WebSocket配置
|
||||
class WSConfig:
|
||||
reconnect_interval = 5 # 重连间隔(秒)
|
||||
max_reconnect_attempts = 999 # 最大重连次数
|
||||
ping_interval = 30 # ping间隔(秒)
|
||||
# 配置类定义
|
||||
|
||||
# OSS上传配置
|
||||
class OSSUploadConfig:
|
||||
api_host = "" # API主机地址
|
||||
authorization = "" # 授权信息
|
||||
init_endpoint = "" # 初始化端点
|
||||
complete_endpoint = "" # 完成端点
|
||||
max_retries = 3 # 最大重试次数
|
||||
class MQConfig:
|
||||
"""MQTT 配置类"""
|
||||
lab_id: str = "YOUR_LAB_ID"
|
||||
# 更多配置...
|
||||
|
||||
# HTTP配置
|
||||
class HTTPConfig:
|
||||
remote_addr = "http://127.0.0.1:48197/api/v1" # 远程地址
|
||||
# 其他配置类...
|
||||
```
|
||||
|
||||
## 配置选项说明
|
||||
|
||||
### MQTT配置 (MQConfig)
|
||||
|
||||
MQTT配置用于连接消息队列服务,是Uni-Lab与云端通信的主要方式。
|
||||
|
||||
```python
|
||||
|
||||
class MQConfig:
|
||||
"""MQTT 配置类"""
|
||||
lab_id: str = "7AAEDBEA" # 实验室唯一标识
|
||||
instance_id: str = "mqtt-cn-instance"
|
||||
access_key: str = "your-access-key"
|
||||
secret_key: str = "your-secret-key"
|
||||
group_id: str = "GID_labs"
|
||||
broker_url: str = "mqtt-cn-instance.mqtt.aliyuncs.com"
|
||||
port: int = 8883
|
||||
|
||||
# 可以直接提供证书文件路径
|
||||
ca_file: str = "/path/to/ca.pem" # 相对config.py所在目录的路径
|
||||
cert_file: str = "/path/to/cert.pem" # 相对config.py所在目录的路径
|
||||
key_file: str = "/path/to/key.pem" # 相对config.py所在目录的路径
|
||||
|
||||
# 或者直接提供证书内容
|
||||
ca_content: str = ""
|
||||
cert_content: str = ""
|
||||
key_content: str = ""
|
||||
```
|
||||
|
||||
#### 证书配置
|
||||
|
||||
MQTT连接支持两种方式配置证书:
|
||||
|
||||
1. **文件路径方式**(推荐):指定证书文件的路径,系统会自动读取文件内容
|
||||
2. **直接内容方式**:直接在配置中提供证书内容
|
||||
|
||||
推荐使用文件路径方式,便于证书的更新和管理。
|
||||
|
||||
### HTTP客户端配置 (HTTPConfig)
|
||||
|
||||
即将开放 Uni-Lab 云端实验室。
|
||||
|
||||
### ROS模块配置 (ROSConfig)
|
||||
|
||||
配置ROS消息转换器需要加载的模块:
|
||||
|
||||
```python
|
||||
|
||||
# ROS配置
|
||||
class ROSConfig:
|
||||
"""ROS模块配置"""
|
||||
modules = [
|
||||
"std_msgs.msg",
|
||||
"geometry_msgs.msg",
|
||||
@@ -78,365 +85,25 @@ class ROSConfig:
|
||||
"nav2_msgs.action",
|
||||
"unilabos_msgs.msg",
|
||||
"unilabos_msgs.action",
|
||||
] # 需要加载的ROS模块
|
||||
```
|
||||
|
||||
## 命令行参数覆盖配置
|
||||
|
||||
Uni-Lab 允许通过命令行参数覆盖配置文件中的设置,提供更灵活的配置方式。命令行参数的优先级高于配置文件。
|
||||
|
||||
### 支持命令行覆盖的配置项
|
||||
|
||||
以下配置项可以通过命令行参数进行覆盖:
|
||||
|
||||
| 配置类 | 配置字段 | 命令行参数 | 说明 |
|
||||
| ------------- | ----------------- | ------------------- | -------------------------------- |
|
||||
| `BasicConfig` | `ak` | `--ak` | 实验室访问密钥 |
|
||||
| `BasicConfig` | `sk` | `--sk` | 实验室私钥 |
|
||||
| `BasicConfig` | `working_dir` | `--working_dir` | 工作目录路径 |
|
||||
| `BasicConfig` | `is_host_mode` | `--is_slave` | 主站模式(参数为从站模式,取反) |
|
||||
| `BasicConfig` | `slave_no_host` | `--slave_no_host` | 从站模式下跳过等待主机服务 |
|
||||
| `BasicConfig` | `upload_registry` | `--upload_registry` | 启动时上传注册表信息 |
|
||||
| `BasicConfig` | `vis_2d_enable` | `--2d_vis` | 启用 2D 可视化 |
|
||||
| `HTTPConfig` | `remote_addr` | `--addr` | 远程服务地址 |
|
||||
|
||||
### 特殊命令行参数
|
||||
|
||||
除了直接覆盖配置项的参数外,还有一些特殊的命令行参数:
|
||||
|
||||
| 参数 | 说明 |
|
||||
| ------------------- | ------------------------------------ |
|
||||
| `--config` | 指定配置文件路径 |
|
||||
| `--port` | Web 服务端口(不影响配置文件) |
|
||||
| `--disable_browser` | 禁用自动打开浏览器(不影响配置文件) |
|
||||
| `--visual` | 可视化工具选择(不影响配置文件) |
|
||||
| `--skip_env_check` | 跳过环境检查(不影响配置文件) |
|
||||
|
||||
### 配置优先级
|
||||
|
||||
配置项的生效优先级从高到低为:
|
||||
|
||||
1. **命令行参数**:最高优先级
|
||||
2. **环境变量**:中等优先级
|
||||
3. **配置文件**:基础优先级
|
||||
|
||||
### 使用示例
|
||||
|
||||
```bash
|
||||
# 通过命令行覆盖认证信息
|
||||
unilab --ak "new_access_key" --sk "new_secret_key"
|
||||
|
||||
# 覆盖服务器地址
|
||||
unilab --addr "https://custom.server.com/api/v1"
|
||||
|
||||
# 启用从站模式并跳过等待主机
|
||||
unilab --is_slave --slave_no_host
|
||||
|
||||
# 启用上传注册表和2D可视化
|
||||
unilab --upload_registry --2d_vis
|
||||
|
||||
# 组合使用多个覆盖参数
|
||||
unilab --ak "key" --sk "secret" --addr "test" --upload_registry --2d_vis
|
||||
```
|
||||
|
||||
### 预设环境地址
|
||||
|
||||
`--addr` 参数支持以下预设值,会自动转换为对应的完整 URL:
|
||||
|
||||
- `test` → `https://uni-lab.test.bohrium.com/api/v1`
|
||||
- `uat` → `https://uni-lab.uat.bohrium.com/api/v1`
|
||||
- `local` → `http://127.0.0.1:48197/api/v1`
|
||||
- 其他值 → 直接使用作为完整 URL
|
||||
|
||||
## 配置选项详解
|
||||
|
||||
### 基础配置 (BasicConfig)
|
||||
|
||||
基础配置包含了系统运行的核心参数:
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
| ------------------------ | ---- | ------------- | ------------------------------------------ |
|
||||
| `ak` | str | `""` | 实验室访问密钥(必需) |
|
||||
| `sk` | str | `""` | 实验室私钥(必需) |
|
||||
| `working_dir` | str | `""` | 工作目录,通常自动设置 |
|
||||
| `is_host_mode` | bool | `True` | 是否为主站模式 |
|
||||
| `slave_no_host` | bool | `False` | 从站模式下是否跳过等待主机服务 |
|
||||
| `upload_registry` | bool | `False` | 启动时是否上传注册表信息 |
|
||||
| `machine_name` | str | `"undefined"` | 机器名称,自动从 hostname 获取(不可配置) |
|
||||
| `vis_2d_enable` | bool | `False` | 是否启用 2D 可视化 |
|
||||
| `communication_protocol` | str | `"websocket"` | 通信协议,固定为 websocket |
|
||||
|
||||
#### 认证配置
|
||||
|
||||
`ak` 和 `sk` 是必需的认证参数:
|
||||
|
||||
1. **获取方式**:在 [Uni-Lab 官网](https://uni-lab.bohrium.com) 注册实验室后获得
|
||||
2. **配置方式**:
|
||||
- **命令行参数**:`--ak "your_key" --sk "your_secret"`(最高优先级)
|
||||
- **配置文件**:在 `BasicConfig` 类中设置
|
||||
- **环境变量**:`UNILABOS_BASICCONFIG_AK` 和 `UNILABOS_BASICCONFIG_SK`
|
||||
3. **优先级顺序**:命令行参数 > 环境变量 > 配置文件
|
||||
4. **安全注意**:请妥善保管您的密钥信息
|
||||
|
||||
**推荐做法**:
|
||||
|
||||
- 开发环境:使用配置文件
|
||||
- 生产环境:使用环境变量或命令行参数
|
||||
- 临时测试:使用命令行参数
|
||||
|
||||
### WebSocket 配置 (WSConfig)
|
||||
|
||||
WebSocket 是 Uni-Lab 的主要通信方式:
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
| ------------------------ | ---- | ------ | ------------------ |
|
||||
| `reconnect_interval` | int | `5` | 断线重连间隔(秒) |
|
||||
| `max_reconnect_attempts` | int | `999` | 最大重连次数 |
|
||||
| `ping_interval` | int | `30` | 心跳检测间隔(秒) |
|
||||
|
||||
### HTTP 配置 (HTTPConfig)
|
||||
|
||||
HTTP 客户端配置用于与云端服务通信:
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
| ------------- | ---- | --------------------------------- | ------------ |
|
||||
| `remote_addr` | str | `"http://127.0.0.1:48197/api/v1"` | 远程服务地址 |
|
||||
|
||||
**预设环境地址**:
|
||||
|
||||
- 生产环境:`https://uni-lab.bohrium.com/api/v1`
|
||||
- 测试环境:`https://uni-lab.test.bohrium.com/api/v1`
|
||||
- UAT 环境:`https://uni-lab.uat.bohrium.com/api/v1`
|
||||
- 本地环境:`http://127.0.0.1:48197/api/v1`
|
||||
|
||||
### ROS 配置 (ROSConfig)
|
||||
|
||||
配置 ROS 消息转换器需要加载的模块:
|
||||
|
||||
```python
|
||||
class ROSConfig:
|
||||
modules = [
|
||||
"std_msgs.msg", # 标准消息类型
|
||||
"geometry_msgs.msg", # 几何消息类型
|
||||
"control_msgs.msg", # 控制消息类型
|
||||
"control_msgs.action", # 控制动作类型
|
||||
"nav2_msgs.action", # 导航动作类型
|
||||
"unilabos_msgs.msg", # UniLab 自定义消息类型
|
||||
"unilabos_msgs.action", # UniLab 自定义动作类型
|
||||
]
|
||||
```
|
||||
|
||||
您可以根据实际使用的设备和功能添加其他 ROS 模块。
|
||||
您可以根据需要添加其他ROS模块。
|
||||
|
||||
### OSS 上传配置 (OSSUploadConfig)
|
||||
### 其他配置选项
|
||||
|
||||
对象存储服务配置,用于文件上传功能:
|
||||
- **OSSUploadConfig**: 对象存储上传配置
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
| ------------------- | ---- | ------ | -------------------- |
|
||||
| `api_host` | str | `""` | OSS API 主机地址 |
|
||||
| `authorization` | str | `""` | 授权认证信息 |
|
||||
| `init_endpoint` | str | `""` | 上传初始化端点 |
|
||||
| `complete_endpoint` | str | `""` | 上传完成端点 |
|
||||
| `max_retries` | int | `3` | 上传失败最大重试次数 |
|
||||
## 如何使用配置文件
|
||||
|
||||
## 环境变量支持
|
||||
|
||||
Uni-Lab 支持通过环境变量覆盖配置文件中的设置。环境变量格式为:
|
||||
|
||||
```
|
||||
UNILABOS_{配置类名}_{字段名}
|
||||
```
|
||||
|
||||
### 环境变量示例
|
||||
启动Uni-Lab时通过`--config`参数指定配置文件路径:
|
||||
|
||||
```bash
|
||||
# 设置基础配置
|
||||
export UNILABOS_BASICCONFIG_AK="your_access_key"
|
||||
export UNILABOS_BASICCONFIG_SK="your_secret_key"
|
||||
export UNILABOS_BASICCONFIG_IS_HOST_MODE="true"
|
||||
|
||||
# 设置WebSocket配置
|
||||
export UNILABOS_WSCONFIG_RECONNECT_INTERVAL="10"
|
||||
export UNILABOS_WSCONFIG_MAX_RECONNECT_ATTEMPTS="500"
|
||||
|
||||
# 设置HTTP配置
|
||||
export UNILABOS_HTTPCONFIG_REMOTE_ADDR="https://uni-lab.bohrium.com/api/v1"
|
||||
unilab --config path/to/your/config.py
|
||||
```
|
||||
|
||||
### 环境变量类型转换
|
||||
如果您不涉及多环境开发,可以在unilabos的安装路径中手动添加local_config.py的文件
|
||||
|
||||
- **布尔值**:`"true"`, `"1"`, `"yes"` → `True`;其他 → `False`
|
||||
- **整数**:自动转换为 `int` 类型
|
||||
- **浮点数**:自动转换为 `float` 类型
|
||||
- **字符串**:保持原值
|
||||
|
||||
## 配置文件使用方法
|
||||
|
||||
### 1. 指定配置文件启动
|
||||
|
||||
```bash
|
||||
# 使用指定配置文件启动
|
||||
unilab --config /path/to/your/config.py
|
||||
# 启动Uni-Lab
|
||||
python -m unilabos.app.main --config path/to/your/config.py
|
||||
```
|
||||
|
||||
### 2. 使用默认配置文件
|
||||
|
||||
如果不指定配置文件,系统会按以下顺序查找:
|
||||
|
||||
1. 环境变量 `UNILABOS_BASICCONFIG_CONFIG_PATH` 指定的路径
|
||||
2. 工作目录下的 `local_config.py`
|
||||
3. 首次使用时会引导创建配置文件
|
||||
|
||||
### 3. 配置文件验证
|
||||
|
||||
系统启动时会自动验证配置文件:
|
||||
|
||||
- **语法检查**:确保 Python 语法正确
|
||||
- **类型检查**:验证配置项类型是否匹配
|
||||
- **必需项检查**:确保 `ak` 和 `sk` 已配置
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 安全配置
|
||||
|
||||
- 不要将包含密钥的配置文件提交到版本控制系统
|
||||
- 使用环境变量或命令行参数在生产环境中配置敏感信息
|
||||
- 定期更换访问密钥
|
||||
- **推荐配置方式**:
|
||||
|
||||
```bash
|
||||
# 生产环境 - 使用环境变量
|
||||
export UNILABOS_BASICCONFIG_AK="your_access_key"
|
||||
export UNILABOS_BASICCONFIG_SK="your_secret_key"
|
||||
unilab
|
||||
|
||||
# 或使用命令行参数
|
||||
unilab --ak "your_access_key" --sk "your_secret_key"
|
||||
```
|
||||
|
||||
### 2. 多环境配置
|
||||
|
||||
为不同环境创建不同的配置文件并结合命令行参数:
|
||||
|
||||
```
|
||||
configs/
|
||||
├── local_config.py # 本地开发
|
||||
├── test_config.py # 测试环境
|
||||
├── prod_config.py # 生产环境
|
||||
└── example_config.py # 示例配置
|
||||
```
|
||||
|
||||
**环境切换示例**:
|
||||
|
||||
```bash
|
||||
# 本地开发环境
|
||||
unilab --config configs/local_config.py --addr local
|
||||
|
||||
# 测试环境
|
||||
unilab --config configs/test_config.py --addr test --upload_registry
|
||||
|
||||
# 生产环境
|
||||
unilab --config configs/prod_config.py --ak "$PROD_AK" --sk "$PROD_SK"
|
||||
```
|
||||
|
||||
### 3. 配置管理
|
||||
|
||||
- 保持配置文件简洁,只包含需要修改的配置项
|
||||
- 为配置项添加注释说明其作用
|
||||
- 定期检查和更新配置文件
|
||||
- **命令行参数优先使用场景**:
|
||||
- 临时测试不同配置
|
||||
- CI/CD 流水线中的动态配置
|
||||
- 不同环境间快速切换
|
||||
- 敏感信息的安全传递
|
||||
|
||||
### 4. 灵活配置策略
|
||||
|
||||
**基础配置文件 + 命令行覆盖**的推荐方式:
|
||||
|
||||
```python
|
||||
# base_config.py - 基础配置
|
||||
class BasicConfig:
|
||||
# 非敏感配置写在文件中
|
||||
is_host_mode = True
|
||||
upload_registry = False
|
||||
vis_2d_enable = False
|
||||
|
||||
class WSConfig:
|
||||
reconnect_interval = 5
|
||||
max_reconnect_attempts = 999
|
||||
ping_interval = 30
|
||||
```
|
||||
|
||||
```bash
|
||||
# 启动时通过命令行覆盖关键参数
|
||||
unilab --config base_config.py \
|
||||
--ak "$AK" \
|
||||
--sk "$SK" \
|
||||
--addr "test" \
|
||||
--upload_registry \
|
||||
--2d_vis
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 1. 配置文件加载失败
|
||||
|
||||
**错误信息**:`[ENV] 配置文件 xxx 不存在`
|
||||
|
||||
**解决方法**:
|
||||
|
||||
- 确认配置文件路径正确
|
||||
- 检查文件权限是否可读
|
||||
- 确保配置文件是 `.py` 格式
|
||||
|
||||
### 2. 语法错误
|
||||
|
||||
**错误信息**:`[ENV] 加载配置文件 xxx 失败`
|
||||
|
||||
**解决方法**:
|
||||
|
||||
- 检查 Python 语法是否正确
|
||||
- 确认类名和字段名拼写正确
|
||||
- 验证缩进是否正确(使用空格而非制表符)
|
||||
|
||||
### 3. 认证失败
|
||||
|
||||
**错误信息**:`后续运行必须拥有一个实验室`
|
||||
|
||||
**解决方法**:
|
||||
|
||||
- 确认 `ak` 和 `sk` 已正确配置
|
||||
- 检查密钥是否有效
|
||||
- 确认网络连接正常
|
||||
|
||||
### 4. 环境变量不生效
|
||||
|
||||
**解决方法**:
|
||||
|
||||
- 确认环境变量名格式正确(`UNILABOS_CLASS_FIELD`)
|
||||
- 检查环境变量是否已正确设置
|
||||
- 重启系统或重新加载环境变量
|
||||
|
||||
### 5. 命令行参数不生效
|
||||
|
||||
**错误现象**:设置了命令行参数但配置没有生效
|
||||
|
||||
**解决方法**:
|
||||
|
||||
- 确认参数名拼写正确(如 `--ak` 而不是 `--access_key`)
|
||||
- 检查参数格式是否正确(布尔参数如 `--is_slave` 不需要值)
|
||||
- 确认参数位置正确(所有参数都应在 `unilab` 之后)
|
||||
- 查看启动日志确认参数是否被正确解析
|
||||
|
||||
### 6. 配置优先级混淆
|
||||
|
||||
**错误现象**:不确定哪个配置生效
|
||||
|
||||
**解决方法**:
|
||||
|
||||
- 记住优先级:命令行参数 > 环境变量 > 配置文件
|
||||
- 使用 `--ak` 和 `--sk` 参数时会看到提示信息
|
||||
- 检查启动日志中的配置加载信息
|
||||
- 临时移除低优先级配置来测试高优先级配置是否生效
|
||||
|
||||
|
Before Width: | Height: | Size: 526 KiB |
|
Before Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 581 KiB |
|
Before Width: | Height: | Size: 120 KiB |
@@ -1,43 +1,24 @@
|
||||
# **Uni-Lab 安装**
|
||||
|
||||
## 快速开始
|
||||
请先 `git clone` 本仓库,随后按照以下步骤安装项目:
|
||||
|
||||
1. **配置 Conda 环境**
|
||||
|
||||
Uni-Lab-OS 建议使用 `mamba` 管理环境。创建新的环境:
|
||||
`Uni-Lab` 建议您采用 `mamba` 管理环境。若需从头建立 `Uni-Lab` 的运行依赖环境,请执行
|
||||
|
||||
```shell
|
||||
mamba create -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge
|
||||
mamba env create -f unilabos-<YOUR_OS>.yaml
|
||||
mamba activate unilab
|
||||
```
|
||||
|
||||
2. **安装开发版 Uni-Lab-OS**
|
||||
其中 `YOUR_OS` 是您的操作系统,可选值 `win64`, `linux-64`, `osx-64`, `osx-arm64`
|
||||
|
||||
若需将依赖安装进当前环境,请执行
|
||||
|
||||
```shell
|
||||
# 配置好conda环境后,克隆仓库
|
||||
git clone https://github.com/dptech-corp/Uni-Lab-OS.git -b dev
|
||||
cd Uni-Lab-OS
|
||||
|
||||
# 安装 Uni-Lab-OS
|
||||
pip install -e .
|
||||
conda env update --file unilabos-<YOUR_OS>.yml
|
||||
```
|
||||
|
||||
3. **安装开发版 ros-humble-unilabos-msgs**
|
||||
随后,可在本仓库安装 `unilabos` 的开发版:
|
||||
|
||||
**卸载老版本:**
|
||||
```shell
|
||||
conda activate unilab
|
||||
conda remove --force ros-humble-unilabos-msgs
|
||||
pip install .
|
||||
```
|
||||
有时相同的安装包版本会由于dev构建得到的md5不一样,触发安全检查,可输入 `config set safety_checks disabled` 来关闭安全检查。
|
||||
|
||||
**安装新版本:**
|
||||
|
||||
访问 https://github.com/dptech-corp/Uni-Lab-OS/actions/workflows/multi-platform-build.yml 选择最新的构建,下载对应平台的压缩包(仅解压一次,得到.conda文件)使用如下指令:
|
||||
```shell
|
||||
conda activate base
|
||||
conda install ros-humble-unilabos-msgs-<version>-<platform>.conda --offline -n <环境名>
|
||||
```
|
||||
|
||||
4. **启动 Uni-Lab 系统**
|
||||
|
||||
请参见{doc}`启动样例 <../boot_examples/index>`或{doc}`启动指南 <launch>`了解详细的启动方法。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Uni-Lab 启动指南
|
||||
# Uni-Lab 启动
|
||||
|
||||
安装完毕后,可以通过 `unilab` 命令行启动:
|
||||
|
||||
@@ -8,315 +8,70 @@ Start Uni-Lab Edge server.
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-g GRAPH, --graph GRAPH
|
||||
Physical setup graph file path.
|
||||
Physical setup graph.
|
||||
-d DEVICES, --devices DEVICES
|
||||
Devices config file.
|
||||
-r RESOURCES, --resources RESOURCES
|
||||
Resources config file.
|
||||
-c CONTROLLERS, --controllers CONTROLLERS
|
||||
Controllers config file path.
|
||||
Controllers config file.
|
||||
--registry_path REGISTRY_PATH
|
||||
Path to the registry directory
|
||||
--working_dir WORKING_DIR
|
||||
Path to the working directory
|
||||
Path to the registry
|
||||
--backend {ros,simple,automancer}
|
||||
Choose the backend to run with: 'ros', 'simple', or 'automancer'.
|
||||
--app_bridges APP_BRIDGES [APP_BRIDGES ...]
|
||||
Bridges to connect to. Now support 'websocket' and 'fastapi'.
|
||||
--is_slave Run the backend as slave node (without host privileges).
|
||||
--slave_no_host Skip waiting for host service in slave mode
|
||||
--upload_registry Upload registry information when starting unilab
|
||||
--use_remote_resource Use remote resources when starting unilab
|
||||
--config CONFIG Configuration file path, supports .py format Python config files
|
||||
--port PORT Port for web service information page
|
||||
--disable_browser Disable opening information page on startup
|
||||
--2d_vis Enable 2D visualization when starting pylabrobot instance
|
||||
--visual {rviz,web,disable}
|
||||
Choose visualization tool: rviz, web, or disable
|
||||
--ak AK Access key for laboratory requests
|
||||
--sk SK Secret key for laboratory requests
|
||||
--addr ADDR Laboratory backend address
|
||||
--skip_env_check Skip environment dependency check on startup
|
||||
--complete_registry Complete registry information
|
||||
Bridges to connect to. Now support 'mqtt' and 'fastapi'.
|
||||
--without_host Run the backend as slave (without host).
|
||||
--config CONFIG Configuration file path for system settings
|
||||
```
|
||||
|
||||
## 启动流程详解
|
||||
|
||||
Uni-Lab 的启动过程分为以下几个阶段:
|
||||
|
||||
### 1. 参数解析阶段
|
||||
|
||||
- 解析命令行参数
|
||||
- 处理参数格式转换(支持 dash 和 underscore 格式)
|
||||
|
||||
### 2. 环境检查阶段 (可选)
|
||||
|
||||
- 默认进行环境依赖检查并自动安装必需包
|
||||
- 使用 `--skip_env_check` 可跳过此步骤
|
||||
|
||||
### 3. 配置文件处理阶段
|
||||
|
||||
您可以直接跟随 unilabos 的提示进行,无需查阅本节
|
||||
|
||||
- **工作目录设置**:
|
||||
|
||||
- 如果当前目录以 `unilabos_data` 结尾,则使用当前目录
|
||||
- 否则使用 `当前目录/unilabos_data` 作为工作目录
|
||||
- 可通过 `--working_dir` 指定自定义工作目录
|
||||
|
||||
- **配置文件查找顺序**:
|
||||
1. 使用 `--config` 参数指定的配置文件
|
||||
2. 在工作目录中查找 `local_config.py`
|
||||
3. 首次使用时会引导创建配置文件
|
||||
|
||||
### 4. 服务器地址配置
|
||||
|
||||
支持多种后端环境:
|
||||
|
||||
- `--addr test`:测试环境 (`https://uni-lab.test.bohrium.com/api/v1`)
|
||||
- `--addr uat`:UAT 环境 (`https://uni-lab.uat.bohrium.com/api/v1`)
|
||||
- `--addr local`:本地环境 (`http://127.0.0.1:48197/api/v1`)
|
||||
- 自定义地址:直接指定完整 URL
|
||||
|
||||
### 5. 认证配置
|
||||
|
||||
- **必需参数**:`--ak` 和 `--sk` 必须同时提供
|
||||
- 命令行参数优先于配置文件中的设置
|
||||
- 未提供认证信息会导致启动失败并提示注册实验室
|
||||
|
||||
### 6. 设备图谱加载
|
||||
|
||||
支持两种方式:
|
||||
|
||||
- **本地文件**:使用 `-g` 指定图谱文件(支持 JSON 和 GraphML 格式)
|
||||
- **远程资源**:使用 `--use_remote_resource` 从云端获取
|
||||
|
||||
### 7. 注册表构建
|
||||
|
||||
- 构建设备和资源注册表
|
||||
- 支持自定义注册表路径 (`--registry_path`)
|
||||
- 可选择补全注册表信息 (`--complete_registry`)
|
||||
|
||||
### 8. 设备验证和注册
|
||||
|
||||
- 验证设备连接和端点配置
|
||||
- 自动注册设备到云端服务
|
||||
|
||||
### 9. 通信桥接配置
|
||||
|
||||
- **WebSocket**:实时通信和任务下发
|
||||
- **FastAPI**:HTTP API 服务和物料更新
|
||||
|
||||
### 10. 可视化和服务启动
|
||||
|
||||
- 可选启动可视化工具 (`--visual`)
|
||||
- 启动 Web 信息服务 (默认端口 8002)
|
||||
- 启动后端通信服务
|
||||
|
||||
## 使用配置文件
|
||||
|
||||
Uni-Lab 支持使用 Python 格式的配置文件进行系统设置。通过 `--config` 参数指定配置文件路径:
|
||||
Uni-Lab支持使用Python格式的配置文件进行系统设置。通过 `--config` 参数指定配置文件路径:
|
||||
|
||||
```bash
|
||||
# 使用配置文件启动
|
||||
unilab --config path/to/your/config.py
|
||||
```
|
||||
|
||||
配置文件包含实验室和 WebSocket 连接等设置。有关配置文件的详细信息,请参阅[配置指南](configuration.md)。
|
||||
配置文件包含MQTT、HTTP、ROS等系统设置。有关配置文件的详细信息,请参阅[配置指南](configuration.md)。
|
||||
|
||||
## 初始化信息来源
|
||||
|
||||
启动 Uni-Lab 时,可以选用两种方式之一配置实验室设备:
|
||||
启动 Uni-Lab 时,可以选用两种方式之一配置实验室设备、耗材、通信、控制逻辑:
|
||||
|
||||
### 1. 组态&拓扑图
|
||||
|
||||
使用 `-g` 时,组态&拓扑图应包含实验室所有信息,详见{ref}`graph`。目前支持 GraphML 和 node-link JSON 两种格式。格式可参照 `tests/experiments` 下的启动文件。
|
||||
使用 `-g` 时,组态&拓扑图应包含实验室所有信息,详见{ref}`graph`。目前支持 graphml 和 node-link json 两种格式。格式可参照 `tests/experiments` 下的启动文件。
|
||||
|
||||
### 2. 分别指定控制逻辑
|
||||
### 2. 分别指定设备、耗材、控制逻辑
|
||||
|
||||
使用 `-c` 传入控制逻辑配置。
|
||||
分别使用 `-d, -r, -c` 依次传入设备组态配置、耗材列表、控制逻辑。
|
||||
|
||||
不管使用哪一种初始化方式,设备/物料字典均需包含 `class` 属性,用于查找注册表信息。默认查找范围都是 Uni-Lab 内部注册表 `unilabos/registry/{devices,device_comms,resources}`。要添加额外的注册表路径,可以使用 `--registry_path` 加入 `<your-registry-path>/{devices,device_comms,resources}`。
|
||||
可参照 `devices.json` 和 `resources.json`。
|
||||
|
||||
不管使用哪一种初始化方式,设备/物料字典均需包含 `class` 属性,用于查找注册表信息。默认查找范围都是 Uni-Lab 内部注册表 `unilabos/registry/{devices,device_comms,resources}`。要添加额外的注册表路径,可以使用 `--registry` 加入 `<your-registry-path>/{devices,device_comms,resources}`。
|
||||
|
||||
## 通信中间件 `--backend`
|
||||
|
||||
目前 Uni-Lab 支持以下通信中间件:
|
||||
|
||||
- **ros** (默认):基于 ROS2 的通信
|
||||
- **simple**:简化通信模式
|
||||
- **automancer**:Automancer 兼容模式
|
||||
目前 Uni-Lab 仅支持 ros2 作为通信中间件。
|
||||
|
||||
## 端云桥接 `--app_bridges`
|
||||
|
||||
目前 Uni-Lab 提供 WebSocket、FastAPI (http) 两种端云通信方式:
|
||||
|
||||
- **WebSocket**:负责实时通信和任务下发
|
||||
- **FastAPI**:负责端对云物料更新和 HTTP API
|
||||
目前 Uni-Lab 提供 FastAPI (http), MQTT 两种端云通信方式。其中默认 MQTT 负责端对云状态同步和云对端任务下发,FastAPI 负责端对云物料更新。
|
||||
|
||||
## 分布式组网
|
||||
|
||||
启动 Uni-Lab 时,加入 `--is_slave` 将作为从站,不加将作为主站:
|
||||
|
||||
- **主站 (host)**:持有物料修改权以及对云端的通信
|
||||
- **从站 (slave)**:无主机权限,可选择跳过等待主机服务 (`--slave_no_host`)
|
||||
|
||||
局域网内分别启动的 Uni-Lab 主站/从站将自动组网,互相能访问所有设备状态、传感器信息并发送指令。
|
||||
|
||||
## 可视化选项
|
||||
|
||||
### 2D 可视化
|
||||
|
||||
使用 `--2d_vis` 在 PyLabRobot 实例启动时同时启动 2D 可视化。
|
||||
|
||||
### 3D 可视化
|
||||
|
||||
通过 `--visual` 参数选择:
|
||||
|
||||
- **rviz**:使用 RViz 进行 3D 可视化
|
||||
- **web**:使用 Web 界面进行可视化
|
||||
- **disable** (默认):禁用可视化
|
||||
|
||||
## 实验室管理
|
||||
|
||||
### 首次使用
|
||||
|
||||
如果是首次使用,系统会:
|
||||
|
||||
1. 提示前往 https://uni-lab.bohrium.com 注册实验室
|
||||
2. 引导创建配置文件
|
||||
3. 设置工作目录
|
||||
|
||||
### 认证设置
|
||||
|
||||
- `--ak`:实验室访问密钥
|
||||
- `--sk`:实验室私钥
|
||||
- 两者必须同时提供才能正常启动
|
||||
启动 Uni-Lab 时,加入 `--without_host` 将作为从站,不加将作为主站,主站 (host) 持有物料修改权以及对云端的通信。局域网内分别启动的 Uni-Lab 主站/从站将自动组网,互相能访问所有设备状态、传感器信息并发送指令。
|
||||
|
||||
## 完整启动示例
|
||||
|
||||
以下是一些常用的启动命令示例:
|
||||
|
||||
```bash
|
||||
# 使用组态图启动,上传注册表
|
||||
unilab --ak your_ak --sk your_sk -g path/to/graph.json --upload_registry
|
||||
# 使用配置文件和组态图启动
|
||||
unilab -g path/to/graph.json
|
||||
|
||||
# 使用远程资源启动
|
||||
unilab --ak your_ak --sk your_sk --use_remote_resource
|
||||
|
||||
# 更新注册表
|
||||
unilab --ak your_ak --sk your_sk --complete_registry
|
||||
|
||||
# 启动从站模式
|
||||
unilab --ak your_ak --sk your_sk --is_slave
|
||||
|
||||
# 启用可视化
|
||||
unilab --ak your_ak --sk your_sk --visual web --2d_vis
|
||||
|
||||
# 指定本地信息网页服务端口和禁用自动跳出浏览器
|
||||
unilab --ak your_ak --sk your_sk --port 8080 --disable_browser
|
||||
# 使用配置文件和分离的设备/资源文件启动
|
||||
unilab -d devices.json -r resources.json
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1. 认证失败
|
||||
|
||||
如果提示 "后续运行必须拥有一个实验室",请确保:
|
||||
|
||||
- 已在 https://uni-lab.bohrium.com 注册实验室
|
||||
- 正确设置了 `--ak` 和 `--sk` 参数
|
||||
- 配置文件中包含正确的认证信息
|
||||
|
||||
### 2. 配置文件问题
|
||||
|
||||
如果配置文件加载失败:
|
||||
|
||||
- 确保配置文件是 `.py` 格式
|
||||
- 检查配置文件语法是否正确
|
||||
- 首次使用可让系统自动创建示例配置文件
|
||||
|
||||
### 3. 网络连接问题
|
||||
|
||||
如果无法连接到服务器:
|
||||
|
||||
- 检查网络连接
|
||||
- 确认服务器地址是否正确
|
||||
- 尝试使用不同的环境地址(test、uat、local)
|
||||
|
||||
### 4. 设备图谱问题
|
||||
|
||||
如果设备加载失败:
|
||||
|
||||
- 检查图谱文件格式是否正确
|
||||
- 验证设备连接和端点配置
|
||||
- 确保注册表路径正确
|
||||
|
||||
## 页面操作
|
||||
|
||||
### 1. 启动成功
|
||||
当您启动成功后,可以看到物料列表,节点模版和组态图如图展示
|
||||

|
||||
|
||||
### 2. 根据需求创建设备和物料
|
||||
我们可以做一个简单的案例
|
||||
* 在容器1中加入水
|
||||
* 通过传输泵将容器1中的水转移到容器2中
|
||||
#### 2.1 添加所需的设备和物料
|
||||
仪器设备work_station中的workstation 数量x1
|
||||
仪器设备virtual_device中的virtual_transfer_pump 数量x1
|
||||
物料耗材container中的container 数量x2
|
||||
|
||||
#### 2.2 将设备和物料根据父子关系进行关联
|
||||
当我们添加设备时,仪器耗材模块的物料列表也会实时更新
|
||||
我们需要将设备和物料拖拽到workstation中并在画布上将它们连接起来,就像真实的设备操作一样
|
||||

|
||||
|
||||
### 3. 创建工作流
|
||||
进入工作流模块 → 点击"我创建的" → 新建工作流
|
||||

|
||||
|
||||
#### 3.1 新增工作流节点
|
||||
我们可以进入指定工作流,在空白处右键
|
||||
* 选择Laboratory→host_node中的creat_resource
|
||||
* 选择Laboratory→workstation中的PumpTransferProtocol
|
||||
|
||||

|
||||
|
||||
#### 3.2 配置节点参数
|
||||
根据案例,工作流包含两个步骤:
|
||||
1. 使用creat_resource在容器中创建水
|
||||
2. 通过泵传输协议将水传输到另一个容器
|
||||
|
||||
我们点击creat_resource卡片上的编辑按钮来配置参数⭐️
|
||||
class_name :container
|
||||
device_id : workstation
|
||||
liquid_input_slot : 0或-1均可
|
||||
liquid_type : water
|
||||
liquid_volume : 根据需求填写即可,默认单位ml,这里举例50
|
||||
parent : workstation
|
||||
res_id : containe
|
||||
关联设备名称(原unilabos_device_id) : 这里就填写host_node
|
||||
**配置完成后点击底部保存按钮**
|
||||
|
||||
我们点击PumpTransferProtocol卡片上的编辑按钮来配置参数⭐️
|
||||
event : transfer_liquid
|
||||
from_vessel : water
|
||||
to_vessel : container1
|
||||
volume : 根据需求填写即可,默认单位ml,这里举例50
|
||||
关联设备名称(原unilabos_device_id) : 这里就填写workstation
|
||||
**配置完成后点击底部保存按钮**
|
||||
|
||||
#### 3.3 运行工作流
|
||||
1. 连接两个节点卡片
|
||||
2. 点击底部保存按钮
|
||||
3. 点击运行按钮执行工作流
|
||||
|
||||

|
||||
|
||||
### 运行监控
|
||||
* 运行状态和消息实时显示在底部控制台
|
||||
* 如有报错,可点击查看详细信息
|
||||
|
||||
### 结果验证
|
||||
工作流完成后,返回仪器耗材模块:
|
||||
* 点击 container1卡片查看详情
|
||||
* 确认其中包含参数指定的水和容量
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
# Uni-Lab-OS 一键安装快速指南
|
||||
|
||||
## 概述
|
||||
|
||||
本指南提供最快速的 Uni-Lab-OS 安装方法,使用预打包的 conda 环境,无需手动配置依赖。
|
||||
|
||||
## 前置要求
|
||||
|
||||
- 已安装 Conda/Miniconda/Miniforge/Mamba
|
||||
- 至少 10GB 可用磁盘空间
|
||||
- Windows 10+, macOS 10.14+, 或 Linux (Ubuntu 20.04+)
|
||||
|
||||
## 安装步骤
|
||||
|
||||
### 第一步:下载预打包环境
|
||||
|
||||
1. 访问 [GitHub Actions - Conda Pack Build](https://github.com/dptech-corp/Uni-Lab-OS/actions/workflows/conda-pack-build.yml)
|
||||
|
||||
2. 选择最新的成功构建记录(绿色勾号 ✓)
|
||||
|
||||
3. 在页面底部的 "Artifacts" 部分,下载对应你操作系统的压缩包:
|
||||
- Windows: `unilab-pack-win-64-{branch}.zip`
|
||||
- macOS (Intel): `unilab-pack-osx-64-{branch}.tar.gz`
|
||||
- macOS (Apple Silicon): `unilab-pack-osx-arm64-{branch}.tar.gz`
|
||||
- Linux: `unilab-pack-linux-64-{branch}.tar.gz`
|
||||
|
||||
### 第二步:解压并运行安装脚本
|
||||
|
||||
#### Windows
|
||||
|
||||
```batch
|
||||
REM 使用 Windows 资源管理器解压下载的 zip 文件
|
||||
REM 或使用命令行:
|
||||
tar -xzf unilab-pack-win-64-dev.zip
|
||||
|
||||
REM 进入解压后的目录
|
||||
cd unilab-pack-win-64-dev
|
||||
|
||||
REM 双击运行 install_unilab.bat
|
||||
REM 或在命令行中执行:
|
||||
install_unilab.bat
|
||||
```
|
||||
|
||||
#### macOS
|
||||
|
||||
```bash
|
||||
# 解压下载的压缩包
|
||||
tar -xzf unilab-pack-osx-arm64-dev.tar.gz
|
||||
|
||||
# 进入解压后的目录
|
||||
cd unilab-pack-osx-arm64-dev
|
||||
|
||||
# 运行安装脚本
|
||||
bash install_unilab.sh
|
||||
```
|
||||
|
||||
#### Linux
|
||||
|
||||
```bash
|
||||
# 解压下载的压缩包
|
||||
tar -xzf unilab-pack-linux-64-dev.tar.gz
|
||||
|
||||
# 进入解压后的目录
|
||||
cd unilab-pack-linux-64-dev
|
||||
|
||||
# 添加执行权限(如果需要)
|
||||
chmod +x install_unilab.sh
|
||||
|
||||
# 运行安装脚本
|
||||
./install_unilab.sh
|
||||
```
|
||||
|
||||
### 第三步:激活环境
|
||||
|
||||
```bash
|
||||
conda activate unilab
|
||||
```
|
||||
|
||||
### 第四步:验证安装(推荐)
|
||||
|
||||
```bash
|
||||
# 确保已激活环境
|
||||
conda activate unilab
|
||||
|
||||
# 运行验证脚本
|
||||
python verify_installation.py
|
||||
```
|
||||
|
||||
如果看到 "✓ All checks passed!",说明安装成功!
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 安装脚本找不到 conda?
|
||||
|
||||
**A:** 确保你已经安装了 conda/miniconda/miniforge,并且安装在标准位置:
|
||||
|
||||
- **Windows**:
|
||||
|
||||
- `%USERPROFILE%\miniforge3`
|
||||
- `%USERPROFILE%\miniconda3`
|
||||
- `%USERPROFILE%\anaconda3`
|
||||
- `C:\ProgramData\miniforge3`
|
||||
|
||||
- **macOS/Linux**:
|
||||
- `~/miniforge3`
|
||||
- `~/miniconda3`
|
||||
- `~/anaconda3`
|
||||
- `/opt/conda`
|
||||
|
||||
如果安装在其他位置,可以先激活 conda base 环境,然后手动运行安装脚本。
|
||||
|
||||
### Q: 安装后激活环境提示找不到?
|
||||
|
||||
**A:** 尝试以下方法:
|
||||
|
||||
```bash
|
||||
# 方法 1: 使用 conda activate
|
||||
conda activate unilab
|
||||
|
||||
# 方法 2: 使用完整路径激活(Windows)
|
||||
call C:\Users\{YourUsername}\miniforge3\envs\unilab\Scripts\activate.bat
|
||||
|
||||
# 方法 2: 使用完整路径激活(Unix)
|
||||
source ~/miniforge3/envs/unilab/bin/activate
|
||||
```
|
||||
|
||||
### Q: conda-unpack 失败怎么办?
|
||||
|
||||
**A:** 尝试手动运行:
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
cd %CONDA_PREFIX%\envs\unilab
|
||||
.\Scripts\conda-unpack.exe
|
||||
|
||||
# macOS/Linux
|
||||
cd $CONDA_PREFIX/envs/unilab
|
||||
./bin/conda-unpack
|
||||
```
|
||||
|
||||
### Q: 验证脚本报错?
|
||||
|
||||
**A:** 首先确认环境已激活:
|
||||
|
||||
```bash
|
||||
# 检查当前环境
|
||||
conda env list
|
||||
|
||||
# 应该看到 unilab 前面有 * 标记
|
||||
```
|
||||
|
||||
如果仍有问题,查看具体报错信息,可能需要:
|
||||
|
||||
- 重新运行安装脚本
|
||||
- 检查磁盘空间
|
||||
- 查看详细文档
|
||||
|
||||
### Q: 环境很大,有办法减小吗?
|
||||
|
||||
**A:** 预打包的环境包含所有依赖,通常较大(压缩后 2-5GB)。这是为了确保离线安装和完整功能。如果空间有限,考虑使用手动安装方式,只安装需要的组件。
|
||||
|
||||
### Q: 如何更新到最新版本?
|
||||
|
||||
**A:** 重新下载最新的预打包环境,运行安装脚本时选择覆盖现有环境。
|
||||
|
||||
或者在现有环境中更新:
|
||||
|
||||
```bash
|
||||
conda activate unilab
|
||||
|
||||
# 更新 unilabos
|
||||
cd /path/to/Uni-Lab-OS
|
||||
git pull
|
||||
pip install -e . --upgrade
|
||||
|
||||
# 更新 ros-humble-unilabos-msgs
|
||||
mamba update ros-humble-unilabos-msgs -c uni-lab -c robostack-staging -c conda-forge
|
||||
```
|
||||
|
||||
## 下一步
|
||||
|
||||
安装完成后,你可以:
|
||||
|
||||
1. **查看启动指南**: {doc}`launch`
|
||||
2. **运行示例**: {doc}`../boot_examples/index`
|
||||
3. **配置设备**: 编辑 `unilabos_data/startup_config.json`
|
||||
4. **阅读开发文档**: {doc}`../developer_guide/workstation_architecture`
|
||||
|
||||
## 需要帮助?
|
||||
|
||||
- **文档**: [docs/user_guide/installation.md](installation.md)
|
||||
- **问题反馈**: [GitHub Issues](https://github.com/dptech-corp/Uni-Lab-OS/issues)
|
||||
- **开发版安装**: 参考 {doc}`installation` 的方式二
|
||||
|
||||
---
|
||||
|
||||
**提示**: 这个预打包环境包含了从指定分支(通常是 `dev`)构建的最新代码。如果需要稳定版本,请使用方式二手动安装 release 版本。
|
||||
22
package.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>unilabos</name>
|
||||
<version>0.0.0</version>
|
||||
<description>ROS2 package for unilabos server</description>
|
||||
<maintainer email="changjh@pku.edu.cn">changjh</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<build_depend>action_msgs</build_depend>
|
||||
<exec_depend>action_msgs</exec_depend>
|
||||
<member_of_group>rosidl_interface_packages</member_of_group>
|
||||
|
||||
<test_depend>ament_copyright</test_depend>
|
||||
<test_depend>ament_flake8</test_depend>
|
||||
<test_depend>ament_pep257</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -1,6 +1,3 @@
|
||||
channel_sources:
|
||||
- robostack,robostack-staging,conda-forge,defaults
|
||||
|
||||
gazebo:
|
||||
- '11'
|
||||
libpqxx:
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
CONDA_BUILD_SYSROOT:
|
||||
- /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
|
||||
MACOSX_DEPLOYMENT_TARGET:
|
||||
- "11.0"
|
||||
CONDA_SUBDIR:
|
||||
- osx-arm64
|
||||
# boa build -m ./recipes/conda_build_config.yaml -m ./recipes/macos_sdk_config.yaml ./recipes/ros-humble-unilabos-msgs
|
||||
@@ -1,76 +0,0 @@
|
||||
package:
|
||||
name: ros-humble-unilabos-msgs
|
||||
version: 0.10.7
|
||||
source:
|
||||
path: ../../unilabos_msgs
|
||||
target_directory: src
|
||||
|
||||
build:
|
||||
script:
|
||||
- if: win
|
||||
then:
|
||||
- copy %RECIPE_DIR%\bld_ament_cmake.bat %SRC_DIR%
|
||||
- call %SRC_DIR%\bld_ament_cmake.bat
|
||||
- if: unix
|
||||
then:
|
||||
- cp $RECIPE_DIR/build_ament_cmake.sh $SRC_DIR
|
||||
- bash $SRC_DIR/build_ament_cmake.sh
|
||||
|
||||
about:
|
||||
repository: https://github.com/dptech-corp/Uni-Lab-OS
|
||||
license: BSD-3-Clause
|
||||
description: "ros-humble-unilabos-msgs is a package that provides message definitions for Uni-Lab-OS."
|
||||
|
||||
requirements:
|
||||
build:
|
||||
- ${{ compiler('cxx') }}
|
||||
- ${{ compiler('c') }}
|
||||
- python ==3.11.11
|
||||
- numpy
|
||||
- if: build_platform != target_platform
|
||||
then:
|
||||
- pkg-config
|
||||
- cross-python_${{ target_platform }}
|
||||
- if: linux and x86_64
|
||||
then:
|
||||
- sysroot_linux-64 ==2.17
|
||||
- ninja
|
||||
- setuptools
|
||||
- cython
|
||||
- cmake
|
||||
- if: unix
|
||||
then:
|
||||
- make
|
||||
- coreutils
|
||||
- if: osx
|
||||
then:
|
||||
- tapi
|
||||
- if: win
|
||||
then:
|
||||
- vs2022_win-64
|
||||
host:
|
||||
- numpy
|
||||
- pip
|
||||
- if: build_platform == target_platform
|
||||
then:
|
||||
- pkg-config
|
||||
- robostack-staging::ros-humble-action-msgs
|
||||
- robostack-staging::ros-humble-ament-cmake
|
||||
- robostack-staging::ros-humble-ament-lint-auto
|
||||
- robostack-staging::ros-humble-ament-lint-common
|
||||
- robostack-staging::ros-humble-ros-environment
|
||||
- robostack-staging::ros-humble-ros-workspace
|
||||
- robostack-staging::ros-humble-rosidl-default-generators
|
||||
- robostack-staging::ros-humble-std-msgs
|
||||
- robostack-staging::ros-humble-geometry-msgs
|
||||
- robostack-staging::ros2-distro-mutex=0.6
|
||||
run:
|
||||
- robostack-staging::ros-humble-action-msgs
|
||||
- robostack-staging::ros-humble-ros-workspace
|
||||
- robostack-staging::ros-humble-rosidl-default-runtime
|
||||
- robostack-staging::ros-humble-std-msgs
|
||||
- robostack-staging::ros-humble-geometry-msgs
|
||||
- robostack-staging::ros2-distro-mutex=0.6
|
||||
- if: osx and x86_64
|
||||
then:
|
||||
- __osx >=${{ MACOSX_DEPLOYMENT_TARGET|default('10.14') }}
|
||||
@@ -1,7 +1,6 @@
|
||||
:: Generated by vinca http://github.com/RoboStack/vinca.
|
||||
:: DO NOT EDIT!
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
set "PYTHONPATH=%LIBRARY_PREFIX%\lib\site-packages;%SP_DIR%"
|
||||
|
||||
@@ -17,11 +16,9 @@ pushd build
|
||||
|
||||
:: try to fix long paths issues by using default generator
|
||||
set "CMAKE_GENERATOR=Visual Studio %VS_MAJOR% %VS_YEAR%"
|
||||
set "SP_DIR_FORWARDSLASHES=%SP_DIR:\=/%"
|
||||
|
||||
set PYTHON="%PREFIX%\python.exe"
|
||||
set PYTHON=%PYTHON:\=/%
|
||||
set SP_DIR="..\Lib\site-packages"
|
||||
set SP_DIR=%SP_DIR:\=/%
|
||||
|
||||
cmake ^
|
||||
-G "%CMAKE_GENERATOR%" ^
|
||||
@@ -35,10 +32,10 @@ cmake ^
|
||||
-DBUILD_SHARED_LIBS=ON ^
|
||||
-DBUILD_TESTING=OFF ^
|
||||
-DCMAKE_OBJECT_PATH_MAX=255 ^
|
||||
-DPYTHON_INSTALL_DIR=%SP_DIR% ^
|
||||
-DPYTHON_INSTALL_DIR=%SP_DIR_FORWARDSLASHES% ^
|
||||
--compile-no-warning-as-error ^
|
||||
%SRC_DIR%\src
|
||||
%SRC_DIR%\%PKG_NAME%\src\work
|
||||
if errorlevel 1 exit 1
|
||||
|
||||
cmake --build . --config Release --target install -j8
|
||||
if errorlevel 1 exit 1
|
||||
cmake --build . --config Release --target install
|
||||
if errorlevel 1 exit 1
|
||||
@@ -24,7 +24,7 @@ echo "USING PKG_CONFIG_EXECUTABLE=${PKG_CONFIG_EXECUTABLE}"
|
||||
export ROS_PYTHON_VERSION=`$PYTHON_EXECUTABLE -c "import sys; print('%i.%i' % (sys.version_info[0:2]))"`
|
||||
echo "Using Python ${ROS_PYTHON_VERSION}"
|
||||
# Fix up SP_DIR which for some reason might contain a path to a wrong Python version
|
||||
FIXED_SP_DIR=$($PYTHON_EXECUTABLE -c "import site; print(site.getsitepackages()[0])")
|
||||
FIXED_SP_DIR=$(echo $SP_DIR | sed -E "s/python[0-9]+\.[0-9]+/python$ROS_PYTHON_VERSION/")
|
||||
echo "Using site-package dir ${FIXED_SP_DIR}"
|
||||
|
||||
# see https://github.com/conda-forge/cross-python-feedstock/issues/24
|
||||
@@ -56,6 +56,7 @@ cmake \
|
||||
-DPYTHON_EXECUTABLE=$PYTHON_EXECUTABLE \
|
||||
-DPython_EXECUTABLE=$PYTHON_EXECUTABLE \
|
||||
-DPython3_EXECUTABLE=$PYTHON_EXECUTABLE \
|
||||
-DPython3_FIND_STRATEGY=LOCATION \
|
||||
-DPKG_CONFIG_EXECUTABLE=$PKG_CONFIG_EXECUTABLE \
|
||||
-DPYTHON_INSTALL_DIR=$FIXED_SP_DIR \
|
||||
-DSETUPTOOLS_DEB_LAYOUT=OFF \
|
||||
@@ -65,6 +66,6 @@ cmake \
|
||||
-DBUILD_TESTING=OFF \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=$OSX_DEPLOYMENT_TARGET \
|
||||
--compile-no-warning-as-error \
|
||||
$SRC_DIR/src
|
||||
$SRC_DIR/$PKG_NAME/src/work
|
||||
|
||||
cmake --build . --config Release --target install -j8
|
||||
cmake --build . --config Release --target install
|
||||
61
recipes/ros-humble-unilabos-msgs/recipe.yaml
Normal file
@@ -0,0 +1,61 @@
|
||||
package:
|
||||
name: ros-humble-unilabos-msgs
|
||||
version: 0.9.0
|
||||
source:
|
||||
path: ../../unilabos_msgs
|
||||
folder: ros-humble-unilabos-msgs/src/work
|
||||
|
||||
build:
|
||||
script:
|
||||
sel(win): bld_ament_cmake.bat
|
||||
sel(unix): build_ament_cmake.sh
|
||||
number: 5
|
||||
about:
|
||||
home: https://www.ros.org/
|
||||
license: BSD-3-Clause
|
||||
summary: |
|
||||
Robot Operating System
|
||||
|
||||
extra:
|
||||
recipe-maintainers:
|
||||
- ros-forge
|
||||
|
||||
requirements:
|
||||
build:
|
||||
- "{{ compiler('cxx') }}"
|
||||
- "{{ compiler('c') }}"
|
||||
- sel(linux64): sysroot_linux-64 2.17
|
||||
- ninja
|
||||
- setuptools
|
||||
- sel(unix): make
|
||||
- sel(unix): coreutils
|
||||
- sel(osx): tapi
|
||||
- sel(build_platform != target_platform): pkg-config
|
||||
- cmake
|
||||
- cython
|
||||
- sel(win): vs2022_win-64
|
||||
- sel(build_platform != target_platform): python
|
||||
- sel(build_platform != target_platform): cross-python_{{ target_platform }}
|
||||
- sel(build_platform != target_platform): numpy
|
||||
host:
|
||||
- numpy
|
||||
- pip
|
||||
- sel(build_platform == target_platform): pkg-config
|
||||
- robostack-staging::ros-humble-action-msgs
|
||||
- robostack-staging::ros-humble-ament-cmake
|
||||
- robostack-staging::ros-humble-ament-lint-auto
|
||||
- robostack-staging::ros-humble-ament-lint-common
|
||||
- robostack-staging::ros-humble-ros-environment
|
||||
- robostack-staging::ros-humble-ros-workspace
|
||||
- robostack-staging::ros-humble-rosidl-default-generators
|
||||
- robostack-staging::ros-humble-std-msgs
|
||||
- robostack-staging::ros-humble-geometry-msgs
|
||||
- robostack-staging::ros2-distro-mutex=0.6.*
|
||||
run:
|
||||
- robostack-staging::ros-humble-action-msgs
|
||||
- robostack-staging::ros-humble-ros-workspace
|
||||
- robostack-staging::ros-humble-rosidl-default-runtime
|
||||
- robostack-staging::ros-humble-std-msgs
|
||||
- robostack-staging::ros-humble-geometry-msgs
|
||||
- robostack-staging::ros2-distro-mutex=0.6.*
|
||||
- sel(osx and x86_64): __osx >={{ MACOSX_DEPLOYMENT_TARGET|default('10.14') }}
|
||||
@@ -1,6 +1,6 @@
|
||||
package:
|
||||
name: unilabos
|
||||
version: "0.10.7"
|
||||
version: "0.9.0"
|
||||
|
||||
source:
|
||||
path: ../..
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Create Distribution Package README
|
||||
===================================
|
||||
|
||||
Generate README.txt for conda-pack distribution packages.
|
||||
|
||||
Usage:
|
||||
python create_readme.py <platform> <branch> <output_file>
|
||||
|
||||
Arguments:
|
||||
platform: Platform identifier (win-64, linux-64, osx-64, osx-arm64)
|
||||
branch: Git branch name
|
||||
output_file: Output file path (e.g., dist-package/README.txt)
|
||||
|
||||
Example:
|
||||
python create_readme.py win-64 dev dist-package/README.txt
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_readme_content(platform: str, branch: str) -> str:
|
||||
"""
|
||||
Generate README content for the specified platform.
|
||||
|
||||
Args:
|
||||
platform: Platform identifier
|
||||
branch: Git branch name
|
||||
|
||||
Returns:
|
||||
str: README content
|
||||
"""
|
||||
# Get current UTC time
|
||||
build_date = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
# Determine platform-specific content
|
||||
is_windows = platform == "win-64"
|
||||
|
||||
if is_windows:
|
||||
archive_ext = "zip"
|
||||
install_script = "install_unilab.bat"
|
||||
platform_instructions = """Windows:
|
||||
1. Extract the downloaded ZIP file
|
||||
2. Double-click install_unilab.bat (or run in cmd)
|
||||
3. Follow the prompts"""
|
||||
else:
|
||||
archive_ext = "tar.gz"
|
||||
install_script = "install_unilab.sh"
|
||||
platform_name = {"linux-64": "linux-64", "osx-64": "osx-64", "osx-arm64": "osx-arm64"}.get(platform, platform)
|
||||
platform_instructions = f"""macOS/Linux:
|
||||
1. Download and extract unilab-pack-{platform_name}.tar.gz
|
||||
2. Run: bash install_unilab.sh
|
||||
3. Follow the prompts
|
||||
|
||||
Alternative (if downloaded from GitHub Actions):
|
||||
1. Extract the artifact ZIP file
|
||||
2. Extract unilab-pack-{platform_name}.tar.gz inside
|
||||
3. Run: bash install_unilab.sh"""
|
||||
|
||||
# Generate README content
|
||||
readme = f"""UniLabOS Conda-Pack Environment
|
||||
================================
|
||||
|
||||
This package contains a pre-built UniLabOS environment.
|
||||
|
||||
Installation Instructions:
|
||||
--------------------------
|
||||
|
||||
{platform_instructions}
|
||||
|
||||
The installation script will:
|
||||
- Automatically find your conda installation
|
||||
- Extract the environment to conda's envs/unilab directory
|
||||
- Run conda-unpack to finalize setup
|
||||
|
||||
After installation:
|
||||
conda activate unilab
|
||||
python verify_installation.py
|
||||
|
||||
Verification:
|
||||
-------------
|
||||
|
||||
The verify_installation.py script will check:
|
||||
- Python version (3.11.11)
|
||||
- ROS2 rclpy installation
|
||||
- UniLabOS installation and dependencies
|
||||
|
||||
If all checks pass, you're ready to use UniLabOS!
|
||||
|
||||
Package Contents:
|
||||
-----------------
|
||||
|
||||
- {install_script} (automatic installation script)
|
||||
- unilab-env-{platform}.tar.gz (packed conda environment)
|
||||
- verify_installation.py (environment verification tool)
|
||||
- README.txt (this file)
|
||||
|
||||
Build Information:
|
||||
------------------
|
||||
|
||||
Branch: {branch}
|
||||
Platform: {platform}
|
||||
Python: 3.11.11
|
||||
Date: {build_date}
|
||||
|
||||
Troubleshooting:
|
||||
----------------
|
||||
|
||||
If installation fails:
|
||||
|
||||
1. Ensure conda or mamba is installed
|
||||
Check: conda --version
|
||||
|
||||
2. Verify you have sufficient disk space
|
||||
Required: ~5-10 GB after extraction
|
||||
|
||||
3. Check installation permissions
|
||||
You need write access to conda's envs directory
|
||||
|
||||
4. For detailed logs, run the install script from terminal
|
||||
|
||||
For more help:
|
||||
- Documentation: docs/user_guide/installation.md
|
||||
- Quick Start: QUICK_START_CONDA_PACK.md
|
||||
- Issues: https://github.com/dptech-corp/Uni-Lab-OS/issues
|
||||
|
||||
License:
|
||||
--------
|
||||
|
||||
UniLabOS is licensed under GPL-3.0-only.
|
||||
See LICENSE file for details.
|
||||
|
||||
Repository: https://github.com/dptech-corp/Uni-Lab-OS
|
||||
"""
|
||||
|
||||
return readme
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate README.txt for conda-pack distribution",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
python create_readme.py win-64 dev dist-package/README.txt
|
||||
python create_readme.py linux-64 main dist-package/README.txt
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument("platform", choices=["win-64", "linux-64", "osx-64", "osx-arm64"], help="Platform identifier")
|
||||
|
||||
parser.add_argument("branch", help="Git branch name")
|
||||
|
||||
parser.add_argument("output_file", help="Output file path")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
# Generate README content
|
||||
readme_content = get_readme_content(args.platform, args.branch)
|
||||
|
||||
# Create output directory if needed
|
||||
output_path = Path(args.output_file)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Write README file
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
f.write(readme_content)
|
||||
|
||||
print(f"✓ README.txt created: {output_path}")
|
||||
print(f" Platform: {args.platform}")
|
||||
print(f" Branch: {args.branch}")
|
||||
|
||||
return 0
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error creating README: {e}", file=sys.stderr)
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,148 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Create ZIP Archive with ZIP64 Support
|
||||
======================================
|
||||
|
||||
This script creates a ZIP archive with ZIP64 support for large files (>2GB).
|
||||
It's used in the conda-pack build workflow to package the distribution.
|
||||
|
||||
PowerShell's Compress-Archive has a 2GB limitation, so we use Python's zipfile
|
||||
module with allowZip64=True to handle large conda-packed environments.
|
||||
|
||||
Usage:
|
||||
python create_zip_archive.py <source_dir> <output_zip> [--compression-level LEVEL]
|
||||
|
||||
Arguments:
|
||||
source_dir: Directory to compress
|
||||
output_zip: Output ZIP file path
|
||||
--compression-level: Compression level (0-9, default: 6)
|
||||
|
||||
Example:
|
||||
python create_zip_archive.py dist-package unilab-pack-win-64.zip
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def create_zip_archive(source_dir: str, output_zip: str, compression_level: int = 6) -> bool:
|
||||
"""
|
||||
Create a ZIP archive with ZIP64 support.
|
||||
|
||||
Args:
|
||||
source_dir: Directory to compress
|
||||
output_zip: Output ZIP file path
|
||||
compression_level: Compression level (0-9)
|
||||
|
||||
Returns:
|
||||
bool: True if successful
|
||||
"""
|
||||
try:
|
||||
source_path = Path(source_dir)
|
||||
output_path = Path(output_zip)
|
||||
|
||||
# Validate source directory
|
||||
if not source_path.exists():
|
||||
print(f"Error: Source directory does not exist: {source_dir}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
if not source_path.is_dir():
|
||||
print(f"Error: Source path is not a directory: {source_dir}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
# Remove existing output file if present
|
||||
if output_path.exists():
|
||||
print(f"Removing existing archive: {output_path}")
|
||||
output_path.unlink()
|
||||
|
||||
# Create ZIP archive
|
||||
print("=" * 70)
|
||||
print(f"Creating ZIP archive with ZIP64 support")
|
||||
print(f" Source: {source_path.absolute()}")
|
||||
print(f" Output: {output_path.absolute()}")
|
||||
print(f" Compression: Level {compression_level}")
|
||||
print("=" * 70)
|
||||
|
||||
total_size = 0
|
||||
file_count = 0
|
||||
|
||||
with zipfile.ZipFile(
|
||||
output_path, "w", zipfile.ZIP_DEFLATED, allowZip64=True, compresslevel=compression_level
|
||||
) as zipf:
|
||||
# Walk through source directory
|
||||
for root, dirs, files in os.walk(source_dir):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
arcname = os.path.relpath(file_path, source_dir)
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
# Add file to archive
|
||||
zipf.write(file_path, arcname)
|
||||
|
||||
# Display progress
|
||||
total_size += file_size
|
||||
file_count += 1
|
||||
print(f" [{file_count:3d}] Adding: {arcname:50s} {file_size:>15,} bytes")
|
||||
|
||||
# Get final archive size
|
||||
archive_size = output_path.stat().st_size
|
||||
compression_ratio = (1 - archive_size / total_size) * 100 if total_size > 0 else 0
|
||||
|
||||
# Display summary
|
||||
print("=" * 70)
|
||||
print("Archive created successfully!")
|
||||
print(f" Files added: {file_count}")
|
||||
print(f" Total size (uncompressed): {total_size:>15,} bytes ({total_size / (1024**3):.2f} GB)")
|
||||
print(f" Archive size (compressed): {archive_size:>15,} bytes ({archive_size / (1024**3):.2f} GB)")
|
||||
print(f" Compression ratio: {compression_ratio:.1f}%")
|
||||
print("=" * 70)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error creating ZIP archive: {e}", file=sys.stderr)
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Create ZIP archive with ZIP64 support for large files",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
python create_zip_archive.py dist-package unilab-pack-win-64.zip
|
||||
python create_zip_archive.py dist-package unilab-pack-win-64.zip --compression-level 9
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument("source_dir", help="Directory to compress")
|
||||
|
||||
parser.add_argument("output_zip", help="Output ZIP file path")
|
||||
|
||||
parser.add_argument(
|
||||
"--compression-level",
|
||||
type=int,
|
||||
default=6,
|
||||
choices=range(0, 10),
|
||||
metavar="LEVEL",
|
||||
help="Compression level (0=no compression, 9=maximum compression, default=6)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create archive
|
||||
success = create_zip_archive(args.source_dir, args.output_zip, args.compression_level)
|
||||
|
||||
# Exit with appropriate code
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,203 +0,0 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
echo ================================================
|
||||
echo UniLabOS Environment Installation Script
|
||||
echo ================================================
|
||||
echo.
|
||||
|
||||
REM Get the directory where this script is located
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
cd /d "%SCRIPT_DIR%"
|
||||
|
||||
REM Find conda installation
|
||||
echo Searching for conda installation...
|
||||
|
||||
REM Method 1: Try to get conda base using 'conda info --base'
|
||||
set "CONDA_BASE="
|
||||
for /f "tokens=*" %%i in ('conda info --base 2^>nul') do (
|
||||
set "CONDA_BASE=%%i"
|
||||
)
|
||||
|
||||
if not "%CONDA_BASE%"=="" (
|
||||
echo Found conda at: %CONDA_BASE% (via conda info)
|
||||
goto :conda_found
|
||||
)
|
||||
|
||||
REM Method 2: Use 'where conda' and parse the path
|
||||
echo Trying alternative method...
|
||||
for /f "tokens=*" %%i in ('where conda 2^>nul') do (
|
||||
set "CONDA_PATH=%%i"
|
||||
goto :parse_conda_path
|
||||
)
|
||||
|
||||
echo ERROR: Could not find conda installation!
|
||||
echo Please make sure conda/mamba is installed and in your PATH.
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
|
||||
:parse_conda_path
|
||||
REM Parse conda path to find base directory
|
||||
REM Common paths:
|
||||
REM C:\Users\hp\miniforge3\Library\bin\conda.bat
|
||||
REM C:\Users\hp\miniforge3\Scripts\conda.exe
|
||||
REM C:\Users\hp\miniforge3\condabin\conda.bat
|
||||
|
||||
echo Found conda executable at: %CONDA_PATH%
|
||||
|
||||
REM Check if path contains \Library\bin\ (typical for conda.bat)
|
||||
echo %CONDA_PATH% | findstr /C:"\Library\bin\" >nul
|
||||
if not errorlevel 1 (
|
||||
REM Path like: C:\Users\hp\miniforge3\Library\bin\conda.bat
|
||||
REM Need to go up 3 levels: bin -> Library -> miniforge3
|
||||
for %%i in ("%CONDA_PATH%") do set "CONDA_BASE=%%~dpi"
|
||||
for %%i in ("%CONDA_BASE%..\..\..") do set "CONDA_BASE=%%~fi"
|
||||
goto :conda_found
|
||||
)
|
||||
|
||||
REM Check if path contains \Scripts\ (typical for conda.exe)
|
||||
echo %CONDA_PATH% | findstr /C:"\Scripts\" >nul
|
||||
if not errorlevel 1 (
|
||||
REM Path like: C:\Users\hp\miniforge3\Scripts\conda.exe
|
||||
REM Need to go up 2 levels: Scripts -> miniforge3
|
||||
for %%i in ("%CONDA_PATH%") do set "CONDA_BASE=%%~dpi"
|
||||
for %%i in ("%CONDA_BASE%..\.") do set "CONDA_BASE=%%~fi"
|
||||
goto :conda_found
|
||||
)
|
||||
|
||||
REM Check if path contains \condabin\ (typical for conda.bat)
|
||||
echo %CONDA_PATH% | findstr /C:"\condabin\" >nul
|
||||
if not errorlevel 1 (
|
||||
REM Path like: C:\Users\hp\miniforge3\condabin\conda.bat
|
||||
REM Need to go up 2 levels: condabin -> miniforge3
|
||||
for %%i in ("%CONDA_PATH%") do set "CONDA_BASE=%%~dpi"
|
||||
for %%i in ("%CONDA_BASE%..\.") do set "CONDA_BASE=%%~fi"
|
||||
goto :conda_found
|
||||
)
|
||||
|
||||
REM Default: assume it's 2 levels up
|
||||
for %%i in ("%CONDA_PATH%") do set "CONDA_BASE=%%~dpi"
|
||||
for %%i in ("%CONDA_BASE%..\.") do set "CONDA_BASE=%%~fi"
|
||||
|
||||
:conda_found
|
||||
echo Found conda base directory: %CONDA_BASE%
|
||||
echo.
|
||||
|
||||
REM Set target environment path
|
||||
set "ENV_NAME=unilab"
|
||||
set "ENV_PATH=%CONDA_BASE%\envs\%ENV_NAME%"
|
||||
|
||||
REM Check if environment already exists
|
||||
if exist "%ENV_PATH%" (
|
||||
echo WARNING: Environment '%ENV_NAME%' already exists at %ENV_PATH%
|
||||
echo.
|
||||
set /p "OVERWRITE=Do you want to overwrite it? (y/n): "
|
||||
if /i not "!OVERWRITE!"=="y" (
|
||||
echo Installation cancelled.
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
echo Removing existing environment...
|
||||
rmdir /s /q "%ENV_PATH%"
|
||||
)
|
||||
|
||||
REM Find the packed environment file
|
||||
set "PACK_FILE="
|
||||
for %%f in (unilab-env*.tar.gz) do (
|
||||
set "PACK_FILE=%%f"
|
||||
goto :found_pack
|
||||
)
|
||||
|
||||
:found_pack
|
||||
if "%PACK_FILE%"=="" (
|
||||
echo ERROR: Could not find unilab-env*.tar.gz file!
|
||||
echo Please make sure the packed environment file is in the same directory as this script.
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Found packed environment: %PACK_FILE%
|
||||
echo.
|
||||
|
||||
REM Extract the packed environment
|
||||
echo Extracting environment to %ENV_PATH%...
|
||||
mkdir "%ENV_PATH%"
|
||||
|
||||
REM Extract using tar (available in Windows 10+)
|
||||
tar -xzf "%PACK_FILE%" -C "%ENV_PATH%"
|
||||
if errorlevel 1 (
|
||||
echo ERROR: Failed to extract environment!
|
||||
echo Make sure you have Windows 10 or later with tar support.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Unpacking conda environment...
|
||||
echo Changing to environment directory: %ENV_PATH%
|
||||
cd /d "%ENV_PATH%"
|
||||
|
||||
REM Run conda-unpack from the environment directory
|
||||
if exist "Scripts\conda-unpack.exe" (
|
||||
echo Running: .\Scripts\conda-unpack.exe
|
||||
.\Scripts\conda-unpack.exe
|
||||
) else if exist "Scripts\activate.bat" (
|
||||
echo Running: .\Scripts\activate.bat followed by conda-unpack
|
||||
call .\Scripts\activate.bat
|
||||
conda-unpack
|
||||
) else (
|
||||
echo ERROR: Could not find Scripts\conda-unpack.exe or Scripts\activate.bat!
|
||||
echo Current directory: %CD%
|
||||
echo Expected location: %ENV_PATH%\Scripts\
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if errorlevel 1 (
|
||||
echo ERROR: conda-unpack failed!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Checking UniLabOS entry point...
|
||||
REM Check if unilab-script.py exists
|
||||
set "UNILAB_SCRIPT=%ENV_PATH%\Scripts\unilab-script.py"
|
||||
if not exist "%UNILAB_SCRIPT%" (
|
||||
echo WARNING: unilab-script.py not found, creating it...
|
||||
(
|
||||
echo # -*- coding: utf-8 -*-
|
||||
echo import re
|
||||
echo import sys
|
||||
echo.
|
||||
echo from unilabos.app.main import main
|
||||
echo.
|
||||
echo if __name__ == '__main__':
|
||||
echo sys.argv[0] = re.sub^(r'(-script\.pyw?^|\.exe^)?$', '', sys.argv[0]^)
|
||||
echo sys.exit^(main^(^)^)
|
||||
) > "%UNILAB_SCRIPT%"
|
||||
echo Created: %UNILAB_SCRIPT%
|
||||
) else (
|
||||
echo Found: %UNILAB_SCRIPT%
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ================================================
|
||||
echo Installation completed successfully!
|
||||
echo ================================================
|
||||
echo.
|
||||
echo To activate the environment, run:
|
||||
echo conda activate %ENV_NAME%
|
||||
echo.
|
||||
echo or
|
||||
echo.
|
||||
echo call %ENV_PATH%\Scripts\activate.bat
|
||||
echo.
|
||||
echo You can verify the installation by running:
|
||||
echo cd /d "%SCRIPT_DIR%"
|
||||
echo python verify_installation.py
|
||||
echo.
|
||||
pause
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "================================================"
|
||||
echo "UniLabOS Environment Installation Script"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
|
||||
# Get the directory where this script is located
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Find conda installation
|
||||
echo "Searching for conda installation..."
|
||||
CONDA_BASE=""
|
||||
|
||||
# Try to find conda in PATH
|
||||
if command -v conda &> /dev/null; then
|
||||
CONDA_BASE=$(conda info --base)
|
||||
echo "Found conda at: $CONDA_BASE"
|
||||
elif [ -d "$HOME/miniforge3" ]; then
|
||||
CONDA_BASE="$HOME/miniforge3"
|
||||
echo "Found conda at: $CONDA_BASE"
|
||||
elif [ -d "$HOME/miniconda3" ]; then
|
||||
CONDA_BASE="$HOME/miniconda3"
|
||||
echo "Found conda at: $CONDA_BASE"
|
||||
elif [ -d "$HOME/anaconda3" ]; then
|
||||
CONDA_BASE="$HOME/anaconda3"
|
||||
echo "Found conda at: $CONDA_BASE"
|
||||
elif [ -d "/opt/conda" ]; then
|
||||
CONDA_BASE="/opt/conda"
|
||||
echo "Found conda at: $CONDA_BASE"
|
||||
else
|
||||
echo "ERROR: Could not find conda installation!"
|
||||
echo "Please make sure conda/mamba is installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Initialize conda for this shell
|
||||
if [ -f "$CONDA_BASE/etc/profile.d/conda.sh" ]; then
|
||||
source "$CONDA_BASE/etc/profile.d/conda.sh"
|
||||
fi
|
||||
|
||||
# Set target environment path
|
||||
ENV_NAME="unilab"
|
||||
ENV_PATH="$CONDA_BASE/envs/$ENV_NAME"
|
||||
|
||||
# Check if environment already exists
|
||||
if [ -d "$ENV_PATH" ]; then
|
||||
echo "WARNING: Environment '$ENV_NAME' already exists at $ENV_PATH"
|
||||
read -p "Do you want to overwrite it? (y/n): " OVERWRITE
|
||||
if [ "$OVERWRITE" != "y" ] && [ "$OVERWRITE" != "Y" ]; then
|
||||
echo "Installation cancelled."
|
||||
exit 0
|
||||
fi
|
||||
echo "Removing existing environment..."
|
||||
rm -rf "$ENV_PATH"
|
||||
fi
|
||||
|
||||
# Find the packed environment file
|
||||
PACK_FILE=$(ls unilab-env*.tar.gz 2>/dev/null | head -n 1)
|
||||
|
||||
if [ -z "$PACK_FILE" ]; then
|
||||
echo "ERROR: Could not find unilab-env*.tar.gz file!"
|
||||
echo "Please make sure the packed environment file is in the same directory as this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found packed environment: $PACK_FILE"
|
||||
echo ""
|
||||
|
||||
# Extract the packed environment
|
||||
echo "Extracting environment to $ENV_PATH..."
|
||||
mkdir -p "$ENV_PATH"
|
||||
tar -xzf "$PACK_FILE" -C "$ENV_PATH"
|
||||
|
||||
echo ""
|
||||
echo "Unpacking conda environment..."
|
||||
echo "Changing to environment directory: $ENV_PATH"
|
||||
cd "$ENV_PATH"
|
||||
|
||||
# Run conda-unpack from the environment directory
|
||||
if [ -f "bin/conda-unpack" ]; then
|
||||
echo "Running: ./bin/conda-unpack"
|
||||
./bin/conda-unpack
|
||||
elif [ -f "bin/activate" ]; then
|
||||
echo "Running: source bin/activate followed by conda-unpack"
|
||||
source bin/activate
|
||||
conda-unpack
|
||||
else
|
||||
echo "ERROR: Could not find bin/conda-unpack or bin/activate!"
|
||||
echo "Current directory: $(pwd)"
|
||||
echo "Expected location: $ENV_PATH/bin/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Checking UniLabOS entry point..."
|
||||
# Check if unilab script exists in bin directory
|
||||
UNILAB_SCRIPT="$ENV_PATH/bin/unilab"
|
||||
if [ ! -f "$UNILAB_SCRIPT" ]; then
|
||||
echo "WARNING: unilab script not found, creating it..."
|
||||
cat > "$UNILAB_SCRIPT" << 'EOF'
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from unilabos.app.main import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
EOF
|
||||
chmod +x "$UNILAB_SCRIPT"
|
||||
echo "Created: $UNILAB_SCRIPT"
|
||||
else
|
||||
echo "Found: $UNILAB_SCRIPT"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo "Installation completed successfully!"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
echo "To activate the environment, run:"
|
||||
echo " conda activate $ENV_NAME"
|
||||
echo ""
|
||||
echo "or"
|
||||
echo ""
|
||||
echo " source $ENV_PATH/bin/activate"
|
||||
echo ""
|
||||
echo "You can verify the installation by running:"
|
||||
echo " cd $SCRIPT_DIR"
|
||||
echo " python verify_installation.py"
|
||||
echo ""
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
UniLabOS Installation Verification Script
|
||||
=========================================
|
||||
|
||||
This script verifies that UniLabOS and its dependencies are correctly installed.
|
||||
Run this script after installing the conda-pack environment to ensure everything works.
|
||||
|
||||
Usage:
|
||||
python verify_installation.py
|
||||
|
||||
Or in the conda environment:
|
||||
conda activate unilab
|
||||
python verify_installation.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# IMPORTANT: Set UTF-8 encoding BEFORE any other imports
|
||||
# This ensures all subsequent imports (including unilabos) can output UTF-8 characters
|
||||
if sys.platform == "win32":
|
||||
# Method 1: Reconfigure stdout/stderr to use UTF-8 with error handling
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
||||
except (AttributeError, OSError):
|
||||
pass
|
||||
|
||||
# Method 2: Set environment variable for subprocess and console
|
||||
os.environ["PYTHONIOENCODING"] = "utf-8"
|
||||
|
||||
# Method 3: Try to change Windows console code page to UTF-8
|
||||
try:
|
||||
import ctypes
|
||||
|
||||
# Set console code page to UTF-8 (CP 65001)
|
||||
ctypes.windll.kernel32.SetConsoleCP(65001)
|
||||
ctypes.windll.kernel32.SetConsoleOutputCP(65001)
|
||||
except (ImportError, AttributeError, OSError):
|
||||
pass
|
||||
|
||||
# Now import other modules
|
||||
import importlib
|
||||
|
||||
# Use ASCII-safe symbols that work across all platforms
|
||||
CHECK_MARK = "[OK]"
|
||||
CROSS_MARK = "[FAIL]"
|
||||
|
||||
|
||||
def check_package(package_name: str, display_name: str = None) -> bool:
|
||||
"""
|
||||
Check if a package can be imported.
|
||||
|
||||
Args:
|
||||
package_name: Name of the package to import
|
||||
display_name: Display name (defaults to package_name)
|
||||
|
||||
Returns:
|
||||
bool: True if package is available
|
||||
"""
|
||||
if display_name is None:
|
||||
display_name = package_name
|
||||
|
||||
try:
|
||||
importlib.import_module(package_name)
|
||||
print(f" {CHECK_MARK} {display_name}")
|
||||
return True
|
||||
except ImportError:
|
||||
print(f" {CROSS_MARK} {display_name}")
|
||||
return False
|
||||
|
||||
|
||||
def check_python_version() -> bool:
|
||||
"""Check Python version."""
|
||||
version = sys.version_info
|
||||
version_str = f"{version.major}.{version.minor}.{version.micro}"
|
||||
|
||||
if version.major == 3 and version.minor >= 11:
|
||||
print(f" {CHECK_MARK} Python {version_str}")
|
||||
return True
|
||||
else:
|
||||
print(f" {CROSS_MARK} Python {version_str} (requires Python 3.11+)")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all verification checks."""
|
||||
print("=" * 60)
|
||||
print("UniLabOS Installation Verification")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
all_passed = True
|
||||
|
||||
# Check Python version
|
||||
print("Checking Python version...")
|
||||
if not check_python_version():
|
||||
all_passed = False
|
||||
print()
|
||||
|
||||
# Check ROS2 rclpy
|
||||
print("Checking ROS2 rclpy...")
|
||||
if not check_package("rclpy", "ROS2 rclpy"):
|
||||
all_passed = False
|
||||
print()
|
||||
|
||||
# Run environment checker from unilabos
|
||||
print("Checking UniLabOS and dependencies...")
|
||||
try:
|
||||
from unilabos.utils.environment_check import check_environment
|
||||
|
||||
print(f" {CHECK_MARK} UniLabOS installed")
|
||||
|
||||
# Check environment without auto-install (verification only)
|
||||
# Set show_details=False to suppress detailed Chinese output that may cause encoding issues
|
||||
env_check_passed = check_environment(auto_install=False, show_details=False)
|
||||
|
||||
if env_check_passed:
|
||||
print(f" {CHECK_MARK} All required packages available")
|
||||
else:
|
||||
print(f" {CROSS_MARK} Some optional packages are missing")
|
||||
except ImportError:
|
||||
print(f" {CROSS_MARK} UniLabOS not installed")
|
||||
all_passed = False
|
||||
except Exception as e:
|
||||
print(f" {CROSS_MARK} Environment check failed: {str(e)}")
|
||||
print()
|
||||
|
||||
# Summary
|
||||
print("=" * 60)
|
||||
print("Verification Summary")
|
||||
print("=" * 60)
|
||||
|
||||
if all_passed:
|
||||
print(f"\n{CHECK_MARK} All checks passed! Your UniLabOS installation is ready.")
|
||||
print("\nNext steps:")
|
||||
print(" 1. Review the documentation: docs/user_guide/launch.md")
|
||||
print(" 2. Try the examples: docs/boot_examples/")
|
||||
print(" 3. Configure your devices: unilabos_data/startup_config.json")
|
||||
return 0
|
||||
else:
|
||||
print(f"\n{CROSS_MARK} Some checks failed. Please review the errors above.")
|
||||
print("\nTroubleshooting:")
|
||||
print(" 1. Ensure you're in the correct conda environment: conda activate unilab")
|
||||
print(" 2. Check the installation documentation: docs/user_guide/installation.md")
|
||||
print(" 3. Try reinstalling: pip install .")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,2 +1,4 @@
|
||||
[develop]
|
||||
script_dir=$base/lib/unilabos
|
||||
[install]
|
||||
install_scripts=$base/lib/unilabos
|
||||
|
||||
9
setup.py
@@ -4,20 +4,19 @@ package_name = 'unilabos'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.10.7',
|
||||
version='0.9.0',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
author="The unilabos developers",
|
||||
maintainer='Junhan Chang, Xuwznln',
|
||||
maintainer_email='Junhan Chang <changjh@pku.edu.cn>, Xuwznln <18435084+Xuwznln@users.noreply.github.com>',
|
||||
maintainer='Junhan Chang',
|
||||
maintainer_email='changjh@pku.edu.cn',
|
||||
description='',
|
||||
license='GPL v3',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
"unilab = unilabos.app.main:main"
|
||||
"unilab = unilabos.app.main:main",
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -2,10 +2,4 @@
|
||||
|
||||
```bash
|
||||
ros2 action send_goal /devices/host_node/create_resource_detailed unilabos_msgs/action/_resource_create_from_outer/ResourceCreateFromOuter "{ resources: [ { 'category': '', 'children': [], 'config': { 'type': 'Well', 'size_x': 6.86, 'size_y': 6.86, 'size_z': 10.67, 'rotation': { 'x': 0, 'y': 0, 'z': 0, 'type': 'Rotation' }, 'category': 'well', 'model': null, 'max_volume': 360, 'material_z_thickness': 0.5, 'compute_volume_from_height': null, 'compute_height_from_volume': null, 'bottom_type': 'flat', 'cross_section_type': 'circle' }, 'data': { 'liquids': [], 'pending_liquids': [], 'liquid_history': [] }, 'id': 'plate_well_11_7', 'name': 'plate_well_11_7', 'pose': { 'orientation': { 'w': 1.0, 'x': 0.0, 'y': 0.0, 'z': 0.0 }, 'position': { 'x': 0.0, 'y': 0.0, 'z': 0.0 } }, 'sample_id': '', 'parent': 'plate', 'type': 'device' } ], device_ids: [ 'PLR_STATION' ], bind_parent_ids: [ 'plate' ], bind_locations: [ { 'x': 0.0, 'y': 0.0, 'z': 0.0 } ], other_calling_params: [ '{}' ] }"
|
||||
```
|
||||
|
||||
使用mock_all.json启动,重新捕获MockContainerForChiller1
|
||||
|
||||
```bash
|
||||
ros2 action send_goal /devices/host_node/create_resource unilabos_msgs/action/_resource_create_from_outer_easy/ResourceCreateFromOuterEasy "{ 'res_id': 'MockContainerForChiller1', 'device_id': 'MockChiller1', 'class_name': 'container', 'parent': 'MockChiller1', 'bind_locations': { 'x': 0.0, 'y': 0.0, 'z': 0.0 }, 'liquid_input_slot': [ -1 ], 'liquid_type': [ 'CuCl2' ], 'liquid_volume': [ 100.0 ], 'slot_on_deck': '' }"
|
||||
```
|
||||
@@ -1,563 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "AddProtocolTestStation",
|
||||
"name": "添加协议测试站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"transfer_pump_2",
|
||||
"multiway_valve_1",
|
||||
"multiway_valve_2",
|
||||
"stirrer_1",
|
||||
"stirrer_2",
|
||||
"flask_DMF",
|
||||
"flask_ethyl_acetate",
|
||||
"flask_methanol",
|
||||
"flask_acetone",
|
||||
"flask_water",
|
||||
"flask_air",
|
||||
"main_reactor",
|
||||
"secondary_reactor",
|
||||
"waste_workup",
|
||||
"collection_bottle_1",
|
||||
"collection_bottle_2"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["PumpTransferProtocol", "AddProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵1",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_2",
|
||||
"name": "转移泵2",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 750,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP2",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "试剂分配阀",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_2",
|
||||
"name": "反应器分配阀",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 750,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE2",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer_1",
|
||||
"name": "主反应器搅拌器",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_STIRRER1",
|
||||
"max_speed": 1500.0,
|
||||
"default_speed": 300.0
|
||||
},
|
||||
"data": {
|
||||
"speed": 0.0,
|
||||
"status": "Stopped"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer_2",
|
||||
"name": "副反应器搅拌器",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_STIRRER2",
|
||||
"max_speed": 1500.0,
|
||||
"default_speed": 300.0
|
||||
},
|
||||
"data": {
|
||||
"speed": 0.0,
|
||||
"status": "Stopped"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_DMF",
|
||||
"name": "DMF试剂瓶",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 50,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "DMF",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethyl_acetate",
|
||||
"name": "乙酸乙酯试剂瓶",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 150,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethyl_acetate",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇试剂瓶",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "methanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_acetone",
|
||||
"name": "丙酮试剂瓶",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 350,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "acetone",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "蒸馏水瓶",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 450,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "water",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 550,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_reactor",
|
||||
"name": "主反应器",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "secondary_reactor",
|
||||
"name": "副反应器",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "废液处理瓶",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_1",
|
||||
"name": "收集瓶1",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_2",
|
||||
"name": "收集瓶2",
|
||||
"children": [],
|
||||
"parent": "AddProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump1_valve1",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_pump2_valve2",
|
||||
"source": "transfer_pump_2",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_2": "transferpump",
|
||||
"multiway_valve_2": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_valve2",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "8",
|
||||
"multiway_valve_2": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_DMF",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_DMF",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_DMF": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethyl_acetate",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethyl_acetate",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_ethyl_acetate": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_methanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_methanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_acetone",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_acetone",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_acetone": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_water",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_water",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "5",
|
||||
"flask_water": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "6",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_main_reactor",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "main_reactor",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "2",
|
||||
"main_reactor": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_secondary_reactor",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "secondary_reactor",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "3",
|
||||
"secondary_reactor": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_waste",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "6",
|
||||
"waste_workup": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_collection1",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "collection_bottle_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "7",
|
||||
"collection_bottle_1": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_collection2",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "collection_bottle_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "8",
|
||||
"collection_bottle_2": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_stirrer1_main_reactor",
|
||||
"source": "stirrer_1",
|
||||
"target": "main_reactor",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"stirrer_1": "stirrer_head",
|
||||
"main_reactor": "stirrer_port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_stirrer2_secondary_reactor",
|
||||
"source": "stirrer_2",
|
||||
"target": "secondary_reactor",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"stirrer_2": "stirrer_head",
|
||||
"secondary_reactor": "stirrer_port"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,438 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "CentrifugeProtocolTestStation",
|
||||
"name": "离心协议测试站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"transfer_pump_2",
|
||||
"multiway_valve_1",
|
||||
"multiway_valve_2",
|
||||
"centrifuge_1",
|
||||
"reaction_mixture",
|
||||
"centrifuge_tube",
|
||||
"collection_bottle_1",
|
||||
"flask_water",
|
||||
"flask_ethanol",
|
||||
"flask_acetone",
|
||||
"flask_air",
|
||||
"waste_workup"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": [
|
||||
"CentrifugeProtocol",
|
||||
"PumpTransferProtocol"
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "主转移泵",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_2",
|
||||
"name": "副转移泵",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP2",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "溶剂分配阀",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_2",
|
||||
"name": "样品分配阀",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE2",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "centrifuge_1",
|
||||
"name": "离心机",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_centrifuge",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 350,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_CENTRIFUGE1",
|
||||
"max_speed": 15000.0,
|
||||
"max_temp": 40.0,
|
||||
"min_temp": 4.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reaction_mixture",
|
||||
"name": "反应混合物",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "cell_suspension",
|
||||
"liquid_volume": 200.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "centrifuge_tube",
|
||||
"name": "离心管",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 15.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_1",
|
||||
"name": "上清液收集瓶",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "蒸馏水瓶",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "water",
|
||||
"liquid_volume": 900.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇清洗瓶",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_acetone",
|
||||
"name": "丙酮清洗瓶",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "acetone",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "CentrifugeProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump1_valve1",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_pump2_valve2",
|
||||
"source": "transfer_pump_2",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_2": "transferpump",
|
||||
"multiway_valve_2": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_water",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_water",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_water": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_ethanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_acetone",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_acetone",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_acetone": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_valve2",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "5",
|
||||
"multiway_valve_2": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_reaction_mixture",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "reaction_mixture",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "2",
|
||||
"reaction_mixture": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_centrifuge_tube",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "centrifuge_tube",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "3",
|
||||
"centrifuge_tube": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_collection",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "collection_bottle_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "4",
|
||||
"collection_bottle_1": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_waste",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "5",
|
||||
"waste_workup": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_centrifuge1_centrifuge_tube",
|
||||
"source": "centrifuge_1",
|
||||
"target": "centrifuge_tube",
|
||||
"type": "transport",
|
||||
"port": {
|
||||
"centrifuge_1": "centrifuge",
|
||||
"centrifuge_tube": "centrifuge_port"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,446 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "CleanVesselProtocolTestStation",
|
||||
"name": "容器清洗协议测试站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"transfer_pump_2",
|
||||
"multiway_valve_1",
|
||||
"multiway_valve_2",
|
||||
"heatchill_1",
|
||||
"flask_water",
|
||||
"flask_acetone",
|
||||
"flask_ethanol",
|
||||
"flask_air",
|
||||
"main_reactor",
|
||||
"secondary_reactor",
|
||||
"waste_workup"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": [
|
||||
"CleanVesselProtocol",
|
||||
"PumpTransferProtocol",
|
||||
"HeatChillProtocol",
|
||||
"HeatChillStartProtocol",
|
||||
"HeatChillStopProtocol"
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "主清洗泵",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.5
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_2",
|
||||
"name": "副清洗泵",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 450,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP2",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.5
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "溶剂分配阀",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_2",
|
||||
"name": "容器分配阀",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 450,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE2",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_1",
|
||||
"name": "加热清洗器",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 350,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_HEATCHILL1",
|
||||
"max_temp": 100.0,
|
||||
"min_temp": 10.0,
|
||||
"max_stir_speed": 500.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "蒸馏水瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 50,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "water",
|
||||
"liquid_volume": 900.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_acetone",
|
||||
"name": "丙酮清洗瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 150,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "acetone",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇清洗瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 350,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_reactor",
|
||||
"name": "主反应器",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "residue",
|
||||
"liquid_volume": 50.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "secondary_reactor",
|
||||
"name": "副反应器",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "organic_residue",
|
||||
"liquid_volume": 30.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "清洗废液瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 3000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump1_to_valve1",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_pump2_to_valve2",
|
||||
"source": "transfer_pump_2",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_2": "transferpump",
|
||||
"multiway_valve_2": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_to_water",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_water",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_water": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_to_acetone",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_acetone",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_acetone": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_to_ethanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_ethanol": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_to_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "6",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_to_valve2_for_cleaning",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"multiway_valve_2": "8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_to_main_reactor_in",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "main_reactor",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "2",
|
||||
"main_reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_to_secondary_reactor_in",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "secondary_reactor",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "3",
|
||||
"secondary_reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_main_reactor_out_to_valve2",
|
||||
"source": "main_reactor",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"main_reactor": "bottom",
|
||||
"multiway_valve_2": "6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_secondary_reactor_out_to_valve2",
|
||||
"source": "secondary_reactor",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"secondary_reactor": "bottom",
|
||||
"multiway_valve_2": "7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_to_waste",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "4",
|
||||
"waste_workup": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_heatchill1_to_main_reactor",
|
||||
"source": "heatchill_1",
|
||||
"target": "main_reactor",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"heatchill_1": "heatchill",
|
||||
"main_reactor": "bind"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_heatchill1_to_secondary_reactor",
|
||||
"source": "heatchill_1",
|
||||
"target": "secondary_reactor",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"heatchill_1": "heatchill",
|
||||
"secondary_reactor": "bind"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,367 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "DualValvePumpStation",
|
||||
"name": "双阀门泵站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"transfer_pump_2",
|
||||
"multiway_valve_1",
|
||||
"multiway_valve_2",
|
||||
"flask_DMF",
|
||||
"flask_ethyl_acetate",
|
||||
"flask_methanol",
|
||||
"flask_air",
|
||||
"main_reactor",
|
||||
"waste_workup",
|
||||
"collection_bottle_1"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["PumpTransferProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵1",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_2",
|
||||
"name": "转移泵2",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP2",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "第一个八通阀",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_2",
|
||||
"name": "第二个八通阀",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE2",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_DMF",
|
||||
"name": "DMF试剂瓶",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "DMF",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethyl_acetate",
|
||||
"name": "乙酸乙酯试剂瓶",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethyl_acetate",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇试剂瓶",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "methanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_reactor",
|
||||
"name": "主反应器",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "废液处理瓶",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_1",
|
||||
"name": "收集瓶1",
|
||||
"children": [],
|
||||
"parent": "DualValvePumpStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump1_valve1",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_pump2_valve2",
|
||||
"source": "transfer_pump_2",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_2": "transferpump",
|
||||
"multiway_valve_2": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_valve2",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "8",
|
||||
"multiway_valve_2": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_DMF",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_DMF",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_DMF": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethyl_acetate",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethyl_acetate",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_ethyl_acetate": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_methanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_methanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_reactor",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "main_reactor",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "5",
|
||||
"main_reactor": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_waste",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "6",
|
||||
"waste_workup": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_collection",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "collection_bottle_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "7",
|
||||
"collection_bottle_1": "inlet"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,557 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "EvacuateRefillTestStation",
|
||||
"name": "抽真空充气测试站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"transfer_pump_2",
|
||||
"multiway_valve_1",
|
||||
"multiway_valve_2",
|
||||
"flask_DMF",
|
||||
"flask_ethyl_acetate",
|
||||
"flask_methanol",
|
||||
"flask_air",
|
||||
"vacuum_pump_1",
|
||||
"gas_source_nitrogen",
|
||||
"gas_source_air",
|
||||
"solenoid_valve_vacuum",
|
||||
"solenoid_valve_gas",
|
||||
"main_reactor",
|
||||
"stirrer_1",
|
||||
"waste_workup",
|
||||
"collection_bottle_1"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["PumpTransferProtocol", "EvacuateAndRefillProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵1",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_2",
|
||||
"name": "转移泵2",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP2",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "第一个八通阀",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_2",
|
||||
"name": "第二个八通阀",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE2",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "vacuum_pump_1",
|
||||
"name": "真空泵1",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_vacuum_pump",
|
||||
"position": {
|
||||
"x": 150,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VACUUM1",
|
||||
"max_pressure": -0.9
|
||||
},
|
||||
"data": {
|
||||
"status": "OFF",
|
||||
"pressure": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gas_source_nitrogen",
|
||||
"name": "氮气源",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_gas_source",
|
||||
"position": {
|
||||
"x": 850,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_GAS_N2",
|
||||
"gas_type": "nitrogen",
|
||||
"max_pressure": 5.0
|
||||
},
|
||||
"data": {
|
||||
"status": "OFF",
|
||||
"flow_rate": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gas_source_air",
|
||||
"name": "空气源",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_gas_source",
|
||||
"position": {
|
||||
"x": 950,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_GAS_AIR",
|
||||
"gas_type": "air",
|
||||
"max_pressure": 3.0
|
||||
},
|
||||
"data": {
|
||||
"status": "OFF",
|
||||
"flow_rate": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "solenoid_valve_vacuum",
|
||||
"name": "真空电磁阀",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_solenoid_valve",
|
||||
"position": {
|
||||
"x": 225,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_SOLENOID_VACUUM"
|
||||
},
|
||||
"data": {
|
||||
"valve_position": "CLOSED"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "solenoid_valve_gas",
|
||||
"name": "气源电磁阀",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_solenoid_valve",
|
||||
"position": {
|
||||
"x": 775,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_SOLENOID_GAS"
|
||||
},
|
||||
"data": {
|
||||
"valve_position": "CLOSED"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_DMF",
|
||||
"name": "DMF试剂瓶",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "DMF",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethyl_acetate",
|
||||
"name": "乙酸乙酯试剂瓶",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethyl_acetate",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇试剂瓶",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "methanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_reactor",
|
||||
"name": "主反应器",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer_1",
|
||||
"name": "搅拌器1",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_STIRRER1",
|
||||
"max_speed": 1500.0
|
||||
},
|
||||
"data": {
|
||||
"speed": 0.0,
|
||||
"status": "OFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "废液处理瓶",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_1",
|
||||
"name": "收集瓶1",
|
||||
"children": [],
|
||||
"parent": "EvacuateRefillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump1_valve1",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_pump2_valve2",
|
||||
"source": "transfer_pump_2",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_2": "transferpump",
|
||||
"multiway_valve_2": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_valve2",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "8",
|
||||
"multiway_valve_2": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_vacuum_solenoid",
|
||||
"source": "vacuum_pump_1",
|
||||
"target": "solenoid_valve_vacuum",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"vacuum_pump_1": "outlet",
|
||||
"solenoid_valve_vacuum": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_solenoid_vacuum_valve1",
|
||||
"source": "solenoid_valve_vacuum",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"solenoid_valve_vacuum": "outlet",
|
||||
"multiway_valve_1": "7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_gas_solenoid",
|
||||
"source": "gas_source_nitrogen",
|
||||
"target": "solenoid_valve_gas",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"gas_source_nitrogen": "outlet",
|
||||
"solenoid_valve_gas": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_solenoid_gas_valve2",
|
||||
"source": "solenoid_valve_gas",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"solenoid_valve_gas": "outlet",
|
||||
"multiway_valve_2": "8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_air_source_valve2",
|
||||
"source": "gas_source_air",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"gas_source_air": "outlet",
|
||||
"multiway_valve_2": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_DMF",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_DMF",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_DMF": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethyl_acetate",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethyl_acetate",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_ethyl_acetate": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_methanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_methanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_reactor",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "main_reactor",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "5",
|
||||
"main_reactor": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_waste",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "6",
|
||||
"waste_workup": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_collection",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "collection_bottle_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "7",
|
||||
"collection_bottle_1": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_stirrer_reactor",
|
||||
"source": "stirrer_1",
|
||||
"target": "main_reactor",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"stirrer_1": "stirrer",
|
||||
"main_reactor": "stirrer"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,503 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "EvaporateProtocolTestStation",
|
||||
"name": "蒸发协议测试站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"transfer_pump_2",
|
||||
"multiway_valve_1",
|
||||
"multiway_valve_2",
|
||||
"rotavap_1",
|
||||
"heatchill_1",
|
||||
"reaction_mixture",
|
||||
"rotavap_flask",
|
||||
"rotavap_condenser",
|
||||
"flask_distillate",
|
||||
"flask_ethanol",
|
||||
"flask_acetone",
|
||||
"flask_water",
|
||||
"flask_air",
|
||||
"waste_workup"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": [
|
||||
"EvaporateProtocol",
|
||||
"PumpTransferProtocol",
|
||||
"HeatChillProtocol",
|
||||
"HeatChillStartProtocol",
|
||||
"HeatChillStopProtocol"
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "主转移泵",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.5
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_2",
|
||||
"name": "副转移泵",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP2",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.5
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "溶剂分配阀",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_2",
|
||||
"name": "容器分配阀",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE2",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rotavap_1",
|
||||
"name": "旋转蒸发仪",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_rotavap",
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 350,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_ROTAVAP1",
|
||||
"max_temp": 180.0,
|
||||
"max_rotation_speed": 280.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_1",
|
||||
"name": "预加热器",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_HEATCHILL1",
|
||||
"max_temp": 100.0,
|
||||
"min_temp": 10.0,
|
||||
"max_stir_speed": 500.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reaction_mixture",
|
||||
"name": "反应混合物",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "reaction_mixture",
|
||||
"liquid_volume": 600.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rotavap_flask",
|
||||
"name": "旋蒸样品瓶",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rotavap_condenser",
|
||||
"name": "旋蒸冷凝器",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 350,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_distillate",
|
||||
"name": "溶剂回收瓶",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇清洗瓶",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 50,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_acetone",
|
||||
"name": "丙酮清洗瓶",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 150,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "acetone",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "蒸馏水瓶",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "water",
|
||||
"liquid_volume": 900.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 350,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "EvaporateProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 3000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump1_valve1",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_pump2_valve2",
|
||||
"source": "transfer_pump_2",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_2": "transferpump",
|
||||
"multiway_valve_2": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_ethanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_acetone",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_acetone",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_acetone": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_water",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_water",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_water": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_valve2",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "5",
|
||||
"multiway_valve_2": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_reaction_mixture",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "reaction_mixture",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "2",
|
||||
"reaction_mixture": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_rotavap_flask",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "rotavap_flask",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "3",
|
||||
"rotavap_flask": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_rotavap_condenser",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "rotavap_condenser",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "4",
|
||||
"rotavap_condenser": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_distillate",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "flask_distillate",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "5",
|
||||
"flask_distillate": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_waste",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "6",
|
||||
"waste_workup": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_rotavap1_rotavap_flask",
|
||||
"source": "rotavap_1",
|
||||
"target": "rotavap_flask",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"rotavap_1": "rotavap-sample",
|
||||
"rotavap_flask": "rotavap_port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_heatchill1_reaction_mixture",
|
||||
"source": "heatchill_1",
|
||||
"target": "reaction_mixture",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"heatchill_1": "heatchill",
|
||||
"reaction_mixture": "heating_jacket"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,534 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "FilterProtocolTestStation",
|
||||
"name": "过滤协议测试站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"transfer_pump_2",
|
||||
"multiway_valve_1",
|
||||
"multiway_valve_2",
|
||||
"filter_1",
|
||||
"heatchill_1",
|
||||
"reaction_mixture",
|
||||
"filter_vessel",
|
||||
"filtrate_vessel",
|
||||
"collection_bottle_1",
|
||||
"collection_bottle_2",
|
||||
"flask_water",
|
||||
"flask_ethanol",
|
||||
"flask_acetone",
|
||||
"flask_air",
|
||||
"waste_workup"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": [
|
||||
"FilterProtocol",
|
||||
"PumpTransferProtocol",
|
||||
"HeatChillProtocol",
|
||||
"HeatChillStartProtocol",
|
||||
"HeatChillStopProtocol"
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "主转移泵",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_2",
|
||||
"name": "副转移泵",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP2",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "溶剂分配阀",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_2",
|
||||
"name": "样品分配阀",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE2",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "filter_1",
|
||||
"name": "过滤器",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_filter",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 350,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_FILTER1",
|
||||
"max_temp": 100.0,
|
||||
"max_stir_speed": 1000.0,
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_1",
|
||||
"name": "加热搅拌器",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_HEATCHILL1",
|
||||
"max_temp": 100.0,
|
||||
"min_temp": 4.0,
|
||||
"max_stir_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reaction_mixture",
|
||||
"name": "反应混合物",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "cell_suspension",
|
||||
"liquid_volume": 200.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "filter_vessel",
|
||||
"name": "过滤器容器",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "filtrate_vessel",
|
||||
"name": "滤液收集容器",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_1",
|
||||
"name": "收集瓶1",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_2",
|
||||
"name": "收集瓶2",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "蒸馏水瓶",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "water",
|
||||
"liquid_volume": 900.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇清洗瓶",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_acetone",
|
||||
"name": "丙酮清洗瓶",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "acetone",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "FilterProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump1_valve1",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_pump2_valve2",
|
||||
"source": "transfer_pump_2",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_2": "transferpump",
|
||||
"multiway_valve_2": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_water",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_water",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_water": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_ethanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_acetone",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_acetone",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_acetone": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_valve2",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "5",
|
||||
"multiway_valve_2": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_reaction_mixture",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "reaction_mixture",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "2",
|
||||
"reaction_mixture": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_filter_vessel",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "filter_vessel",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "3",
|
||||
"filter_vessel": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_filtrate_vessel",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "filtrate_vessel",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "4",
|
||||
"filtrate_vessel": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_collection1",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "collection_bottle_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "5",
|
||||
"collection_bottle_1": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_collection2",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "collection_bottle_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "6",
|
||||
"collection_bottle_2": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_waste",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "7",
|
||||
"waste_workup": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_filter1_filter_vessel",
|
||||
"source": "filter_1",
|
||||
"target": "filter_vessel",
|
||||
"type": "transport",
|
||||
"port": {
|
||||
"filter_1": "filter",
|
||||
"filter_vessel": "filter_port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_heatchill1_filter_vessel",
|
||||
"source": "heatchill_1",
|
||||
"target": "filter_vessel",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"heatchill_1": "heatchill",
|
||||
"filter_vessel": "heating_jacket"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,671 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "HeatChillProtocolTestStation",
|
||||
"name": "加热冷却协议测试站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"transfer_pump_2",
|
||||
"multiway_valve_1",
|
||||
"multiway_valve_2",
|
||||
"stirrer_1",
|
||||
"stirrer_2",
|
||||
"heatchill_1",
|
||||
"heatchill_2",
|
||||
"flask_DMF",
|
||||
"flask_ethyl_acetate",
|
||||
"flask_methanol",
|
||||
"flask_acetone",
|
||||
"flask_water",
|
||||
"flask_ethanol",
|
||||
"flask_air",
|
||||
"main_reactor",
|
||||
"secondary_reactor",
|
||||
"waste_workup",
|
||||
"collection_bottle_1",
|
||||
"collection_bottle_2"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": [
|
||||
"PumpTransferProtocol",
|
||||
"AddProtocol",
|
||||
"HeatChillProtocol",
|
||||
"HeatChillStartProtocol",
|
||||
"HeatChillStopProtocol",
|
||||
"DissolveProtocol"
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵1",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_2",
|
||||
"name": "转移泵2",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 750,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP2",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "试剂分配阀",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_2",
|
||||
"name": "反应器分配阀",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 750,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE2",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer_1",
|
||||
"name": "主反应器搅拌器",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_STIRRER1",
|
||||
"max_speed": 1500.0,
|
||||
"default_speed": 300.0
|
||||
},
|
||||
"data": {
|
||||
"speed": 0.0,
|
||||
"status": "Stopped"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer_2",
|
||||
"name": "副反应器搅拌器",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_STIRRER2",
|
||||
"max_speed": 1500.0,
|
||||
"default_speed": 300.0
|
||||
},
|
||||
"data": {
|
||||
"speed": 0.0,
|
||||
"status": "Stopped"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_1",
|
||||
"name": "主反应器加热冷却器",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 550,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_HEATCHILL1",
|
||||
"max_temp": 200.0,
|
||||
"min_temp": -80.0,
|
||||
"max_stir_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_2",
|
||||
"name": "副反应器加热冷却器",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 850,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_HEATCHILL2",
|
||||
"max_temp": 200.0,
|
||||
"min_temp": -80.0,
|
||||
"max_stir_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_DMF",
|
||||
"name": "DMF试剂瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 50,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "DMF",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethyl_acetate",
|
||||
"name": "乙酸乙酯试剂瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 150,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethyl_acetate",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇试剂瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "methanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇试剂瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 650,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_acetone",
|
||||
"name": "丙酮试剂瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 350,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "acetone",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "蒸馏水瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 450,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "water",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 550,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_reactor",
|
||||
"name": "主反应器",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "secondary_reactor",
|
||||
"name": "副反应器",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "废液处理瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_1",
|
||||
"name": "收集瓶1",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_2",
|
||||
"name": "收集瓶2",
|
||||
"children": [],
|
||||
"parent": "HeatChillProtocolTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump1_valve1",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_pump2_valve2",
|
||||
"source": "transfer_pump_2",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_2": "transferpump",
|
||||
"multiway_valve_2": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_valve2",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "8",
|
||||
"multiway_valve_2": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_DMF",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_DMF",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_DMF": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethyl_acetate",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethyl_acetate",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_ethyl_acetate": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_methanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_methanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_acetone",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_acetone",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_acetone": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_water",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_water",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "5",
|
||||
"flask_water": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "6",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_main_reactor",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "main_reactor",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "2",
|
||||
"main_reactor": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_secondary_reactor",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "secondary_reactor",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "3",
|
||||
"secondary_reactor": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_waste",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "6",
|
||||
"waste_workup": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_collection1",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "collection_bottle_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "7",
|
||||
"collection_bottle_1": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_collection2",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "collection_bottle_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "8",
|
||||
"collection_bottle_2": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_stirrer1_main_reactor",
|
||||
"source": "stirrer_1",
|
||||
"target": "main_reactor",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"stirrer_1": "stirrer_head",
|
||||
"main_reactor": "stirrer_port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_stirrer2_secondary_reactor",
|
||||
"source": "stirrer_2",
|
||||
"target": "secondary_reactor",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"stirrer_2": "stirrer_head",
|
||||
"secondary_reactor": "stirrer_port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_heatchill1_main_reactor",
|
||||
"source": "heatchill_1",
|
||||
"target": "main_reactor",
|
||||
"type": "thermal",
|
||||
"port": {
|
||||
"heatchill_1": "heating_surface",
|
||||
"main_reactor": "heating_jacket"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_heatchill2_secondary_reactor",
|
||||
"source": "heatchill_2",
|
||||
"target": "secondary_reactor",
|
||||
"type": "thermal",
|
||||
"port": {
|
||||
"heatchill_2": "heating_surface",
|
||||
"secondary_reactor": "heating_jacket"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "7",
|
||||
"flask_ethanol": "outlet"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,778 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "PumpTransferFilterThroughTestStation",
|
||||
"name": "泵转移+过滤介质测试站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"transfer_pump_2",
|
||||
"multiway_valve_1",
|
||||
"multiway_valve_2",
|
||||
"reaction_mixture",
|
||||
"crude_product",
|
||||
"filter_celite",
|
||||
"column_silica_gel",
|
||||
"filter_C18",
|
||||
"pure_product",
|
||||
"collection_bottle_1",
|
||||
"collection_bottle_2",
|
||||
"collection_bottle_3",
|
||||
"intermediate_vessel_1",
|
||||
"intermediate_vessel_2",
|
||||
"flask_water",
|
||||
"flask_ethanol",
|
||||
"flask_methanol",
|
||||
"flask_ethyl_acetate",
|
||||
"flask_acetone",
|
||||
"flask_hexane",
|
||||
"flask_air",
|
||||
"waste_workup"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": [
|
||||
"PumpTransferProtocol",
|
||||
"FilterThroughProtocol"
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "主转移泵",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_2",
|
||||
"name": "副转移泵",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP2",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 2.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "溶剂分配阀",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_2",
|
||||
"name": "样品分配阀",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE2",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reaction_mixture",
|
||||
"name": "反应混合物",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "organic_reaction_mixture",
|
||||
"liquid_volume": 250.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "crude_product",
|
||||
"name": "粗产品",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "crude_organic_compound",
|
||||
"liquid_volume": 150.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "filter_celite",
|
||||
"name": "硅藻土过滤器",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 300.0,
|
||||
"filter_type": "celite_pad"
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "column_silica_gel",
|
||||
"name": "硅胶柱",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 200.0,
|
||||
"filter_type": "silica_gel_column"
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "filter_C18",
|
||||
"name": "C18固相萃取柱",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 100.0,
|
||||
"filter_type": "C18_cartridge"
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "pure_product",
|
||||
"name": "纯产品",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_1",
|
||||
"name": "收集瓶1",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_2",
|
||||
"name": "收集瓶2",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_3",
|
||||
"name": "收集瓶3",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 550,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "intermediate_vessel_1",
|
||||
"name": "中间容器1",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "intermediate_vessel_2",
|
||||
"name": "中间容器2",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "蒸馏水瓶",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "water",
|
||||
"liquid_volume": 900.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇瓶",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇瓶",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "methanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethyl_acetate",
|
||||
"name": "乙酸乙酯瓶",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethyl_acetate",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_acetone",
|
||||
"name": "丙酮瓶",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "acetone",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_hexane",
|
||||
"name": "正己烷瓶",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "hexane",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "PumpTransferFilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 600,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump1_valve1",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_pump2_valve2",
|
||||
"source": "transfer_pump_2",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_2": "transferpump",
|
||||
"multiway_valve_2": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_water",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_water",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_water": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_ethanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_methanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_methanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_ethyl_acetate",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethyl_acetate",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "5",
|
||||
"flask_ethyl_acetate": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_acetone",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_acetone",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "6",
|
||||
"flask_acetone": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_hexane",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_hexane",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "7",
|
||||
"flask_hexane": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve1_valve2",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "multiway_valve_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "8",
|
||||
"multiway_valve_2": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_reaction_mixture",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "reaction_mixture",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "2",
|
||||
"reaction_mixture": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_crude_product",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "crude_product",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "3",
|
||||
"crude_product": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_intermediate1",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "intermediate_vessel_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "4",
|
||||
"intermediate_vessel_1": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_intermediate2",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "intermediate_vessel_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "5",
|
||||
"intermediate_vessel_2": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_celite",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "filter_celite",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "6",
|
||||
"filter_celite": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_silica_gel",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "column_silica_gel",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "7",
|
||||
"column_silica_gel": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve2_C18",
|
||||
"source": "multiway_valve_2",
|
||||
"target": "filter_C18",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "8",
|
||||
"filter_C18": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_celite_collection1",
|
||||
"source": "filter_celite",
|
||||
"target": "collection_bottle_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"filter_celite": "outlet",
|
||||
"collection_bottle_1": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_silica_gel_collection2",
|
||||
"source": "column_silica_gel",
|
||||
"target": "collection_bottle_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"column_silica_gel": "outlet",
|
||||
"collection_bottle_2": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_C18_collection3",
|
||||
"source": "filter_C18",
|
||||
"target": "collection_bottle_3",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"filter_C18": "outlet",
|
||||
"collection_bottle_3": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_collection1_pure_product",
|
||||
"source": "collection_bottle_1",
|
||||
"target": "pure_product",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"collection_bottle_1": "outlet",
|
||||
"pure_product": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_collection2_pure_product",
|
||||
"source": "collection_bottle_2",
|
||||
"target": "pure_product",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"collection_bottle_2": "outlet",
|
||||
"pure_product": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_collection3_pure_product",
|
||||
"source": "collection_bottle_3",
|
||||
"target": "pure_product",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"collection_bottle_3": "outlet",
|
||||
"pure_product": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_waste_connection",
|
||||
"source": "pure_product",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"pure_product": "waste_outlet",
|
||||
"waste_workup": "inlet"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "SimpleProtocolStation",
|
||||
"name": "简单协议工作站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"multiway_valve_1",
|
||||
"flask_DMF",
|
||||
"flask_ethyl_acetate",
|
||||
"flask_methanol",
|
||||
"main_reactor",
|
||||
"waste_workup",
|
||||
"collection_bottle_1",
|
||||
"flask_air"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["PumpTransferProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵1",
|
||||
"children": [],
|
||||
"parent": "SimpleProtocolStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 25.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"position": 0.0,
|
||||
"status": "Idle",
|
||||
"valve_position": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "八通阀1",
|
||||
"children": [],
|
||||
"parent": "SimpleProtocolStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_DMF",
|
||||
"name": "DMF试剂瓶",
|
||||
"children": [],
|
||||
"parent": "SimpleProtocolStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "DMF",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethyl_acetate",
|
||||
"name": "乙酸乙酯试剂瓶",
|
||||
"children": [],
|
||||
"parent": "SimpleProtocolStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "ethyl_acetate",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇试剂瓶",
|
||||
"children": [],
|
||||
"parent": "SimpleProtocolStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "methanol",
|
||||
"liquid_volume": 800.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_reactor",
|
||||
"name": "主反应器",
|
||||
"children": [],
|
||||
"parent": "SimpleProtocolStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_workup",
|
||||
"name": "废液处理瓶",
|
||||
"children": [],
|
||||
"parent": "SimpleProtocolStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_bottle_1",
|
||||
"name": "收集瓶1",
|
||||
"children": [],
|
||||
"parent": "SimpleProtocolStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "SimpleProtocolStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump_valve",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_air",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_air",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_DMF",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_DMF",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_DMF": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_ethyl_acetate",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethyl_acetate",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_ethyl_acetate": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_methanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_methanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_reactor",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "main_reactor",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "5",
|
||||
"main_reactor": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_waste",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "waste_workup",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "6",
|
||||
"waste_workup": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_collection",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "collection_bottle_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "7",
|
||||
"collection_bottle_1": "inlet"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,432 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "RunColumnTestStation",
|
||||
"name": "柱层析测试工作站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"multiway_valve_1",
|
||||
"column_1",
|
||||
"flask_sample",
|
||||
"flask_hexane",
|
||||
"flask_ethyl_acetate",
|
||||
"flask_methanol",
|
||||
"column_vessel",
|
||||
"collection_flask_1",
|
||||
"collection_flask_2",
|
||||
"collection_flask_3",
|
||||
"waste_flask",
|
||||
"main_reactor"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["RunColumnProtocol", "PumpTransferProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_PUMP1",
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"position": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve_1",
|
||||
"name": "八通阀门",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_VALVE1",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "column_1",
|
||||
"name": "柱层析设备",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_column",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 350,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_COLUMN1",
|
||||
"max_flow_rate": 5.0,
|
||||
"column_length": 30.0,
|
||||
"column_diameter": 2.5
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"column_state": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_sample",
|
||||
"name": "样品瓶",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "crude_mixture",
|
||||
"volume": 200.0,
|
||||
"concentration": 70.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_hexane",
|
||||
"name": "正己烷洗脱剂",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "hexane",
|
||||
"volume": 1500.0,
|
||||
"concentration": 99.8
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethyl_acetate",
|
||||
"name": "乙酸乙酯洗脱剂",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "ethyl_acetate",
|
||||
"volume": 1500.0,
|
||||
"concentration": 99.5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇洗脱剂",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "methanol",
|
||||
"volume": 800.0,
|
||||
"concentration": 99.9
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "column_vessel",
|
||||
"name": "柱容器",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 300.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_flask_1",
|
||||
"name": "收集瓶1",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_flask_2",
|
||||
"name": "收集瓶2",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_flask_3",
|
||||
"name": "收集瓶3",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_flask",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 1000,
|
||||
"y": 500,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "reaction_mixture",
|
||||
"volume": 300.0,
|
||||
"concentration": 85.0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_pump_valve",
|
||||
"source": "transfer_pump_1",
|
||||
"target": "multiway_valve_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"transfer_pump_1": "transferpump",
|
||||
"multiway_valve_1": "transferpump"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_sample",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_sample",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "1",
|
||||
"flask_sample": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_hexane",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_hexane",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_hexane": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_ethyl_acetate",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_ethyl_acetate",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "3",
|
||||
"flask_ethyl_acetate": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_methanol",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "4",
|
||||
"flask_methanol": "outlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_column_vessel",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "column_vessel",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "5",
|
||||
"column_vessel": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_collection1",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "collection_flask_1",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "6",
|
||||
"collection_flask_1": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_collection2",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "collection_flask_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "7",
|
||||
"collection_flask_2": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_valve_waste",
|
||||
"source": "multiway_valve_1",
|
||||
"target": "waste_flask",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "8",
|
||||
"waste_flask": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_column_device_vessel",
|
||||
"source": "column_1",
|
||||
"target": "column_vessel",
|
||||
"type": "transport",
|
||||
"port": {
|
||||
"column_1": "columnin",
|
||||
"column_vessel": "column_port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_column_collection3",
|
||||
"source": "column_1",
|
||||
"target": "collection_flask_3",
|
||||
"type": "transport",
|
||||
"port": {
|
||||
"column_1": "columnout",
|
||||
"collection_flask_3": "column_outlet"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "SimpleStirHeatChillTestStation",
|
||||
"name": "搅拌加热测试站",
|
||||
"children": [
|
||||
"stirrer_1",
|
||||
"heatchill_1",
|
||||
"main_reactor",
|
||||
"secondary_reactor"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": [
|
||||
"StirProtocol",
|
||||
"StartStirProtocol",
|
||||
"StopStirProtocol",
|
||||
"HeatChillProtocol",
|
||||
"HeatChillStartProtocol",
|
||||
"HeatChillStopProtocol"
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "stirrer_1",
|
||||
"name": "主搅拌器",
|
||||
"children": [],
|
||||
"parent": "SimpleStirHeatChillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 350,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_STIRRER1",
|
||||
"max_speed": 1500.0,
|
||||
"min_speed": 50.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_1",
|
||||
"name": "主加热冷却器",
|
||||
"children": [],
|
||||
"parent": "SimpleStirHeatChillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 600,
|
||||
"y": 350,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL_HEATCHILL1",
|
||||
"max_temp": 200.0,
|
||||
"min_temp": -80.0,
|
||||
"max_stir_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_reactor",
|
||||
"name": "主反应器",
|
||||
"children": [],
|
||||
"parent": "SimpleStirHeatChillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"liquid_type": "water",
|
||||
"liquid_volume": 500.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "secondary_reactor",
|
||||
"name": "副反应器",
|
||||
"children": [],
|
||||
"parent": "SimpleStirHeatChillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "link_stirrer1_main_reactor",
|
||||
"source": "stirrer_1",
|
||||
"target": "main_reactor",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"stirrer_1": "stirrer",
|
||||
"main_reactor": "stirrer_port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "link_heatchill1_main_reactor",
|
||||
"source": "heatchill_1",
|
||||
"target": "main_reactor",
|
||||
"type": "mechanical",
|
||||
"port": {
|
||||
"heatchill_1": "heatchill",
|
||||
"main_reactor": "heating_jacket"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "BIOMEK",
|
||||
"name": "BIOMEK",
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "liquid_handler.biomek",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
},
|
||||
"data": {},
|
||||
"children": [
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "Camera",
|
||||
"name": "摄像头",
|
||||
"children": [
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "camera",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"camera_index": 0,
|
||||
"period": 0.05
|
||||
},
|
||||
"data": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "Gripper1",
|
||||
"name": "假夹爪",
|
||||
"children": [
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "gripper.mock",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
},
|
||||
"data": {
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
|
||||
]
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
1. 用到的仪器
|
||||
virtual_multiway_valve(√) 八通阀门
|
||||
virtual_transfer_pump(√) 转移泵
|
||||
virtual_centrifuge() 离心机
|
||||
virtual_rotavap() 旋蒸仪
|
||||
virtual_heatchill() 加热器
|
||||
virtual_stirrer() 搅拌器
|
||||
virtual_solenoid_valve() 电磁阀
|
||||
virtual_vacuum_pump(√) vacuum_pump.mock 真空泵
|
||||
virtual_gas_source(√) 气源
|
||||
virtual_filter() 过滤器
|
||||
virtual_column(√) 层析柱
|
||||
separator() homemade_grbl_conductivity 分液漏斗
|
||||
2. 用到的protocol
|
||||
PumpTransferProtocol: generate_pump_protocol_with_rinsing, (√)
|
||||
这个重复了,删掉CleanProtocol: generate_clean_protocol,
|
||||
SeparateProtocol: generate_separate_protocol, (×)
|
||||
EvaporateProtocol: generate_evaporate_protocol, (√)
|
||||
EvacuateAndRefillProtocol: generate_evacuateandrefill_protocol, (√)
|
||||
CentrifugeProtocol: generate_centrifuge_protocol, (√)
|
||||
AddProtocol: generate_add_protocol, (√)
|
||||
FilterProtocol: generate_filter_protocol, (√)
|
||||
HeatChillProtocol: generate_heat_chill_protocol, (√)
|
||||
HeatChillStartProtocol: generate_heat_chill_start_protocol, (√)
|
||||
HeatChillStopProtocol: generate_heat_chill_stop_protocol, (√)
|
||||
HeatChillToTempProtocol:
|
||||
StirProtocol: generate_stir_protocol, (√)
|
||||
StartStirProtocol: generate_start_stir_protocol, (√)
|
||||
StopStirProtocol: generate_stop_stir_protocol, (√)
|
||||
这个重复了,删掉TransferProtocol: generate_transfer_protocol,
|
||||
CleanVesselProtocol: generate_clean_vessel_protocol, (√)
|
||||
DissolveProtocol: generate_dissolve_protocol, (√)
|
||||
FilterThroughProtocol: generate_filter_through_protocol, (√)
|
||||
RunColumnProtocol: generate_run_column_protocol, (√)<RunColumn Rf="?" column="column" from_vessel="rotavap" ratio="5:95" solvent1="methanol" solvent2="chloroform" to_vessel="rotavap"/>
|
||||
|
||||
上下文体积搜索
|
||||
3. 还没创建的protocol
|
||||
ResetHandling 写完了 <ResetHandling solvent="methanol"/>
|
||||
Dry 写完了 <Dry compound="product" vessel="filter"/>
|
||||
AdjustPH 写完了 <AdjustPH pH="8.0" reagent="hydrochloric acid" vessel="main_reactor"/>
|
||||
Recrystallize 写完了 <Recrystallize ratio="?" solvent1="dichloromethane" solvent2="methanol" vessel="filter" volume="?"/>
|
||||
TakeSample <TakeSample id="a" vessel="rotavap"/>
|
||||
Hydrogenate <Hydrogenate temp="45 °C" time="?" vessel="main_reactor"/>
|
||||
4. 参数对齐
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class PumpTransferProtocol(BaseModel):
|
||||
from_vessel: str
|
||||
to_vessel: str
|
||||
volume: float
|
||||
amount: str = ""
|
||||
time: float = 0
|
||||
viscous: bool = False
|
||||
rinsing_solvent: str = "air" <Transfer from_vessel="main_reactor" to_vessel="rotavap"/>
|
||||
rinsing_volume: float = 5000 <Transfer event="A" from_vessel="reactor" rate_spec="dropwise" to_vessel="main_reactor"/>
|
||||
rinsing_repeats: int = 2 <Transfer from_vessel="separator" through="cartridge" to_vessel="rotavap"/>
|
||||
solid: bool = False 测完了三个都能跑✅
|
||||
flowrate: float = 500
|
||||
transfer_flowrate: float = 2500
|
||||
|
||||
class SeparateProtocol(BaseModel):
|
||||
purpose: str
|
||||
product_phase: str
|
||||
from_vessel: str
|
||||
separation_vessel: str
|
||||
to_vessel: str
|
||||
waste_phase_to_vessel: str
|
||||
solvent: str
|
||||
solvent_volume: float <Separate product_phase="bottom" purpose="wash" solvent="water" vessel="separator" volume="?"/>
|
||||
through: str <Separate product_phase="top" purpose="separate" vessel="separator"/>
|
||||
repeats: int <Separate product_phase="bottom" purpose="extract" repeats="3" solvent="CH2Cl2" vessel="separator" volume="?"/>
|
||||
stir_time: float <Separate product_phase="top" product_vessel="flask" purpose="separate" vessel="separator" waste_vessel="separator"/>
|
||||
stir_speed: float
|
||||
settling_time: float 测完了能跑✅
|
||||
|
||||
|
||||
class EvaporateProtocol(BaseModel):
|
||||
vessel: str
|
||||
pressure: float
|
||||
temp: float <Evaporate solvent="ethanol" vessel="rotavap"/>
|
||||
time: float 测完了能跑✅
|
||||
stir_speed: float
|
||||
|
||||
|
||||
class EvacuateAndRefillProtocol(BaseModel):
|
||||
vessel: str
|
||||
gas: str <EvacuateAndRefill gas="nitrogen" vessel="main_reactor"/>
|
||||
repeats: int 测完了能跑✅
|
||||
|
||||
class AddProtocol(BaseModel):
|
||||
vessel: str
|
||||
reagent: str
|
||||
volume: float
|
||||
mass: float
|
||||
amount: str
|
||||
time: float
|
||||
stir: bool
|
||||
stir_speed: float <Add reagent="ethanol" vessel="main_reactor" volume="2.7 mL"/>
|
||||
<Add event="A" mass="19.3 g" mol="0.28 mol" rate_spec="portionwise" reagent="sodium nitrite" time="1 h" vessel="main_reactor"/>
|
||||
<Add mass="4.5 g" mol="16.2 mmol" reagent="(S)-2-phthalimido-6-hydroxyhexanoic acid" vessel="main_reactor"/>
|
||||
<Add purpose="dilute" reagent="hydrochloric acid" vessel="main_reactor" volume="?"/>
|
||||
<Add equiv="1.1" event="B" mol="25.2 mmol" rate_spec="dropwise" reagent="1-fluoro-2-nitrobenzene" time="20 min"
|
||||
vessel="main_reactor" volume="2.67 mL"/>
|
||||
<Add ratio="?" reagent="tetrahydrofuran|tert-butanol" vessel="main_reactor" volume="?"/>
|
||||
viscous: bool
|
||||
purpose: str 测完了能跑✅
|
||||
|
||||
class CentrifugeProtocol(BaseModel):
|
||||
vessel: str
|
||||
speed: float
|
||||
time: float 没毛病
|
||||
temp: float
|
||||
|
||||
class FilterProtocol(BaseModel):
|
||||
vessel: str
|
||||
filtrate_vessel: str
|
||||
stir: bool <Filter vessel="filter"/>
|
||||
stir_speed: float <Filter filtrate_vessel="rotavap" vessel="filter"/>
|
||||
temp: float 测完了能跑✅
|
||||
continue_heatchill: bool
|
||||
volume: float
|
||||
|
||||
class HeatChillProtocol(BaseModel):
|
||||
vessel: str
|
||||
temp: float
|
||||
time: float <HeatChill pressure="1 mbar" temp_spec="room temperature" time="?" vessel="main_reactor"/>
|
||||
<HeatChill temp_spec="room temperature" time_spec="overnight" vessel="main_reactor"/>
|
||||
<HeatChill temp="256 °C" time="?" vessel="main_reactor"/>
|
||||
<HeatChill reflux_solvent="methanol" temp_spec="reflux" time="2 h" vessel="main_reactor"/>
|
||||
<HeatChillToTemp temp_spec="room temperature" vessel="main_reactor"/>
|
||||
stir: bool 测完了能跑✅
|
||||
stir_speed: float
|
||||
purpose: str
|
||||
|
||||
class HeatChillStartProtocol(BaseModel):
|
||||
vessel: str
|
||||
temp: float 疑似没有
|
||||
purpose: str
|
||||
|
||||
class HeatChillStopProtocol(BaseModel):
|
||||
vessel: str 疑似没有
|
||||
|
||||
class StirProtocol(BaseModel):
|
||||
stir_time: float
|
||||
stir_speed: float <Stir time="0.5 h" vessel="main_reactor"/>
|
||||
<Stir event="A" time="30 min" vessel="main_reactor"/>
|
||||
<Stir time_spec="several minutes" vessel="filter"/>
|
||||
settling_time: float 测完了能跑✅
|
||||
|
||||
class StartStirProtocol(BaseModel):
|
||||
vessel: str
|
||||
stir_speed: float 疑似没有
|
||||
purpose: str
|
||||
|
||||
class StopStirProtocol(BaseModel):
|
||||
vessel: str 疑似没有
|
||||
|
||||
class TransferProtocol(BaseModel):
|
||||
from_vessel: str
|
||||
to_vessel: str
|
||||
volume: float
|
||||
amount: str = ""
|
||||
time: float = 0
|
||||
viscous: bool = False
|
||||
rinsing_solvent: str = ""
|
||||
rinsing_volume: float = 0.0
|
||||
rinsing_repeats: int = 0
|
||||
solid: bool = False 这个protocol早该删掉了
|
||||
|
||||
class CleanVesselProtocol(BaseModel):
|
||||
vessel: str
|
||||
solvent: str
|
||||
volume: float
|
||||
temp: float
|
||||
repeats: int = 1 <CleanVessel vessel="centrifuge"/>
|
||||
|
||||
class DissolveProtocol(BaseModel):
|
||||
vessel: str
|
||||
solvent: str
|
||||
volume: float <Dissolve mass="2.9 g" mol="0.12 mol" reagent="magnesium" vessel="main_reactor"/>
|
||||
amount: str = "" <Dissolve mass="12.9 g" reagent="4-tert-butylbenzyl bromide" vessel="main_reactor"/>
|
||||
temp: float = 25.0 <Dissolve solvent="diisopropyl ether" vessel="rotavap" volume="?"/>
|
||||
time: float = 0.0 <Dissolve event="A" mass="?" reagent="pyridinone" vessel="main_reactor"/>
|
||||
stir_speed: float = 0.0 测完了能跑✅
|
||||
|
||||
class FilterThroughProtocol(BaseModel):
|
||||
from_vessel: str
|
||||
to_vessel: str
|
||||
filter_through: str
|
||||
eluting_solvent: str = ""
|
||||
eluting_volume: float = 0.0 疑似没有
|
||||
eluting_repeats: int = 0
|
||||
residence_time: float = 0.0
|
||||
|
||||
class RunColumnProtocol(BaseModel):
|
||||
from_vessel: str
|
||||
to_vessel: str <RunColumn Rf="?" column="column" from_vessel="rotavap" pct1="40 %" pct2="50 %" solvent1="ethyl acetate" solvent2="hexane" to_vessel="rotavap"/>
|
||||
column: str 测完了能跑✅
|
||||
|
||||
class WashSolidProtocol(BaseModel):
|
||||
vessel: str
|
||||
solvent: str
|
||||
volume: float
|
||||
filtrate_vessel: str = "" <WashSolid repeats="4" solvent="water" vessel="main_reactor" volume="400 mL"/>
|
||||
temp: float = 25.0 <WashSolid filtrate_vessel="rotavap" solvent="formic acid" vessel="main_reactor" volume="?"/>
|
||||
stir: bool = False <WashSolid solvent="acetone" vessel="rotavap" volume="5 mL"/>
|
||||
<WashSolid solvent="ethyl alcohol" vessel="main_reactor" volume_spec="small volume"/>
|
||||
<WashSolid filtrate_vessel="rotavap" mass="10 g" solvent="toluene" vessel="separator"/>
|
||||
<WashSolid repeats_spec="several" solvent="water" vessel="main_reactor" volume="?"/>
|
||||
stir_speed: float = 0.0 测完了能跑✅
|
||||
time: float = 0.0
|
||||
repeats: int = 1
|
||||
|
||||
class AdjustPHProtocol(BaseModel):
|
||||
vessel: str = Field(..., description="目标容器")
|
||||
ph_value: float = Field(..., description="目标pH值") # 改为 ph_value
|
||||
reagent: str = Field(..., description="酸碱试剂名称")
|
||||
# 移除其他可选参数,使用默认值 <新写的,没问题>
|
||||
|
||||
class ResetHandlingProtocol(BaseModel):
|
||||
solvent: str = Field(..., description="溶剂名称") <新写的,没问题>
|
||||
|
||||
class DryProtocol(BaseModel):
|
||||
compound: str = Field(..., description="化合物名称") <新写的,没问题>
|
||||
vessel: str = Field(..., description="目标容器")
|
||||
|
||||
class RecrystallizeProtocol(BaseModel):
|
||||
ratio: str = Field(..., description="溶剂比例(如 '1:1', '3:7')")
|
||||
solvent1: str = Field(..., description="第一种溶剂名称") <新写的,没问题>
|
||||
solvent2: str = Field(..., description="第二种溶剂名称")
|
||||
vessel: str = Field(..., description="目标容器")
|
||||
volume: float = Field(..., description="总体积 (mL)")
|
||||
|
||||
class HydrogenateProtocol(BaseModel):
|
||||
temp: str = Field(..., description="反应温度(如 '45 °C')")
|
||||
time: str = Field(..., description="反应时间(如 '2 h')") <新写的,没问题>
|
||||
vessel: str = Field(..., description="反应容器")
|
||||
|
||||
还差
|
||||
<dissolve>
|
||||
<separate>
|
||||
<CleanVessel vessel="centrifuge"/>
|
||||
|
||||
|
||||
单位修复:
|
||||
evaporate
|
||||
heatchill
|
||||
recrysitallize
|
||||
stir
|
||||
wash solid
|
||||
@@ -1,60 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "dispensing_station_bioyond",
|
||||
"name": "dispensing_station_bioyond",
|
||||
"children": [
|
||||
"Bioyond_Dispensing_Deck"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "dispensing_station.bioyond",
|
||||
"config": {
|
||||
"config": {
|
||||
"api_key": "DE9BDDA0",
|
||||
"api_host": "http://192.168.1.200:44388"
|
||||
},
|
||||
"deck": {
|
||||
"data": {
|
||||
"_resource_child_name": "Bioyond_Dispensing_Deck",
|
||||
"_resource_type": "unilabos.resources.bioyond.decks:BIOYOND_PolymerPreparationStation_Deck"
|
||||
}
|
||||
},
|
||||
"station_config": {
|
||||
"station_type": "dispensing_station",
|
||||
"enable_dispensing_station": true,
|
||||
"enable_reaction_station": false,
|
||||
"station_name": "DispensingStation_001",
|
||||
"description": "Bioyond配液工作站"
|
||||
},
|
||||
"protocol_type": []
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "Bioyond_Dispensing_Deck",
|
||||
"name": "Bioyond_Dispensing_Deck",
|
||||
"sample_id": null,
|
||||
"children": [],
|
||||
"parent": "dispensing_station_bioyond",
|
||||
"type": "deck",
|
||||
"class": "BIOYOND_PolymerPreparationStation_Deck",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"type": "BIOYOND_PolymerPreparationStation_Deck",
|
||||
"setup": true,
|
||||
"rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"type": "Rotation"
|
||||
}
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "laiyu_liquid_station",
|
||||
"name": "LaiYu液体处理工作站",
|
||||
"children": [
|
||||
"module_1_8tubes",
|
||||
"module_2_96well_deep",
|
||||
"module_3_beaker",
|
||||
"module_4_96well_tips"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "laiyu_liquid",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"total_modules": 4,
|
||||
"total_wells": 201,
|
||||
"safety_margin": {
|
||||
"x": 5.0,
|
||||
"y": 5.0,
|
||||
"z": 5.0
|
||||
},
|
||||
"protocol_type": ["LiquidHandlingProtocol", "PipettingProtocol", "TransferProtocol"]
|
||||
},
|
||||
"data": {
|
||||
"status": "Ready",
|
||||
"version": "1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "module_1_8tubes",
|
||||
"name": "8管位置模块",
|
||||
"children": [],
|
||||
"parent": "laiyu_liquid_station",
|
||||
"type": "container",
|
||||
"class": "opentrons_24_tuberack_nest_1point5ml_snapcap",
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"module_type": "tube_rack",
|
||||
"wells_count": 8,
|
||||
"well_diameter": 29.0,
|
||||
"well_depth": 117.0,
|
||||
"well_volume": 77000.0,
|
||||
"well_shape": "circular",
|
||||
"layout": "2x4"
|
||||
},
|
||||
"data": {
|
||||
"max_volume": 77000.0,
|
||||
"current_volume": 0.0,
|
||||
"wells": ["A1", "A2", "A3", "A4", "B1", "B2", "B3", "B4"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "module_2_96well_deep",
|
||||
"name": "96深孔板",
|
||||
"children": [],
|
||||
"parent": "laiyu_liquid_station",
|
||||
"type": "plate",
|
||||
"class": "nest_96_wellplate_2ml_deep",
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"module_type": "96_well_deep_plate",
|
||||
"wells_count": 96,
|
||||
"well_diameter": 8.2,
|
||||
"well_depth": 39.4,
|
||||
"well_volume": 2080.0,
|
||||
"well_shape": "circular",
|
||||
"layout": "8x12"
|
||||
},
|
||||
"data": {
|
||||
"max_volume": 2080.0,
|
||||
"current_volume": 0.0,
|
||||
"plate_type": "deep_well"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "module_3_beaker",
|
||||
"name": "敞口玻璃瓶",
|
||||
"children": [],
|
||||
"parent": "laiyu_liquid_station",
|
||||
"type": "container",
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"module_type": "beaker_holder",
|
||||
"wells_count": 1,
|
||||
"well_diameter": 85.0,
|
||||
"well_depth": 120.0,
|
||||
"well_volume": 500000.0,
|
||||
"well_shape": "circular",
|
||||
"supported_containers": ["250ml", "500ml", "1000ml"]
|
||||
},
|
||||
"data": {
|
||||
"max_volume": 500000.0,
|
||||
"current_volume": 0.0,
|
||||
"container_type": "beaker",
|
||||
"wells": ["A1"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "module_4_96well_tips",
|
||||
"name": "96吸头架",
|
||||
"children": [],
|
||||
"parent": "laiyu_liquid_station",
|
||||
"type": "container",
|
||||
"class": "tip_rack",
|
||||
"position": {
|
||||
"x": 700,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"module_type": "tip_rack",
|
||||
"wells_count": 96,
|
||||
"well_diameter": 8.2,
|
||||
"well_depth": 60.0,
|
||||
"well_volume": 6000.0,
|
||||
"well_shape": "circular",
|
||||
"layout": "8x12",
|
||||
"tip_type": "standard"
|
||||
},
|
||||
"data": {
|
||||
"max_volume": 6000.0,
|
||||
"current_volume": 0.0,
|
||||
"tip_capacity": "1000μL",
|
||||
"tips_available": 96
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockChiller1",
|
||||
"name": "模拟冷却器",
|
||||
"children": [
|
||||
"MockContainerForChiller1"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_chiller",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"current_temperature": 25.0,
|
||||
"target_temperature": 25.0,
|
||||
"status": "Idle",
|
||||
"is_cooling": false,
|
||||
"is_heating": false,
|
||||
"vessel": "",
|
||||
"purpose": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "MockContainerForChiller1",
|
||||
"name": "模拟容器",
|
||||
"type": "container",
|
||||
"parent": "MockChiller1",
|
||||
"position": {
|
||||
"x": 5,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"data": {
|
||||
"liquid_type": "CuCl2",
|
||||
"liquid_volume": "100"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "MockFilter1",
|
||||
"name": "模拟过滤器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_filter",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"is_filtering": false,
|
||||
"flow_rate": 0.0,
|
||||
"filter_life": 100.0,
|
||||
"vessel": "",
|
||||
"filtrate_vessel": "",
|
||||
"filtered_volume": 0.0,
|
||||
"target_volume": 0.0,
|
||||
"progress": 0.0,
|
||||
"stir": false,
|
||||
"stir_speed": 0.0,
|
||||
"temperature": 25.0,
|
||||
"continue_heatchill": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "MockHeater1",
|
||||
"name": "模拟加热器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_heater",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"current_temperature": 25.0,
|
||||
"target_temperature": 25.0,
|
||||
"status": "Idle",
|
||||
"is_heating": false,
|
||||
"heating_power": 0.0,
|
||||
"max_temperature": 300.0,
|
||||
"vessel": "Unknown",
|
||||
"purpose": "Unknown",
|
||||
"stir": false,
|
||||
"stir_speed": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "MockPump1",
|
||||
"name": "模拟泵设备",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_pump",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_device": "MockPump1",
|
||||
"pump_state": "Stopped",
|
||||
"flow_rate": 0.0,
|
||||
"target_flow_rate": 0.0,
|
||||
"pressure": 0.0,
|
||||
"total_volume": 0.0,
|
||||
"max_flow_rate": 100.0,
|
||||
"max_pressure": 10.0,
|
||||
"from_vessel": "",
|
||||
"to_vessel": "",
|
||||
"transfer_volume": 0.0,
|
||||
"amount": "",
|
||||
"transfer_time": 0.0,
|
||||
"is_viscous": false,
|
||||
"rinsing_solvent": "",
|
||||
"rinsing_volume": 0.0,
|
||||
"rinsing_repeats": 0,
|
||||
"is_solid": false,
|
||||
"time_spent": 0.0,
|
||||
"time_remaining": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "MockRotavap1",
|
||||
"name": "模拟旋转蒸发器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_rotavap",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"rotate_state": "Stopped",
|
||||
"rotate_time": 0.0,
|
||||
"rotate_speed": 0.0,
|
||||
"pump_state": "Stopped",
|
||||
"pump_time": 0.0,
|
||||
"vacuum_level": 1013.25,
|
||||
"temperature": 25.0,
|
||||
"target_temperature": 25.0,
|
||||
"success": "True"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "MockSeparator1",
|
||||
"name": "模拟分离器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_separator",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"settling_time": 0.0,
|
||||
"valve_state": "Closed",
|
||||
"shake_time": 0.0,
|
||||
"shake_status": "Not Shaking",
|
||||
"current_device": "MockSeparator1",
|
||||
"purpose": "",
|
||||
"product_phase": "",
|
||||
"from_vessel": "",
|
||||
"separation_vessel": "",
|
||||
"to_vessel": "",
|
||||
"waste_phase_to_vessel": "",
|
||||
"solvent": "",
|
||||
"solvent_volume": 0.0,
|
||||
"through": "",
|
||||
"repeats": 1,
|
||||
"stir_time": 0.0,
|
||||
"stir_speed": 0.0,
|
||||
"time_spent": 0.0,
|
||||
"time_remaining": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "MockSolenoidValve1",
|
||||
"name": "模拟电磁阀",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_solenoid_valve",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"valve_status": "Closed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "MockStirrer1NEW",
|
||||
"name": "模拟搅拌器(new)",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_stirrer_new",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"vessel": "",
|
||||
"purpose": "",
|
||||
"stir_speed": 0.0,
|
||||
"target_stir_speed": 0.0,
|
||||
"stir_state": "Stopped",
|
||||
"stir_time": 0.0,
|
||||
"settling_time": 0.0,
|
||||
"progress": 0.0,
|
||||
"max_stir_speed": 2000.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "MockStirrer1",
|
||||
"name": "模拟搅拌器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_stirrer",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"stir_speed": 0.0,
|
||||
"target_stir_speed": 0.0,
|
||||
"stir_state": "Stopped",
|
||||
"temperature": 25.0,
|
||||
"target_temperature": 25.0,
|
||||
"heating_state": "Off",
|
||||
"heating_power": 0.0,
|
||||
"max_stir_speed": 2000.0,
|
||||
"max_temperature": 300.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "MockVacuum1",
|
||||
"name": "模拟真空泵",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_vacuum",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"power_state": "Off",
|
||||
"pump_state": "Stopped",
|
||||
"vacuum_level": 1013.25,
|
||||
"target_vacuum": 50.0,
|
||||
"pump_speed": 0.0,
|
||||
"pump_efficiency": 95.0,
|
||||
"max_pump_speed": 100.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockChiller1",
|
||||
"name": "模拟冷却器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_chiller",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"current_temperature": 25.0,
|
||||
"target_temperature": 25.0,
|
||||
"status": "Idle",
|
||||
"is_cooling": false,
|
||||
"is_heating": false,
|
||||
"vessel": "",
|
||||
"purpose": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockFilter1",
|
||||
"name": "模拟过滤器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_filter",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"is_filtering": false,
|
||||
"flow_rate": 0.0,
|
||||
"filter_life": 100.0,
|
||||
"vessel": "",
|
||||
"filtrate_vessel": "",
|
||||
"filtered_volume": 0.0,
|
||||
"target_volume": 0.0,
|
||||
"progress": 0.0,
|
||||
"stir": false,
|
||||
"stir_speed": 0.0,
|
||||
"temperature": 25.0,
|
||||
"continue_heatchill": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockHeater1",
|
||||
"name": "模拟加热器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_heater",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"current_temperature": 25.0,
|
||||
"target_temperature": 25.0,
|
||||
"status": "Idle",
|
||||
"is_heating": false,
|
||||
"heating_power": 0.0,
|
||||
"max_temperature": 300.0,
|
||||
"vessel": "Unknown",
|
||||
"purpose": "Unknown",
|
||||
"stir": false,
|
||||
"stir_speed": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockPump1",
|
||||
"name": "模拟泵设备",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_pump",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_device": "MockPump1",
|
||||
"pump_state": "Stopped",
|
||||
"flow_rate": 0.0,
|
||||
"target_flow_rate": 0.0,
|
||||
"pressure": 0.0,
|
||||
"total_volume": 0.0,
|
||||
"max_flow_rate": 100.0,
|
||||
"max_pressure": 10.0,
|
||||
"from_vessel": "",
|
||||
"to_vessel": "",
|
||||
"transfer_volume": 0.0,
|
||||
"amount": "",
|
||||
"transfer_time": 0.0,
|
||||
"is_viscous": false,
|
||||
"rinsing_solvent": "",
|
||||
"rinsing_volume": 0.0,
|
||||
"rinsing_repeats": 0,
|
||||
"is_solid": false,
|
||||
"time_spent": 0.0,
|
||||
"time_remaining": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockRotavap1",
|
||||
"name": "模拟旋转蒸发器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_rotavap",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"rotate_state": "Stopped",
|
||||
"rotate_time": 0.0,
|
||||
"rotate_speed": 0.0,
|
||||
"pump_state": "Stopped",
|
||||
"pump_time": 0.0,
|
||||
"vacuum_level": 1013.25,
|
||||
"temperature": 25.0,
|
||||
"target_temperature": 25.0,
|
||||
"success": "True"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockSeparator1",
|
||||
"name": "模拟分离器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_separator",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"settling_time": 0.0,
|
||||
"valve_state": "Closed",
|
||||
"shake_time": 0.0,
|
||||
"shake_status": "Not Shaking",
|
||||
"current_device": "MockSeparator1",
|
||||
"purpose": "",
|
||||
"product_phase": "",
|
||||
"from_vessel": "",
|
||||
"separation_vessel": "",
|
||||
"to_vessel": "",
|
||||
"waste_phase_to_vessel": "",
|
||||
"solvent": "",
|
||||
"solvent_volume": 0.0,
|
||||
"through": "",
|
||||
"repeats": 1,
|
||||
"stir_time": 0.0,
|
||||
"stir_speed": 0.0,
|
||||
"time_spent": 0.0,
|
||||
"time_remaining": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockSolenoidValve1",
|
||||
"name": "模拟电磁阀",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_solenoid_valve",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"valve_status": "Closed"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockStirrer1",
|
||||
"name": "模拟搅拌器",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_stirrer",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"stir_speed": 0.0,
|
||||
"target_stir_speed": 0.0,
|
||||
"stir_state": "Stopped",
|
||||
"temperature": 25.0,
|
||||
"target_temperature": 25.0,
|
||||
"heating_state": "Off",
|
||||
"heating_power": 0.0,
|
||||
"max_stir_speed": 2000.0,
|
||||
"max_temperature": 300.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockStirrer1COPY",
|
||||
"name": "模拟搅拌器(Copy)",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_stirrer_new",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"vessel": "",
|
||||
"purpose": "",
|
||||
"stir_speed": 0.0,
|
||||
"target_stir_speed": 0.0,
|
||||
"stir_state": "Stopped",
|
||||
"stir_time": 0.0,
|
||||
"settling_time": 0.0,
|
||||
"progress": 0.0,
|
||||
"max_stir_speed": 2000.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "MockVacuum1",
|
||||
"name": "模拟真空泵",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "mock_vacuum",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "MOCK"
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"power_state": "Off",
|
||||
"pump_state": "Stopped",
|
||||
"vacuum_level": 1013.25,
|
||||
"target_vacuum": 50.0,
|
||||
"pump_speed": 0.0,
|
||||
"pump_efficiency": 95.0,
|
||||
"max_pump_speed": 100.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
@@ -1,376 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "AddTestStation",
|
||||
"name": "添加试剂测试工作站",
|
||||
"children": [
|
||||
"transfer_pump",
|
||||
"multiway_valve",
|
||||
"stirrer",
|
||||
"flask_reagent1",
|
||||
"flask_reagent2",
|
||||
"flask_reagent3",
|
||||
"flask_reagent4",
|
||||
"reactor",
|
||||
"flask_waste",
|
||||
"flask_rinsing",
|
||||
"flask_buffer"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["AddProtocol", "TransferProtocol", "StartStirProtocol", "StopStirProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump",
|
||||
"name": "注射器泵",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 520,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "multiway_valve",
|
||||
"name": "八通阀门",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_multiway_valve",
|
||||
"position": {
|
||||
"x": 420,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"positions": 8
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_position": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer",
|
||||
"name": "搅拌器",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 720,
|
||||
"y": 450,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 100.0,
|
||||
"max_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_reagent1",
|
||||
"name": "试剂瓶1 (甲醇)",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "甲醇",
|
||||
"volume": 800.0,
|
||||
"concentration": "99.9%"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_reagent2",
|
||||
"name": "试剂瓶2 (乙醇)",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 180,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "乙醇",
|
||||
"volume": 750.0,
|
||||
"concentration": "95%"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_reagent3",
|
||||
"name": "试剂瓶3 (丙酮)",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 260,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "丙酮",
|
||||
"volume": 900.0,
|
||||
"concentration": "99.5%"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_reagent4",
|
||||
"name": "试剂瓶4 (二氯甲烷)",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 340,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "二氯甲烷",
|
||||
"volume": 850.0,
|
||||
"concentration": "99.8%"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 720,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_waste",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 850,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 3000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_rinsing",
|
||||
"name": "冲洗液瓶",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 950,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "去离子水",
|
||||
"volume": 800.0,
|
||||
"concentration": "纯净"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_buffer",
|
||||
"name": "缓冲液瓶",
|
||||
"children": [],
|
||||
"parent": "AddTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 950,
|
||||
"y": 400,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "磷酸盐缓冲液",
|
||||
"volume": 700.0,
|
||||
"concentration": "0.1M, pH 7.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "transfer_pump",
|
||||
"target": "multiway_valve",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump": "syringe-port",
|
||||
"multiway_valve": "multiway-valve-inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "multiway_valve",
|
||||
"target": "flask_reagent1",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"multiway_valve": "multiway-valve-port-1",
|
||||
"flask_reagent1": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "multiway_valve",
|
||||
"target": "flask_reagent2",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"multiway_valve": "multiway-valve-port-2",
|
||||
"flask_reagent2": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "multiway_valve",
|
||||
"target": "flask_reagent3",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"multiway_valve": "multiway-valve-port-3",
|
||||
"flask_reagent3": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "multiway_valve",
|
||||
"target": "flask_reagent4",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"multiway_valve": "multiway-valve-port-4",
|
||||
"flask_reagent4": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "multiway_valve",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"multiway_valve": "multiway-valve-port-5",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "multiway_valve",
|
||||
"target": "flask_waste",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"multiway_valve": "multiway-valve-port-6",
|
||||
"flask_waste": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "multiway_valve",
|
||||
"target": "flask_rinsing",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"multiway_valve": "multiway-valve-port-7",
|
||||
"flask_rinsing": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "multiway_valve",
|
||||
"target": "flask_buffer",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"multiway_valve": "multiway-valve-port-8",
|
||||
"flask_buffer": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "stirrer",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"stirrer": "stirrer-vessel",
|
||||
"reactor": "bottom"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "CentrifugeTestStation",
|
||||
"name": "离心机测试工作站",
|
||||
"children": [
|
||||
"pump_add",
|
||||
"flask_1",
|
||||
"flask_2",
|
||||
"flask_3",
|
||||
"reactor",
|
||||
"stirrer",
|
||||
"centrifuge_1",
|
||||
"flask_air"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["AddProtocol", "PumpTransferProtocol", "CleanProtocol", "CentrifugeProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "pump_add",
|
||||
"name": "pump_add",
|
||||
"children": [],
|
||||
"parent": "CentrifugeTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 25.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer",
|
||||
"name": "stirrer",
|
||||
"children": [],
|
||||
"parent": "CentrifugeTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 650.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 100.0,
|
||||
"max_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "centrifuge_1",
|
||||
"name": "离心机",
|
||||
"children": [],
|
||||
"parent": "CentrifugeTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_centrifuge",
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_speed": 15000.0,
|
||||
"max_temp": 40.0,
|
||||
"min_temp": 4.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_1",
|
||||
"name": "样品瓶1",
|
||||
"children": [],
|
||||
"parent": "CentrifugeTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_2",
|
||||
"name": "样品瓶2",
|
||||
"children": [],
|
||||
"parent": "CentrifugeTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_3",
|
||||
"name": "缓冲液瓶",
|
||||
"children": [],
|
||||
"parent": "CentrifugeTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "CentrifugeTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 5000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "CentrifugeTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 950,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "stirrer",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"stirrer": "top",
|
||||
"reactor": "bottom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_1",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "outlet",
|
||||
"flask_1": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_2",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_2": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_3",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_3": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "outlet",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_air",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "centrifuge_1",
|
||||
"target": "reactor",
|
||||
"type": "logical",
|
||||
"port": {
|
||||
"centrifuge_1": "chamber",
|
||||
"reactor": "vessel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "centrifuge_1",
|
||||
"target": "flask_1",
|
||||
"type": "logical",
|
||||
"port": {
|
||||
"centrifuge_1": "chamber",
|
||||
"flask_1": "vessel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "centrifuge_1",
|
||||
"target": "flask_2",
|
||||
"type": "logical",
|
||||
"port": {
|
||||
"centrifuge_1": "chamber",
|
||||
"flask_2": "vessel"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,362 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "CleanVesselTestStation",
|
||||
"name": "容器清洗测试工作站",
|
||||
"children": [
|
||||
"transfer_pump_cleaner",
|
||||
"heatchill_1",
|
||||
"flask_water",
|
||||
"flask_ethanol",
|
||||
"flask_acetone",
|
||||
"flask_waste",
|
||||
"reactor",
|
||||
"flask_buffer",
|
||||
"flask_sample",
|
||||
"flask_air"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["CleanVesselProtocol", "TransferProtocol", "AddProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_cleaner",
|
||||
"name": "清洗转移泵",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_volume": 0.0,
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0,
|
||||
"from_vessel": "",
|
||||
"to_vessel": "",
|
||||
"progress": 0.0,
|
||||
"transferred_volume": 0.0,
|
||||
"current_status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_1",
|
||||
"name": "加热冷却器",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 650.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 150.0,
|
||||
"min_temp": -20.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_temp": 25.0,
|
||||
"target_temp": 25.0,
|
||||
"vessel": "",
|
||||
"purpose": "",
|
||||
"progress": 0.0,
|
||||
"current_status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "水溶剂瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "water",
|
||||
"volume": 1500.0,
|
||||
"concentration": 100.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇溶剂瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "ethanol",
|
||||
"volume": 1500.0,
|
||||
"concentration": 99.5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_acetone",
|
||||
"name": "丙酮溶剂瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "acetone",
|
||||
"volume": 1800.0,
|
||||
"concentration": 99.9
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_waste",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 550,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 5000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "residue",
|
||||
"volume": 50.0,
|
||||
"concentration": 100.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_buffer",
|
||||
"name": "缓冲液瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 850,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "buffer",
|
||||
"volume": 1000.0,
|
||||
"concentration": 10.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_sample",
|
||||
"name": "样品瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 1000,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "CleanVesselTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 950,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "transfer_pump_cleaner",
|
||||
"target": "flask_water",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_cleaner": "1",
|
||||
"flask_water": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_cleaner",
|
||||
"target": "flask_ethanol",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_cleaner": "2",
|
||||
"flask_ethanol": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_cleaner",
|
||||
"target": "flask_acetone",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_cleaner": "3",
|
||||
"flask_acetone": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_cleaner",
|
||||
"target": "flask_waste",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_cleaner": "4",
|
||||
"flask_waste": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_cleaner",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_cleaner": "5",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_cleaner",
|
||||
"target": "flask_buffer",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_cleaner": "6",
|
||||
"flask_buffer": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_cleaner",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_cleaner": "7",
|
||||
"flask_sample": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_cleaner",
|
||||
"target": "flask_air",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_cleaner": "8",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "heatchill_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"heatchill_1": "heating_element",
|
||||
"reactor": "bottom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "heatchill_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"heatchill_1": "heating_element",
|
||||
"flask_sample": "bottom"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,343 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "DissolveTestStation",
|
||||
"name": "溶解测试工作站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"heatchill_1",
|
||||
"stirrer_1",
|
||||
"flask_water",
|
||||
"flask_ethanol",
|
||||
"flask_dmso",
|
||||
"reactor",
|
||||
"flask_sample",
|
||||
"flask_buffer"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["DissolveProtocol", "TransferProtocol", "HeatChillProtocol", "StirProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵",
|
||||
"children": [],
|
||||
"parent": "DissolveTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_volume": 0.0,
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0,
|
||||
"from_vessel": "",
|
||||
"to_vessel": "",
|
||||
"progress": 0.0,
|
||||
"transferred_volume": 0.0,
|
||||
"current_status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_1",
|
||||
"name": "加热冷却器",
|
||||
"children": [],
|
||||
"parent": "DissolveTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 650.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 150.0,
|
||||
"min_temp": -20.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_temp": 25.0,
|
||||
"target_temp": 25.0,
|
||||
"vessel": "",
|
||||
"purpose": "",
|
||||
"progress": 0.0,
|
||||
"current_status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer_1",
|
||||
"name": "搅拌器",
|
||||
"children": [],
|
||||
"parent": "DissolveTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 750.1111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "水溶剂瓶",
|
||||
"children": [],
|
||||
"parent": "DissolveTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "water",
|
||||
"volume": 1500.0,
|
||||
"concentration": 100.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇溶剂瓶",
|
||||
"children": [],
|
||||
"parent": "DissolveTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "ethanol",
|
||||
"volume": 1500.0,
|
||||
"concentration": 99.5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_dmso",
|
||||
"name": "DMSO溶剂瓶",
|
||||
"children": [],
|
||||
"parent": "DissolveTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "dmso",
|
||||
"volume": 800.0,
|
||||
"concentration": 99.9
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "DissolveTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "solid_sample",
|
||||
"volume": 10.0,
|
||||
"concentration": 100.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_sample",
|
||||
"name": "样品瓶",
|
||||
"children": [],
|
||||
"parent": "DissolveTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 1000,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_buffer",
|
||||
"name": "缓冲液瓶",
|
||||
"children": [],
|
||||
"parent": "DissolveTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 850,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "buffer",
|
||||
"volume": 1000.0,
|
||||
"concentration": 10.0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_water",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "1",
|
||||
"flask_water": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_ethanol",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "2",
|
||||
"flask_ethanol": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_dmso",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "3",
|
||||
"flask_dmso": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "4",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "5",
|
||||
"flask_sample": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_buffer",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "6",
|
||||
"flask_buffer": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "heatchill_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"heatchill_1": "heating_element",
|
||||
"reactor": "bottom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "heatchill_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"heatchill_1": "heating_element",
|
||||
"flask_sample": "bottom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "stirrer_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"stirrer_1": "stir_rod",
|
||||
"reactor": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "stirrer_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"stirrer_1": "stir_rod",
|
||||
"flask_sample": "center"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "FilterTestStation",
|
||||
"name": "过滤器测试工作站",
|
||||
"children": [
|
||||
"pump_add",
|
||||
"flask_sample",
|
||||
"flask_filtrate",
|
||||
"flask_buffer",
|
||||
"reactor",
|
||||
"stirrer",
|
||||
"filter_1",
|
||||
"flask_air"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["AddProtocol", "PumpTransferProtocol", "CleanProtocol", "FilterProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "pump_add",
|
||||
"name": "pump_add",
|
||||
"children": [],
|
||||
"parent": "FilterTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 25.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer",
|
||||
"name": "stirrer",
|
||||
"children": [],
|
||||
"parent": "FilterTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 650.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 100.0,
|
||||
"max_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "filter_1",
|
||||
"name": "过滤器",
|
||||
"children": [],
|
||||
"parent": "FilterTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_filter",
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 100.0,
|
||||
"max_stir_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_sample",
|
||||
"name": "样品瓶",
|
||||
"children": [],
|
||||
"parent": "FilterTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_filtrate",
|
||||
"name": "滤液瓶",
|
||||
"children": [],
|
||||
"parent": "FilterTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_buffer",
|
||||
"name": "缓冲液瓶",
|
||||
"children": [],
|
||||
"parent": "FilterTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "FilterTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 5000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "FilterTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 950,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "stirrer",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"stirrer": "top",
|
||||
"reactor": "bottom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "outlet",
|
||||
"flask_sample": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_filtrate",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_filtrate": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_buffer",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_buffer": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "outlet",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_air",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "filter_1",
|
||||
"target": "reactor",
|
||||
"type": "logical",
|
||||
"port": {
|
||||
"filter_1": "input",
|
||||
"reactor": "vessel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "filter_1",
|
||||
"target": "flask_sample",
|
||||
"type": "logical",
|
||||
"port": {
|
||||
"filter_1": "input",
|
||||
"flask_sample": "vessel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "filter_1",
|
||||
"target": "flask_filtrate",
|
||||
"type": "logical",
|
||||
"port": {
|
||||
"filter_1": "output",
|
||||
"flask_filtrate": "vessel"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "FilterThroughTestStation",
|
||||
"name": "过滤通过测试工作站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"filter_1",
|
||||
"flask_ethanol",
|
||||
"flask_water",
|
||||
"flask_methanol",
|
||||
"reactor",
|
||||
"collection_flask",
|
||||
"waste_flask",
|
||||
"flask_sample",
|
||||
"flask_celite",
|
||||
"flask_silica"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["FilterThroughProtocol", "TransferProtocol", "FilterProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_volume": 0.0,
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0,
|
||||
"from_vessel": "",
|
||||
"to_vessel": "",
|
||||
"progress": 0.0,
|
||||
"transferred_volume": 0.0,
|
||||
"current_status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "filter_1",
|
||||
"name": "过滤器",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_filter",
|
||||
"position": {
|
||||
"x": 650.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 100.0,
|
||||
"max_stir_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"filter_state": "Ready",
|
||||
"current_temp": 25.0,
|
||||
"target_temp": 25.0,
|
||||
"max_temp": 100.0,
|
||||
"stir_speed": 0.0,
|
||||
"max_stir_speed": 1000.0,
|
||||
"filtered_volume": 0.0,
|
||||
"progress": 0.0,
|
||||
"message": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇溶剂瓶",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "ethanol",
|
||||
"volume": 1500.0,
|
||||
"concentration": 99.5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "水溶剂瓶",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "water",
|
||||
"volume": 1800.0,
|
||||
"concentration": 100.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇溶剂瓶",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "methanol",
|
||||
"volume": 800.0,
|
||||
"concentration": 99.9
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "crude_product",
|
||||
"volume": 200.0,
|
||||
"concentration": 80.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_flask",
|
||||
"name": "收集瓶",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 850,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_flask",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 1000,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_sample",
|
||||
"name": "样品瓶",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 550,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "sample_mixture",
|
||||
"volume": 100.0,
|
||||
"concentration": 50.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_celite",
|
||||
"name": "硅藻土容器",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 150,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "celite",
|
||||
"volume": 50.0,
|
||||
"concentration": 100.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_silica",
|
||||
"name": "硅胶容器",
|
||||
"children": [],
|
||||
"parent": "FilterThroughTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "silica",
|
||||
"volume": 30.0,
|
||||
"concentration": 100.0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_ethanol",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "1",
|
||||
"flask_ethanol": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_water",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "2",
|
||||
"flask_water": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "3",
|
||||
"flask_methanol": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "4",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "collection_flask",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "5",
|
||||
"collection_flask": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "waste_flask",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "6",
|
||||
"waste_flask": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "7",
|
||||
"flask_sample": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "filter_1",
|
||||
"target": "collection_flask",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"filter_1": "filter_element",
|
||||
"collection_flask": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "filter_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"filter_1": "filter_element",
|
||||
"reactor": "top"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "HeatChillTestStation",
|
||||
"name": "加热冷却测试工作站",
|
||||
"children": [
|
||||
"pump_add",
|
||||
"flask_sample",
|
||||
"flask_buffer1",
|
||||
"flask_buffer2",
|
||||
"reactor",
|
||||
"stirrer",
|
||||
"heatchill_1",
|
||||
"flask_air"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["AddProtocol", "PumpTransferProtocol", "CleanProtocol", "HeatChillProtocol", "HeatChillStartProtocol", "HeatChillStopProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "pump_add",
|
||||
"name": "pump_add",
|
||||
"children": [],
|
||||
"parent": "HeatChillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 25.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer",
|
||||
"name": "stirrer",
|
||||
"children": [],
|
||||
"parent": "HeatChillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 650.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 100.0,
|
||||
"max_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_1",
|
||||
"name": "加热冷却器",
|
||||
"children": [],
|
||||
"parent": "HeatChillTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 800,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 200.0,
|
||||
"min_temp": -80.0,
|
||||
"max_stir_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_sample",
|
||||
"name": "样品瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_buffer1",
|
||||
"name": "缓冲液瓶1",
|
||||
"children": [],
|
||||
"parent": "HeatChillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_buffer2",
|
||||
"name": "缓冲液瓶2",
|
||||
"children": [],
|
||||
"parent": "HeatChillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "HeatChillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 5000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "HeatChillTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 950,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "stirrer",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"stirrer": "top",
|
||||
"reactor": "bottom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "outlet",
|
||||
"flask_sample": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_buffer1",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_buffer1": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_buffer2",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_buffer2": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "outlet",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_air",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_air": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "heatchill_1",
|
||||
"target": "reactor",
|
||||
"type": "logical",
|
||||
"port": {
|
||||
"heatchill_1": "heating_element",
|
||||
"reactor": "vessel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "heatchill_1",
|
||||
"target": "flask_sample",
|
||||
"type": "logical",
|
||||
"port": {
|
||||
"heatchill_1": "heating_element",
|
||||
"flask_sample": "vessel"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,412 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "RunColumnTestStation",
|
||||
"name": "柱层析测试工作站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"column_1",
|
||||
"flask_sample",
|
||||
"flask_hexane",
|
||||
"flask_ethyl_acetate",
|
||||
"flask_methanol",
|
||||
"collection_flask_1",
|
||||
"collection_flask_2",
|
||||
"collection_flask_3",
|
||||
"waste_flask",
|
||||
"reactor"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["RunColumnProtocol", "TransferProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_volume": 0.0,
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0,
|
||||
"from_vessel": "",
|
||||
"to_vessel": "",
|
||||
"progress": 0.0,
|
||||
"transferred_volume": 0.0,
|
||||
"current_status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "column_1",
|
||||
"name": "柱层析设备",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_column",
|
||||
"position": {
|
||||
"x": 650.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_flow_rate": 5.0,
|
||||
"column_length": 30.0,
|
||||
"column_diameter": 2.5
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"column_state": "Ready",
|
||||
"current_flow_rate": 0.0,
|
||||
"max_flow_rate": 5.0,
|
||||
"column_length": 30.0,
|
||||
"column_diameter": 2.5,
|
||||
"processed_volume": 0.0,
|
||||
"progress": 0.0,
|
||||
"current_status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_sample",
|
||||
"name": "样品瓶",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "crude_mixture",
|
||||
"volume": 200.0,
|
||||
"concentration": 70.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_hexane",
|
||||
"name": "正己烷洗脱剂",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "hexane",
|
||||
"volume": 1500.0,
|
||||
"concentration": 99.8
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethyl_acetate",
|
||||
"name": "乙酸乙酯洗脱剂",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "ethyl_acetate",
|
||||
"volume": 1500.0,
|
||||
"concentration": 99.5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇洗脱剂",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 550,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "methanol",
|
||||
"volume": 800.0,
|
||||
"concentration": 99.9
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_flask_1",
|
||||
"name": "收集瓶1",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 750,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_flask_2",
|
||||
"name": "收集瓶2",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 900,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_flask_3",
|
||||
"name": "收集瓶3",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 1050,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_flask",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 1200,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "RunColumnTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "reaction_mixture",
|
||||
"volume": 300.0,
|
||||
"concentration": 85.0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "1",
|
||||
"flask_sample": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_hexane",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "2",
|
||||
"flask_hexane": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_ethyl_acetate",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "3",
|
||||
"flask_ethyl_acetate": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "4",
|
||||
"flask_methanol": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "column_1",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "5",
|
||||
"column_1": "inlet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "collection_flask_1",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "6",
|
||||
"collection_flask_1": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "collection_flask_2",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "7",
|
||||
"collection_flask_2": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "collection_flask_3",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "8",
|
||||
"collection_flask_3": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "waste_flask",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "9",
|
||||
"waste_flask": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "10",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "column_1",
|
||||
"target": "collection_flask_1",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"column_1": "outlet",
|
||||
"collection_flask_1": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "column_1",
|
||||
"target": "collection_flask_2",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"column_1": "outlet",
|
||||
"collection_flask_2": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "column_1",
|
||||
"target": "collection_flask_3",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"column_1": "outlet",
|
||||
"collection_flask_3": "top"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "StirTestStation",
|
||||
"name": "搅拌测试工作站",
|
||||
"children": [
|
||||
"pump_add",
|
||||
"flask_sample",
|
||||
"flask_buffer1",
|
||||
"flask_buffer2",
|
||||
"reactor",
|
||||
"stirrer",
|
||||
"flask_waste",
|
||||
"flask_air"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["AddProtocol", "PumpTransferProtocol", "CleanProtocol", "StirProtocol", "StartStirProtocol", "StopStirProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "pump_add",
|
||||
"name": "添加泵",
|
||||
"children": [],
|
||||
"parent": "StirTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 25.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer",
|
||||
"name": "搅拌器",
|
||||
"children": [],
|
||||
"parent": "StirTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 650.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 100.0,
|
||||
"max_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_sample",
|
||||
"name": "样品瓶",
|
||||
"children": [],
|
||||
"parent": "StirTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_buffer1",
|
||||
"name": "缓冲液瓶1",
|
||||
"children": [],
|
||||
"parent": "StirTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_buffer2",
|
||||
"name": "缓冲液瓶2",
|
||||
"children": [],
|
||||
"parent": "StirTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "StirTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 5000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_waste",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "StirTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 850,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 3000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_air",
|
||||
"name": "空气瓶",
|
||||
"children": [],
|
||||
"parent": "StirTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 950,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "stirrer",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"stirrer": "top",
|
||||
"reactor": "bottom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_sample": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_buffer1",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_buffer1": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_buffer2",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_buffer2": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "outlet",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_waste",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "outlet",
|
||||
"flask_waste": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "pump_add",
|
||||
"target": "flask_air",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"pump_add": "inlet",
|
||||
"flask_air": "top"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "TransferTestStation",
|
||||
"name": "液体转移测试工作站",
|
||||
"children": [
|
||||
"transfer_pump",
|
||||
"flask_source1",
|
||||
"flask_source2",
|
||||
"flask_target1",
|
||||
"flask_target2",
|
||||
"reactor",
|
||||
"flask_waste",
|
||||
"flask_rinsing"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["TransferProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump",
|
||||
"name": "转移泵",
|
||||
"children": [],
|
||||
"parent": "TransferTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 5.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_source1",
|
||||
"name": "源容器1",
|
||||
"children": [],
|
||||
"parent": "TransferTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_source2",
|
||||
"name": "源容器2",
|
||||
"children": [],
|
||||
"parent": "TransferTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_target1",
|
||||
"name": "目标容器1",
|
||||
"children": [],
|
||||
"parent": "TransferTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_target2",
|
||||
"name": "目标容器2",
|
||||
"children": [],
|
||||
"parent": "TransferTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 550,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "TransferTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_waste",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "TransferTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 850,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_rinsing",
|
||||
"name": "冲洗液瓶",
|
||||
"children": [],
|
||||
"parent": "TransferTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 950,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "transfer_pump",
|
||||
"target": "flask_source1",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump": "inlet",
|
||||
"flask_source1": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump",
|
||||
"target": "flask_source2",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump": "inlet",
|
||||
"flask_source2": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump",
|
||||
"target": "flask_target1",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump": "outlet",
|
||||
"flask_target1": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump",
|
||||
"target": "flask_target2",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump": "outlet",
|
||||
"flask_target2": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump": "outlet",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump",
|
||||
"target": "flask_waste",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump": "outlet",
|
||||
"flask_waste": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump",
|
||||
"target": "flask_rinsing",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump": "inlet",
|
||||
"flask_rinsing": "top"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,494 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "WashSolidTestStation",
|
||||
"name": "固体清洗测试工作站",
|
||||
"children": [
|
||||
"transfer_pump_1",
|
||||
"heatchill_1",
|
||||
"stirrer_1",
|
||||
"filter_1",
|
||||
"flask_ethanol",
|
||||
"flask_water",
|
||||
"flask_acetone",
|
||||
"flask_methanol",
|
||||
"reactor",
|
||||
"collection_flask",
|
||||
"waste_flask",
|
||||
"flask_sample",
|
||||
"filtrate_flask"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"protocol_type": ["WashSolidProtocol", "TransferProtocol", "FilterProtocol", "HeatChillProtocol", "StirProtocol"]
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "transfer_pump_1",
|
||||
"name": "转移泵",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_transfer_pump",
|
||||
"position": {
|
||||
"x": 520.6111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_volume": 0.0,
|
||||
"max_volume": 50.0,
|
||||
"transfer_rate": 10.0,
|
||||
"from_vessel": "",
|
||||
"to_vessel": "",
|
||||
"progress": 0.0,
|
||||
"transferred_volume": 0.0,
|
||||
"current_status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "heatchill_1",
|
||||
"name": "加热冷却器",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_heatchill",
|
||||
"position": {
|
||||
"x": 650.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 150.0,
|
||||
"min_temp": -20.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"current_temp": 25.0,
|
||||
"target_temp": 25.0,
|
||||
"vessel": "",
|
||||
"purpose": "",
|
||||
"progress": 0.0,
|
||||
"current_status": "Ready"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stirrer_1",
|
||||
"name": "搅拌器",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_stirrer",
|
||||
"position": {
|
||||
"x": 750.1111111111111,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "filter_1",
|
||||
"name": "过滤器",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "device",
|
||||
"class": "virtual_filter",
|
||||
"position": {
|
||||
"x": 850.1111111111111,
|
||||
"y": 478,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"port": "VIRTUAL",
|
||||
"max_temp": 100.0,
|
||||
"max_stir_speed": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle",
|
||||
"filter_state": "Ready",
|
||||
"current_temp": 25.0,
|
||||
"target_temp": 25.0,
|
||||
"max_temp": 100.0,
|
||||
"stir_speed": 0.0,
|
||||
"max_stir_speed": 1000.0,
|
||||
"filtered_volume": 0.0,
|
||||
"progress": 0.0,
|
||||
"message": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_ethanol",
|
||||
"name": "乙醇清洗剂",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 100,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "ethanol",
|
||||
"volume": 1500.0,
|
||||
"concentration": 99.5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_water",
|
||||
"name": "水清洗剂",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "water",
|
||||
"volume": 1800.0,
|
||||
"concentration": 100.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_acetone",
|
||||
"name": "丙酮清洗剂",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 400,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "acetone",
|
||||
"volume": 800.0,
|
||||
"concentration": 99.8
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_methanol",
|
||||
"name": "甲醇清洗剂",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 550,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "methanol",
|
||||
"volume": 800.0,
|
||||
"concentration": 99.9
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reactor",
|
||||
"name": "反应器",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "solid_product",
|
||||
"volume": 50.0,
|
||||
"concentration": 100.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collection_flask",
|
||||
"name": "收集瓶",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 850,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "waste_flask",
|
||||
"name": "废液瓶",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 1000,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "flask_sample",
|
||||
"name": "样品瓶",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 1150,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
{
|
||||
"name": "crude_solid",
|
||||
"volume": 30.0,
|
||||
"concentration": 80.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "filtrate_flask",
|
||||
"name": "滤液收集瓶",
|
||||
"children": [],
|
||||
"parent": "WashSolidTestStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 1000,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 1500.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_ethanol",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "1",
|
||||
"flask_ethanol": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_water",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "2",
|
||||
"flask_water": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_acetone",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "3",
|
||||
"flask_acetone": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_methanol",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "4",
|
||||
"flask_methanol": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "5",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "collection_flask",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "6",
|
||||
"collection_flask": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "waste_flask",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "7",
|
||||
"waste_flask": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "8",
|
||||
"flask_sample": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "transfer_pump_1",
|
||||
"target": "filtrate_flask",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"transfer_pump_1": "9",
|
||||
"filtrate_flask": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "heatchill_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"heatchill_1": "heating_element",
|
||||
"reactor": "bottom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "heatchill_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"heatchill_1": "heating_element",
|
||||
"flask_sample": "bottom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "stirrer_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"stirrer_1": "stir_rod",
|
||||
"reactor": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "stirrer_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"stirrer_1": "stir_rod",
|
||||
"flask_sample": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "filter_1",
|
||||
"target": "reactor",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"filter_1": "filter_element",
|
||||
"reactor": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "filter_1",
|
||||
"target": "flask_sample",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"filter_1": "filter_element",
|
||||
"flask_sample": "top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "filter_1",
|
||||
"target": "filtrate_flask",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"filter_1": "filter_element",
|
||||
"filtrate_flask": "top"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -14,8 +14,8 @@
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
@@ -30,17 +30,14 @@
|
||||
"children": [],
|
||||
"parent": "ReactorX",
|
||||
"type": "container",
|
||||
"class": "container",
|
||||
"class": null,
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"max_volume": 5000.0,
|
||||
"size_x": 200.0,
|
||||
"size_y": 200.0,
|
||||
"size_z": 200.0
|
||||
"max_volume": 5000.0
|
||||
},
|
||||
"data": {
|
||||
"liquid": [
|
||||
@@ -74,7 +71,7 @@
|
||||
"type": "device",
|
||||
"class": "solenoid_valve.mock",
|
||||
"position": {
|
||||
"x": 780,
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
@@ -92,7 +89,7 @@
|
||||
"type": "device",
|
||||
"class": "vacuum_pump.mock",
|
||||
"position": {
|
||||
"x": 500,
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
@@ -110,7 +107,7 @@
|
||||
"type": "device",
|
||||
"class": "gas_source.mock",
|
||||
"position": {
|
||||
"x": 900,
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
"z": 0
|
||||
},
|
||||
@@ -122,39 +119,39 @@
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "vacuum_valve",
|
||||
"target": "reactor",
|
||||
"type": "fluid",
|
||||
"source": "reactor",
|
||||
"target": "vacuum_valve",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"reactor": "top",
|
||||
"vacuum_valve": "out"
|
||||
"vacuum_valve": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "gas_valve",
|
||||
"target": "reactor",
|
||||
"type": "fluid",
|
||||
"source": "reactor",
|
||||
"target": "gas_valve",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"reactor": "top",
|
||||
"gas_valve": "out"
|
||||
"gas_valve": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "vacuum_pump",
|
||||
"target": "vacuum_valve",
|
||||
"type": "fluid",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"vacuum_pump": "out",
|
||||
"vacuum_valve": "in"
|
||||
"vacuum_valve": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "gas_source",
|
||||
"target": "gas_valve",
|
||||
"type": "fluid",
|
||||
"type": "physical",
|
||||
"port": {
|
||||
"gas_source": "out",
|
||||
"gas_valve": "in"
|
||||
"gas_valve": "0"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "reaction_station_bioyond",
|
||||
"name": "reaction_station_bioyond",
|
||||
"parent": null,
|
||||
"children": [
|
||||
"Bioyond_Deck"
|
||||
],
|
||||
"type": "device",
|
||||
"class": "reaction_station.bioyond",
|
||||
"config": {
|
||||
"bioyond_config": {
|
||||
"api_key": "DE9BDDA0",
|
||||
"api_host": "http://192.168.1.200:44402",
|
||||
"workflow_mappings": {
|
||||
"reactor_taken_out": "3a16081e-4788-ca37-eff4-ceed8d7019d1",
|
||||
"reactor_taken_in": "3a160df6-76b3-0957-9eb0-cb496d5721c6",
|
||||
"Solid_feeding_vials": "3a160877-87e7-7699-7bc6-ec72b05eb5e6",
|
||||
"Liquid_feeding_vials(non-titration)": "3a167d99-6158-c6f0-15b5-eb030f7d8e47",
|
||||
"Liquid_feeding_solvents": "3a160824-0665-01ed-285a-51ef817a9046",
|
||||
"Liquid_feeding(titration)": "3a160824-0665-01ed-285a-51ef817a9046",
|
||||
"Liquid_feeding_beaker": "3a16087e-124f-8ddb-8ec1-c2dff09ca784",
|
||||
"Drip_back": "3a162cf9-6aac-565a-ddd7-682ba1796a4a"
|
||||
},
|
||||
"material_type_mappings": {
|
||||
"烧杯": "BIOYOND_PolymerStation_1FlaskCarrier",
|
||||
"试剂瓶": "BIOYOND_PolymerStation_1BottleCarrier",
|
||||
"样品板": "BIOYOND_PolymerStation_6VialCarrier"
|
||||
}
|
||||
},
|
||||
"deck": {
|
||||
"data": {
|
||||
"_resource_child_name": "Bioyond_Deck",
|
||||
"_resource_type": "unilabos.resources.bioyond.decks:BIOYOND_PolymerReactionStation_Deck"
|
||||
}
|
||||
},
|
||||
"protocol_type": []
|
||||
},
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "Bioyond_Deck",
|
||||
"name": "Bioyond_Deck",
|
||||
"sample_id": null,
|
||||
"children": [
|
||||
],
|
||||
"parent": "reaction_station_bioyond",
|
||||
"type": "deck",
|
||||
"class": "BIOYOND_PolymerReactionStation_Deck",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"type": "BIOYOND_PolymerReactionStation_Deck",
|
||||
"setup": true,
|
||||
"rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"type": "Rotation"
|
||||
}
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
]
|
||||
}
|
||||