mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-17 21:11:12 +00:00
Merge remote-tracking branch 'upstream/workstation_dev_YB3' into workstation_dev_YB3
This commit is contained in:
@@ -24,13 +24,13 @@
|
|||||||
"Drip_back": "3a162cf9-6aac-565a-ddd7-682ba1796a4a"
|
"Drip_back": "3a162cf9-6aac-565a-ddd7-682ba1796a4a"
|
||||||
},
|
},
|
||||||
"material_type_mappings": {
|
"material_type_mappings": {
|
||||||
"烧杯": ["BIOYOND_PolymerStation_1FlaskCarrier", "3a14196b-24f2-ca49-9081-0cab8021bf1a"],
|
"烧杯": ["YB_1FlaskCarrier", "3a14196b-24f2-ca49-9081-0cab8021bf1a"],
|
||||||
"试剂瓶": ["BIOYOND_PolymerStation_1BottleCarrier", ""],
|
"试剂瓶": ["YB_1BottleCarrier", ""],
|
||||||
"样品板": ["BIOYOND_PolymerStation_6StockCarrier", "3a14196e-b7a0-a5da-1931-35f3000281e9"],
|
"样品板": ["YB_6StockCarrier", "3a14196e-b7a0-a5da-1931-35f3000281e9"],
|
||||||
"分装板": ["BIOYOND_PolymerStation_6VialCarrier", "3a14196e-5dfe-6e21-0c79-fe2036d052c4"],
|
"分装板": ["YB_6VialCarrier", "3a14196e-5dfe-6e21-0c79-fe2036d052c4"],
|
||||||
"样品瓶": ["BIOYOND_PolymerStation_Solid_Stock", "3a14196a-cf7d-8aea-48d8-b9662c7dba94"],
|
"样品瓶": ["YB_Solid_Stock", "3a14196a-cf7d-8aea-48d8-b9662c7dba94"],
|
||||||
"90%分装小瓶": ["BIOYOND_PolymerStation_Solid_Vial", "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea"],
|
"90%分装小瓶": ["YB_Solid_Vial", "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea"],
|
||||||
"10%分装小瓶": ["BIOYOND_PolymerStation_Liquid_Vial", "3a14196c-76be-2279-4e22-7310d69aed68"]
|
"10%分装小瓶": ["YB_Liquid_Vial", "3a14196c-76be-2279-4e22-7310d69aed68"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"deck": {
|
"deck": {
|
||||||
|
|||||||
@@ -24,9 +24,9 @@
|
|||||||
"Drip_back": "3a162cf9-6aac-565a-ddd7-682ba1796a4a"
|
"Drip_back": "3a162cf9-6aac-565a-ddd7-682ba1796a4a"
|
||||||
},
|
},
|
||||||
"material_type_mappings": {
|
"material_type_mappings": {
|
||||||
"烧杯": "BIOYOND_PolymerStation_1FlaskCarrier",
|
"烧杯": "YB_1FlaskCarrier",
|
||||||
"试剂瓶": "BIOYOND_PolymerStation_1BottleCarrier",
|
"试剂瓶": "YB_1BottleCarrier",
|
||||||
"样品板": "BIOYOND_PolymerStation_6VialCarrier"
|
"样品板": "YB_6VialCarrier"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"deck": {
|
"deck": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from unilabos.resources.bioyond.bottle_carriers import BIOYOND_Electrolyte_6VialCarrier, BIOYOND_Electrolyte_1BottleCarrier
|
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
|
from unilabos.resources.bioyond.bottles import YB_Solid_Vial, YB_Solution_Beaker, YB_Reagent_Bottle
|
||||||
|
|
||||||
|
|
||||||
def test_bottle_carrier() -> "BottleCarrier":
|
def test_bottle_carrier() -> "BottleCarrier":
|
||||||
@@ -16,9 +16,9 @@ def test_bottle_carrier() -> "BottleCarrier":
|
|||||||
print(f"1烧杯载架: {beaker_carrier.name}, 位置数: {len(beaker_carrier.sites)}")
|
print(f"1烧杯载架: {beaker_carrier.name}, 位置数: {len(beaker_carrier.sites)}")
|
||||||
|
|
||||||
# 创建瓶子和烧杯
|
# 创建瓶子和烧杯
|
||||||
powder_bottle = BIOYOND_PolymerStation_Solid_Vial("powder_bottle_01")
|
powder_bottle = YB_Solid_Vial("powder_bottle_01")
|
||||||
solution_beaker = BIOYOND_PolymerStation_Solution_Beaker("solution_beaker_01")
|
solution_beaker = YB_Solution_Beaker("solution_beaker_01")
|
||||||
reagent_bottle = BIOYOND_PolymerStation_Reagent_Bottle("reagent_bottle_01")
|
reagent_bottle = YB_Reagent_Bottle("reagent_bottle_01")
|
||||||
|
|
||||||
print(f"\n创建的物料:")
|
print(f"\n创建的物料:")
|
||||||
print(f"粉末瓶: {powder_bottle.name} - {powder_bottle.diameter}mm x {powder_bottle.height}mm, {powder_bottle.max_volume}μL")
|
print(f"粉末瓶: {powder_bottle.name} - {powder_bottle.diameter}mm x {powder_bottle.height}mm, {powder_bottle.max_volume}μL")
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ lab_registry.setup()
|
|||||||
|
|
||||||
|
|
||||||
type_mapping = {
|
type_mapping = {
|
||||||
"烧杯": ("BIOYOND_PolymerStation_1FlaskCarrier", "3a14196b-24f2-ca49-9081-0cab8021bf1a"),
|
"烧杯": ("YB_1FlaskCarrier", "3a14196b-24f2-ca49-9081-0cab8021bf1a"),
|
||||||
"试剂瓶": ("BIOYOND_PolymerStation_1BottleCarrier", ""),
|
"试剂瓶": ("YB_1BottleCarrier", ""),
|
||||||
"样品板": ("BIOYOND_PolymerStation_6StockCarrier", "3a14196e-b7a0-a5da-1931-35f3000281e9"),
|
"样品板": ("YB_6StockCarrier", "3a14196e-b7a0-a5da-1931-35f3000281e9"),
|
||||||
"分装板": ("BIOYOND_PolymerStation_6VialCarrier", "3a14196e-5dfe-6e21-0c79-fe2036d052c4"),
|
"分装板": ("YB_6VialCarrier", "3a14196e-5dfe-6e21-0c79-fe2036d052c4"),
|
||||||
"样品瓶": ("BIOYOND_PolymerStation_Solid_Stock", "3a14196a-cf7d-8aea-48d8-b9662c7dba94"),
|
"样品瓶": ("YB_Solid_Stock", "3a14196a-cf7d-8aea-48d8-b9662c7dba94"),
|
||||||
"90%分装小瓶": ("BIOYOND_PolymerStation_Solid_Vial", "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea"),
|
"90%分装小瓶": ("YB_Solid_Vial", "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea"),
|
||||||
"10%分装小瓶": ("BIOYOND_PolymerStation_Liquid_Vial", "3a14196c-76be-2279-4e22-7310d69aed68"),
|
"10%分装小瓶": ("YB_Liquid_Vial", "3a14196c-76be-2279-4e22-7310d69aed68"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ lab_registry.setup()
|
|||||||
|
|
||||||
|
|
||||||
type_mapping = {
|
type_mapping = {
|
||||||
"烧杯": ("BIOYOND_PolymerStation_1FlaskCarrier", "3a14196b-24f2-ca49-9081-0cab8021bf1a"),
|
"烧杯": ("YB_1FlaskCarrier", "3a14196b-24f2-ca49-9081-0cab8021bf1a"),
|
||||||
"试剂瓶": ("BIOYOND_PolymerStation_1BottleCarrier", ""),
|
"试剂瓶": ("YB_1BottleCarrier", ""),
|
||||||
"样品板": ("BIOYOND_PolymerStation_6StockCarrier", "3a14196e-b7a0-a5da-1931-35f3000281e9"),
|
"样品板": ("YB_6StockCarrier", "3a14196e-b7a0-a5da-1931-35f3000281e9"),
|
||||||
"分装板": ("BIOYOND_PolymerStation_6VialCarrier", "3a14196e-5dfe-6e21-0c79-fe2036d052c4"),
|
"分装板": ("YB_6VialCarrier", "3a14196e-5dfe-6e21-0c79-fe2036d052c4"),
|
||||||
"样品瓶": ("BIOYOND_PolymerStation_Solid_Stock", "3a14196a-cf7d-8aea-48d8-b9662c7dba94"),
|
"样品瓶": ("YB_Solid_Stock", "3a14196a-cf7d-8aea-48d8-b9662c7dba94"),
|
||||||
"90%分装小瓶": ("BIOYOND_PolymerStation_Solid_Vial", "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea"),
|
"90%分装小瓶": ("YB_Solid_Vial", "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea"),
|
||||||
"10%分装小瓶": ("BIOYOND_PolymerStation_Liquid_Vial", "3a14196c-76be-2279-4e22-7310d69aed68"),
|
"10%分装小瓶": ("YB_Liquid_Vial", "3a14196c-76be-2279-4e22-7310d69aed68"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ WAREHOUSE_MAPPING = {
|
|||||||
"粉末加样头堆栈": {
|
"粉末加样头堆栈": {
|
||||||
"uuid": "",
|
"uuid": "",
|
||||||
"site_uuids": {
|
"site_uuids": {
|
||||||
"A01": "3a19da56-1379-20c8-5886-f7c4fbcb5733",
|
"A01": "3a19da56-1379-ff7c-1745-07e200b44ce2",
|
||||||
"B01": "3a19da56-1379-2424-d751-fe6e94cef938",
|
"B01": "3a19da56-1379-2424-d751-fe6e94cef938",
|
||||||
"C01": "3a19da56-1379-271c-03e3-6bdb590e395e",
|
"C01": "3a19da56-1379-271c-03e3-6bdb590e395e",
|
||||||
"D01": "3a19da56-1379-277f-2b1b-0d11f7cf92c6",
|
"D01": "3a19da56-1379-277f-2b1b-0d11f7cf92c6",
|
||||||
@@ -47,7 +47,89 @@ WAREHOUSE_MAPPING = {
|
|||||||
"S01": "3a19da56-1379-f924-7f68-df1fa51489f4",
|
"S01": "3a19da56-1379-f924-7f68-df1fa51489f4",
|
||||||
"T01": "3a19da56-1379-ff7c-1745-07e200b44ce2"
|
"T01": "3a19da56-1379-ff7c-1745-07e200b44ce2"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"配液站内试剂仓库": {
|
||||||
|
"uuid": "",
|
||||||
|
"site_uuids": {
|
||||||
|
"A01": "3a19da43-57b5-294f-d663-154a1cc32270",
|
||||||
|
"B01": "3a19da43-57b5-7394-5f49-54efe2c9bef2",
|
||||||
|
"C01": "3a19da43-57b5-5e75-552f-8dbd0ad1075f",
|
||||||
|
"A02": "3a19da43-57b5-8441-db94-b4d3875a4b6c",
|
||||||
|
"B02": "3a19da43-57b5-3e41-c181-5119dddaf50c",
|
||||||
|
"C02": "3a19da43-57b5-269b-282d-fba61fe8ce96",
|
||||||
|
"A03": "3a19da43-57b5-7c1e-d02e-c40e8c33f8a1",
|
||||||
|
"B03": "3a19da43-57b5-659f-621f-1dcf3f640363",
|
||||||
|
"C03": "3a19da43-57b5-855a-6e71-f398e376dee1",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"试剂替换仓库": {
|
||||||
|
"uuid": "",
|
||||||
|
"site_uuids": {
|
||||||
|
"A01": "3a19da51-8f4e-30f3-ea08-4f8498e9b097",
|
||||||
|
"B01": "3a19da51-8f4e-1da7-beb0-80a4a01e67a8",
|
||||||
|
"C01": "3a19da51-8f4e-337d-2675-bfac46880b06",
|
||||||
|
"D01": "3a19da51-8f4e-e514-b92c-9c44dc5e489d",
|
||||||
|
"E01": "3a19da51-8f4e-22d1-dd5b-9774ddc80402",
|
||||||
|
"F01": "3a19da51-8f4e-273a-4871-dff41c29bfd9",
|
||||||
|
"G01": "3a19da51-8f4e-b32f-454f-74bc1a665653",
|
||||||
|
"H01": "3a19da51-8f4e-8c93-68c9-0b4382320f59",
|
||||||
|
"I01": "3a19da51-8f4e-360c-0149-291b47c6089b",
|
||||||
|
"J01": "3a19da51-8f4e-4152-9bca-8d64df8c1af0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"自动堆栈-左": {
|
||||||
|
"uuid": "",
|
||||||
|
"site_uuids": {
|
||||||
|
"A01": "3a19debc-84b5-4c1c-d3a1-26830cf273ff",
|
||||||
|
"A02": "3a19debc-84b5-033b-b31f-6b87f7c2bf52",
|
||||||
|
"B01": "3a19debc-84b5-3924-172f-719ab01b125c",
|
||||||
|
"B02": "3a19debc-84b5-aad8-70c6-b8c6bb2d8750"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"自动堆栈-右": {
|
||||||
|
"uuid": "",
|
||||||
|
"site_uuids": {
|
||||||
|
"A01": "3a19debe-5200-7df2-1dd9-7d202f158864",
|
||||||
|
"A02": "3a19debe-5200-573b-6120-8b51f50e1e50",
|
||||||
|
"B01": "3a19debe-5200-7cd8-7666-851b0a97e309",
|
||||||
|
"B02": "3a19debe-5200-e6d3-96a3-baa6e3d5e484"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"手动堆栈": {
|
||||||
|
"uuid": "",
|
||||||
|
"site_uuids": {
|
||||||
|
"A01": "3a19deae-2c7a-36f5-5e41-02c5b66feaea",
|
||||||
|
"A02": "3a19deae-2c7a-dc6d-c41e-ef285d946cfe",
|
||||||
|
"A03": "3a19deae-2c7a-5876-c454-6b7e224ca927",
|
||||||
|
"B01": "3a19deae-2c7a-2426-6d71-e9de3cb158b1",
|
||||||
|
"B02": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3",
|
||||||
|
"B03": "3a19deae-2c7a-b9eb-f4e3-e308e0cf839a",
|
||||||
|
"C01": "3a19deae-2c7a-32bc-768e-556647e292f3",
|
||||||
|
"C02": "3a19deae-2c7a-e97a-8484-f5a4599447c4",
|
||||||
|
"C03": "3a19deae-2c7a-3056-6504-10dc73fbc276",
|
||||||
|
"D01": "3a19deae-2c7a-ffad-875e-8c4cda61d440",
|
||||||
|
"D02": "3a19deae-2c7a-61be-601c-b6fb5610499a",
|
||||||
|
"D03": "3a19deae-2c7a-c0f7-05a7-e3fe2491e560",
|
||||||
|
"E01": "3a19deae-2c7a-a6f4-edd1-b436a7576363",
|
||||||
|
"E02": "3a19deae-2c7a-4367-96dd-1ca2186f4910",
|
||||||
|
"E03": "3a19deae-2c7a-b163-2219-23df15200311",
|
||||||
|
"F01": "3a19deae-2c7a-d594-fd6a-0d20de3c7c4a",
|
||||||
|
"F02": "3a19deae-2c7a-a194-ea63-8b342b8d8679",
|
||||||
|
"F03": "3a19deae-2c7a-f7c4-12bd-425799425698",
|
||||||
|
"G01": "3a19deae-2c7a-0b56-72f1-8ab86e53b955",
|
||||||
|
"G02": "3a19deae-2c7a-204e-95ed-1f1950f28343",
|
||||||
|
"G03": "3a19deae-2c7a-392b-62f1-4907c66343f8",
|
||||||
|
"H01": "3a19deae-2c7a-5602-e876-d27aca4e3201",
|
||||||
|
"H02": "3a19deae-2c7a-f15c-70e0-25b58a8c9702",
|
||||||
|
"H03": "3a19deae-2c7a-780b-8965-2e1345f7e834",
|
||||||
|
"I01": "3a19deae-2c7a-8849-e172-07de14ede928",
|
||||||
|
"I02": "3a19deae-2c7a-4772-a37f-ff99270bafc0",
|
||||||
|
"I03": "3a19deae-2c7a-cce7-6e4a-25ea4a2068c4",
|
||||||
|
"J01": "3a19deae-2c7a-1848-de92-b5d5ed054cc6",
|
||||||
|
"J02": "3a19deae-2c7a-1d45-b4f8-6f866530e205",
|
||||||
|
"J03": "3a19deae-2c7a-f237-89d9-8fe19025dee9"
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
# 物料类型配置
|
# 物料类型配置
|
||||||
@@ -88,10 +170,10 @@ SOLID_LIQUID_MAPPINGS = {
|
|||||||
# "barCode": "",
|
# "barCode": "",
|
||||||
# "name": "LiFSI",
|
# "name": "LiFSI",
|
||||||
# "unit": "g",
|
# "unit": "g",
|
||||||
# "parameters": {"Density": "1.533"},
|
# "parameters": "",
|
||||||
# "quantity": 2,
|
# "quantity": 2,
|
||||||
# "warningQuantity": 1,
|
# "warningQuantity": 1,
|
||||||
# "details": [{}]
|
# "details": []
|
||||||
# },
|
# },
|
||||||
# "DTC": {
|
# "DTC": {
|
||||||
# "typeId": "3a190ca0-b2f6-9aeb-8067-547e72c11469",
|
# "typeId": "3a190ca0-b2f6-9aeb-8067-547e72c11469",
|
||||||
@@ -99,10 +181,10 @@ SOLID_LIQUID_MAPPINGS = {
|
|||||||
# "barCode": "",
|
# "barCode": "",
|
||||||
# "name": "DTC",
|
# "name": "DTC",
|
||||||
# "unit": "g",
|
# "unit": "g",
|
||||||
# "parameters": {"Density": "1.533"},
|
# "parameters": "",
|
||||||
# "quantity": 2,
|
# "quantity": 2,
|
||||||
# "warningQuantity": 1,
|
# "warningQuantity": 1,
|
||||||
# "details": [{}]
|
# "details": []
|
||||||
# },
|
# },
|
||||||
# "LiPO2F2": {
|
# "LiPO2F2": {
|
||||||
# "typeId": "3a190ca0-b2f6-9aeb-8067-547e72c11469",
|
# "typeId": "3a190ca0-b2f6-9aeb-8067-547e72c11469",
|
||||||
@@ -110,10 +192,10 @@ SOLID_LIQUID_MAPPINGS = {
|
|||||||
# "barCode": "",
|
# "barCode": "",
|
||||||
# "name": "LiPO2F2",
|
# "name": "LiPO2F2",
|
||||||
# "unit": "g",
|
# "unit": "g",
|
||||||
# "parameters": {"Density": "1.533"},
|
# "parameters": "",
|
||||||
# "quantity": 2,
|
# "quantity": 2,
|
||||||
# "warningQuantity": 1,
|
# "warningQuantity": 1,
|
||||||
# "details": [{}]
|
# "details": []
|
||||||
# },
|
# },
|
||||||
# 液体
|
# 液体
|
||||||
# "SA": ("BIOYOND_PolymerStation_Solid_Stock", "3a190ca0-b2f6-9aeb-8067-547e72c11469"),
|
# "SA": ("BIOYOND_PolymerStation_Solid_Stock", "3a190ca0-b2f6-9aeb-8067-547e72c11469"),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
|||||||
|
Name,DataType,InitValue,Comment,Attribute,DeviceType,Address,
|
||||||
|
COIL_SYS_START_CMD,BOOL,,,,coil,8010,
|
||||||
|
COIL_SYS_STOP_CMD,BOOL,,,,coil,8020,
|
||||||
|
COIL_SYS_RESET_CMD,BOOL,,,,coil,8030,
|
||||||
|
COIL_SYS_HAND_CMD,BOOL,,,,coil,8040,
|
||||||
|
COIL_SYS_AUTO_CMD,BOOL,,,,coil,8050,
|
||||||
|
COIL_SYS_INIT_CMD,BOOL,,,,coil,8060,
|
||||||
|
COIL_UNILAB_SEND_MSG_SUCC_CMD,BOOL,,,,coil,8700,
|
||||||
|
COIL_UNILAB_REC_MSG_SUCC_CMD,BOOL,,,,coil,8710,unilab_rec_msg_succ_cmd
|
||||||
|
COIL_SYS_START_STATUS,BOOL,,,,coil,8210,
|
||||||
|
COIL_SYS_STOP_STATUS,BOOL,,,,coil,8220,
|
||||||
|
COIL_SYS_RESET_STATUS,BOOL,,,,coil,8230,
|
||||||
|
COIL_SYS_HAND_STATUS,BOOL,,,,coil,8240,
|
||||||
|
COIL_SYS_AUTO_STATUS,BOOL,,,,coil,8250,
|
||||||
|
COIL_SYS_INIT_STATUS,BOOL,,,,coil,8260,
|
||||||
|
COIL_REQUEST_REC_MSG_STATUS,BOOL,,,,coil,8500,
|
||||||
|
COIL_REQUEST_SEND_MSG_STATUS,BOOL,,,,coil,8510,request_send_msg_status
|
||||||
|
REG_MSG_ELECTROLYTE_USE_NUM,INT16,,,,hold_register,11000,
|
||||||
|
REG_MSG_ELECTROLYTE_NUM,INT16,,,,hold_register,11002,unilab_send_msg_electrolyte_num
|
||||||
|
REG_MSG_ELECTROLYTE_VOLUME,INT16,,,,hold_register,11004,unilab_send_msg_electrolyte_vol
|
||||||
|
REG_MSG_ASSEMBLY_TYPE,INT16,,,,hold_register,11006,unilab_send_msg_assembly_type
|
||||||
|
REG_MSG_ASSEMBLY_PRESSURE,INT16,,,,hold_register,11008,unilab_send_msg_assembly_pressure
|
||||||
|
REG_DATA_ASSEMBLY_COIN_CELL_NUM,INT16,,,,hold_register,10000,data_assembly_coin_cell_num
|
||||||
|
REG_DATA_OPEN_CIRCUIT_VOLTAGE,FLOAT32,,,,hold_register,10002,data_open_circuit_voltage
|
||||||
|
REG_DATA_AXIS_X_POS,FLOAT32,,,,hold_register,10004,
|
||||||
|
REG_DATA_AXIS_Y_POS,FLOAT32,,,,hold_register,10006,
|
||||||
|
REG_DATA_AXIS_Z_POS,FLOAT32,,,,hold_register,10008,
|
||||||
|
REG_DATA_POLE_WEIGHT,FLOAT32,,,,hold_register,10010,data_pole_weight
|
||||||
|
REG_DATA_ASSEMBLY_PER_TIME,FLOAT32,,,,hold_register,10012,data_assembly_time
|
||||||
|
REG_DATA_ASSEMBLY_PRESSURE,INT16,,,,hold_register,10014,data_assembly_pressure
|
||||||
|
REG_DATA_ELECTROLYTE_VOLUME,INT16,,,,hold_register,10016,data_electrolyte_volume
|
||||||
|
REG_DATA_COIN_NUM,INT16,,,,hold_register,10018,data_coin_num
|
||||||
|
REG_DATA_ELECTROLYTE_CODE,STRING,,,,hold_register,10020,data_electrolyte_code()
|
||||||
|
REG_DATA_COIN_CELL_CODE,STRING,,,,hold_register,10030,data_coin_cell_code()
|
||||||
|
REG_DATA_STACK_VISON_CODE,STRING,,,,hold_register,12004,data_stack_vision_code()
|
||||||
|
REG_DATA_GLOVE_BOX_PRESSURE,FLOAT32,,,,hold_register,10050,data_glove_box_pressure
|
||||||
|
REG_DATA_GLOVE_BOX_WATER_CONTENT,FLOAT32,,,,hold_register,10052,data_glove_box_water_content
|
||||||
|
REG_DATA_GLOVE_BOX_O2_CONTENT,FLOAT32,,,,hold_register,10054,data_glove_box_o2_content
|
||||||
|
UNILAB_SEND_ELECTROLYTE_BOTTLE_NUM,BOOL,,,,coil,8720,
|
||||||
|
UNILAB_RECE_ELECTROLYTE_BOTTLE_NUM,BOOL,,,,coil,8520,
|
||||||
|
REG_MSG_ELECTROLYTE_NUM_USED,INT16,,,,hold_register,496,
|
||||||
|
REG_DATA_ELECTROLYTE_USE_NUM,INT16,,,,hold_register,10000,
|
||||||
|
UNILAB_SEND_FINISHED_CMD,BOOL,,,,coil,8730,
|
||||||
|
UNILAB_RECE_FINISHED_CMD,BOOL,,,,coil,8530,
|
||||||
|
REG_DATA_ASSEMBLY_TYPE,INT16,,,,hold_register,10018,ASSEMBLY_TYPE7or8
|
||||||
|
COIL_ALUMINUM_FOIL,BOOL,,ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,,coil,8340,
|
||||||
|
REG_MSG_NE_PLATE_MATRIX,INT16,,<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ,,hold_register,440,
|
||||||
|
REG_MSG_SEPARATOR_PLATE_MATRIX,INT16,,<EFBFBD><EFBFBD>Ĥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ,,hold_register,450,
|
||||||
|
REG_MSG_TIP_BOX_MATRIX,INT16,,<EFBFBD><EFBFBD>Һǹͷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ,,hold_register,480,
|
||||||
|
REG_MSG_NE_PLATE_NUM,INT16,,<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,,hold_register,443,
|
||||||
|
REG_MSG_SEPARATOR_PLATE_NUM,INT16,,<EFBFBD><EFBFBD>Ĥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,,hold_register,453,
|
||||||
|
REG_MSG_PRESS_MODE,BOOL,,ѹ<EFBFBD><EFBFBD>ģʽ<EFBFBD><EFBFBD>false:ѹ<><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ<C4A3><CABD>True:<3A><><EFBFBD><EFBFBD>ģʽ<C4A3><CABD>,,coil,8360,<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѹ<EFBFBD><EFBFBD>ģʽ
|
||||||
|
,,,,,,,
|
||||||
|
,BOOL,,<EFBFBD>Ӿ<EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD>false:ʹ<>ã<EFBFBD>true:<3A><><EFBFBD>ԣ<EFBFBD>,,coil,8300,<EFBFBD>Ӿ<EFBFBD><EFBFBD><EFBFBD>λ
|
||||||
|
,BOOL,,<EFBFBD><EFBFBD><EFBFBD>죨false:ʹ<>ã<EFBFBD>true:<3A><><EFBFBD>ԣ<EFBFBD>,,coil,8310,<EFBFBD>Ӿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
,BOOL,,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>_<EFBFBD><EFBFBD><EFBFBD>֣<EFBFBD>false:ʹ<>ã<EFBFBD>true:<3A><><EFBFBD>ԣ<EFBFBD>,,coil,8320,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
,BOOL,,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>_<EFBFBD>Ҳ֣<EFBFBD>false:ʹ<>ã<EFBFBD>true:<3A><><EFBFBD>ԣ<EFBFBD>,,coil,8420,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҳ<EFBFBD>
|
||||||
|
,BOOL,,<EFBFBD><EFBFBD><EFBFBD>ռ<EFBFBD>֪<EFBFBD><EFBFBD>false:ʹ<>ã<EFBFBD>true:<3A><><EFBFBD>ԣ<EFBFBD>,,coil,8350,<EFBFBD><EFBFBD><EFBFBD>ռ<EFBFBD>֪
|
||||||
|
,BOOL,,<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Һ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ<EFBFBD><EFBFBD>false:<3A><><EFBFBD>ε<EFBFBD>Һ<EFBFBD><D2BA>true:<3A><><EFBFBD>ε<EFBFBD>Һ<EFBFBD><D2BA>,,coil,8370,<EFBFBD><EFBFBD>Һģʽ
|
||||||
|
,BOOL,,<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭ<EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD>false:ʹ<>ã<EFBFBD>true:<3A><><EFBFBD>ԣ<EFBFBD>,,coil,8380,<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
,BOOL,,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭ<EFBFBD><EFBFBD>װ<EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD>false:<3A><>װ<EFBFBD><D7B0>true:<3A><>װ<EFBFBD><D7B0>,,coil,8390,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ
|
||||||
|
,BOOL,,ѹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ࣨfalse:ʹ<>ã<EFBFBD>true:<3A><><EFBFBD>ԣ<EFBFBD>,,coil,8400,ѹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
,BOOL,,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̰<EFBFBD><EFBFBD>̷<EFBFBD>ʽ<EFBFBD><EFBFBD>false:ˮƽ<CBAE><C6BD><EFBFBD>̣<EFBFBD>true:<3A>ѵ<EFBFBD><D1B5><EFBFBD><EFBFBD>̣<EFBFBD>,,coil,8410,<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭ<EFBFBD><EFBFBD><EFBFBD>̷<EFBFBD>ʽ
|
||||||
|
@@ -0,0 +1,691 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "BatteryStation",
|
||||||
|
"name": "扣电工作站",
|
||||||
|
"children": [
|
||||||
|
"coin_cell_deck"
|
||||||
|
],
|
||||||
|
"parent": null,
|
||||||
|
"type": "device",
|
||||||
|
"class": "bettery_station_registry",
|
||||||
|
"position": {
|
||||||
|
"x": 600,
|
||||||
|
"y": 400,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"debug_mode": false,
|
||||||
|
"_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定",
|
||||||
|
"protocol_type": [],
|
||||||
|
"station_resource": {
|
||||||
|
"data": {
|
||||||
|
"_resource_child_name": "coin_cell_deck",
|
||||||
|
"_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"address": "192.168.1.20",
|
||||||
|
"port": 502
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "coin_cell_deck",
|
||||||
|
"name": "coin_cell_deck",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [
|
||||||
|
"\u7535\u6c60\u6599\u76d8"
|
||||||
|
],
|
||||||
|
"parent": null,
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "CoincellDeck",
|
||||||
|
"size_x": 1000,
|
||||||
|
"size_y": 1000,
|
||||||
|
"size_z": 900,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "coin_cell_deck",
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_0_0",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_0_1",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_0_2",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_0_3",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_1_0",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_1_1",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_1_2",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_1_3",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_2_0",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_2_1",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_2_2",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_2_3",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_3_0",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_3_1",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_3_2",
|
||||||
|
"\u7535\u6c60\u6599\u76d8_materialhole_3_3"
|
||||||
|
],
|
||||||
|
"parent": "coin_cell_deck",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 100,
|
||||||
|
"y": 100,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialPlate",
|
||||||
|
"size_x": 120.8,
|
||||||
|
"size_y": 160.5,
|
||||||
|
"size_z": 10.0,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_plate",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null,
|
||||||
|
"ordering": {
|
||||||
|
"A1": "\u7535\u6c60\u6599\u76d8_materialhole_0_0",
|
||||||
|
"B1": "\u7535\u6c60\u6599\u76d8_materialhole_0_1",
|
||||||
|
"C1": "\u7535\u6c60\u6599\u76d8_materialhole_0_2",
|
||||||
|
"D1": "\u7535\u6c60\u6599\u76d8_materialhole_0_3",
|
||||||
|
"A2": "\u7535\u6c60\u6599\u76d8_materialhole_1_0",
|
||||||
|
"B2": "\u7535\u6c60\u6599\u76d8_materialhole_1_1",
|
||||||
|
"C2": "\u7535\u6c60\u6599\u76d8_materialhole_1_2",
|
||||||
|
"D2": "\u7535\u6c60\u6599\u76d8_materialhole_1_3",
|
||||||
|
"A3": "\u7535\u6c60\u6599\u76d8_materialhole_2_0",
|
||||||
|
"B3": "\u7535\u6c60\u6599\u76d8_materialhole_2_1",
|
||||||
|
"C3": "\u7535\u6c60\u6599\u76d8_materialhole_2_2",
|
||||||
|
"D3": "\u7535\u6c60\u6599\u76d8_materialhole_2_3",
|
||||||
|
"A4": "\u7535\u6c60\u6599\u76d8_materialhole_3_0",
|
||||||
|
"B4": "\u7535\u6c60\u6599\u76d8_materialhole_3_1",
|
||||||
|
"C4": "\u7535\u6c60\u6599\u76d8_materialhole_3_2",
|
||||||
|
"D4": "\u7535\u6c60\u6599\u76d8_materialhole_3_3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_0_0",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_0_0",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 12.4,
|
||||||
|
"y": 104.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_0_1",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_0_1",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 12.4,
|
||||||
|
"y": 80.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_0_2",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_0_2",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 12.4,
|
||||||
|
"y": 56.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_0_3",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_0_3",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 12.4,
|
||||||
|
"y": 32.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_1_0",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_1_0",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 36.4,
|
||||||
|
"y": 104.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_1_1",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_1_1",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 36.4,
|
||||||
|
"y": 80.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_1_2",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_1_2",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 36.4,
|
||||||
|
"y": 56.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_1_3",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_1_3",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 36.4,
|
||||||
|
"y": 32.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_2_0",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_2_0",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 60.4,
|
||||||
|
"y": 104.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_2_1",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_2_1",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 60.4,
|
||||||
|
"y": 80.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_2_2",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_2_2",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 60.4,
|
||||||
|
"y": 56.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_2_3",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_2_3",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 60.4,
|
||||||
|
"y": 32.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_3_0",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_3_0",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 84.4,
|
||||||
|
"y": 104.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_3_1",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_3_1",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 84.4,
|
||||||
|
"y": 80.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_3_2",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_3_2",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 84.4,
|
||||||
|
"y": 56.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "\u7535\u6c60\u6599\u76d8_materialhole_3_3",
|
||||||
|
"name": "\u7535\u6c60\u6599\u76d8_materialhole_3_3",
|
||||||
|
"sample_id": null,
|
||||||
|
"children": [],
|
||||||
|
"parent": "\u7535\u6c60\u6599\u76d8",
|
||||||
|
"type": "container",
|
||||||
|
"class": "",
|
||||||
|
"position": {
|
||||||
|
"x": 84.4,
|
||||||
|
"y": 32.25,
|
||||||
|
"z": 10.0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"type": "MaterialHole",
|
||||||
|
"size_x": 16,
|
||||||
|
"size_y": 16,
|
||||||
|
"size_z": 16,
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"type": "Rotation"
|
||||||
|
},
|
||||||
|
"category": "material_hole",
|
||||||
|
"model": null,
|
||||||
|
"barcode": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"diameter": 20,
|
||||||
|
"depth": 10,
|
||||||
|
"max_sheets": 1,
|
||||||
|
"info": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,489 @@
|
|||||||
|
"""
|
||||||
|
工作站基类
|
||||||
|
Workstation Base Class - 简化版
|
||||||
|
|
||||||
|
基于PLR Deck的简化工作站架构
|
||||||
|
专注于核心物料系统和工作流管理
|
||||||
|
"""
|
||||||
|
|
||||||
|
import collections
|
||||||
|
import time
|
||||||
|
from typing import Dict, Any, List, Optional, Union
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
from pylabrobot.resources import Deck, Plate, Resource as PLRResource
|
||||||
|
|
||||||
|
from pylabrobot.resources.coordinate import Coordinate
|
||||||
|
from unilabos.ros.nodes.presets.workstation import ROS2WorkstationNode
|
||||||
|
|
||||||
|
from unilabos.utils.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowStatus(Enum):
|
||||||
|
"""工作流状态"""
|
||||||
|
|
||||||
|
IDLE = "idle"
|
||||||
|
INITIALIZING = "initializing"
|
||||||
|
RUNNING = "running"
|
||||||
|
PAUSED = "paused"
|
||||||
|
STOPPING = "stopping"
|
||||||
|
STOPPED = "stopped"
|
||||||
|
ERROR = "error"
|
||||||
|
COMPLETED = "completed"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WorkflowInfo:
|
||||||
|
"""工作流信息"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
estimated_duration: float # 预估持续时间(秒)
|
||||||
|
required_materials: List[str] # 所需物料类型
|
||||||
|
output_product: str # 输出产品类型
|
||||||
|
parameters_schema: Dict[str, Any] # 参数架构
|
||||||
|
|
||||||
|
|
||||||
|
class WorkStationContainer(Plate):
|
||||||
|
"""
|
||||||
|
WorkStation 专用 Container 类,继承自 Plate和TipRack
|
||||||
|
注意这个物料必须通过plr_additional_res_reg.py注册到edge,才能正常序列化
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
size_x: float,
|
||||||
|
size_y: float,
|
||||||
|
size_z: float,
|
||||||
|
category: str,
|
||||||
|
ordering: collections.OrderedDict,
|
||||||
|
model: Optional[str] = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
这里的初始化入参要和plr的保持一致
|
||||||
|
"""
|
||||||
|
super().__init__(name, size_x, size_y, size_z, category=category, ordering=ordering, model=model)
|
||||||
|
self._unilabos_state = {} # 必须有此行,自己的类描述的是物料的
|
||||||
|
|
||||||
|
def load_state(self, state: Dict[str, Any]) -> None:
|
||||||
|
"""从给定的状态加载工作台信息。"""
|
||||||
|
super().load_state(state)
|
||||||
|
self._unilabos_state = state
|
||||||
|
|
||||||
|
def serialize_state(self) -> Dict[str, Dict[str, Any]]:
|
||||||
|
data = super().serialize_state()
|
||||||
|
data.update(
|
||||||
|
self._unilabos_state
|
||||||
|
) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_workstation_plate_resource(name: str) -> PLRResource: # 要给定一个返回plr的方法
|
||||||
|
"""
|
||||||
|
用于获取一些模板,例如返回一个带有特定信息/子物料的 Plate,这里需要到注册表注册,例如unilabos/registry/resources/organic/workstation.yaml
|
||||||
|
可以直接运行该函数或者利用注册表补全机制,来检查是否资源出错
|
||||||
|
:param name: 资源名称
|
||||||
|
:return: Resource对象
|
||||||
|
"""
|
||||||
|
plate = WorkStationContainer(
|
||||||
|
name, size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()
|
||||||
|
)
|
||||||
|
tip_rack = WorkStationContainer(
|
||||||
|
"tip_rack_inside_plate",
|
||||||
|
size_x=50,
|
||||||
|
size_y=50,
|
||||||
|
size_z=10,
|
||||||
|
category="tip_rack",
|
||||||
|
ordering=collections.OrderedDict(),
|
||||||
|
)
|
||||||
|
plate.assign_child_resource(tip_rack, Coordinate.zero())
|
||||||
|
return plate
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceSynchronizer(ABC):
|
||||||
|
"""资源同步器基类
|
||||||
|
|
||||||
|
负责与外部物料系统的同步,并对 self.deck 做修改
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, workstation: "WorkstationBase"):
|
||||||
|
self.workstation = workstation
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def sync_from_external(self) -> bool:
|
||||||
|
"""从外部系统同步物料到本地deck"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def sync_to_external(self, plr_resource: PLRResource) -> bool:
|
||||||
|
"""将本地物料同步到外部系统"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def handle_external_change(self, change_info: Dict[str, Any]) -> bool:
|
||||||
|
"""处理外部系统的变更通知"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WorkstationBase(ABC):
|
||||||
|
"""工作站基类 - 简化版
|
||||||
|
|
||||||
|
核心功能:
|
||||||
|
1. 基于 PLR Deck 的物料系统,支持格式转换
|
||||||
|
2. 可选的资源同步器支持外部物料系统
|
||||||
|
3. 简化的工作流管理
|
||||||
|
"""
|
||||||
|
|
||||||
|
_ros_node: ROS2WorkstationNode
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _children(self) -> Dict[str, Any]: # 不要删除这个下划线,不然会自动导入注册表,后面改成装饰器识别
|
||||||
|
return self._ros_node.children
|
||||||
|
|
||||||
|
async def update_resource_example(self):
|
||||||
|
return await self._ros_node.update_resource([get_workstation_plate_resource("test")])
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
station_resource: PLRResource,
|
||||||
|
*args,
|
||||||
|
**kwargs, # 必须有kwargs
|
||||||
|
):
|
||||||
|
# 基本配置
|
||||||
|
print(station_resource)
|
||||||
|
self.deck_config = station_resource
|
||||||
|
|
||||||
|
# PLR 物料系统
|
||||||
|
self.deck: Optional[Deck] = None
|
||||||
|
self.plr_resources: Dict[str, PLRResource] = {}
|
||||||
|
|
||||||
|
# 资源同步器(可选)
|
||||||
|
# self.resource_synchronizer = ResourceSynchronizer(self) # 要在driver中自行初始化,只有workstation用
|
||||||
|
|
||||||
|
# 硬件接口
|
||||||
|
self.hardware_interface: Union[Any, str] = None
|
||||||
|
|
||||||
|
# 工作流状态
|
||||||
|
self.current_workflow_status = WorkflowStatus.IDLE
|
||||||
|
self.current_workflow_info = None
|
||||||
|
self.workflow_start_time = None
|
||||||
|
self.workflow_parameters = {}
|
||||||
|
|
||||||
|
# 支持的工作流(静态预定义)
|
||||||
|
self.supported_workflows: Dict[str, WorkflowInfo] = {}
|
||||||
|
|
||||||
|
# 初始化物料系统
|
||||||
|
self._initialize_material_system()
|
||||||
|
|
||||||
|
# 注册支持的工作流
|
||||||
|
# self._register_supported_workflows()
|
||||||
|
|
||||||
|
# logger.info(f"工作站 {device_id} 初始化完成(简化版)")
|
||||||
|
|
||||||
|
def _initialize_material_system(self):
|
||||||
|
"""初始化物料系统 - 使用 graphio 转换"""
|
||||||
|
try:
|
||||||
|
from unilabos.resources.graphio import resource_ulab_to_plr
|
||||||
|
|
||||||
|
# # 1. 合并 deck_config 和 children 创建完整的资源树
|
||||||
|
# complete_resource_config = self._create_complete_resource_config()
|
||||||
|
|
||||||
|
# # 2. 使用 graphio 转换为 PLR 资源
|
||||||
|
# self.deck = resource_ulab_to_plr(complete_resource_config, plr_model=True)
|
||||||
|
|
||||||
|
# # 3. 建立资源映射
|
||||||
|
# self._build_resource_mappings(self.deck)
|
||||||
|
|
||||||
|
# # 4. 如果有资源同步器,执行初始同步
|
||||||
|
# if self.resource_synchronizer:
|
||||||
|
# # 这里可以异步执行,暂时跳过
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# logger.info(f"工作站 {self.device_id} 物料系统初始化成功,创建了 {len(self.plr_resources)} 个资源")
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
# logger.error(f"工作站 {self.device_id} 物料系统初始化失败: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _create_complete_resource_config(self) -> Dict[str, Any]:
|
||||||
|
"""创建完整的资源配置 - 合并 deck_config 和 children"""
|
||||||
|
# 创建主 deck 配置
|
||||||
|
deck_resource = {
|
||||||
|
"id": f"{self.device_id}_deck",
|
||||||
|
"name": f"{self.device_id}_deck",
|
||||||
|
"type": "deck",
|
||||||
|
"position": {"x": 0, "y": 0, "z": 0},
|
||||||
|
"config": {
|
||||||
|
"size_x": self.deck_config.get("size_x", 1000.0),
|
||||||
|
"size_y": self.deck_config.get("size_y", 1000.0),
|
||||||
|
"size_z": self.deck_config.get("size_z", 100.0),
|
||||||
|
**{k: v for k, v in self.deck_config.items() if k not in ["size_x", "size_y", "size_z"]},
|
||||||
|
},
|
||||||
|
"data": {},
|
||||||
|
"children": [],
|
||||||
|
"parent": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加子资源
|
||||||
|
if self._children:
|
||||||
|
children_list = []
|
||||||
|
for child_id, child_config in self._children.items():
|
||||||
|
child_resource = self._normalize_child_resource(child_id, child_config, deck_resource["id"])
|
||||||
|
children_list.append(child_resource)
|
||||||
|
deck_resource["children"] = children_list
|
||||||
|
|
||||||
|
return deck_resource
|
||||||
|
|
||||||
|
def _normalize_child_resource(self, resource_id: str, config: Dict[str, Any], parent_id: str) -> Dict[str, Any]:
|
||||||
|
"""标准化子资源配置"""
|
||||||
|
return {
|
||||||
|
"id": resource_id,
|
||||||
|
"name": config.get("name", resource_id),
|
||||||
|
"type": config.get("type", "container"),
|
||||||
|
"position": self._normalize_position(config.get("position", {})),
|
||||||
|
"config": config.get("config", {}),
|
||||||
|
"data": config.get("data", {}),
|
||||||
|
"children": [], # 简化版本:只支持一层子资源
|
||||||
|
"parent": parent_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _normalize_position(self, position: Any) -> Dict[str, float]:
|
||||||
|
"""标准化位置信息"""
|
||||||
|
if isinstance(position, dict):
|
||||||
|
return {
|
||||||
|
"x": float(position.get("x", 0)),
|
||||||
|
"y": float(position.get("y", 0)),
|
||||||
|
"z": float(position.get("z", 0)),
|
||||||
|
}
|
||||||
|
elif isinstance(position, (list, tuple)) and len(position) >= 2:
|
||||||
|
return {
|
||||||
|
"x": float(position[0]),
|
||||||
|
"y": float(position[1]),
|
||||||
|
"z": float(position[2]) if len(position) > 2 else 0.0,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {"x": 0.0, "y": 0.0, "z": 0.0}
|
||||||
|
|
||||||
|
def _build_resource_mappings(self, deck: Deck):
|
||||||
|
"""递归构建资源映射"""
|
||||||
|
|
||||||
|
def add_resource_recursive(resource: PLRResource):
|
||||||
|
if hasattr(resource, "name"):
|
||||||
|
self.plr_resources[resource.name] = resource
|
||||||
|
|
||||||
|
if hasattr(resource, "children"):
|
||||||
|
for child in resource.children:
|
||||||
|
add_resource_recursive(child)
|
||||||
|
|
||||||
|
add_resource_recursive(deck)
|
||||||
|
|
||||||
|
# ============ 硬件接口管理 ============
|
||||||
|
|
||||||
|
def set_hardware_interface(self, hardware_interface: Union[Any, str]):
|
||||||
|
"""设置硬件接口"""
|
||||||
|
self.hardware_interface = hardware_interface
|
||||||
|
logger.info(f"工作站 {self.device_id} 硬件接口设置: {type(hardware_interface).__name__}")
|
||||||
|
|
||||||
|
def set_workstation_node(self, workstation_node: "ROS2WorkstationNode"):
|
||||||
|
"""设置协议节点引用(用于代理模式)"""
|
||||||
|
self._ros_node = workstation_node
|
||||||
|
logger.info(f"工作站 {self.device_id} 关联协议节点")
|
||||||
|
|
||||||
|
# ============ 设备操作接口 ============
|
||||||
|
|
||||||
|
def call_device_method(self, method: str, *args, **kwargs) -> Any:
|
||||||
|
"""调用设备方法的统一接口"""
|
||||||
|
# 1. 代理模式:通过协议节点转发
|
||||||
|
if isinstance(self.hardware_interface, str) and self.hardware_interface.startswith("proxy:"):
|
||||||
|
if not self._ros_node:
|
||||||
|
raise RuntimeError("代理模式需要设置workstation_node")
|
||||||
|
|
||||||
|
device_id = self.hardware_interface[6:] # 移除 "proxy:" 前缀
|
||||||
|
return self._ros_node.call_device_method(device_id, method, *args, **kwargs)
|
||||||
|
|
||||||
|
# 2. 直接模式:直接调用硬件接口方法
|
||||||
|
elif self.hardware_interface and hasattr(self.hardware_interface, method):
|
||||||
|
return getattr(self.hardware_interface, method)(*args, **kwargs)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise AttributeError(f"硬件接口不支持方法: {method}")
|
||||||
|
|
||||||
|
def get_device_status(self) -> Dict[str, Any]:
|
||||||
|
"""获取设备状态"""
|
||||||
|
try:
|
||||||
|
return self.call_device_method("get_status")
|
||||||
|
except AttributeError:
|
||||||
|
# 如果设备不支持get_status方法,返回基础状态
|
||||||
|
return {
|
||||||
|
"status": "unknown",
|
||||||
|
"interface_type": type(self.hardware_interface).__name__,
|
||||||
|
"timestamp": time.time(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def is_device_available(self) -> bool:
|
||||||
|
"""检查设备是否可用"""
|
||||||
|
try:
|
||||||
|
self.get_device_status()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ============ 物料系统接口 ============
|
||||||
|
|
||||||
|
def get_deck(self) -> Deck:
|
||||||
|
"""获取主 Deck"""
|
||||||
|
return self.deck
|
||||||
|
|
||||||
|
def get_all_resources(self) -> Dict[str, PLRResource]:
|
||||||
|
"""获取所有 PLR 资源"""
|
||||||
|
return self.plr_resources.copy()
|
||||||
|
|
||||||
|
def find_resource_by_name(self, name: str) -> Optional[PLRResource]:
|
||||||
|
"""按名称查找资源"""
|
||||||
|
return self.plr_resources.get(name)
|
||||||
|
|
||||||
|
def find_resources_by_type(self, resource_type: type) -> List[PLRResource]:
|
||||||
|
"""按类型查找资源"""
|
||||||
|
return [res for res in self.plr_resources.values() if isinstance(res, resource_type)]
|
||||||
|
|
||||||
|
async def sync_with_external_system(self) -> bool:
|
||||||
|
"""与外部物料系统同步"""
|
||||||
|
if not self.resource_synchronizer:
|
||||||
|
logger.info(f"工作站 {self.device_id} 没有配置资源同步器")
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
success = await self.resource_synchronizer.sync_from_external()
|
||||||
|
if success:
|
||||||
|
logger.info(f"工作站 {self.device_id} 外部同步成功")
|
||||||
|
else:
|
||||||
|
logger.warning(f"工作站 {self.device_id} 外部同步失败")
|
||||||
|
return success
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"工作站 {self.device_id} 外部同步异常: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ============ 简化的工作流控制 ============
|
||||||
|
|
||||||
|
def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
|
||||||
|
"""执行工作流"""
|
||||||
|
try:
|
||||||
|
# 设置工作流状态
|
||||||
|
self.current_workflow_status = WorkflowStatus.INITIALIZING
|
||||||
|
self.workflow_parameters = parameters
|
||||||
|
self.workflow_start_time = time.time()
|
||||||
|
|
||||||
|
# 委托给子类实现
|
||||||
|
success = self._execute_workflow_impl(workflow_name, parameters)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
self.current_workflow_status = WorkflowStatus.RUNNING
|
||||||
|
logger.info(f"工作站 {self.device_id} 工作流 {workflow_name} 启动成功")
|
||||||
|
else:
|
||||||
|
self.current_workflow_status = WorkflowStatus.ERROR
|
||||||
|
logger.error(f"工作站 {self.device_id} 工作流 {workflow_name} 启动失败")
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.current_workflow_status = WorkflowStatus.ERROR
|
||||||
|
logger.error(f"工作站 {self.device_id} 执行工作流失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def stop_workflow(self, emergency: bool = False) -> bool:
|
||||||
|
"""停止工作流"""
|
||||||
|
try:
|
||||||
|
if self.current_workflow_status in [WorkflowStatus.IDLE, WorkflowStatus.STOPPED]:
|
||||||
|
logger.warning(f"工作站 {self.device_id} 没有正在运行的工作流")
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.current_workflow_status = WorkflowStatus.STOPPING
|
||||||
|
|
||||||
|
# 委托给子类实现
|
||||||
|
success = self._stop_workflow_impl(emergency)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
self.current_workflow_status = WorkflowStatus.STOPPED
|
||||||
|
logger.info(f"工作站 {self.device_id} 工作流停止成功 (紧急: {emergency})")
|
||||||
|
else:
|
||||||
|
self.current_workflow_status = WorkflowStatus.ERROR
|
||||||
|
logger.error(f"工作站 {self.device_id} 工作流停止失败")
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.current_workflow_status = WorkflowStatus.ERROR
|
||||||
|
logger.error(f"工作站 {self.device_id} 停止工作流失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ============ 状态属性 ============
|
||||||
|
|
||||||
|
@property
|
||||||
|
def workflow_status(self) -> WorkflowStatus:
|
||||||
|
"""获取当前工作流状态"""
|
||||||
|
return self.current_workflow_status
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_busy(self) -> bool:
|
||||||
|
"""检查工作站是否忙碌"""
|
||||||
|
return self.current_workflow_status in [
|
||||||
|
WorkflowStatus.INITIALIZING,
|
||||||
|
WorkflowStatus.RUNNING,
|
||||||
|
WorkflowStatus.STOPPING,
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def workflow_runtime(self) -> float:
|
||||||
|
"""获取工作流运行时间(秒)"""
|
||||||
|
if self.workflow_start_time is None:
|
||||||
|
return 0.0
|
||||||
|
return time.time() - self.workflow_start_time
|
||||||
|
|
||||||
|
# ============ 抽象方法 - 子类必须实现 ============
|
||||||
|
|
||||||
|
# @abstractmethod
|
||||||
|
# def _register_supported_workflows(self):
|
||||||
|
# """注册支持的工作流 - 子类必须实现"""
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# @abstractmethod
|
||||||
|
# def _execute_workflow_impl(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
|
||||||
|
# """执行工作流的具体实现 - 子类必须实现"""
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# @abstractmethod
|
||||||
|
# def _stop_workflow_impl(self, emergency: bool = False) -> bool:
|
||||||
|
# """停止工作流的具体实现 - 子类必须实现"""
|
||||||
|
# pass
|
||||||
|
|
||||||
|
class WorkstationExample(WorkstationBase):
|
||||||
|
"""工作站示例实现"""
|
||||||
|
|
||||||
|
def _register_supported_workflows(self):
|
||||||
|
"""注册支持的工作流"""
|
||||||
|
self.supported_workflows["example_workflow"] = WorkflowInfo(
|
||||||
|
name="example_workflow",
|
||||||
|
description="这是一个示例工作流",
|
||||||
|
estimated_duration=300.0,
|
||||||
|
required_materials=["sample_plate"],
|
||||||
|
output_product="processed_plate",
|
||||||
|
parameters_schema={"param1": "string", "param2": "integer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _execute_workflow_impl(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
|
||||||
|
"""执行工作流的具体实现"""
|
||||||
|
if workflow_name not in self.supported_workflows:
|
||||||
|
logger.error(f"工作站 {self.device_id} 不支持工作流: {workflow_name}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 这里添加实际的工作流逻辑
|
||||||
|
logger.info(f"工作站 {self.device_id} 正在执行工作流: {workflow_name} with parameters {parameters}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _stop_workflow_impl(self, emergency: bool = False) -> bool:
|
||||||
|
"""停止工作流的具体实现"""
|
||||||
|
# 这里添加实际的停止逻辑
|
||||||
|
logger.info(f"工作站 {self.device_id} 正在停止工作流 (紧急: {emergency})")
|
||||||
|
return True
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -8,3 +8,144 @@ YB_jia_yang_tou_da:
|
|||||||
icon: ''
|
icon: ''
|
||||||
init_param_schema: {}
|
init_param_schema: {}
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|
||||||
|
YB_Liquid_Vial:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Liquid_Vial
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_Reagent_Bottle:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Reagent_Bottle
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_Solid_Stock:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Solid_Stock
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_Solid_Vial:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Solid_Vial
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_Solution_Beaker:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Solution_Beaker
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_100ml_Liquid_Bottle:
|
||||||
|
category:
|
||||||
|
- yb3
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_100ml_Liquid_Bottle
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_Liquid_Bottle:
|
||||||
|
category:
|
||||||
|
- yb3
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Liquid_Bottle
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_High_Viscosity_Liquid_Bottle:
|
||||||
|
category:
|
||||||
|
- yb3
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_High_Viscosity_Liquid_Bottle
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_Large_Dispense_Head:
|
||||||
|
category:
|
||||||
|
- yb3
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Large_Dispense_Head
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_5ml_Dispensing_Vial:
|
||||||
|
category:
|
||||||
|
- yb3
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_5ml_Dispensing_Vial
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_20ml_Dispensing_Vial:
|
||||||
|
category:
|
||||||
|
- yb3
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_20ml_Dispensing_Vial
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_Small_Solution_Bottle:
|
||||||
|
category:
|
||||||
|
- yb3
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Small_Solution_Bottle
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_Large_Solution_Bottle:
|
||||||
|
category:
|
||||||
|
- yb3
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Large_Solution_Bottle
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
YB_Pipette_Tip:
|
||||||
|
category:
|
||||||
|
- yb3
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:YB_Pipette_Tip
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
|||||||
620
unilabos/resources/bioyond/bottle_carriers.py
Normal file
620
unilabos/resources/bioyond/bottle_carriers.py
Normal file
@@ -0,0 +1,620 @@
|
|||||||
|
from pylabrobot.resources import create_homogeneous_resources, Coordinate, ResourceHolder, create_ordered_items_2d
|
||||||
|
|
||||||
|
from unilabos.resources.itemized_carrier import Bottle, BottleCarrier
|
||||||
|
from unilabos.resources.bioyond.bottles import (
|
||||||
|
YB_Solid_Stock,
|
||||||
|
YB_Solid_Vial,
|
||||||
|
YB_Liquid_Vial,
|
||||||
|
YB_Solution_Beaker,
|
||||||
|
YB_Reagent_Bottle,
|
||||||
|
YB_5ml_Dispensing_Vial,
|
||||||
|
YB_20ml_Dispensing_Vial,
|
||||||
|
YB_Small_Solution_Bottle,
|
||||||
|
YB_Large_Solution_Bottle,
|
||||||
|
YB_Large_Dispense_Head,
|
||||||
|
YB_Pipette_Tip
|
||||||
|
)
|
||||||
|
# 命名约定:试剂瓶-Bottle,烧杯-Beaker,烧瓶-Flask,小瓶-Vial
|
||||||
|
|
||||||
|
|
||||||
|
def BIOYOND_Electrolyte_6VialCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""6瓶载架 - 2x3布局"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 50.0
|
||||||
|
|
||||||
|
# 瓶位尺寸
|
||||||
|
bottle_diameter = 30.0
|
||||||
|
bottle_spacing_x = 42.0 # X方向间距
|
||||||
|
bottle_spacing_y = 35.0 # Y方向间距
|
||||||
|
|
||||||
|
# 计算起始位置 (居中排列)
|
||||||
|
start_x = (carrier_size_x - (3 - 1) * bottle_spacing_x - bottle_diameter) / 2
|
||||||
|
start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2
|
||||||
|
|
||||||
|
sites = create_ordered_items_2d(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
num_items_x=3,
|
||||||
|
num_items_y=2,
|
||||||
|
dx=start_x,
|
||||||
|
dy=start_y,
|
||||||
|
dz=5.0,
|
||||||
|
item_dx=bottle_spacing_x,
|
||||||
|
item_dy=bottle_spacing_y,
|
||||||
|
|
||||||
|
size_x=bottle_diameter,
|
||||||
|
size_y=bottle_diameter,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
)
|
||||||
|
for k, v in sites.items():
|
||||||
|
v.name = f"{name}_{v.name}"
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=sites,
|
||||||
|
model="Electrolyte_6VialCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 3
|
||||||
|
carrier.num_items_y = 2
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
for i in range(6):
|
||||||
|
carrier[i] = YB_Solid_Vial(f"{name}_vial_{i+1}")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def BIOYOND_Electrolyte_1BottleCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""1瓶载架 - 单个中央位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 100.0
|
||||||
|
|
||||||
|
# 烧杯尺寸
|
||||||
|
beaker_diameter = 80.0
|
||||||
|
|
||||||
|
# 计算中央位置
|
||||||
|
center_x = (carrier_size_x - beaker_diameter) / 2
|
||||||
|
center_y = (carrier_size_y - beaker_diameter) / 2
|
||||||
|
center_z = 5.0
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=create_homogeneous_resources(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
locations=[Coordinate(center_x, center_y, center_z)],
|
||||||
|
resource_size_x=beaker_diameter,
|
||||||
|
resource_size_y=beaker_diameter,
|
||||||
|
name_prefix=name,
|
||||||
|
),
|
||||||
|
model="Electrolyte_1BottleCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 1
|
||||||
|
carrier.num_items_y = 1
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
carrier[0] = YB_Solution_Beaker(f"{name}_beaker_1")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_6StockCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""6瓶载架 - 2x3布局"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 50.0
|
||||||
|
|
||||||
|
# 瓶位尺寸
|
||||||
|
bottle_diameter = 20.0
|
||||||
|
bottle_spacing_x = 42.0 # X方向间距
|
||||||
|
bottle_spacing_y = 35.0 # Y方向间距
|
||||||
|
|
||||||
|
# 计算起始位置 (居中排列)
|
||||||
|
start_x = (carrier_size_x - (3 - 1) * bottle_spacing_x - bottle_diameter) / 2
|
||||||
|
start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2
|
||||||
|
|
||||||
|
sites = create_ordered_items_2d(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
num_items_x=3,
|
||||||
|
num_items_y=2,
|
||||||
|
dx=start_x,
|
||||||
|
dy=start_y,
|
||||||
|
dz=5.0,
|
||||||
|
item_dx=bottle_spacing_x,
|
||||||
|
item_dy=bottle_spacing_y,
|
||||||
|
|
||||||
|
size_x=bottle_diameter,
|
||||||
|
size_y=bottle_diameter,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
)
|
||||||
|
for k, v in sites.items():
|
||||||
|
v.name = f"{name}_{v.name}"
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=sites,
|
||||||
|
model="6StockCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 3
|
||||||
|
carrier.num_items_y = 2
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
ordering = ["A1", "A2", "A3", "B1", "B2", "B3"] # 自定义顺序
|
||||||
|
for i in range(6):
|
||||||
|
carrier[i] = YB_Solid_Stock(f"{name}_vial_{ordering[i]}")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_6VialCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""6瓶载架 - 2x3布局"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 50.0
|
||||||
|
|
||||||
|
# 瓶位尺寸
|
||||||
|
bottle_diameter = 30.0
|
||||||
|
bottle_spacing_x = 42.0 # X方向间距
|
||||||
|
bottle_spacing_y = 35.0 # Y方向间距
|
||||||
|
|
||||||
|
# 计算起始位置 (居中排列)
|
||||||
|
start_x = (carrier_size_x - (3 - 1) * bottle_spacing_x - bottle_diameter) / 2
|
||||||
|
start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2
|
||||||
|
|
||||||
|
sites = create_ordered_items_2d(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
num_items_x=3,
|
||||||
|
num_items_y=2,
|
||||||
|
dx=start_x,
|
||||||
|
dy=start_y,
|
||||||
|
dz=5.0,
|
||||||
|
item_dx=bottle_spacing_x,
|
||||||
|
item_dy=bottle_spacing_y,
|
||||||
|
|
||||||
|
size_x=bottle_diameter,
|
||||||
|
size_y=bottle_diameter,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
)
|
||||||
|
for k, v in sites.items():
|
||||||
|
v.name = f"{name}_{v.name}"
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=sites,
|
||||||
|
model="6VialCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 3
|
||||||
|
carrier.num_items_y = 2
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
ordering = ["A1", "A2", "A3", "B1", "B2", "B3"] # 自定义顺序
|
||||||
|
for i in range(3):
|
||||||
|
carrier[i] = YB_Solid_Vial(f"{name}_solidvial_{ordering[i]}")
|
||||||
|
for i in range(3, 6):
|
||||||
|
carrier[i] = YB_Liquid_Vial(f"{name}_liquidvial_{ordering[i]}")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_1BottleCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""1瓶载架 - 单个中央位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 20.0
|
||||||
|
|
||||||
|
# 烧杯尺寸
|
||||||
|
beaker_diameter = 60.0
|
||||||
|
|
||||||
|
# 计算中央位置
|
||||||
|
center_x = (carrier_size_x - beaker_diameter) / 2
|
||||||
|
center_y = (carrier_size_y - beaker_diameter) / 2
|
||||||
|
center_z = 5.0
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=create_homogeneous_resources(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
locations=[Coordinate(center_x, center_y, center_z)],
|
||||||
|
resource_size_x=beaker_diameter,
|
||||||
|
resource_size_y=beaker_diameter,
|
||||||
|
name_prefix=name,
|
||||||
|
),
|
||||||
|
model="1BottleCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 1
|
||||||
|
carrier.num_items_y = 1
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
carrier[0] = YB_Reagent_Bottle(f"{name}_flask_1")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_1FlaskCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""1瓶载架 - 单个中央位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 20.0
|
||||||
|
|
||||||
|
# 烧杯尺寸
|
||||||
|
beaker_diameter = 70.0
|
||||||
|
|
||||||
|
# 计算中央位置
|
||||||
|
center_x = (carrier_size_x - beaker_diameter) / 2
|
||||||
|
center_y = (carrier_size_y - beaker_diameter) / 2
|
||||||
|
center_z = 5.0
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=create_homogeneous_resources(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
locations=[Coordinate(center_x, center_y, center_z)],
|
||||||
|
resource_size_x=beaker_diameter,
|
||||||
|
resource_size_y=beaker_diameter,
|
||||||
|
name_prefix=name,
|
||||||
|
),
|
||||||
|
model="1FlaskCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 1
|
||||||
|
carrier.num_items_y = 1
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
carrier[0] = YB_Reagent_Bottle(f"{name}_bottle_1")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_6x5ml_DispensingVialCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""5ml分液瓶板 - 4x2布局,8个位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 50.0
|
||||||
|
|
||||||
|
# 瓶位尺寸
|
||||||
|
bottle_diameter = 15.0
|
||||||
|
bottle_spacing_x = 42.0 # X方向间距
|
||||||
|
bottle_spacing_y = 35.0 # Y方向间距
|
||||||
|
|
||||||
|
# 计算起始位置 (居中排列)
|
||||||
|
start_x = (carrier_size_x - (4 - 1) * bottle_spacing_x - bottle_diameter) / 2
|
||||||
|
start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2
|
||||||
|
|
||||||
|
sites = create_ordered_items_2d(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
num_items_x=4,
|
||||||
|
num_items_y=2,
|
||||||
|
dx=start_x,
|
||||||
|
dy=start_y,
|
||||||
|
dz=5.0,
|
||||||
|
item_dx=bottle_spacing_x,
|
||||||
|
item_dy=bottle_spacing_y,
|
||||||
|
size_x=bottle_diameter,
|
||||||
|
size_y=bottle_diameter,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
)
|
||||||
|
for k, v in sites.items():
|
||||||
|
v.name = f"{name}_{v.name}"
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=sites,
|
||||||
|
model="6x5ml_DispensingVialCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 4
|
||||||
|
carrier.num_items_y = 2
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
ordering = ["A1", "A2", "A3", "A4", "B1", "B2", "B3", "B4"]
|
||||||
|
for i in range(8):
|
||||||
|
carrier[i] = YB_5ml_Dispensing_Vial(f"{name}_vial_{ordering[i]}")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_6x20ml_DispensingVialCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""20ml分液瓶板 - 4x2布局,8个位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 70.0
|
||||||
|
|
||||||
|
# 瓶位尺寸
|
||||||
|
bottle_diameter = 20.0
|
||||||
|
bottle_spacing_x = 42.0 # X方向间距
|
||||||
|
bottle_spacing_y = 35.0 # Y方向间距
|
||||||
|
|
||||||
|
# 计算起始位置 (居中排列)
|
||||||
|
start_x = (carrier_size_x - (4 - 1) * bottle_spacing_x - bottle_diameter) / 2
|
||||||
|
start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2
|
||||||
|
|
||||||
|
sites = create_ordered_items_2d(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
num_items_x=4,
|
||||||
|
num_items_y=2,
|
||||||
|
dx=start_x,
|
||||||
|
dy=start_y,
|
||||||
|
dz=5.0,
|
||||||
|
item_dx=bottle_spacing_x,
|
||||||
|
item_dy=bottle_spacing_y,
|
||||||
|
size_x=bottle_diameter,
|
||||||
|
size_y=bottle_diameter,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
)
|
||||||
|
for k, v in sites.items():
|
||||||
|
v.name = f"{name}_{v.name}"
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=sites,
|
||||||
|
model="6x20ml_DispensingVialCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 4
|
||||||
|
carrier.num_items_y = 2
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
ordering = ["A1", "A2", "A3", "A4", "B1", "B2", "B3", "B4"]
|
||||||
|
for i in range(8):
|
||||||
|
carrier[i] = YB_20ml_Dispensing_Vial(f"{name}_vial_{ordering[i]}")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_6x_SmallSolutionBottleCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""配液瓶(小)板 - 4x2布局,8个位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 65.0
|
||||||
|
|
||||||
|
# 瓶位尺寸
|
||||||
|
bottle_diameter = 35.0
|
||||||
|
bottle_spacing_x = 42.0 # X方向间距
|
||||||
|
bottle_spacing_y = 35.0 # Y方向间距
|
||||||
|
|
||||||
|
# 计算起始位置 (居中排列)
|
||||||
|
start_x = (carrier_size_x - (4 - 1) * bottle_spacing_x - bottle_diameter) / 2
|
||||||
|
start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2
|
||||||
|
|
||||||
|
sites = create_ordered_items_2d(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
num_items_x=4,
|
||||||
|
num_items_y=2,
|
||||||
|
dx=start_x,
|
||||||
|
dy=start_y,
|
||||||
|
dz=5.0,
|
||||||
|
item_dx=bottle_spacing_x,
|
||||||
|
item_dy=bottle_spacing_y,
|
||||||
|
size_x=bottle_diameter,
|
||||||
|
size_y=bottle_diameter,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
)
|
||||||
|
for k, v in sites.items():
|
||||||
|
v.name = f"{name}_{v.name}"
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=sites,
|
||||||
|
model="6x_SmallSolutionBottleCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 4
|
||||||
|
carrier.num_items_y = 2
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
ordering = ["A1", "A2", "A3", "A4", "B1", "B2", "B3", "B4"]
|
||||||
|
for i in range(8):
|
||||||
|
carrier[i] = YB_Small_Solution_Bottle(f"{name}_bottle_{ordering[i]}")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_4x_LargeSolutionBottleCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""配液瓶(大)板 - 2x2布局,4个位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 95.0
|
||||||
|
|
||||||
|
# 瓶位尺寸
|
||||||
|
bottle_diameter = 55.0
|
||||||
|
bottle_spacing_x = 60.0 # X方向间距
|
||||||
|
bottle_spacing_y = 60.0 # Y方向间距
|
||||||
|
|
||||||
|
# 计算起始位置 (居中排列)
|
||||||
|
start_x = (carrier_size_x - (2 - 1) * bottle_spacing_x - bottle_diameter) / 2
|
||||||
|
start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2
|
||||||
|
|
||||||
|
sites = create_ordered_items_2d(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
num_items_x=2,
|
||||||
|
num_items_y=2,
|
||||||
|
dx=start_x,
|
||||||
|
dy=start_y,
|
||||||
|
dz=5.0,
|
||||||
|
item_dx=bottle_spacing_x,
|
||||||
|
item_dy=bottle_spacing_y,
|
||||||
|
size_x=bottle_diameter,
|
||||||
|
size_y=bottle_diameter,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
)
|
||||||
|
for k, v in sites.items():
|
||||||
|
v.name = f"{name}_{v.name}"
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=sites,
|
||||||
|
model="4x_LargeSolutionBottleCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 2
|
||||||
|
carrier.num_items_y = 2
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
ordering = ["A1", "A2", "B1", "B2"]
|
||||||
|
for i in range(4):
|
||||||
|
carrier[i] = YB_Large_Solution_Bottle(f"{name}_bottle_{ordering[i]}")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_6x_LargeDispenseHeadCarrier(name: str) -> BottleCarrier:
|
||||||
|
"""加样头(大)板 - 1x1布局,1个位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 95.0
|
||||||
|
|
||||||
|
# 瓶位尺寸
|
||||||
|
bottle_diameter = 35.0
|
||||||
|
bottle_spacing_x = 42.0 # X方向间距
|
||||||
|
bottle_spacing_y = 35.0 # Y方向间距
|
||||||
|
|
||||||
|
# 计算起始位置 (居中排列)
|
||||||
|
start_x = (carrier_size_x - (1 - 1) * bottle_spacing_x - bottle_diameter) / 2
|
||||||
|
start_y = (carrier_size_y - (1 - 1) * bottle_spacing_y - bottle_diameter) / 2
|
||||||
|
|
||||||
|
sites = create_ordered_items_2d(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
num_items_x=1,
|
||||||
|
num_items_y=1,
|
||||||
|
dx=start_x,
|
||||||
|
dy=start_y,
|
||||||
|
dz=5.0,
|
||||||
|
item_dx=bottle_spacing_x,
|
||||||
|
item_dy=bottle_spacing_y,
|
||||||
|
size_x=bottle_diameter,
|
||||||
|
size_y=bottle_diameter,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
)
|
||||||
|
for k, v in sites.items():
|
||||||
|
v.name = f"{name}_{v.name}"
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=sites,
|
||||||
|
model="6x_LargeDispenseHeadCarrier",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 1
|
||||||
|
carrier.num_items_y = 1
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
carrier[0] = YB_Large_Dispense_Head(f"{name}_head_1")
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_AdapterBlock(name: str) -> BottleCarrier:
|
||||||
|
"""适配器块 - 单个中央位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 30.0
|
||||||
|
|
||||||
|
# 适配器尺寸
|
||||||
|
adapter_diameter = 80.0
|
||||||
|
|
||||||
|
# 计算中央位置
|
||||||
|
center_x = (carrier_size_x - adapter_diameter) / 2
|
||||||
|
center_y = (carrier_size_y - adapter_diameter) / 2
|
||||||
|
center_z = 0.0
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=create_homogeneous_resources(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
locations=[Coordinate(center_x, center_y, center_z)],
|
||||||
|
resource_size_x=adapter_diameter,
|
||||||
|
resource_size_y=adapter_diameter,
|
||||||
|
name_prefix=name,
|
||||||
|
),
|
||||||
|
model="AdapterBlock",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 1
|
||||||
|
carrier.num_items_y = 1
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
# 适配器块本身不包含瓶子,只是一个支撑结构
|
||||||
|
return carrier
|
||||||
|
|
||||||
|
|
||||||
|
def YB_TipBox(name: str) -> BottleCarrier:
|
||||||
|
"""枪头盒 - 8x12布局,96个位置"""
|
||||||
|
|
||||||
|
# 载架尺寸 (mm)
|
||||||
|
carrier_size_x = 127.8
|
||||||
|
carrier_size_y = 85.5
|
||||||
|
carrier_size_z = 55.0
|
||||||
|
|
||||||
|
# 枪头尺寸
|
||||||
|
tip_diameter = 10.0
|
||||||
|
tip_spacing_x = 9.0 # X方向间距
|
||||||
|
tip_spacing_y = 9.0 # Y方向间距
|
||||||
|
|
||||||
|
# 计算起始位置 (居中排列)
|
||||||
|
start_x = (carrier_size_x - (12 - 1) * tip_spacing_x - tip_diameter) / 2
|
||||||
|
start_y = (carrier_size_y - (8 - 1) * tip_spacing_y - tip_diameter) / 2
|
||||||
|
|
||||||
|
sites = create_ordered_items_2d(
|
||||||
|
klass=ResourceHolder,
|
||||||
|
num_items_x=12,
|
||||||
|
num_items_y=8,
|
||||||
|
dx=start_x,
|
||||||
|
dy=start_y,
|
||||||
|
dz=5.0,
|
||||||
|
item_dx=tip_spacing_x,
|
||||||
|
item_dy=tip_spacing_y,
|
||||||
|
size_x=tip_diameter,
|
||||||
|
size_y=tip_diameter,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
)
|
||||||
|
for k, v in sites.items():
|
||||||
|
v.name = f"{name}_{v.name}"
|
||||||
|
|
||||||
|
carrier = BottleCarrier(
|
||||||
|
name=name,
|
||||||
|
size_x=carrier_size_x,
|
||||||
|
size_y=carrier_size_y,
|
||||||
|
size_z=carrier_size_z,
|
||||||
|
sites=sites,
|
||||||
|
model="TipBox",
|
||||||
|
)
|
||||||
|
carrier.num_items_x = 12
|
||||||
|
carrier.num_items_y = 8
|
||||||
|
carrier.num_items_z = 1
|
||||||
|
# 创建96个枪头
|
||||||
|
for i in range(96):
|
||||||
|
row = chr(65 + i // 12) # A-H
|
||||||
|
col = (i % 12) + 1 # 1-12
|
||||||
|
carrier[i] = YB_Pipette_Tip(f"{name}_tip_{row}{col}")
|
||||||
|
return carrier
|
||||||
|
|
||||||
255
unilabos/resources/bioyond/bottles.py
Normal file
255
unilabos/resources/bioyond/bottles.py
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
from unilabos.resources.itemized_carrier import Bottle, BottleCarrier
|
||||||
|
# 工厂函数
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Solid_Stock(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 20.0,
|
||||||
|
height: float = 100.0,
|
||||||
|
max_volume: float = 30000.0, # 30mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建粉末瓶"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,# 未知
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Solid_Stock",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Solid_Vial(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 25.0,
|
||||||
|
height: float = 60.0,
|
||||||
|
max_volume: float = 30000.0, # 30mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建粉末瓶"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Solid_Vial",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Liquid_Vial(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 25.0,
|
||||||
|
height: float = 60.0,
|
||||||
|
max_volume: float = 30000.0, # 30mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建滴定液瓶"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Liquid_Vial",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Solution_Beaker(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 60.0,
|
||||||
|
height: float = 70.0,
|
||||||
|
max_volume: float = 200000.0, # 200mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建溶液烧杯"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Solution_Beaker",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Reagent_Bottle(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 70.0,
|
||||||
|
height: float = 120.0,
|
||||||
|
max_volume: float = 500000.0, # 500mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建试剂瓶"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Reagent_Bottle",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_100ml_Liquid_Bottle(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 50.0,
|
||||||
|
height: float = 80.0,
|
||||||
|
max_volume: float = 100000.0, # 100mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建100ml液体瓶"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="100ml_Liquid_Bottle",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Liquid_Bottle(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 40.0,
|
||||||
|
height: float = 70.0,
|
||||||
|
max_volume: float = 50000.0, # 50mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建液体瓶"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Liquid_Bottle",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_High_Viscosity_Liquid_Bottle(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 45.0,
|
||||||
|
height: float = 75.0,
|
||||||
|
max_volume: float = 60000.0, # 60mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建高粘液瓶"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="High_Viscosity_Liquid_Bottle",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Large_Dispense_Head(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 35.0,
|
||||||
|
height: float = 90.0,
|
||||||
|
max_volume: float = 50000.0, # 50mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建加样头(大)"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Large_Dispense_Head",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_5ml_Dispensing_Vial(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 15.0,
|
||||||
|
height: float = 45.0,
|
||||||
|
max_volume: float = 5000.0, # 5mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建5ml分液瓶"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="5ml_Dispensing_Vial",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_20ml_Dispensing_Vial(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 20.0,
|
||||||
|
height: float = 65.0,
|
||||||
|
max_volume: float = 20000.0, # 20mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建20ml分液瓶"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="20ml_Dispensing_Vial",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Small_Solution_Bottle(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 35.0,
|
||||||
|
height: float = 60.0,
|
||||||
|
max_volume: float = 40000.0, # 40mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建配液瓶(小)"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Small_Solution_Bottle",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Large_Solution_Bottle(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 55.0,
|
||||||
|
height: float = 90.0,
|
||||||
|
max_volume: float = 150000.0, # 150mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建配液瓶(大)"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Large_Solution_Bottle",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def YB_Pipette_Tip(
|
||||||
|
name: str,
|
||||||
|
diameter: float = 10.0,
|
||||||
|
height: float = 50.0,
|
||||||
|
max_volume: float = 1000.0, # 1mL
|
||||||
|
barcode: str = None,
|
||||||
|
) -> Bottle:
|
||||||
|
"""创建枪头"""
|
||||||
|
return Bottle(
|
||||||
|
name=name,
|
||||||
|
diameter=diameter,
|
||||||
|
height=height,
|
||||||
|
max_volume=max_volume,
|
||||||
|
barcode=barcode,
|
||||||
|
model="Pipette_Tip",
|
||||||
|
)
|
||||||
|
|
||||||
Reference in New Issue
Block a user