mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-04 13:25:13 +00:00
Add result schema and add TypedDict conversion.
(cherry picked from commit 8fa3407649)
This commit is contained in:
@@ -2,6 +2,7 @@ from datetime import datetime
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from typing import Optional, Dict, Any, List
|
from typing import Optional, Dict, Any, List
|
||||||
|
from typing_extensions import TypedDict
|
||||||
import requests
|
import requests
|
||||||
from unilabos.devices.workstation.bioyond_studio.config import API_CONFIG
|
from unilabos.devices.workstation.bioyond_studio.config import API_CONFIG
|
||||||
|
|
||||||
@@ -13,6 +14,14 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
|
class ComputeExperimentDesignReturn(TypedDict):
|
||||||
|
solutions: list
|
||||||
|
titration: dict
|
||||||
|
solvents: dict
|
||||||
|
feeding_order: list
|
||||||
|
return_info: str
|
||||||
|
|
||||||
|
|
||||||
class BioyondDispensingStation(BioyondWorkstation):
|
class BioyondDispensingStation(BioyondWorkstation):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -102,7 +111,7 @@ class BioyondDispensingStation(BioyondWorkstation):
|
|||||||
wt_percent: str = "0.25",
|
wt_percent: str = "0.25",
|
||||||
m_tot: str = "70",
|
m_tot: str = "70",
|
||||||
titration_percent: str = "0.03",
|
titration_percent: str = "0.03",
|
||||||
) -> dict:
|
) -> ComputeExperimentDesignReturn:
|
||||||
try:
|
try:
|
||||||
if isinstance(ratio, str):
|
if isinstance(ratio, str):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -83,6 +83,96 @@ workstation.bioyond_dispensing_station:
|
|||||||
title: batch_create_diamine_solution_tasks参数
|
title: batch_create_diamine_solution_tasks参数
|
||||||
type: object
|
type: object
|
||||||
type: UniLabJsonCommand
|
type: UniLabJsonCommand
|
||||||
|
auto-brief_step_parameters:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
data: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- data
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: brief_step_parameters参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-compute_experiment_design:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
m_tot: '70'
|
||||||
|
ratio: null
|
||||||
|
titration_percent: '0.03'
|
||||||
|
wt_percent: '0.25'
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
m_tot:
|
||||||
|
default: '70'
|
||||||
|
type: string
|
||||||
|
ratio:
|
||||||
|
type: object
|
||||||
|
titration_percent:
|
||||||
|
default: '0.03'
|
||||||
|
type: string
|
||||||
|
wt_percent:
|
||||||
|
default: '0.25'
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- ratio
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
feeding_order:
|
||||||
|
items: {}
|
||||||
|
title: Feeding Order
|
||||||
|
type: array
|
||||||
|
return_info:
|
||||||
|
title: Return Info
|
||||||
|
type: string
|
||||||
|
solutions:
|
||||||
|
items: {}
|
||||||
|
title: Solutions
|
||||||
|
type: array
|
||||||
|
solvents:
|
||||||
|
additionalProperties: true
|
||||||
|
title: Solvents
|
||||||
|
type: object
|
||||||
|
titration:
|
||||||
|
additionalProperties: true
|
||||||
|
title: Titration
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- solutions
|
||||||
|
- titration
|
||||||
|
- solvents
|
||||||
|
- feeding_order
|
||||||
|
- return_info
|
||||||
|
title: ComputeExperimentDesignReturn
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: compute_experiment_design参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
auto-process_order_finish_report:
|
auto-process_order_finish_report:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal: {}
|
goal: {}
|
||||||
@@ -112,6 +202,85 @@ workstation.bioyond_dispensing_station:
|
|||||||
title: process_order_finish_report参数
|
title: process_order_finish_report参数
|
||||||
type: object
|
type: object
|
||||||
type: UniLabJsonCommand
|
type: UniLabJsonCommand
|
||||||
|
auto-project_order_report:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
order_id: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
order_id:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- order_id
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: project_order_report参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-query_resource_by_name:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
material_name: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
material_name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- material_name
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: query_resource_by_name参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-transfer_materials_to_reaction_station:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
target_device_id: null
|
||||||
|
transfer_groups: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
target_device_id:
|
||||||
|
type: string
|
||||||
|
transfer_groups:
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- target_device_id
|
||||||
|
- transfer_groups
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: transfer_materials_to_reaction_station参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
auto-wait_for_multiple_orders_and_get_reports:
|
auto-wait_for_multiple_orders_and_get_reports:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal: {}
|
goal: {}
|
||||||
@@ -144,6 +313,31 @@ workstation.bioyond_dispensing_station:
|
|||||||
title: wait_for_multiple_orders_and_get_reports参数
|
title: wait_for_multiple_orders_and_get_reports参数
|
||||||
type: object
|
type: object
|
||||||
type: UniLabJsonCommand
|
type: UniLabJsonCommand
|
||||||
|
auto-workflow_sample_locations:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
workflow_id: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
workflow_id:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- workflow_id
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: workflow_sample_locations参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
create_90_10_vial_feeding_task:
|
create_90_10_vial_feeding_task:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
|
|||||||
@@ -5,6 +5,96 @@ bioyond_dispensing_station:
|
|||||||
- bioyond_dispensing_station
|
- bioyond_dispensing_station
|
||||||
class:
|
class:
|
||||||
action_value_mappings:
|
action_value_mappings:
|
||||||
|
auto-brief_step_parameters:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
data: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- data
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: brief_step_parameters参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-compute_experiment_design:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
m_tot: '70'
|
||||||
|
ratio: null
|
||||||
|
titration_percent: '0.03'
|
||||||
|
wt_percent: '0.25'
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
m_tot:
|
||||||
|
default: '70'
|
||||||
|
type: string
|
||||||
|
ratio:
|
||||||
|
type: object
|
||||||
|
titration_percent:
|
||||||
|
default: '0.03'
|
||||||
|
type: string
|
||||||
|
wt_percent:
|
||||||
|
default: '0.25'
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- ratio
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
feeding_order:
|
||||||
|
items: {}
|
||||||
|
title: Feeding Order
|
||||||
|
type: array
|
||||||
|
return_info:
|
||||||
|
title: Return Info
|
||||||
|
type: string
|
||||||
|
solutions:
|
||||||
|
items: {}
|
||||||
|
title: Solutions
|
||||||
|
type: array
|
||||||
|
solvents:
|
||||||
|
additionalProperties: true
|
||||||
|
title: Solvents
|
||||||
|
type: object
|
||||||
|
titration:
|
||||||
|
additionalProperties: true
|
||||||
|
title: Titration
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- solutions
|
||||||
|
- titration
|
||||||
|
- solvents
|
||||||
|
- feeding_order
|
||||||
|
- return_info
|
||||||
|
title: ComputeExperimentDesignReturn
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: compute_experiment_design参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
auto-process_order_finish_report:
|
auto-process_order_finish_report:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal: {}
|
goal: {}
|
||||||
@@ -34,6 +124,110 @@ bioyond_dispensing_station:
|
|||||||
title: process_order_finish_report参数
|
title: process_order_finish_report参数
|
||||||
type: object
|
type: object
|
||||||
type: UniLabJsonCommand
|
type: UniLabJsonCommand
|
||||||
|
auto-project_order_report:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
order_id: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
order_id:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- order_id
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: project_order_report参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-query_resource_by_name:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
material_name: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
material_name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- material_name
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: query_resource_by_name参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-transfer_materials_to_reaction_station:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
target_device_id: null
|
||||||
|
transfer_groups: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
target_device_id:
|
||||||
|
type: string
|
||||||
|
transfer_groups:
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- target_device_id
|
||||||
|
- transfer_groups
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: transfer_materials_to_reaction_station参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-workflow_sample_locations:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
workflow_id: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
workflow_id:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- workflow_id
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: workflow_sample_locations参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
batch_create_90_10_vial_feeding_tasks:
|
batch_create_90_10_vial_feeding_tasks:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ camera:
|
|||||||
device_id:
|
device_id:
|
||||||
default: video_publisher
|
default: video_publisher
|
||||||
type: string
|
type: string
|
||||||
|
device_uuid:
|
||||||
|
default: ''
|
||||||
|
type: string
|
||||||
period:
|
period:
|
||||||
default: 0.1
|
default: 0.1
|
||||||
type: number
|
type: number
|
||||||
|
|||||||
@@ -4497,9 +4497,6 @@ liquid_handler:
|
|||||||
simulator:
|
simulator:
|
||||||
default: false
|
default: false
|
||||||
type: boolean
|
type: boolean
|
||||||
total_height:
|
|
||||||
default: 310
|
|
||||||
type: number
|
|
||||||
required:
|
required:
|
||||||
- backend
|
- backend
|
||||||
- deck
|
- deck
|
||||||
|
|||||||
@@ -4,6 +4,215 @@ reaction_station.bioyond:
|
|||||||
- reaction_station_bioyond
|
- reaction_station_bioyond
|
||||||
class:
|
class:
|
||||||
action_value_mappings:
|
action_value_mappings:
|
||||||
|
auto-create_order:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
json_str: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
json_str:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- json_str
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: create_order参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-hard_delete_merged_workflows:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
workflow_ids: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
workflow_ids:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- workflow_ids
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: hard_delete_merged_workflows参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-merge_workflow_with_parameters:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
json_str: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
json_str:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- json_str
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: merge_workflow_with_parameters参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-process_temperature_cutoff_report:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
report_request: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
report_request:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- report_request
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: process_temperature_cutoff_report参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-process_web_workflows:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
web_workflow_json: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
web_workflow_json:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- web_workflow_json
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: process_web_workflows参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-skip_titration_steps:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
preintake_id: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
preintake_id:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- preintake_id
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: skip_titration_steps参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-wait_for_multiple_orders_and_get_reports:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
batch_create_result: null
|
||||||
|
check_interval: 10
|
||||||
|
timeout: 7200
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
batch_create_result:
|
||||||
|
type: string
|
||||||
|
check_interval:
|
||||||
|
default: 10
|
||||||
|
type: integer
|
||||||
|
timeout:
|
||||||
|
default: 7200
|
||||||
|
type: integer
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: wait_for_multiple_orders_and_get_reports参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-workflow_step_query:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
workflow_id: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
workflow_id:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- workflow_id
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: workflow_step_query参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
drip_back:
|
drip_back:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
@@ -524,19 +733,7 @@ reaction_station.bioyond:
|
|||||||
module: unilabos.devices.workstation.bioyond_studio.reaction_station:BioyondReactionStation
|
module: unilabos.devices.workstation.bioyond_studio.reaction_station:BioyondReactionStation
|
||||||
protocol_type: []
|
protocol_type: []
|
||||||
status_types:
|
status_types:
|
||||||
all_workflows: dict
|
workflow_sequence: String
|
||||||
average_viscosity: float
|
|
||||||
bioyond_status: dict
|
|
||||||
force: float
|
|
||||||
in_temperature: float
|
|
||||||
out_temperature: float
|
|
||||||
pt100_temperature: float
|
|
||||||
sensor_average_temperature: float
|
|
||||||
setting_temperature: float
|
|
||||||
speed: float
|
|
||||||
target_temperature: float
|
|
||||||
viscosity: float
|
|
||||||
workstation_status: dict
|
|
||||||
type: python
|
type: python
|
||||||
config_info: []
|
config_info: []
|
||||||
description: Bioyond反应站
|
description: Bioyond反应站
|
||||||
@@ -548,21 +745,19 @@ reaction_station.bioyond:
|
|||||||
config:
|
config:
|
||||||
type: object
|
type: object
|
||||||
deck:
|
deck:
|
||||||
type: object
|
type: string
|
||||||
|
protocol_type:
|
||||||
|
type: string
|
||||||
required: []
|
required: []
|
||||||
type: object
|
type: object
|
||||||
data:
|
data:
|
||||||
properties:
|
properties:
|
||||||
all_workflows:
|
workflow_sequence:
|
||||||
type: object
|
items:
|
||||||
bioyond_status:
|
type: string
|
||||||
type: object
|
type: array
|
||||||
workstation_status:
|
|
||||||
type: object
|
|
||||||
required:
|
required:
|
||||||
- bioyond_status
|
- workflow_sequence
|
||||||
- all_workflows
|
|
||||||
- workstation_status
|
|
||||||
type: object
|
type: object
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
reaction_station.reactor:
|
reaction_station.reactor:
|
||||||
@@ -570,19 +765,34 @@ reaction_station.reactor:
|
|||||||
- reactor
|
- reactor
|
||||||
- reaction_station_bioyond
|
- reaction_station_bioyond
|
||||||
class:
|
class:
|
||||||
action_value_mappings: {}
|
action_value_mappings:
|
||||||
|
auto-update_metrics:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
payload: null
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
payload:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- payload
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: update_metrics参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
module: unilabos.devices.workstation.bioyond_studio.reaction_station:BioyondReactor
|
module: unilabos.devices.workstation.bioyond_studio.reaction_station:BioyondReactor
|
||||||
status_types:
|
status_types: {}
|
||||||
average_viscosity: float
|
|
||||||
force: float
|
|
||||||
in_temperature: float
|
|
||||||
out_temperature: float
|
|
||||||
pt100_temperature: float
|
|
||||||
sensor_average_temperature: float
|
|
||||||
setting_temperature: float
|
|
||||||
speed: float
|
|
||||||
target_temperature: float
|
|
||||||
viscosity: float
|
|
||||||
type: python
|
type: python
|
||||||
config_info: []
|
config_info: []
|
||||||
description: 反应站子设备-反应器
|
description: 反应站子设备-反应器
|
||||||
@@ -593,30 +803,14 @@ reaction_station.reactor:
|
|||||||
properties:
|
properties:
|
||||||
config:
|
config:
|
||||||
type: object
|
type: object
|
||||||
|
deck:
|
||||||
|
type: string
|
||||||
|
protocol_type:
|
||||||
|
type: string
|
||||||
required: []
|
required: []
|
||||||
type: object
|
type: object
|
||||||
data:
|
data:
|
||||||
properties:
|
properties: {}
|
||||||
average_viscosity:
|
|
||||||
type: number
|
|
||||||
force:
|
|
||||||
type: number
|
|
||||||
in_temperature:
|
|
||||||
type: number
|
|
||||||
out_temperature:
|
|
||||||
type: number
|
|
||||||
pt100_temperature:
|
|
||||||
type: number
|
|
||||||
sensor_average_temperature:
|
|
||||||
type: number
|
|
||||||
setting_temperature:
|
|
||||||
type: number
|
|
||||||
speed:
|
|
||||||
type: number
|
|
||||||
target_temperature:
|
|
||||||
type: number
|
|
||||||
viscosity:
|
|
||||||
type: number
|
|
||||||
required: []
|
required: []
|
||||||
type: object
|
type: object
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|||||||
@@ -453,7 +453,7 @@ class Registry:
|
|||||||
return status_schema
|
return status_schema
|
||||||
|
|
||||||
def _generate_unilab_json_command_schema(
|
def _generate_unilab_json_command_schema(
|
||||||
self, method_args: List[Dict[str, Any]], method_name: str
|
self, method_args: List[Dict[str, Any]], method_name: str, return_annotation: Any = None
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
根据UniLabJsonCommand方法信息生成JSON Schema,暂不支持嵌套类型
|
根据UniLabJsonCommand方法信息生成JSON Schema,暂不支持嵌套类型
|
||||||
@@ -461,6 +461,7 @@ class Registry:
|
|||||||
Args:
|
Args:
|
||||||
method_args: 方法信息字典,包含args等
|
method_args: 方法信息字典,包含args等
|
||||||
method_name: 方法名称
|
method_name: 方法名称
|
||||||
|
return_annotation: 返回类型注解,用于生成result schema(仅支持TypedDict)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
JSON Schema格式的参数schema
|
JSON Schema格式的参数schema
|
||||||
@@ -489,14 +490,68 @@ class Registry:
|
|||||||
if param_required:
|
if param_required:
|
||||||
schema["required"].append(param_name)
|
schema["required"].append(param_name)
|
||||||
|
|
||||||
|
# 生成result schema(仅当return_annotation是TypedDict时)
|
||||||
|
result_schema = {}
|
||||||
|
if return_annotation is not None and self._is_typed_dict(return_annotation):
|
||||||
|
result_schema = self._generate_typed_dict_result_schema(return_annotation)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"title": f"{method_name}参数",
|
"title": f"{method_name}参数",
|
||||||
"description": f"",
|
"description": f"",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {"goal": schema, "feedback": {}, "result": {}},
|
"properties": {"goal": schema, "feedback": {}, "result": result_schema},
|
||||||
"required": ["goal"],
|
"required": ["goal"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _is_typed_dict(self, annotation: Any) -> bool:
|
||||||
|
"""
|
||||||
|
检查类型注解是否是TypedDict
|
||||||
|
|
||||||
|
Args:
|
||||||
|
annotation: 类型注解对象
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
是否为TypedDict
|
||||||
|
"""
|
||||||
|
if annotation is None or annotation == inspect.Parameter.empty:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 使用 typing_extensions.is_typeddict 进行检查(Python < 3.12 兼容)
|
||||||
|
try:
|
||||||
|
from typing_extensions import is_typeddict
|
||||||
|
|
||||||
|
return is_typeddict(annotation)
|
||||||
|
except ImportError:
|
||||||
|
# 回退方案:检查 TypedDict 特有的属性
|
||||||
|
if isinstance(annotation, type):
|
||||||
|
return hasattr(annotation, "__required_keys__") and hasattr(annotation, "__optional_keys__")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _generate_typed_dict_result_schema(self, return_annotation: Any) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
根据TypedDict类型生成result的JSON Schema
|
||||||
|
|
||||||
|
Args:
|
||||||
|
return_annotation: TypedDict类型注解
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON Schema格式的result schema
|
||||||
|
"""
|
||||||
|
if not self._is_typed_dict(return_annotation):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
from msgcenterpy.instances.typed_dict_instance import TypedDictMessageInstance
|
||||||
|
|
||||||
|
result_schema = TypedDictMessageInstance.get_json_schema_from_typed_dict(return_annotation)
|
||||||
|
return result_schema
|
||||||
|
except ImportError:
|
||||||
|
logger.warning("[UniLab Registry] msgcenterpy未安装,无法生成TypedDict的result schema")
|
||||||
|
return {}
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"[UniLab Registry] 生成TypedDict result schema失败: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
def _add_builtin_actions(self, device_config: Dict[str, Any], device_id: str):
|
def _add_builtin_actions(self, device_config: Dict[str, Any], device_id: str):
|
||||||
"""
|
"""
|
||||||
为设备配置添加内置的执行驱动命令动作
|
为设备配置添加内置的执行驱动命令动作
|
||||||
@@ -577,9 +632,15 @@ class Registry:
|
|||||||
if "init_param_schema" not in device_config:
|
if "init_param_schema" not in device_config:
|
||||||
device_config["init_param_schema"] = {}
|
device_config["init_param_schema"] = {}
|
||||||
if "class" in device_config:
|
if "class" in device_config:
|
||||||
if "status_types" not in device_config["class"] or device_config["class"]["status_types"] is None:
|
if (
|
||||||
|
"status_types" not in device_config["class"]
|
||||||
|
or device_config["class"]["status_types"] is None
|
||||||
|
):
|
||||||
device_config["class"]["status_types"] = {}
|
device_config["class"]["status_types"] = {}
|
||||||
if "action_value_mappings" not in device_config["class"] or device_config["class"]["action_value_mappings"] is None:
|
if (
|
||||||
|
"action_value_mappings" not in device_config["class"]
|
||||||
|
or device_config["class"]["action_value_mappings"] is None
|
||||||
|
):
|
||||||
device_config["class"]["action_value_mappings"] = {}
|
device_config["class"]["action_value_mappings"] = {}
|
||||||
enhanced_info = {}
|
enhanced_info = {}
|
||||||
if complete_registry:
|
if complete_registry:
|
||||||
@@ -631,7 +692,9 @@ class Registry:
|
|||||||
"goal": {},
|
"goal": {},
|
||||||
"feedback": {},
|
"feedback": {},
|
||||||
"result": {},
|
"result": {},
|
||||||
"schema": self._generate_unilab_json_command_schema(v["args"], k),
|
"schema": self._generate_unilab_json_command_schema(
|
||||||
|
v["args"], k, v.get("return_annotation")
|
||||||
|
),
|
||||||
"goal_default": {i["name"]: i["default"] for i in v["args"]},
|
"goal_default": {i["name"]: i["default"] for i in v["args"]},
|
||||||
"handles": [],
|
"handles": [],
|
||||||
"placeholder_keys": {
|
"placeholder_keys": {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import threading
|
|||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import uuid
|
import uuid
|
||||||
from typing import TYPE_CHECKING, Optional, Dict, Any, List, ClassVar, Set, Union
|
from typing import TYPE_CHECKING, Optional, Dict, Any, List, ClassVar, Set, TypedDict, Union
|
||||||
|
|
||||||
from action_msgs.msg import GoalStatus
|
from action_msgs.msg import GoalStatus
|
||||||
from geometry_msgs.msg import Point
|
from geometry_msgs.msg import Point
|
||||||
@@ -38,6 +38,7 @@ from unilabos.ros.msgs.message_converter import (
|
|||||||
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode, ROS2DeviceNode, DeviceNodeResourceTracker
|
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode, ROS2DeviceNode, DeviceNodeResourceTracker
|
||||||
from unilabos.ros.nodes.presets.controller_node import ControllerNode
|
from unilabos.ros.nodes.presets.controller_node import ControllerNode
|
||||||
from unilabos.ros.nodes.resource_tracker import (
|
from unilabos.ros.nodes.resource_tracker import (
|
||||||
|
ResourceDict,
|
||||||
ResourceDictInstance,
|
ResourceDictInstance,
|
||||||
ResourceTreeSet,
|
ResourceTreeSet,
|
||||||
ResourceTreeInstance,
|
ResourceTreeInstance,
|
||||||
@@ -48,7 +49,7 @@ from unilabos.utils.type_check import serialize_result_info
|
|||||||
from unilabos.registry.placeholder_type import ResourceSlot, DeviceSlot
|
from unilabos.registry.placeholder_type import ResourceSlot, DeviceSlot
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from unilabos.app.ws_client import QueueItem, WSResourceChatData
|
from unilabos.app.ws_client import QueueItem
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -56,6 +57,11 @@ class DeviceActionStatus:
|
|||||||
job_ids: Dict[str, float] = field(default_factory=dict)
|
job_ids: Dict[str, float] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
class TestResourceReturn(TypedDict):
|
||||||
|
resources: List[List[ResourceDict]]
|
||||||
|
devices: List[DeviceSlot]
|
||||||
|
|
||||||
|
|
||||||
class HostNode(BaseROS2DeviceNode):
|
class HostNode(BaseROS2DeviceNode):
|
||||||
"""
|
"""
|
||||||
主机节点类,负责管理设备、资源和控制器
|
主机节点类,负责管理设备、资源和控制器
|
||||||
@@ -1347,7 +1353,7 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
|
|
||||||
def test_resource(
|
def test_resource(
|
||||||
self, resource: ResourceSlot, resources: List[ResourceSlot], device: DeviceSlot, devices: List[DeviceSlot]
|
self, resource: ResourceSlot, resources: List[ResourceSlot], device: DeviceSlot, devices: List[DeviceSlot]
|
||||||
):
|
) -> TestResourceReturn:
|
||||||
return {
|
return {
|
||||||
"resources": ResourceTreeSet.from_plr_resources([resource, *resources]).dump(),
|
"resources": ResourceTreeSet.from_plr_resources([resource, *resources]).dump(),
|
||||||
"devices": [device, *devices],
|
"devices": [device, *devices],
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import importlib
|
import importlib
|
||||||
|
import locale
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -22,13 +23,33 @@ class EnvironmentChecker:
|
|||||||
"websockets": "websockets",
|
"websockets": "websockets",
|
||||||
"msgcenterpy": "msgcenterpy",
|
"msgcenterpy": "msgcenterpy",
|
||||||
"opentrons_shared_data": "opentrons_shared_data",
|
"opentrons_shared_data": "opentrons_shared_data",
|
||||||
|
"typing_extensions": "typing_extensions",
|
||||||
}
|
}
|
||||||
|
|
||||||
# 特殊安装包(需要特殊处理的包)
|
# 特殊安装包(需要特殊处理的包)
|
||||||
self.special_packages = {"pylabrobot": "git+https://github.com/Xuwznln/pylabrobot.git"}
|
self.special_packages = {"pylabrobot": "git+https://github.com/Xuwznln/pylabrobot.git"}
|
||||||
|
|
||||||
|
# 包版本要求(包名: 最低版本)
|
||||||
|
self.version_requirements = {
|
||||||
|
"msgcenterpy": "0.1.5", # msgcenterpy 最低版本要求
|
||||||
|
}
|
||||||
|
|
||||||
self.missing_packages = []
|
self.missing_packages = []
|
||||||
self.failed_installs = []
|
self.failed_installs = []
|
||||||
|
self.packages_need_upgrade = []
|
||||||
|
|
||||||
|
# 检测系统语言
|
||||||
|
self.is_chinese = self._is_chinese_locale()
|
||||||
|
|
||||||
|
def _is_chinese_locale(self) -> bool:
|
||||||
|
"""检测系统是否为中文环境"""
|
||||||
|
try:
|
||||||
|
lang = locale.getdefaultlocale()[0]
|
||||||
|
if lang and ("zh" in lang.lower() or "chinese" in lang.lower()):
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
def check_package_installed(self, package_name: str) -> bool:
|
def check_package_installed(self, package_name: str) -> bool:
|
||||||
"""检查包是否已安装"""
|
"""检查包是否已安装"""
|
||||||
@@ -38,31 +59,74 @@ class EnvironmentChecker:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def install_package(self, package_name: str, pip_name: str) -> bool:
|
def get_package_version(self, package_name: str) -> str | None:
|
||||||
|
"""获取已安装包的版本"""
|
||||||
|
try:
|
||||||
|
module = importlib.import_module(package_name)
|
||||||
|
return getattr(module, "__version__", None)
|
||||||
|
except (ImportError, AttributeError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def compare_version(self, current: str, required: str) -> bool:
|
||||||
|
"""
|
||||||
|
比较版本号
|
||||||
|
Returns:
|
||||||
|
True: current >= required
|
||||||
|
False: current < required
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
current_parts = [int(x) for x in current.split(".")]
|
||||||
|
required_parts = [int(x) for x in required.split(".")]
|
||||||
|
|
||||||
|
# 补齐长度
|
||||||
|
max_len = max(len(current_parts), len(required_parts))
|
||||||
|
current_parts.extend([0] * (max_len - len(current_parts)))
|
||||||
|
required_parts.extend([0] * (max_len - len(required_parts)))
|
||||||
|
|
||||||
|
return current_parts >= required_parts
|
||||||
|
except Exception:
|
||||||
|
return True # 如果无法比较,假设版本满足要求
|
||||||
|
|
||||||
|
def install_package(self, package_name: str, pip_name: str, upgrade: bool = False) -> bool:
|
||||||
"""安装包"""
|
"""安装包"""
|
||||||
try:
|
try:
|
||||||
print_status(f"正在安装 {package_name} ({pip_name})...", "info")
|
action = "升级" if upgrade else "安装"
|
||||||
|
print_status(f"正在{action} {package_name} ({pip_name})...", "info")
|
||||||
|
|
||||||
# 构建安装命令
|
# 构建安装命令
|
||||||
cmd = [sys.executable, "-m", "pip", "install", pip_name]
|
cmd = [sys.executable, "-m", "pip", "install"]
|
||||||
|
|
||||||
|
# 如果是升级操作,添加 --upgrade 参数
|
||||||
|
if upgrade:
|
||||||
|
cmd.append("--upgrade")
|
||||||
|
|
||||||
|
cmd.append(pip_name)
|
||||||
|
|
||||||
|
# 如果是中文环境,使用清华镜像源
|
||||||
|
if self.is_chinese:
|
||||||
|
cmd.extend(["-i", "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"])
|
||||||
|
|
||||||
# 执行安装
|
# 执行安装
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300) # 5分钟超时
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300) # 5分钟超时
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
print_status(f"✓ {package_name} 安装成功", "success")
|
print_status(f"✓ {package_name} {action}成功", "success")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
print_status(f"× {package_name} 安装失败: {result.stderr}", "error")
|
print_status(f"× {package_name} {action}失败: {result.stderr}", "error")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
print_status(f"× {package_name} 安装超时", "error")
|
print_status(f"× {package_name} {action}超时", "error")
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print_status(f"× {package_name} 安装异常: {str(e)}", "error")
|
print_status(f"× {package_name} {action}异常: {str(e)}", "error")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def upgrade_package(self, package_name: str, pip_name: str) -> bool:
|
||||||
|
"""升级包"""
|
||||||
|
return self.install_package(package_name, pip_name, upgrade=True)
|
||||||
|
|
||||||
def check_all_packages(self) -> bool:
|
def check_all_packages(self) -> bool:
|
||||||
"""检查所有必需的包"""
|
"""检查所有必需的包"""
|
||||||
print_status("开始检查环境依赖...", "info")
|
print_status("开始检查环境依赖...", "info")
|
||||||
@@ -71,30 +135,56 @@ class EnvironmentChecker:
|
|||||||
for import_name, pip_name in self.required_packages.items():
|
for import_name, pip_name in self.required_packages.items():
|
||||||
if not self.check_package_installed(import_name):
|
if not self.check_package_installed(import_name):
|
||||||
self.missing_packages.append((import_name, pip_name))
|
self.missing_packages.append((import_name, pip_name))
|
||||||
|
else:
|
||||||
|
# 检查版本要求
|
||||||
|
if import_name in self.version_requirements:
|
||||||
|
current_version = self.get_package_version(import_name)
|
||||||
|
required_version = self.version_requirements[import_name]
|
||||||
|
|
||||||
|
if current_version:
|
||||||
|
if not self.compare_version(current_version, required_version):
|
||||||
|
print_status(
|
||||||
|
f"{import_name} 版本过低 (当前: {current_version}, 需要: >={required_version})",
|
||||||
|
"warning",
|
||||||
|
)
|
||||||
|
self.packages_need_upgrade.append((import_name, pip_name))
|
||||||
|
|
||||||
# 检查特殊包
|
# 检查特殊包
|
||||||
for package_name, install_url in self.special_packages.items():
|
for package_name, install_url in self.special_packages.items():
|
||||||
if not self.check_package_installed(package_name):
|
if not self.check_package_installed(package_name):
|
||||||
self.missing_packages.append((package_name, install_url))
|
self.missing_packages.append((package_name, install_url))
|
||||||
|
|
||||||
if not self.missing_packages:
|
all_ok = not self.missing_packages and not self.packages_need_upgrade
|
||||||
|
|
||||||
|
if all_ok:
|
||||||
print_status("✓ 所有依赖包检查完成,环境正常", "success")
|
print_status("✓ 所有依赖包检查完成,环境正常", "success")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if self.missing_packages:
|
||||||
print_status(f"发现 {len(self.missing_packages)} 个缺失的包", "warning")
|
print_status(f"发现 {len(self.missing_packages)} 个缺失的包", "warning")
|
||||||
|
if self.packages_need_upgrade:
|
||||||
|
print_status(f"发现 {len(self.packages_need_upgrade)} 个需要升级的包", "warning")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def install_missing_packages(self, auto_install: bool = True) -> bool:
|
def install_missing_packages(self, auto_install: bool = True) -> bool:
|
||||||
"""安装缺失的包"""
|
"""安装缺失的包"""
|
||||||
if not self.missing_packages:
|
if not self.missing_packages and not self.packages_need_upgrade:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not auto_install:
|
if not auto_install:
|
||||||
|
if self.missing_packages:
|
||||||
print_status("缺失以下包:", "warning")
|
print_status("缺失以下包:", "warning")
|
||||||
for import_name, pip_name in self.missing_packages:
|
for import_name, pip_name in self.missing_packages:
|
||||||
print_status(f" - {import_name} (pip install {pip_name})", "warning")
|
print_status(f" - {import_name} (pip install {pip_name})", "warning")
|
||||||
|
if self.packages_need_upgrade:
|
||||||
|
print_status("需要升级以下包:", "warning")
|
||||||
|
for import_name, pip_name in self.packages_need_upgrade:
|
||||||
|
print_status(f" - {import_name} (pip install --upgrade {pip_name})", "warning")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# 安装缺失的包
|
||||||
|
if self.missing_packages:
|
||||||
print_status(f"开始自动安装 {len(self.missing_packages)} 个缺失的包...", "info")
|
print_status(f"开始自动安装 {len(self.missing_packages)} 个缺失的包...", "info")
|
||||||
|
|
||||||
success_count = 0
|
success_count = 0
|
||||||
@@ -104,27 +194,57 @@ class EnvironmentChecker:
|
|||||||
else:
|
else:
|
||||||
self.failed_installs.append((import_name, pip_name))
|
self.failed_installs.append((import_name, pip_name))
|
||||||
|
|
||||||
|
print_status(f"✓ 成功安装 {success_count}/{len(self.missing_packages)} 个包", "success")
|
||||||
|
|
||||||
|
# 升级需要更新的包
|
||||||
|
if self.packages_need_upgrade:
|
||||||
|
print_status(f"开始自动升级 {len(self.packages_need_upgrade)} 个包...", "info")
|
||||||
|
|
||||||
|
upgrade_success_count = 0
|
||||||
|
for import_name, pip_name in self.packages_need_upgrade:
|
||||||
|
if self.upgrade_package(import_name, pip_name):
|
||||||
|
upgrade_success_count += 1
|
||||||
|
else:
|
||||||
|
self.failed_installs.append((import_name, pip_name))
|
||||||
|
|
||||||
|
print_status(f"✓ 成功升级 {upgrade_success_count}/{len(self.packages_need_upgrade)} 个包", "success")
|
||||||
|
|
||||||
if self.failed_installs:
|
if self.failed_installs:
|
||||||
print_status(f"有 {len(self.failed_installs)} 个包安装失败:", "error")
|
print_status(f"有 {len(self.failed_installs)} 个包操作失败:", "error")
|
||||||
for import_name, pip_name in self.failed_installs:
|
for import_name, pip_name in self.failed_installs:
|
||||||
print_status(f" - {import_name} (pip install {pip_name})", "error")
|
print_status(f" - {import_name} ({pip_name})", "error")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print_status(f"✓ 成功安装 {success_count} 个包", "success")
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def verify_installation(self) -> bool:
|
def verify_installation(self) -> bool:
|
||||||
"""验证安装结果"""
|
"""验证安装结果"""
|
||||||
if not self.missing_packages:
|
if not self.missing_packages and not self.packages_need_upgrade:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
print_status("验证安装结果...", "info")
|
print_status("验证安装结果...", "info")
|
||||||
|
|
||||||
failed_verification = []
|
failed_verification = []
|
||||||
|
|
||||||
|
# 验证新安装的包
|
||||||
for import_name, pip_name in self.missing_packages:
|
for import_name, pip_name in self.missing_packages:
|
||||||
if not self.check_package_installed(import_name):
|
if not self.check_package_installed(import_name):
|
||||||
failed_verification.append((import_name, pip_name))
|
failed_verification.append((import_name, pip_name))
|
||||||
|
|
||||||
|
# 验证升级的包
|
||||||
|
for import_name, pip_name in self.packages_need_upgrade:
|
||||||
|
if not self.check_package_installed(import_name):
|
||||||
|
failed_verification.append((import_name, pip_name))
|
||||||
|
elif import_name in self.version_requirements:
|
||||||
|
current_version = self.get_package_version(import_name)
|
||||||
|
required_version = self.version_requirements[import_name]
|
||||||
|
if current_version and not self.compare_version(current_version, required_version):
|
||||||
|
failed_verification.append((import_name, pip_name))
|
||||||
|
print_status(
|
||||||
|
f" {import_name} 版本仍然过低 (当前: {current_version}, 需要: >={required_version})",
|
||||||
|
"error",
|
||||||
|
)
|
||||||
|
|
||||||
if failed_verification:
|
if failed_verification:
|
||||||
print_status(f"有 {len(failed_verification)} 个包验证失败:", "error")
|
print_status(f"有 {len(failed_verification)} 个包验证失败:", "error")
|
||||||
for import_name, pip_name in failed_verification:
|
for import_name, pip_name in failed_verification:
|
||||||
|
|||||||
@@ -239,8 +239,12 @@ class ImportManager:
|
|||||||
cls = get_class(class_path)
|
cls = get_class(class_path)
|
||||||
class_name = cls.__name__
|
class_name = cls.__name__
|
||||||
|
|
||||||
result = {"class_name": class_name, "init_params": self._analyze_method_signature(cls.__init__)["args"],
|
result = {
|
||||||
"status_methods": {}, "action_methods": {}}
|
"class_name": class_name,
|
||||||
|
"init_params": self._analyze_method_signature(cls.__init__)["args"],
|
||||||
|
"status_methods": {},
|
||||||
|
"action_methods": {},
|
||||||
|
}
|
||||||
# 分析类的所有成员
|
# 分析类的所有成员
|
||||||
for name, method in cls.__dict__.items():
|
for name, method in cls.__dict__.items():
|
||||||
if name.startswith("_"):
|
if name.startswith("_"):
|
||||||
@@ -374,6 +378,7 @@ class ImportManager:
|
|||||||
"name": method.__name__,
|
"name": method.__name__,
|
||||||
"args": args,
|
"args": args,
|
||||||
"return_type": self._get_type_string(signature.return_annotation),
|
"return_type": self._get_type_string(signature.return_annotation),
|
||||||
|
"return_annotation": signature.return_annotation, # 保留原始类型注解,用于TypedDict等特殊处理
|
||||||
"is_async": inspect.iscoroutinefunction(method),
|
"is_async": inspect.iscoroutinefunction(method),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user