mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-04-01 18:56:47 +00:00
新增 tip_above_rack_length 参数并更新 PRCXI 枪头尺寸
- TipInfo 新增 tip_above_rack_length 可选字段 - 编辑器支持 tip_above 与 dz 互算,更新中文标签 - 侧视图绘制枪头露出部分并标注,俯视图/侧视图增加 dx/dy/dz 标注 - 预览增加回中按钮,详情页展示新字段 - 导入时自动计算 tip_above_rack_length - 批量更新 PRCXI 枪头物理尺寸及 registry YAML Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -399,6 +399,11 @@ def import_from_code() -> LabwareDB:
|
|||||||
tip_fitting_depth=tip_depth,
|
tip_fitting_depth=tip_depth,
|
||||||
has_filter=tip_filter,
|
has_filter=tip_filter,
|
||||||
)
|
)
|
||||||
|
# 计算 tip_above_rack_length = tip_length - (size_z - dz)
|
||||||
|
if grid_data:
|
||||||
|
_dz = grid_data.get("dz", 0.0)
|
||||||
|
_above = tip_length - (item.size_z - _dz)
|
||||||
|
item.tip.tip_above_rack_length = round(_above, 4) if _above > 0 else None
|
||||||
|
|
||||||
elif type_name == "tube_rack" and children:
|
elif type_name == "tube_rack" and children:
|
||||||
if grid_data:
|
if grid_data:
|
||||||
|
|||||||
@@ -306,9 +306,9 @@
|
|||||||
"type": "tip_rack",
|
"type": "tip_rack",
|
||||||
"function_name": "PRCXI_10ul_eTips",
|
"function_name": "PRCXI_10ul_eTips",
|
||||||
"docstring": "对应 JSON Code: ZX-001-10+",
|
"docstring": "对应 JSON Code: ZX-001-10+",
|
||||||
"size_x": 122.11,
|
"size_x": 127.76,
|
||||||
"size_y": 85.48,
|
"size_y": 85.48,
|
||||||
"size_z": 58.23,
|
"size_z": 58.0,
|
||||||
"model": "PRCXI_10ul_eTips",
|
"model": "PRCXI_10ul_eTips",
|
||||||
"category": "tip_rack",
|
"category": "tip_rack",
|
||||||
"plate_type": null,
|
"plate_type": null,
|
||||||
@@ -327,21 +327,22 @@
|
|||||||
"grid": {
|
"grid": {
|
||||||
"num_items_x": 12,
|
"num_items_x": 12,
|
||||||
"num_items_y": 8,
|
"num_items_y": 8,
|
||||||
"dx": 7.97,
|
"dx": 10.63,
|
||||||
"dy": 5.0,
|
"dy": 7.49,
|
||||||
"dz": 2.0,
|
"dz": 14.6,
|
||||||
"item_dx": 9.0,
|
"item_dx": 9.0,
|
||||||
"item_dy": 9.0
|
"item_dy": 9.0
|
||||||
},
|
},
|
||||||
"well": null,
|
"well": null,
|
||||||
"volume_functions": null,
|
"volume_functions": null,
|
||||||
"tip": {
|
"tip": {
|
||||||
"spot_size_x": 7.0,
|
"spot_size_x": 7.5,
|
||||||
"spot_size_y": 7.0,
|
"spot_size_y": 7.5,
|
||||||
"spot_size_z": 0.0,
|
"spot_size_z": 0.0,
|
||||||
"tip_volume": 300.0,
|
"tip_volume": 10.0,
|
||||||
"tip_length": 60.0,
|
"tip_length": 52.0,
|
||||||
"tip_fitting_depth": 51.0,
|
"tip_fitting_depth": 8.2,
|
||||||
|
"tip_above_rack_length": 8.6,
|
||||||
"has_filter": false
|
"has_filter": false
|
||||||
},
|
},
|
||||||
"tube": null,
|
"tube": null,
|
||||||
@@ -354,9 +355,9 @@
|
|||||||
"type": "tip_rack",
|
"type": "tip_rack",
|
||||||
"function_name": "PRCXI_300ul_Tips",
|
"function_name": "PRCXI_300ul_Tips",
|
||||||
"docstring": "对应 JSON Code: ZX-001-300\n吸头盒通常比较特殊,需要定义 Tip 对象",
|
"docstring": "对应 JSON Code: ZX-001-300\n吸头盒通常比较特殊,需要定义 Tip 对象",
|
||||||
"size_x": 122.11,
|
"size_x": 127.76,
|
||||||
"size_y": 85.48,
|
"size_y": 85.48,
|
||||||
"size_z": 58.23,
|
"size_z": 58.0,
|
||||||
"model": "PRCXI_300ul_Tips",
|
"model": "PRCXI_300ul_Tips",
|
||||||
"category": "tip_rack",
|
"category": "tip_rack",
|
||||||
"plate_type": null,
|
"plate_type": null,
|
||||||
@@ -375,21 +376,22 @@
|
|||||||
"grid": {
|
"grid": {
|
||||||
"num_items_x": 12,
|
"num_items_x": 12,
|
||||||
"num_items_y": 8,
|
"num_items_y": 8,
|
||||||
"dx": 7.97,
|
"dx": 10.63,
|
||||||
"dy": 5.0,
|
"dy": 7.49,
|
||||||
"dz": 2.0,
|
"dz": 6.6,
|
||||||
"item_dx": 9.0,
|
"item_dx": 9.0,
|
||||||
"item_dy": 9.0
|
"item_dy": 9.0
|
||||||
},
|
},
|
||||||
"well": null,
|
"well": null,
|
||||||
"volume_functions": null,
|
"volume_functions": null,
|
||||||
"tip": {
|
"tip": {
|
||||||
"spot_size_x": 7.0,
|
"spot_size_x": 7.5,
|
||||||
"spot_size_y": 7.0,
|
"spot_size_y": 7.5,
|
||||||
"spot_size_z": 0.0,
|
"spot_size_z": 0.0,
|
||||||
"tip_volume": 300.0,
|
"tip_volume": 300.0,
|
||||||
"tip_length": 60.0,
|
"tip_length": 60.0,
|
||||||
"tip_fitting_depth": 51.0,
|
"tip_fitting_depth": 8.2,
|
||||||
|
"tip_above_rack_length": 8.6,
|
||||||
"has_filter": false
|
"has_filter": false
|
||||||
},
|
},
|
||||||
"tube": null,
|
"tube": null,
|
||||||
@@ -788,9 +790,9 @@
|
|||||||
"type": "tip_rack",
|
"type": "tip_rack",
|
||||||
"function_name": "PRCXI_1250uL_Tips",
|
"function_name": "PRCXI_1250uL_Tips",
|
||||||
"docstring": "Code: ZX-001-1250",
|
"docstring": "Code: ZX-001-1250",
|
||||||
"size_x": 118.09,
|
"size_x": 127.76,
|
||||||
"size_y": 80.7,
|
"size_y": 85.48,
|
||||||
"size_z": 107.67,
|
"size_z": 98.0,
|
||||||
"model": "PRCXI_1250uL_Tips",
|
"model": "PRCXI_1250uL_Tips",
|
||||||
"category": "tip_rack",
|
"category": "tip_rack",
|
||||||
"plate_type": null,
|
"plate_type": null,
|
||||||
@@ -809,21 +811,22 @@
|
|||||||
"grid": {
|
"grid": {
|
||||||
"num_items_x": 12,
|
"num_items_x": 12,
|
||||||
"num_items_y": 8,
|
"num_items_y": 8,
|
||||||
"dx": 5.57,
|
"dx": 10.63,
|
||||||
"dy": 4.875,
|
"dy": 7.49,
|
||||||
"dz": 2.0,
|
"dz": 6.6,
|
||||||
"item_dx": 9.0,
|
"item_dx": 9.0,
|
||||||
"item_dy": 9.0
|
"item_dy": 9.0
|
||||||
},
|
},
|
||||||
"well": null,
|
"well": null,
|
||||||
"volume_functions": null,
|
"volume_functions": null,
|
||||||
"tip": {
|
"tip": {
|
||||||
"spot_size_x": 7.0,
|
"spot_size_x": 7.5,
|
||||||
"spot_size_y": 7.0,
|
"spot_size_y": 7.5,
|
||||||
"spot_size_z": 0.0,
|
"spot_size_z": 100.0,
|
||||||
"tip_volume": 300.0,
|
"tip_volume": 1250.0,
|
||||||
"tip_length": 60.0,
|
"tip_length": 100.0,
|
||||||
"tip_fitting_depth": 51.0,
|
"tip_fitting_depth": 8.2,
|
||||||
|
"tip_above_rack_length": 8.6,
|
||||||
"has_filter": false
|
"has_filter": false
|
||||||
},
|
},
|
||||||
"tube": null,
|
"tube": null,
|
||||||
@@ -836,9 +839,9 @@
|
|||||||
"type": "tip_rack",
|
"type": "tip_rack",
|
||||||
"function_name": "PRCXI_10uL_Tips",
|
"function_name": "PRCXI_10uL_Tips",
|
||||||
"docstring": "Code: ZX-001-10",
|
"docstring": "Code: ZX-001-10",
|
||||||
"size_x": 120.98,
|
"size_x": 127.76,
|
||||||
"size_y": 82.12,
|
"size_y": 85.48,
|
||||||
"size_z": 67.0,
|
"size_z": 58.0,
|
||||||
"model": "PRCXI_10uL_Tips",
|
"model": "PRCXI_10uL_Tips",
|
||||||
"category": "tip_rack",
|
"category": "tip_rack",
|
||||||
"plate_type": null,
|
"plate_type": null,
|
||||||
@@ -857,21 +860,22 @@
|
|||||||
"grid": {
|
"grid": {
|
||||||
"num_items_x": 12,
|
"num_items_x": 12,
|
||||||
"num_items_y": 8,
|
"num_items_y": 8,
|
||||||
"dx": 8.49,
|
"dx": 10.63,
|
||||||
"dy": 7.06,
|
"dy": 7.49,
|
||||||
"dz": 2.0,
|
"dz": 14.6,
|
||||||
"item_dx": 9.0,
|
"item_dx": 9.0,
|
||||||
"item_dy": 9.0
|
"item_dy": 9.0
|
||||||
},
|
},
|
||||||
"well": null,
|
"well": null,
|
||||||
"volume_functions": null,
|
"volume_functions": null,
|
||||||
"tip": {
|
"tip": {
|
||||||
"spot_size_x": 7.0,
|
"spot_size_x": 7.5,
|
||||||
"spot_size_y": 7.0,
|
"spot_size_y": 7.5,
|
||||||
"spot_size_z": 0.0,
|
"spot_size_z": 0.0,
|
||||||
"tip_volume": 300.0,
|
"tip_volume": 10.0,
|
||||||
"tip_length": 60.0,
|
"tip_length": 52.0,
|
||||||
"tip_fitting_depth": 51.0,
|
"tip_fitting_depth": 8.2,
|
||||||
|
"tip_above_rack_length": 8.6,
|
||||||
"has_filter": false
|
"has_filter": false
|
||||||
},
|
},
|
||||||
"tube": null,
|
"tube": null,
|
||||||
@@ -884,8 +888,8 @@
|
|||||||
"type": "tip_rack",
|
"type": "tip_rack",
|
||||||
"function_name": "PRCXI_1000uL_Tips",
|
"function_name": "PRCXI_1000uL_Tips",
|
||||||
"docstring": "Code: ZX-001-1000",
|
"docstring": "Code: ZX-001-1000",
|
||||||
"size_x": 128.09,
|
"size_x": 127.76,
|
||||||
"size_y": 85.8,
|
"size_y": 85.48,
|
||||||
"size_z": 98.0,
|
"size_z": 98.0,
|
||||||
"model": "PRCXI_1000uL_Tips",
|
"model": "PRCXI_1000uL_Tips",
|
||||||
"category": "tip_rack",
|
"category": "tip_rack",
|
||||||
@@ -905,21 +909,22 @@
|
|||||||
"grid": {
|
"grid": {
|
||||||
"num_items_x": 12,
|
"num_items_x": 12,
|
||||||
"num_items_y": 8,
|
"num_items_y": 8,
|
||||||
"dx": 10.525,
|
"dx": 10.63,
|
||||||
"dy": 7.425,
|
"dy": 7.49,
|
||||||
"dz": 2.0,
|
"dz": 6.6,
|
||||||
"item_dx": 9.0,
|
"item_dx": 9.0,
|
||||||
"item_dy": 9.0
|
"item_dy": 9.0
|
||||||
},
|
},
|
||||||
"well": null,
|
"well": null,
|
||||||
"volume_functions": null,
|
"volume_functions": null,
|
||||||
"tip": {
|
"tip": {
|
||||||
"spot_size_x": 7.0,
|
"spot_size_x": 7.5,
|
||||||
"spot_size_y": 7.0,
|
"spot_size_y": 7.5,
|
||||||
"spot_size_z": 0.0,
|
"spot_size_z": 100.0,
|
||||||
"tip_volume": 300.0,
|
"tip_volume": 1000.0,
|
||||||
"tip_length": 60.0,
|
"tip_length": 100.0,
|
||||||
"tip_fitting_depth": 51.0,
|
"tip_fitting_depth": 8.2,
|
||||||
|
"tip_above_rack_length": 8.6,
|
||||||
"has_filter": false
|
"has_filter": false
|
||||||
},
|
},
|
||||||
"tube": null,
|
"tube": null,
|
||||||
@@ -968,6 +973,7 @@
|
|||||||
"tip_volume": 300.0,
|
"tip_volume": 300.0,
|
||||||
"tip_length": 60.0,
|
"tip_length": 60.0,
|
||||||
"tip_fitting_depth": 51.0,
|
"tip_fitting_depth": 51.0,
|
||||||
|
"tip_above_rack_length": null,
|
||||||
"has_filter": false
|
"has_filter": false
|
||||||
},
|
},
|
||||||
"tube": null,
|
"tube": null,
|
||||||
@@ -1256,6 +1262,55 @@
|
|||||||
},
|
},
|
||||||
"include_in_template_matching": false,
|
"include_in_template_matching": false,
|
||||||
"template_kind": null
|
"template_kind": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5bb65eb2",
|
||||||
|
"type": "tip_rack",
|
||||||
|
"function_name": "PRCXI_50uL_tips",
|
||||||
|
"docstring": "",
|
||||||
|
"size_x": 127.76,
|
||||||
|
"size_y": 85.48,
|
||||||
|
"size_z": 58.0,
|
||||||
|
"model": "PRCXI_50uL_tips",
|
||||||
|
"category": null,
|
||||||
|
"plate_type": null,
|
||||||
|
"material_info": {
|
||||||
|
"uuid": "",
|
||||||
|
"Code": "",
|
||||||
|
"Name": "",
|
||||||
|
"materialEnum": null,
|
||||||
|
"SupplyType": 1
|
||||||
|
},
|
||||||
|
"registry_category": [
|
||||||
|
"prcxi",
|
||||||
|
"tip_racks"
|
||||||
|
],
|
||||||
|
"registry_description": "",
|
||||||
|
"grid": {
|
||||||
|
"num_items_x": 12,
|
||||||
|
"num_items_y": 8,
|
||||||
|
"dx": 10.63,
|
||||||
|
"dy": 7.49,
|
||||||
|
"dz": 13.6,
|
||||||
|
"item_dx": 9.0,
|
||||||
|
"item_dy": 9.0
|
||||||
|
},
|
||||||
|
"well": null,
|
||||||
|
"volume_functions": null,
|
||||||
|
"tip": {
|
||||||
|
"spot_size_x": 7.5,
|
||||||
|
"spot_size_y": 7.5,
|
||||||
|
"spot_size_z": 0.0,
|
||||||
|
"tip_volume": 50.0,
|
||||||
|
"tip_length": 53.0,
|
||||||
|
"tip_fitting_depth": 8.2,
|
||||||
|
"tip_above_rack_length": 8.6,
|
||||||
|
"has_filter": false
|
||||||
|
},
|
||||||
|
"tube": null,
|
||||||
|
"adapter": null,
|
||||||
|
"include_in_template_matching": false,
|
||||||
|
"template_kind": "tip_rack"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -53,6 +53,7 @@ class TipInfo(BaseModel):
|
|||||||
tip_volume: float = 300.0
|
tip_volume: float = 300.0
|
||||||
tip_length: float = 60.0
|
tip_length: float = 60.0
|
||||||
tip_fitting_depth: float = 51.0
|
tip_fitting_depth: float = 51.0
|
||||||
|
tip_above_rack_length: Optional[float] = None
|
||||||
has_filter: bool = False
|
has_filter: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ function collectFormData() {
|
|||||||
tip_volume: g('f-tip_vol') || 300,
|
tip_volume: g('f-tip_vol') || 300,
|
||||||
tip_length: g('f-tip_len') || 60,
|
tip_length: g('f-tip_len') || 60,
|
||||||
tip_fitting_depth: g('f-tip_dep') || 51,
|
tip_fitting_depth: g('f-tip_dep') || 51,
|
||||||
|
tip_above_rack_length: g('f-tip_above'),
|
||||||
has_filter: g('f-tip_filter') || false,
|
has_filter: g('f-tip_filter') || false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -155,6 +156,60 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (!form) return;
|
if (!form) return;
|
||||||
form.addEventListener('input', updatePreview);
|
form.addEventListener('input', updatePreview);
|
||||||
form.addEventListener('change', updatePreview);
|
form.addEventListener('change', updatePreview);
|
||||||
|
|
||||||
|
// tip_above_rack_length 与 dz 互算
|
||||||
|
// 公式: tip_length = tip_above_rack_length + size_z - dz
|
||||||
|
// 规则: 填 tip_above → 自动算 dz;填 dz → 自动算 tip_above
|
||||||
|
// 改 size_z / tip_length → 优先重算 tip_above(若有值),否则算 dz
|
||||||
|
|
||||||
|
function _getVal(id) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
return (el && el.value !== '') ? parseFloat(el.value) : null;
|
||||||
|
}
|
||||||
|
function _setVal(id, v) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (el) el.value = Math.round(v * 1000) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoCalcTipAbove(changedId) {
|
||||||
|
const typeEl = document.getElementById('f-type');
|
||||||
|
if (!typeEl || typeEl.value !== 'tip_rack') return;
|
||||||
|
|
||||||
|
const tipLen = _getVal('f-tip_len');
|
||||||
|
const sizeZ = _getVal('f-size_z');
|
||||||
|
const dz = _getVal('f-grid_dz');
|
||||||
|
const above = _getVal('f-tip_above');
|
||||||
|
|
||||||
|
// 需要 tip_length 和 size_z 才能计算
|
||||||
|
if (tipLen == null || sizeZ == null) return;
|
||||||
|
|
||||||
|
if (changedId === 'f-tip_above') {
|
||||||
|
// 用户填了 tip_above → 算 dz
|
||||||
|
if (above != null) _setVal('f-grid_dz', above + sizeZ - tipLen);
|
||||||
|
} else if (changedId === 'f-grid_dz') {
|
||||||
|
// 用户填了 dz → 算 tip_above
|
||||||
|
if (dz != null) _setVal('f-tip_above', tipLen - sizeZ + dz);
|
||||||
|
} else {
|
||||||
|
// size_z 或 tip_length 变了 → 优先重算 tip_above(若已有值或 dz 已有值)
|
||||||
|
if (dz != null) {
|
||||||
|
_setVal('f-tip_above', tipLen - sizeZ + dz);
|
||||||
|
} else if (above != null) {
|
||||||
|
_setVal('f-grid_dz', above + sizeZ - tipLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定 input 事件
|
||||||
|
for (const id of ['f-tip_len', 'f-size_z', 'f-grid_dz', 'f-tip_above']) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (el) el.addEventListener('input', () => autoCalcTipAbove(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑已有 tip_rack 条目时自动补算 tip_above_rack_length
|
||||||
|
const typeEl = document.getElementById('f-type');
|
||||||
|
if (typeEl && typeEl.value === 'tip_rack') {
|
||||||
|
autoCalcTipAbove('f-grid_dz');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 自动居中:根据板尺寸和孔阵列参数计算 dx/dy
|
// 自动居中:根据板尺寸和孔阵列参数计算 dx/dy
|
||||||
|
|||||||
@@ -161,6 +161,27 @@ function renderTopDown(container, data) {
|
|||||||
el.addEventListener('mouseleave', () => el.setAttribute('fill', '#fff'));
|
el.addEventListener('mouseleave', () => el.setAttribute('fill', '#fff'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dx / dy 标注(板边到首个子元素左上角)
|
||||||
|
const dimColor = '#e67e22';
|
||||||
|
const firstLeft = pad + dx; // 首列子元素左边 X
|
||||||
|
const firstTop = pad + dy; // 首行子元素上边 Y
|
||||||
|
if (dx > 0.1) {
|
||||||
|
// dx: 板左边 → 首列子元素左边,画在第一行子元素中心高度
|
||||||
|
const annY = firstTop + csy / 2;
|
||||||
|
_line(svg, pad, annY, firstLeft, annY, dimColor, '1,1');
|
||||||
|
_line(svg, pad, annY - 2, pad, annY + 2, dimColor);
|
||||||
|
_line(svg, firstLeft, annY - 2, firstLeft, annY + 2, dimColor);
|
||||||
|
_text(svg, pad + dx / 2, annY - 2, `dx=${dx}`, '2.5', 'middle', dimColor);
|
||||||
|
}
|
||||||
|
if (dy > 0.1) {
|
||||||
|
// dy: 板上边 → 首行子元素上边,画在第一列子元素中心宽度
|
||||||
|
const annX = firstLeft + csx / 2;
|
||||||
|
_line(svg, annX, pad, annX, firstTop, dimColor, '1,1');
|
||||||
|
_line(svg, annX - 2, pad, annX + 2, pad, dimColor);
|
||||||
|
_line(svg, annX - 2, firstTop, annX + 2, firstTop, dimColor);
|
||||||
|
_text(svg, annX + 4, pad + dy / 2 + 1, `dy=${dy}`, '2.5', 'start', dimColor);
|
||||||
|
}
|
||||||
} else if (data.type === 'plate_adapter' && data.adapter) {
|
} else if (data.type === 'plate_adapter' && data.adapter) {
|
||||||
// 绘制适配器凹槽
|
// 绘制适配器凹槽
|
||||||
const adp = data.adapter;
|
const adp = data.adapter;
|
||||||
@@ -190,17 +211,33 @@ function renderSideProfile(container, data) {
|
|||||||
|
|
||||||
// 按比例缩放,侧面以 X-Z 面
|
// 按比例缩放,侧面以 X-Z 面
|
||||||
const scaleH = Math.max(1, sz / 60); // 让较矮的板子不会太小
|
const scaleH = Math.max(1, sz / 60); // 让较矮的板子不会太小
|
||||||
|
|
||||||
|
// 计算枪头露出高度(仅 tip_rack)
|
||||||
|
const tip = data.tip;
|
||||||
|
const grid = data.grid;
|
||||||
|
let tipAbove = 0;
|
||||||
|
if (data.type === 'tip_rack' && tip) {
|
||||||
|
if (tip.tip_above_rack_length != null && tip.tip_above_rack_length > 0) {
|
||||||
|
tipAbove = tip.tip_above_rack_length;
|
||||||
|
} else if (tip.tip_length && grid) {
|
||||||
|
const dz = grid.dz || 0;
|
||||||
|
const calc = tip.tip_length - (sz - dz);
|
||||||
|
if (calc > 0) tipAbove = calc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const drawW = sx;
|
const drawW = sx;
|
||||||
const drawH = sz;
|
const drawH = sz;
|
||||||
const w = drawW + pad * 2 + 30; // 额外空间给标注
|
const w = drawW + pad * 2 + 30; // 额外空间给标注
|
||||||
const h = drawH + pad * 2 + 10;
|
const h = drawH + tipAbove + pad * 2 + 10;
|
||||||
const svg = _makeSVG(w, h);
|
const svg = _makeSVG(w, h);
|
||||||
|
|
||||||
const color = TYPE_COLORS[data.type] || '#3b82f6';
|
const color = TYPE_COLORS[data.type] || '#3b82f6';
|
||||||
const baseY = pad + drawH; // 底部 Y
|
const baseY = pad + tipAbove + drawH; // 底部 Y
|
||||||
|
const rackTopY = pad + tipAbove; // rack 顶部 Y
|
||||||
|
|
||||||
// 板壳矩形
|
// 板壳矩形
|
||||||
_rect(svg, pad, pad, drawW, drawH, color + '15', color);
|
_rect(svg, pad, rackTopY, drawW, drawH, color + '15', color);
|
||||||
|
|
||||||
// 尺寸标注
|
// 尺寸标注
|
||||||
// X 方向
|
// X 方向
|
||||||
@@ -208,21 +245,19 @@ function renderSideProfile(container, data) {
|
|||||||
_text(svg, pad + drawW / 2, baseY + 12, `${sx} mm`, '3.5', 'middle', '#333');
|
_text(svg, pad + drawW / 2, baseY + 12, `${sx} mm`, '3.5', 'middle', '#333');
|
||||||
|
|
||||||
// Z 方向
|
// Z 方向
|
||||||
_line(svg, pad + drawW + 5, pad, pad + drawW + 5, baseY, '#333');
|
_line(svg, pad + drawW + 5, rackTopY, pad + drawW + 5, baseY, '#333');
|
||||||
const zt = document.createElementNS(_svgNS(), 'text');
|
const zt = document.createElementNS(_svgNS(), 'text');
|
||||||
zt.setAttribute('x', pad + drawW + 12);
|
zt.setAttribute('x', pad + drawW + 12);
|
||||||
zt.setAttribute('y', pad + drawH / 2);
|
zt.setAttribute('y', rackTopY + drawH / 2);
|
||||||
zt.setAttribute('font-size', '3.5');
|
zt.setAttribute('font-size', '3.5');
|
||||||
zt.setAttribute('text-anchor', 'middle');
|
zt.setAttribute('text-anchor', 'middle');
|
||||||
zt.setAttribute('fill', '#333');
|
zt.setAttribute('fill', '#333');
|
||||||
zt.setAttribute('font-family', 'sans-serif');
|
zt.setAttribute('font-family', 'sans-serif');
|
||||||
zt.setAttribute('transform', `rotate(-90, ${pad + drawW + 12}, ${pad + drawH / 2})`);
|
zt.setAttribute('transform', `rotate(-90, ${pad + drawW + 12}, ${rackTopY + drawH / 2})`);
|
||||||
zt.textContent = `${sz} mm`;
|
zt.textContent = `${sz} mm`;
|
||||||
svg.appendChild(zt);
|
svg.appendChild(zt);
|
||||||
|
|
||||||
const grid = data.grid;
|
|
||||||
const well = data.well;
|
const well = data.well;
|
||||||
const tip = data.tip;
|
|
||||||
const tube = data.tube;
|
const tube = data.tube;
|
||||||
|
|
||||||
if (grid && (well || tip || tube)) {
|
if (grid && (well || tip || tube)) {
|
||||||
@@ -230,6 +265,7 @@ function renderSideProfile(container, data) {
|
|||||||
const dz = grid.dz || 0;
|
const dz = grid.dz || 0;
|
||||||
const idx = grid.item_dx || 9;
|
const idx = grid.item_dx || 9;
|
||||||
const nx = Math.min(grid.num_items_x || 1, 24); // 最多画24列
|
const nx = Math.min(grid.num_items_x || 1, 24); // 最多画24列
|
||||||
|
const dimColor = '#e67e22';
|
||||||
|
|
||||||
const child = well || tube;
|
const child = well || tube;
|
||||||
const childTip = tip;
|
const childTip = tip;
|
||||||
@@ -275,31 +311,71 @@ function renderSideProfile(container, data) {
|
|||||||
// dz 标注
|
// dz 标注
|
||||||
if (dz > 0) {
|
if (dz > 0) {
|
||||||
const lx = pad + dx + 0.5 * idx * nDraw + csx / 2 + 5;
|
const lx = pad + dx + 0.5 * idx * nDraw + csx / 2 + 5;
|
||||||
_line(svg, lx, baseY, lx, baseY - dz, '#999', '1,1');
|
_line(svg, lx, baseY, lx, baseY - dz, dimColor, '1,1');
|
||||||
_text(svg, lx + 6, baseY - dz / 2, `dz=${dz}`, '2.5', 'start', '#999');
|
_line(svg, lx - 2, baseY, lx + 2, baseY, dimColor);
|
||||||
|
_line(svg, lx - 2, baseY - dz, lx + 2, baseY - dz, dimColor);
|
||||||
|
_text(svg, lx + 4, baseY - dz / 2 + 1, `dz=${dz}`, '2.5', 'start', dimColor);
|
||||||
|
}
|
||||||
|
// dx 标注
|
||||||
|
if (dx > 0.1) {
|
||||||
|
const annY = rackTopY + 4;
|
||||||
|
_line(svg, pad, annY, pad + dx, annY, dimColor, '1,1');
|
||||||
|
_line(svg, pad, annY - 2, pad, annY + 2, dimColor);
|
||||||
|
_line(svg, pad + dx, annY - 2, pad + dx, annY + 2, dimColor);
|
||||||
|
_text(svg, pad + dx / 2, annY - 2, `dx=${dx}`, '2.5', 'middle', dimColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (childTip) {
|
if (childTip) {
|
||||||
// 枪头截面
|
// 枪头截面
|
||||||
|
const tipLen = childTip.tip_length || 50;
|
||||||
const nDraw = Math.min(nx, 12);
|
const nDraw = Math.min(nx, 12);
|
||||||
for (let i = 0; i < nDraw; i++) {
|
for (let i = 0; i < nDraw; i++) {
|
||||||
const cx = pad + dx + 3.5 + i * idx;
|
const cx = pad + dx + 3.5 + i * idx;
|
||||||
const topZ = pad + dz;
|
// 枪头顶部 = rack顶部 - 露出长度
|
||||||
const tipLen = childTip.tip_length || 50;
|
const tipTopZ = rackTopY - tipAbove;
|
||||||
const drawLen = Math.min(tipLen, sz - dz);
|
const drawLen = Math.min(tipLen, sz - dz + tipAbove);
|
||||||
|
|
||||||
// 枪头轮廓 (梯形)
|
// 枪头轮廓 (梯形)
|
||||||
const topW = 4;
|
const topW = 4;
|
||||||
const botW = 1.5;
|
const botW = 1.5;
|
||||||
const p = document.createElementNS(_svgNS(), 'polygon');
|
const p = document.createElementNS(_svgNS(), 'polygon');
|
||||||
p.setAttribute('points',
|
p.setAttribute('points',
|
||||||
`${cx - topW / 2},${topZ} ${cx + topW / 2},${topZ} ${cx + botW / 2},${topZ + drawLen} ${cx - botW / 2},${topZ + drawLen}`);
|
`${cx - topW / 2},${tipTopZ} ${cx + topW / 2},${tipTopZ} ${cx + botW / 2},${tipTopZ + drawLen} ${cx - botW / 2},${tipTopZ + drawLen}`);
|
||||||
p.setAttribute('fill', '#10b98133');
|
p.setAttribute('fill', '#10b98133');
|
||||||
p.setAttribute('stroke', '#10b981');
|
p.setAttribute('stroke', '#10b981');
|
||||||
p.setAttribute('stroke-width', '0.3');
|
p.setAttribute('stroke-width', '0.3');
|
||||||
svg.appendChild(p);
|
svg.appendChild(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dz 标注
|
||||||
|
if (dz > 0) {
|
||||||
|
const lx = pad + dx + nDraw * idx + 5;
|
||||||
|
_line(svg, lx, baseY, lx, baseY - dz, dimColor, '1,1');
|
||||||
|
_line(svg, lx - 2, baseY, lx + 2, baseY, dimColor);
|
||||||
|
_line(svg, lx - 2, baseY - dz, lx + 2, baseY - dz, dimColor);
|
||||||
|
_text(svg, lx + 4, baseY - dz / 2 + 1, `dz=${dz}`, '2.5', 'start', dimColor);
|
||||||
|
}
|
||||||
|
// dx 标注
|
||||||
|
if (dx > 0.1) {
|
||||||
|
const annY = rackTopY + 4;
|
||||||
|
_line(svg, pad, annY, pad + dx, annY, dimColor, '1,1');
|
||||||
|
_line(svg, pad, annY - 2, pad, annY + 2, dimColor);
|
||||||
|
_line(svg, pad + dx, annY - 2, pad + dx, annY + 2, dimColor);
|
||||||
|
_text(svg, pad + dx / 2, annY - 2, `dx=${dx}`, '2.5', 'middle', dimColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 露出长度标注线
|
||||||
|
if (tipAbove > 0) {
|
||||||
|
const annotX = pad + dx + nDraw * idx + 8;
|
||||||
|
// rack 顶部水平参考线
|
||||||
|
_line(svg, annotX - 3, rackTopY, annotX + 3, rackTopY, '#10b981');
|
||||||
|
// 枪头顶部水平参考线
|
||||||
|
_line(svg, annotX - 3, rackTopY - tipAbove, annotX + 3, rackTopY - tipAbove, '#10b981');
|
||||||
|
// 竖直标注线
|
||||||
|
_line(svg, annotX, rackTopY - tipAbove, annotX, rackTopY, '#10b981', '1,1');
|
||||||
|
_text(svg, annotX + 2, rackTopY - tipAbove / 2 + 1, `露出=${Math.round(tipAbove * 100) / 100}mm`, '2.5', 'start', '#10b981');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (data.type === 'plate_adapter' && data.adapter) {
|
} else if (data.type === 'plate_adapter' && data.adapter) {
|
||||||
const adp = data.adapter;
|
const adp = data.adapter;
|
||||||
@@ -309,10 +385,10 @@ function renderSideProfile(container, data) {
|
|||||||
const ahx = adp.adapter_hole_size_x || 127;
|
const ahx = adp.adapter_hole_size_x || 127;
|
||||||
|
|
||||||
// 凹槽截面
|
// 凹槽截面
|
||||||
_rect(svg, pad + adx_val, pad + adz, ahx, ahz, '#ede9fe', '#8b5cf6');
|
_rect(svg, pad + adx_val, rackTopY + adz, ahx, ahz, '#ede9fe', '#8b5cf6');
|
||||||
_text(svg, pad + adx_val + ahx / 2, pad + adz + ahz / 2 + 1, `hole: ${ahz}mm deep`, '3', 'middle', '#8b5cf6');
|
_text(svg, pad + adx_val + ahx / 2, rackTopY + adz + ahz / 2 + 1, `hole: ${ahz}mm deep`, '3', 'middle', '#8b5cf6');
|
||||||
} else if (data.type === 'trash') {
|
} else if (data.type === 'trash') {
|
||||||
_text(svg, pad + drawW / 2, pad + drawH / 2, 'TRASH', '8', 'middle', '#ef4444');
|
_text(svg, pad + drawW / 2, rackTopY + drawH / 2, 'TRASH', '8', 'middle', '#ef4444');
|
||||||
}
|
}
|
||||||
|
|
||||||
container.appendChild(svg);
|
container.appendChild(svg);
|
||||||
@@ -323,13 +399,21 @@ function renderSideProfile(container, data) {
|
|||||||
function _enableZoomPan(svgEl, origViewBox) {
|
function _enableZoomPan(svgEl, origViewBox) {
|
||||||
const parts = origViewBox.split(' ').map(Number);
|
const parts = origViewBox.split(' ').map(Number);
|
||||||
let vx = parts[0], vy = parts[1], vw = parts[2], vh = parts[3];
|
let vx = parts[0], vy = parts[1], vw = parts[2], vh = parts[3];
|
||||||
const origW = vw, origH = vh;
|
const origVx = vx, origVy = vy, origW = vw, origH = vh;
|
||||||
const MIN_SCALE = 0.5, MAX_SCALE = 5;
|
const MIN_SCALE = 0.5, MAX_SCALE = 5;
|
||||||
|
|
||||||
function applyViewBox() {
|
function applyViewBox() {
|
||||||
svgEl.setAttribute('viewBox', `${vx} ${vy} ${vw} ${vh}`);
|
svgEl.setAttribute('viewBox', `${vx} ${vy} ${vw} ${vh}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetView() {
|
||||||
|
vx = origVx; vy = origVy; vw = origW; vh = origH;
|
||||||
|
applyViewBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 resetView 挂到 svg 元素上,方便外部调用
|
||||||
|
svgEl._resetView = resetView;
|
||||||
|
|
||||||
svgEl.addEventListener('wheel', function (e) {
|
svgEl.addEventListener('wheel', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (e.ctrlKey) {
|
if (e.ctrlKey) {
|
||||||
@@ -356,3 +440,11 @@ function _enableZoomPan(svgEl, origViewBox) {
|
|||||||
applyViewBox();
|
applyViewBox();
|
||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 回中按钮:重置指定容器内 SVG 的 viewBox
|
||||||
|
function resetSvgView(containerId) {
|
||||||
|
const container = document.getElementById(containerId);
|
||||||
|
if (!container) return;
|
||||||
|
const svg = container.querySelector('svg');
|
||||||
|
if (svg && svg._resetView) svg._resetView();
|
||||||
|
}
|
||||||
|
|||||||
@@ -85,7 +85,10 @@
|
|||||||
<tr><td class="label">Spot 尺寸</td><td>{{ item.tip.spot_size_x }} x {{ item.tip.spot_size_y }} x {{ item.tip.spot_size_z }}</td></tr>
|
<tr><td class="label">Spot 尺寸</td><td>{{ item.tip.spot_size_x }} x {{ item.tip.spot_size_y }} x {{ item.tip.spot_size_z }}</td></tr>
|
||||||
<tr><td class="label">容量</td><td>{{ item.tip.tip_volume }} uL</td></tr>
|
<tr><td class="label">容量</td><td>{{ item.tip.tip_volume }} uL</td></tr>
|
||||||
<tr><td class="label">长度</td><td>{{ item.tip.tip_length }} mm</td></tr>
|
<tr><td class="label">长度</td><td>{{ item.tip.tip_length }} mm</td></tr>
|
||||||
<tr><td class="label">配合深度</td><td>{{ item.tip.tip_fitting_depth }} mm</td></tr>
|
<tr><td class="label">取枪头插入深度</td><td>{{ item.tip.tip_fitting_depth }} mm</td></tr>
|
||||||
|
{% if item.tip.tip_above_rack_length is not none %}
|
||||||
|
<tr><td class="label">枪头露出枪头盒长度</td><td>{{ item.tip.tip_above_rack_length }} mm</td></tr>
|
||||||
|
{% endif %}
|
||||||
<tr><td class="label">有滤芯</td><td>{{ item.tip.has_filter }}</td></tr>
|
<tr><td class="label">有滤芯</td><td>{{ item.tip.has_filter }}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -127,11 +130,17 @@
|
|||||||
<!-- 右侧: 可视化 -->
|
<!-- 右侧: 可视化 -->
|
||||||
<div class="detail-viz">
|
<div class="detail-viz">
|
||||||
<div class="viz-card">
|
<div class="viz-card">
|
||||||
<h3>俯视图 (Top-Down)</h3>
|
<h3 style="display:flex;align-items:center;justify-content:space-between;">
|
||||||
|
俯视图 (Top-Down)
|
||||||
|
<button type="button" class="btn btn-sm btn-outline" onclick="resetSvgView('svg-topdown')">回中</button>
|
||||||
|
</h3>
|
||||||
<div id="svg-topdown"></div>
|
<div id="svg-topdown"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="viz-card">
|
<div class="viz-card">
|
||||||
<h3>侧面截面图 (Side Profile)</h3>
|
<h3 style="display:flex;align-items:center;justify-content:space-between;">
|
||||||
|
侧面截面图 (Side Profile)
|
||||||
|
<button type="button" class="btn btn-sm btn-outline" onclick="resetSvgView('svg-side')">回中</button>
|
||||||
|
</h3>
|
||||||
<div id="svg-side"></div>
|
<div id="svg-side"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -147,8 +147,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-row-3">
|
<div class="form-row-3">
|
||||||
<div><label>tip_volume <span class="label-cn">枪头容量 (uL)</span></label><input type="number" step="any" name="tip_vol" id="f-tip_vol" value="{{ item.tip.tip_volume if item and item.tip else 300 }}"></div>
|
<div><label>tip_volume <span class="label-cn">枪头容量 (uL)</span></label><input type="number" step="any" name="tip_vol" id="f-tip_vol" value="{{ item.tip.tip_volume if item and item.tip else 300 }}"></div>
|
||||||
<div><label>tip_length <span class="label-cn">枪头长度 (mm)</span></label><input type="number" step="any" name="tip_len" id="f-tip_len" value="{{ item.tip.tip_length if item and item.tip else 60 }}"></div>
|
<div><label>tip_length <span class="label-cn">枪头总长度 (mm)</span></label><input type="number" step="any" name="tip_len" id="f-tip_len" value="{{ item.tip.tip_length if item and item.tip else 60 }}"></div>
|
||||||
<div><label>fitting_depth <span class="label-cn">配合深度</span></label><input type="number" step="any" name="tip_dep" id="f-tip_dep" value="{{ item.tip.tip_fitting_depth if item and item.tip else 51 }}"></div>
|
<div><label>fitting_depth <span class="label-cn">取枪头时插入的长度 (mm)</span></label><input type="number" step="any" name="tip_dep" id="f-tip_dep" value="{{ item.tip.tip_fitting_depth if item and item.tip else 51 }}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>tip_above_rack_length <span class="label-cn">枪头在枪头盒上方的部分的长度 (mm)</span></label>
|
||||||
|
<input type="number" step="any" name="tip_above" id="f-tip_above"
|
||||||
|
value="{{ item.tip.tip_above_rack_length if item and item.tip and item.tip.tip_above_rack_length is not none else '' }}"
|
||||||
|
placeholder="tip_length - (size_z - dz)">
|
||||||
|
<small style="color:#888;margin-top:2px;">公式: tip_length = tip_above + size_z - dz;填 tip_above 自动算 dz,填 dz 自动算 tip_above</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label><input type="checkbox" name="tip_filter" id="f-tip_filter" {% if item and item.tip and item.tip.has_filter %}checked{% endif %}> has_filter</label>
|
<label><input type="checkbox" name="tip_filter" id="f-tip_filter" {% if item and item.tip and item.tip.has_filter %}checked{% endif %}> has_filter</label>
|
||||||
@@ -219,11 +226,17 @@
|
|||||||
<!-- 右侧: 实时预览 -->
|
<!-- 右侧: 实时预览 -->
|
||||||
<div class="edit-preview">
|
<div class="edit-preview">
|
||||||
<div class="viz-card">
|
<div class="viz-card">
|
||||||
<h3>预览: 俯视图</h3>
|
<h3 style="display:flex;align-items:center;justify-content:space-between;">
|
||||||
|
预览: 俯视图
|
||||||
|
<button type="button" class="btn btn-sm btn-outline" onclick="resetSvgView('svg-topdown')">回中</button>
|
||||||
|
</h3>
|
||||||
<div id="svg-topdown"></div>
|
<div id="svg-topdown"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="viz-card">
|
<div class="viz-card">
|
||||||
<h3>预览: 侧面截面图</h3>
|
<h3 style="display:flex;align-items:center;justify-content:space-between;">
|
||||||
|
预览: 侧面截面图
|
||||||
|
<button type="button" class="btn btn-sm btn-outline" onclick="resetSvgView('svg-side')">回中</button>
|
||||||
|
</h3>
|
||||||
<div id="svg-side"></div>
|
<div id="svg-side"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ PRCXI_30mm_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plate_adapters
|
- plate_adapters
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_30mm_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_30mm_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '30mm适配器 (Code: ZX-58-30)'
|
description: '30mm适配器 (Code: ZX-58-30)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -15,7 +15,7 @@ PRCXI_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plate_adapters
|
- plate_adapters
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '适配器 (Code: Fhh478)'
|
description: '适配器 (Code: Fhh478)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -27,7 +27,7 @@ PRCXI_Deep10_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plate_adapters
|
- plate_adapters
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Deep10_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Deep10_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '10ul专用深孔板适配器 (Code: ZX-002-10)'
|
description: '10ul专用深孔板适配器 (Code: ZX-002-10)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -39,7 +39,7 @@ PRCXI_Deep300_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plate_adapters
|
- plate_adapters
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Deep300_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Deep300_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '300ul深孔板适配器 (Code: ZX-002-300)'
|
description: '300ul深孔板适配器 (Code: ZX-002-300)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -51,7 +51,7 @@ PRCXI_PCR_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plate_adapters
|
- plate_adapters
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '全裙边 PCR适配器 (Code: ZX-58-0001)'
|
description: '全裙边 PCR适配器 (Code: ZX-58-0001)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -63,7 +63,7 @@ PRCXI_Reservoir_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plate_adapters
|
- plate_adapters
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Reservoir_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Reservoir_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '储液槽 适配器 (Code: ZX-ADP-001)'
|
description: '储液槽 适配器 (Code: ZX-ADP-001)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -75,7 +75,7 @@ PRCXI_Tip10_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plate_adapters
|
- plate_adapters
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip10_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip10_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '吸头10ul 适配器 (Code: ZX-58-10)'
|
description: '吸头10ul 适配器 (Code: ZX-58-10)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -87,7 +87,7 @@ PRCXI_Tip1250_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plate_adapters
|
- plate_adapters
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip1250_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip1250_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: 'Tip头适配器 1250uL (Code: ZX-58-1250)'
|
description: 'Tip头适配器 1250uL (Code: ZX-58-1250)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -99,7 +99,7 @@ PRCXI_Tip300_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plate_adapters
|
- plate_adapters
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip300_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip300_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: 'ZHONGXI 适配器 300uL (Code: ZX-58-300)'
|
description: 'ZHONGXI 适配器 300uL (Code: ZX-58-300)'
|
||||||
handles: []
|
handles: []
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ PRCXI_48_DeepWell:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_48_DeepWell
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_48_DeepWell'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '48孔深孔板 (Code: 22)'
|
description: '48孔深孔板 (Code: 22)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -15,7 +15,7 @@ PRCXI_96_DeepWell:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_96_DeepWell
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_96_DeepWell'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '96深孔板 (Code: q2)'
|
description: '96深孔板 (Code: q2)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -27,7 +27,7 @@ PRCXI_AGenBio_4_troughplate:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_AGenBio_4_troughplate
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_AGenBio_4_troughplate'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '4道储液槽 (Code: sdfrth654)'
|
description: '4道储液槽 (Code: sdfrth654)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -39,7 +39,7 @@ PRCXI_BioER_96_wellplate:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_BioER_96_wellplate
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_BioER_96_wellplate'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '2.2ml 深孔板 (Code: ZX-019-2.2)'
|
description: '2.2ml 深孔板 (Code: ZX-019-2.2)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -51,7 +51,7 @@ PRCXI_BioRad_384_wellplate:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_BioRad_384_wellplate
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_BioRad_384_wellplate'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '384板 (Code: q3)'
|
description: '384板 (Code: q3)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -63,7 +63,7 @@ PRCXI_CellTreat_96_wellplate:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_CellTreat_96_wellplate
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_CellTreat_96_wellplate'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '细菌培养皿 (Code: ZX-78-096)'
|
description: '细菌培养皿 (Code: ZX-78-096)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -75,7 +75,7 @@ PRCXI_PCR_Plate_200uL_nonskirted:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_nonskirted
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_nonskirted'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '0.2ml PCR 板 (Code: ZX-023-0.2)'
|
description: '0.2ml PCR 板 (Code: ZX-023-0.2)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -87,7 +87,7 @@ PRCXI_PCR_Plate_200uL_semiskirted:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_semiskirted
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_semiskirted'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '0.2ml PCR 板 (Code: ZX-023-0.2)'
|
description: '0.2ml PCR 板 (Code: ZX-023-0.2)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -99,7 +99,7 @@ PRCXI_PCR_Plate_200uL_skirted:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_skirted
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_skirted'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '0.2ml PCR 板 (Code: ZX-023-0.2)'
|
description: '0.2ml PCR 板 (Code: ZX-023-0.2)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -111,7 +111,7 @@ PRCXI_nest_12_troughplate:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_nest_12_troughplate
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_nest_12_troughplate'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '12道储液槽 (Code: 12道储液槽)'
|
description: '12道储液槽 (Code: 12道储液槽)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -123,7 +123,7 @@ PRCXI_nest_1_troughplate:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- plates
|
- plates
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_nest_1_troughplate
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_nest_1_troughplate'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '储液槽 (Code: ZX-58-10000)'
|
description: '储液槽 (Code: ZX-58-10000)'
|
||||||
handles: []
|
handles: []
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ PRCXI_1000uL_Tips:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- tip_racks
|
- tip_racks
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_1000uL_Tips
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_1000uL_Tips'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '1000μL Tip头 (Code: ZX-001-1000)'
|
description: '1000μL Tip头 (Code: ZX-001-1000)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -15,7 +15,7 @@ PRCXI_10uL_Tips:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- tip_racks
|
- tip_racks
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_10uL_Tips
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_10uL_Tips'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '10μL Tip头 (Code: ZX-001-10)'
|
description: '10μL Tip头 (Code: ZX-001-10)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -27,7 +27,7 @@ PRCXI_10ul_eTips:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- tip_racks
|
- tip_racks
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_10ul_eTips
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_10ul_eTips'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '10μL加长 Tip头 (Code: ZX-001-10+)'
|
description: '10μL加长 Tip头 (Code: ZX-001-10+)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -39,7 +39,7 @@ PRCXI_1250uL_Tips:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- tip_racks
|
- tip_racks
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_1250uL_Tips
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_1250uL_Tips'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '1250μL Tip头 (Code: ZX-001-1250)'
|
description: '1250μL Tip头 (Code: ZX-001-1250)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -51,7 +51,7 @@ PRCXI_200uL_Tips:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- tip_racks
|
- tip_racks
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_200uL_Tips
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_200uL_Tips'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '200μL Tip头 (Code: ZX-001-200)'
|
description: '200μL Tip头 (Code: ZX-001-200)'
|
||||||
handles: []
|
handles: []
|
||||||
@@ -63,10 +63,22 @@ PRCXI_300ul_Tips:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- tip_racks
|
- tip_racks
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_300ul_Tips
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_300ul_Tips'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '300μL Tip头 (Code: ZX-001-300)'
|
description: '300μL Tip头 (Code: ZX-001-300)'
|
||||||
handles: []
|
handles: []
|
||||||
icon: ''
|
icon: ''
|
||||||
init_param_schema: {}
|
init_param_schema: {}
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
PRCXI_50uL_tips:
|
||||||
|
category:
|
||||||
|
- prcxi
|
||||||
|
- tip_racks
|
||||||
|
class:
|
||||||
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_50uL_tips'
|
||||||
|
type: pylabrobot
|
||||||
|
description: PRCXI_50uL_tips
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ PRCXI_trash:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- trash
|
- trash
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_trash
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_trash'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: '废弃槽 (Code: q1)'
|
description: '废弃槽 (Code: q1)'
|
||||||
handles: []
|
handles: []
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ PRCXI_EP_Adapter:
|
|||||||
- prcxi
|
- prcxi
|
||||||
- tube_racks
|
- tube_racks
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_EP_Adapter
|
module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_EP_Adapter'
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
description: 'ep适配器 (Code: 1)'
|
description: 'ep适配器 (Code: 1)'
|
||||||
handles: []
|
handles: []
|
||||||
|
|||||||
Reference in New Issue
Block a user