Workstation templates: Resources and its CRUD, and workstation tasks (#95)

* coin_cell_station draft

* refactor: rename "station_resource" to "deck"

* add standardized BIOYOND resources: bottle_carrier, bottle

* refactor and add BIOYOND resources tests

* add BIOYOND deck assignment and pass all tests

* fix: update resource with correct structure; remove deprecated liquid_handler set_group action

* feat: 将新威电池测试系统驱动与配置文件并入 workstation_dev_YB2 (#92)

* feat: 新威电池测试系统驱动与注册文件

* feat: bring neware driver & battery.json into workstation_dev_YB2

* add bioyond studio draft

* bioyond station with communication init and resource sync

* fix bioyond station and registry

* create/update resources with POST/PUT for big amount/ small amount data

* refactor: add itemized_carrier instead of carrier consists of ResourceHolder

* create warehouse by factory func

* update bioyond launch json

* add child_size for itemized_carrier

* fix bioyond resource io

---------

Co-authored-by: h840473807 <47357934+h840473807@users.noreply.github.com>
Co-authored-by: Xie Qiming <97236197+Andy6M@users.noreply.github.com>
This commit is contained in:
Junhan Chang
2025-09-30 17:23:13 +08:00
committed by GitHub
parent 709eb0d91c
commit cfc1ee6e79
29 changed files with 21729 additions and 110 deletions

View File

@@ -0,0 +1,198 @@
{
"data": [
{
"id": "3a1c67a9-aed7-b94d-9e24-bfdf10c8baa9",
"typeName": "烧杯",
"code": "0006-00160",
"barCode": "",
"name": "ODA",
"quantity": 120000.00000000000000000000000,
"lockQuantity": 695374.00000000000000000000000,
"unit": "微升",
"status": 1,
"isUse": false,
"locations": [
{
"id": "3a14aa17-0d49-11d7-a6e1-f236b3e5e5a3",
"whid": "3a14aa17-0d49-dce4-486e-4b5c85c8b366",
"whName": "堆栈1",
"code": "0001-0001",
"x": 1,
"y": 1,
"z": 1,
"quantity": 0
}
],
"detail": []
},
{
"id": "3a1c67a9-aed9-1ade-5fe1-cc04b24b171c",
"typeName": "烧杯",
"code": "0006-00161",
"barCode": "",
"name": "MPDA",
"quantity": 120000.00000000000000000000000,
"lockQuantity": 681618.00000000000000000000000,
"unit": "",
"status": 1,
"isUse": false,
"locations": [
{
"id": "3a14aa17-0d49-4bc5-8836-517b75473f5f",
"whid": "3a14aa17-0d49-dce4-486e-4b5c85c8b366",
"whName": "堆栈1",
"code": "0001-0002",
"x": 1,
"y": 2,
"z": 1,
"quantity": 0
}
],
"detail": []
},
{
"id": "3a1c67a9-aed9-2864-6783-2cee4e701ba6",
"typeName": "试剂瓶",
"code": "0004-00041",
"barCode": "",
"name": "NMP",
"quantity": 300000.00000000000000000000000,
"lockQuantity": 380000.00000000000000000000000,
"unit": "微升",
"status": 1,
"isUse": false,
"locations": [
{
"id": "3a14aa3b-9fab-adac-7b9c-e1ee446b51d5",
"whid": "3a14aa3b-9fab-9d8e-d1a7-828f01f51f0c",
"whName": "站内试剂存放堆栈",
"code": "0003-0001",
"x": 1,
"y": 1,
"z": 1,
"quantity": 0
}
],
"detail": []
},
{
"id": "3a1c67a9-aed9-32c7-5809-3ba1b8db1aa1",
"typeName": "试剂瓶",
"code": "0004-00042",
"barCode": "",
"name": "PGME",
"quantity": 300000.00000000000000000000000,
"lockQuantity": 337892.00000000000000000000000,
"unit": "",
"status": 1,
"isUse": false,
"locations": [
{
"id": "3a14aa3b-9fab-ca72-febc-b7c304476c78",
"whid": "3a14aa3b-9fab-9d8e-d1a7-828f01f51f0c",
"whName": "站内试剂存放堆栈",
"code": "0003-0002",
"x": 1,
"y": 2,
"z": 1,
"quantity": 0
}
],
"detail": []
},
{
"id": "3a1c68c8-0574-d748-725e-97a2e549f085",
"typeName": "样品板",
"code": "0001-00004",
"barCode": "",
"name": "0917",
"quantity": 1.0000000000000000000000000000,
"lockQuantity": 4.0000000000000000000000000000,
"unit": "块",
"status": 1,
"isUse": false,
"locations": [
{
"id": "3a14aa17-0d49-f49c-6b66-b27f185a3b32",
"whid": "3a14aa17-0d49-dce4-486e-4b5c85c8b366",
"whName": "堆栈1",
"code": "0001-0009",
"x": 2,
"y": 1,
"z": 1,
"quantity": 0
}
],
"detail": [
{
"id": "3a1c68c8-0574-69a1-9858-4637e0193451",
"detailMaterialId": "3a1c68c8-0574-3630-bd42-bbf3623c5208",
"code": null,
"name": "SIDA",
"quantity": "300000",
"lockQuantity": "4",
"unit": "微升",
"x": 1,
"y": 2,
"z": 1,
"associateId": null
},
{
"id": "3a1c68c8-0574-8d51-3191-a31f5be421e5",
"detailMaterialId": "3a1c68c8-0574-3b20-9ad7-90755f123d53",
"code": null,
"name": "BTDA-2",
"quantity": "300000",
"lockQuantity": "4",
"unit": "微升",
"x": 2,
"y": 2,
"z": 1,
"associateId": null
},
{
"id": "3a1c68c8-0574-da80-735b-53ae2197a360",
"detailMaterialId": "3a1c68c8-0574-f2e4-33b3-90d813567939",
"code": null,
"name": "BTDA-DD",
"quantity": "300000",
"lockQuantity": "28",
"unit": "微升",
"x": 1,
"y": 1,
"z": 1,
"associateId": null
},
{
"id": "3a1c68c8-0574-e717-1b1b-99891f875455",
"detailMaterialId": "3a1c68c8-0574-a0ef-e636-68cdc98960e2",
"code": null,
"name": "BTDA-3",
"quantity": "300000",
"lockQuantity": "4",
"unit": "微升",
"x": 2,
"y": 3,
"z": 1,
"associateId": null
},
{
"id": "3a1c68c8-0574-e9bd-6cca-5e261b4f89cb",
"detailMaterialId": "3a1c68c8-0574-9d11-5115-283e8e5510b1",
"code": null,
"name": "BTDA-1",
"quantity": "300000",
"lockQuantity": "4",
"unit": "微升",
"x": 2,
"y": 1,
"z": 1,
"associateId": null
}
]
}
],
"code": 1,
"message": "",
"timestamp": 1758560573511
}

View File

@@ -0,0 +1,48 @@
import pytest
from unilabos.resources.bioyond.bottle_carriers import BIOYOND_Electrolyte_6VialCarrier, BIOYOND_Electrolyte_1BottleCarrier
from unilabos.resources.bioyond.bottles import BIOYOND_PolymerStation_Solid_Vial, BIOYOND_PolymerStation_Solution_Beaker, BIOYOND_PolymerStation_Reagent_Bottle
def test_bottle_carrier() -> "BottleCarrier":
print("创建载架...")
# 创建6瓶载架
bottle_carrier = BIOYOND_Electrolyte_6VialCarrier("powder_carrier_01")
print(f"6瓶载架: {bottle_carrier.name}, 位置数: {len(bottle_carrier.sites)}")
# 创建1烧杯载架
beaker_carrier = BIOYOND_Electrolyte_1BottleCarrier("solution_carrier_01")
print(f"1烧杯载架: {beaker_carrier.name}, 位置数: {len(beaker_carrier.sites)}")
# 创建瓶子和烧杯
powder_bottle = BIOYOND_PolymerStation_Solid_Vial("powder_bottle_01")
solution_beaker = BIOYOND_PolymerStation_Solution_Beaker("solution_beaker_01")
reagent_bottle = BIOYOND_PolymerStation_Reagent_Bottle("reagent_bottle_01")
print(f"\n创建的物料:")
print(f"粉末瓶: {powder_bottle.name} - {powder_bottle.diameter}mm x {powder_bottle.height}mm, {powder_bottle.max_volume}μL")
print(f"溶液烧杯: {solution_beaker.name} - {solution_beaker.diameter}mm x {solution_beaker.height}mm, {solution_beaker.max_volume}μL")
print(f"试剂瓶: {reagent_bottle.name} - {reagent_bottle.diameter}mm x {reagent_bottle.height}mm, {reagent_bottle.max_volume}μL")
# 测试放置容器
print(f"\n测试放置容器...")
# 通过载架的索引操作来放置容器
# bottle_carrier[0] = powder_bottle # 放置粉末瓶到第一个位置
print(f"粉末瓶已放置到6瓶载架的位置 0")
# beaker_carrier[0] = solution_beaker # 放置烧杯到第一个位置
print(f"溶液烧杯已放置到1烧杯载架的位置 0")
# 验证放置结果
print(f"\n验证放置结果:")
bottle_at_0 = bottle_carrier[0].resource
beaker_at_0 = beaker_carrier[0].resource
if bottle_at_0:
print(f"位置 0 的瓶子: {bottle_at_0.name}")
if beaker_at_0:
print(f"位置 0 的烧杯: {beaker_at_0.name}")
print("\n载架设置完成!")

View File

@@ -0,0 +1,35 @@
import pytest
import json
import os
from unilabos.resources.graphio import resource_bioyond_to_plr
from unilabos.registry.registry import lab_registry
from unilabos.resources.bioyond.decks import BIOYOND_PolymerReactionStation_Deck
lab_registry.setup()
type_mapping = {
"烧杯": "BIOYOND_PolymerStation_1FlaskCarrier",
"试剂瓶": "BIOYOND_PolymerStation_1BottleCarrier",
"样品板": "BIOYOND_PolymerStation_6VialCarrier",
}
@pytest.fixture
def bioyond_materials() -> list[dict]:
print("加载 BioYond 物料数据...")
print(os.getcwd())
with open("bioyond_materials.json", "r", encoding="utf-8") as f:
data = json.load(f)["data"]
print(f"加载了 {len(data)} 条物料数据")
return data
def test_bioyond_to_plr(bioyond_materials) -> list[dict]:
deck = BIOYOND_PolymerReactionStation_Deck("test_deck")
print("将 BioYond 物料数据转换为 PLR 格式...")
output = resource_bioyond_to_plr(bioyond_materials, type_mapping=type_mapping, deck=deck)
print(deck.summary())
print([resource.serialize() for resource in output])
print([resource.serialize_all_state() for resource in output])