ablelabs 0.0.66__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ablelabs-0.0.66/PKG-INFO +16 -0
- ablelabs-0.0.66/ablelabs/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/common/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/common/alma/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/common/alma/constants.py +13 -0
- ablelabs-0.0.66/ablelabs/neon/common/alma/enums.py +65 -0
- ablelabs-0.0.66/ablelabs/neon/common/alma/structs.py +71 -0
- ablelabs-0.0.66/ablelabs/neon/common/notable/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/common/notable/constants.py +13 -0
- ablelabs-0.0.66/ablelabs/neon/common/notable/enums.py +105 -0
- ablelabs-0.0.66/ablelabs/neon/common/notable/kribb/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/common/notable/kribb/structs.py +33 -0
- ablelabs-0.0.66/ablelabs/neon/common/notable/structs.py +149 -0
- ablelabs-0.0.66/ablelabs/neon/common/path.py +72 -0
- ablelabs-0.0.66/ablelabs/neon/common/suitable/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/common/suitable/constants.py +16 -0
- ablelabs-0.0.66/ablelabs/neon/common/suitable/enums.py +100 -0
- ablelabs-0.0.66/ablelabs/neon/common/suitable/structs.py +53 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/axis_api.py +64 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/deck_module.py +15 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/deck_modules/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/deck_modules/heater.py +24 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/deck_modules/inspector.py +36 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/get_api.py +15 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/robot_api.py +99 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/robot_router.py +245 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/set_api.py +15 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/time_api.py +16 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/upper_module.py +27 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/upper_modules/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/upper_modules/gripper.py +62 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/upper_modules/pipette.py +74 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/alma/sample.py +263 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/axis_api.py +64 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/get_api.py +31 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/motion_api.py +103 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/robot_api.py +76 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/robot_router.py +190 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/set_api.py +37 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/motion_api.py +31 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/optic_api.py +77 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/robot_api.py +56 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/robot_router.py +276 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/set_api.py +21 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/sample.py +357 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/sample.py +209 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/notable/sample_en.py +189 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/axis_api.py +64 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/get_api.py +15 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/motion_api.py +126 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/robot_api.py +406 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/robot_router.py +297 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/set_api.py +34 -0
- ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/time_api.py +106 -0
- ablelabs-0.0.66/ablelabs/neon/models/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/utils/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/utils/decorators.py +292 -0
- ablelabs-0.0.66/ablelabs/neon/utils/device_utils.py +18 -0
- ablelabs-0.0.66/ablelabs/neon/utils/dict_utils.py +31 -0
- ablelabs-0.0.66/ablelabs/neon/utils/enum_types.py +46 -0
- ablelabs-0.0.66/ablelabs/neon/utils/excel_utils.py +325 -0
- ablelabs-0.0.66/ablelabs/neon/utils/format_conversion.py +154 -0
- ablelabs-0.0.66/ablelabs/neon/utils/location_conversion.py +252 -0
- ablelabs-0.0.66/ablelabs/neon/utils/long_polling_getter.py +82 -0
- ablelabs-0.0.66/ablelabs/neon/utils/math/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/utils/math/interpolator.py +24 -0
- ablelabs-0.0.66/ablelabs/neon/utils/math/linear_map.py +35 -0
- ablelabs-0.0.66/ablelabs/neon/utils/network/__init__.py +0 -0
- ablelabs-0.0.66/ablelabs/neon/utils/network/base_tcp.py +105 -0
- ablelabs-0.0.66/ablelabs/neon/utils/network/messenger.py +325 -0
- ablelabs-0.0.66/ablelabs/neon/utils/network/tcp_client.py +37 -0
- ablelabs-0.0.66/ablelabs/neon/utils/network/tcp_server.py +88 -0
- ablelabs-0.0.66/ablelabs/neon/utils/work_node.py +189 -0
- ablelabs-0.0.66/ablelabs.egg-info/PKG-INFO +16 -0
- ablelabs-0.0.66/ablelabs.egg-info/SOURCES.txt +90 -0
- ablelabs-0.0.66/ablelabs.egg-info/dependency_links.txt +1 -0
- ablelabs-0.0.66/ablelabs.egg-info/not-zip-safe +1 -0
- ablelabs-0.0.66/ablelabs.egg-info/requires.txt +7 -0
- ablelabs-0.0.66/ablelabs.egg-info/top_level.txt +1 -0
- ablelabs-0.0.66/setup.cfg +4 -0
- ablelabs-0.0.66/setup.py +27 -0
ablelabs-0.0.66/PKG-INFO
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ablelabs
|
|
3
|
+
Version: 0.0.66
|
|
4
|
+
Home-page: https://github.com/ABLE-Labs/ABLE-API
|
|
5
|
+
Author: sypark
|
|
6
|
+
Author-email: sy.park@ablelabsinc.com
|
|
7
|
+
Keywords: ablelabs
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: et-xmlfile>=1.1.0
|
|
11
|
+
Requires-Dist: future>=1.0.0
|
|
12
|
+
Requires-Dist: iso8601>=2.1.0
|
|
13
|
+
Requires-Dist: loguru>=0.7.2
|
|
14
|
+
Requires-Dist: openpyxl>=3.1.5
|
|
15
|
+
Requires-Dist: pyserial>=3.5
|
|
16
|
+
Requires-Dist: PyYAML>=6.0.1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
DECK_COUNT: int = 12
|
|
2
|
+
DECK_NUMBERS = [i + 1 for i in range(DECK_COUNT)]
|
|
3
|
+
|
|
4
|
+
LID_HOLDER_COUNT: int = 4
|
|
5
|
+
LID_HOLDER_NUMBERS = [i + 1 for i in range(LID_HOLDER_COUNT)]
|
|
6
|
+
|
|
7
|
+
CHIP_HOLDER_COUNT: int = 8
|
|
8
|
+
CHIP_HOLDER_NUMBERS = [i + 1 for i in range(CHIP_HOLDER_COUNT)]
|
|
9
|
+
|
|
10
|
+
RESERVOIR_COUNT: int = 2
|
|
11
|
+
RESERVOIR_NUMBERS = [i + 1 for i in range(RESERVOIR_COUNT)]
|
|
12
|
+
|
|
13
|
+
CHIP_WELL_COUNT: int = 192
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import sys, os
|
|
2
|
+
|
|
3
|
+
sys.path.append(os.path.abspath(os.curdir))
|
|
4
|
+
from ablelabs.neon.common.notable.enums import (
|
|
5
|
+
NUMBERING_ORDER,
|
|
6
|
+
Color,
|
|
7
|
+
RunStatus,
|
|
8
|
+
TaskStatus,
|
|
9
|
+
LabwareType,
|
|
10
|
+
Height,
|
|
11
|
+
Plunger,
|
|
12
|
+
LocationReference,
|
|
13
|
+
)
|
|
14
|
+
from ablelabs.neon.utils.enum_types import Enum, auto, StrEnum, ItemGetable
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DioInput(StrEnum):
|
|
18
|
+
EMS = auto()
|
|
19
|
+
DOOR_LEFT = auto()
|
|
20
|
+
DOOR_RIGHT = auto()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DioPDO(StrEnum):
|
|
24
|
+
LED_LAMP = auto()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Axis(StrEnum):
|
|
28
|
+
X = auto()
|
|
29
|
+
Y = auto()
|
|
30
|
+
Z1 = auto()
|
|
31
|
+
Z2 = auto()
|
|
32
|
+
G = auto()
|
|
33
|
+
P = auto()
|
|
34
|
+
|
|
35
|
+
Z = ItemGetable[int](lambda number: Axis.from_str(f"{Axis.Z1[0]}{number}"))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LocationType(StrEnum):
|
|
39
|
+
READY = auto()
|
|
40
|
+
LID_HOLDER = auto()
|
|
41
|
+
INSPECTOR = auto()
|
|
42
|
+
DECK = auto()
|
|
43
|
+
CHIP_HOLDER = auto()
|
|
44
|
+
WASTE = auto()
|
|
45
|
+
TIP_RACK = auto()
|
|
46
|
+
RESERVOIR = auto()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ZGripper(StrEnum):
|
|
50
|
+
MOVE = auto()
|
|
51
|
+
PLATE_LID_UNGRIP = auto()
|
|
52
|
+
PLATE_LID_GRIP = auto()
|
|
53
|
+
LID_UNGRIP = auto()
|
|
54
|
+
LID_GRIP = auto()
|
|
55
|
+
FORK = auto()
|
|
56
|
+
UNDER_PLATE = auto()
|
|
57
|
+
PUSH = auto()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Gripper(StrEnum):
|
|
61
|
+
UNGRIP = auto()
|
|
62
|
+
PUSH = auto()
|
|
63
|
+
GRIP_LID = auto()
|
|
64
|
+
FORK = auto()
|
|
65
|
+
GRIP_PLATE = auto()
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
|
|
5
|
+
import sys, os
|
|
6
|
+
|
|
7
|
+
sys.path.append(os.path.abspath(os.curdir))
|
|
8
|
+
from ablelabs.neon.common.notable.structs import Speed, FlowRate, LedBarParam
|
|
9
|
+
from ablelabs.neon.common.alma.enums import LocationType, LocationReference
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class Location:
|
|
14
|
+
location_type: LocationType
|
|
15
|
+
location_number: int = None
|
|
16
|
+
well: str = None
|
|
17
|
+
reference: LocationReference = None
|
|
18
|
+
offset: tuple[float, float, float] = (0, 0, 0)
|
|
19
|
+
|
|
20
|
+
def __str__(self):
|
|
21
|
+
# 필드값이 기본값과 다를 때만 표시
|
|
22
|
+
fields = []
|
|
23
|
+
if self.location_number == None:
|
|
24
|
+
fields.append(f"{self.location_type}")
|
|
25
|
+
else:
|
|
26
|
+
fields.append(f"{self.location_type}.{self.location_number}")
|
|
27
|
+
if self.well != None:
|
|
28
|
+
fields.append(f"well={self.well}")
|
|
29
|
+
if self.reference != None:
|
|
30
|
+
fields.append(f"reference={self.reference}")
|
|
31
|
+
if self.offset != (0, 0, 0):
|
|
32
|
+
fields.append(f"offset={self.offset}")
|
|
33
|
+
return f"Location({', '.join(fields)})"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def location(
|
|
37
|
+
location_type: LocationType = LocationType.DECK,
|
|
38
|
+
location_number: int = None,
|
|
39
|
+
well: str = None,
|
|
40
|
+
reference: LocationReference = None,
|
|
41
|
+
offset: tuple[float] = (0, 0, 0),
|
|
42
|
+
):
|
|
43
|
+
return Location(
|
|
44
|
+
location_type=location_type,
|
|
45
|
+
location_number=location_number,
|
|
46
|
+
well=well,
|
|
47
|
+
reference=reference,
|
|
48
|
+
offset=offset,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class MeasurementTimeMode(Enum):
|
|
53
|
+
SHORT = "SHOR"
|
|
54
|
+
MEDIUM = "MED"
|
|
55
|
+
LONG = "LONG"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class LCRParam:
|
|
60
|
+
# preset value
|
|
61
|
+
data_format_long: bool = False
|
|
62
|
+
# function_impedance_type: FunctionImpedanceType = FunctionImpedanceType.CP_D
|
|
63
|
+
frequency: float = 1000
|
|
64
|
+
voltage_level: float = 1
|
|
65
|
+
measurement_time_mode: MeasurementTimeMode = MeasurementTimeMode.MEDIUM
|
|
66
|
+
averaging_rate: int = 1
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class HeaterData(TypedDict):
|
|
70
|
+
on: bool
|
|
71
|
+
temperature: float
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import sys, os
|
|
2
|
+
|
|
3
|
+
sys.path.append(os.path.abspath(os.curdir))
|
|
4
|
+
from ablelabs.neon.common.suitable.enums import Axis
|
|
5
|
+
|
|
6
|
+
PIPETTE_COUNT: int = 2
|
|
7
|
+
DECK_COUNT: int = 12
|
|
8
|
+
|
|
9
|
+
PIPETTE_NUMBERS = [i + 1 for i in range(PIPETTE_COUNT)]
|
|
10
|
+
DECK_NUMBERS = [i + 1 for i in range(DECK_COUNT)]
|
|
11
|
+
|
|
12
|
+
AXIS_ZS = [Axis.Z[n] for n in PIPETTE_NUMBERS]
|
|
13
|
+
AXIS_PS = [Axis.P[n] for n in PIPETTE_NUMBERS]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import sys, os
|
|
2
|
+
|
|
3
|
+
sys.path.append(os.path.abspath(os.curdir))
|
|
4
|
+
from ablelabs.neon.utils.enum_types import StrEnum, auto, IntFlag, IntEnum, ItemGetable
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NUMBERING_ORDER(StrEnum):
|
|
8
|
+
LTR_TTB = auto() # Left to Right, Top to Bottom
|
|
9
|
+
LTR_BTT = auto() # Left to Right, Bottom to Top
|
|
10
|
+
TTB_LTR = auto() # Top to Bottom, Left to Right
|
|
11
|
+
BTT_LTR = auto() # Bottom to Top, Left to Right
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Color(IntFlag):
|
|
15
|
+
NONE = 0
|
|
16
|
+
RED = 1 << 0
|
|
17
|
+
GREEN = 1 << 1
|
|
18
|
+
BLUE = 1 << 2
|
|
19
|
+
WHITE = 1 << 3
|
|
20
|
+
CYAN = GREEN | BLUE
|
|
21
|
+
MAGENTA = RED | BLUE
|
|
22
|
+
YELLOW = RED | GREEN
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RunStatus(IntEnum):
|
|
26
|
+
BOOT = auto()
|
|
27
|
+
SHUTDOWN = auto()
|
|
28
|
+
READY = auto()
|
|
29
|
+
DONE = auto()
|
|
30
|
+
INITIALIZE = auto()
|
|
31
|
+
RUN = auto()
|
|
32
|
+
PAUSE = auto()
|
|
33
|
+
# 의도한 상황
|
|
34
|
+
STOP = auto()
|
|
35
|
+
EMERGENCY = auto()
|
|
36
|
+
# 예기치 못한 상황
|
|
37
|
+
ERROR = auto() # 일반적인 에러
|
|
38
|
+
TIMEOUT = auto()
|
|
39
|
+
FAULT = auto()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TaskStatus(StrEnum):
|
|
43
|
+
RUNNING = auto()
|
|
44
|
+
PAUSED = auto()
|
|
45
|
+
STOPPED = auto()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Axis(StrEnum):
|
|
49
|
+
X = auto()
|
|
50
|
+
Y = auto()
|
|
51
|
+
Z1 = auto()
|
|
52
|
+
Z2 = auto()
|
|
53
|
+
P1 = auto()
|
|
54
|
+
P2 = auto()
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def Z_(pipette_number: int):
|
|
58
|
+
return Axis.from_str(f"{Axis.Z1[0]}{pipette_number}")
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def P_(pipette_number: int):
|
|
62
|
+
return Axis.from_str(f"{Axis.P1[0]}{pipette_number}")
|
|
63
|
+
|
|
64
|
+
Z = ItemGetable[int](lambda number: Axis.from_str(f"{Axis.Z1[0]}{number}"))
|
|
65
|
+
P = ItemGetable[int](lambda number: Axis.from_str(f"{Axis.P1[0]}{number}"))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class LabwareType(StrEnum):
|
|
69
|
+
TIP_RACK = auto()
|
|
70
|
+
WASTE = auto()
|
|
71
|
+
WELL_PLATE = auto()
|
|
72
|
+
|
|
73
|
+
def __eq__(self, __value: object) -> bool:
|
|
74
|
+
return super().__eq__(str(__value).upper())
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Height(StrEnum):
|
|
78
|
+
UP = auto()
|
|
79
|
+
TOP = auto()
|
|
80
|
+
TOP_JUST = auto()
|
|
81
|
+
BOTTOM = auto()
|
|
82
|
+
BOTTOM_JUST = auto()
|
|
83
|
+
TIP_RACK_TOUCHED = auto()
|
|
84
|
+
TIP_RACK_PRESSED = auto()
|
|
85
|
+
WASTE_DROP_TIP = auto()
|
|
86
|
+
WASTE_BLOW_OUT = auto()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Plunger(StrEnum):
|
|
90
|
+
HOME_OFFSET = auto()
|
|
91
|
+
DROP_TIP = auto()
|
|
92
|
+
TOP = auto()
|
|
93
|
+
FIRST_STOP = auto()
|
|
94
|
+
SECOND_STOP = auto()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class LocationType(StrEnum):
|
|
98
|
+
DECK = auto()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class LocationReference(StrEnum):
|
|
102
|
+
TOP = auto()
|
|
103
|
+
TOP_JUST = auto()
|
|
104
|
+
BOTTOM = auto()
|
|
105
|
+
BOTTOM_JUST = auto()
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
import cv2
|
|
3
|
+
|
|
4
|
+
import sys, os
|
|
5
|
+
|
|
6
|
+
sys.path.append(os.path.abspath(os.curdir))
|
|
7
|
+
from ablelabs.neon.common.notable.structs import *
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class LabelInfo:
|
|
12
|
+
index: int
|
|
13
|
+
width: int
|
|
14
|
+
height: int
|
|
15
|
+
area: int
|
|
16
|
+
centroid: tuple[float, float]
|
|
17
|
+
inscribed_center: tuple[float, float]
|
|
18
|
+
inscribed_radius: int
|
|
19
|
+
perimeter: float
|
|
20
|
+
circularity: float
|
|
21
|
+
brightness: float
|
|
22
|
+
color: tuple[float, float, float]
|
|
23
|
+
contours: list[cv2.Mat]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ColonyInfo:
|
|
28
|
+
x_ratio: float
|
|
29
|
+
y_ratio: float
|
|
30
|
+
area: float
|
|
31
|
+
circularity: float
|
|
32
|
+
brightness: float
|
|
33
|
+
color: tuple[float, float, float]
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
import sys, os
|
|
4
|
+
|
|
5
|
+
sys.path.append(os.path.abspath(os.curdir))
|
|
6
|
+
from ablelabs.neon.common.notable.enums import Color, LocationType, LocationReference
|
|
7
|
+
from ablelabs.neon.utils.format_conversion import floor_precision
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Speed:
|
|
12
|
+
unit: str
|
|
13
|
+
unit_s: float = None
|
|
14
|
+
rate: float = None
|
|
15
|
+
|
|
16
|
+
def __str__(self) -> str:
|
|
17
|
+
result = []
|
|
18
|
+
if self.unit_s != None:
|
|
19
|
+
result.append(f"{self.unit_s}{self.unit}/s")
|
|
20
|
+
if self.rate != None:
|
|
21
|
+
result.append(f"{floor_precision(self.rate * 100, digit=3)}%")
|
|
22
|
+
return f"{' '.join(result)}"
|
|
23
|
+
|
|
24
|
+
def __repr__(self) -> str:
|
|
25
|
+
return self.__str__()
|
|
26
|
+
|
|
27
|
+
# None인 속성 제외
|
|
28
|
+
# def __repr__(self):
|
|
29
|
+
# field_strings = []
|
|
30
|
+
# for field in fields(self):
|
|
31
|
+
# value = getattr(self, field.name)
|
|
32
|
+
# if value is not None:
|
|
33
|
+
# field_strings.append(f"{field.name}={repr(value)}")
|
|
34
|
+
# field_string = ", ".join(field_strings)
|
|
35
|
+
# return f"{self.__class__.__name__}({field_string})"
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def from_mm(mm: float):
|
|
39
|
+
return Speed(unit="mm", unit_s=mm)
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def from_rate(rate: float = 1.0):
|
|
43
|
+
return Speed(unit="mm", rate=rate)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class FlowRate(Speed):
|
|
47
|
+
@staticmethod
|
|
48
|
+
def from_ul(ul: float):
|
|
49
|
+
return Speed(unit="ul", unit_s=ul)
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def from_rate(rate: float = 1.0):
|
|
53
|
+
return Speed(unit="ul", rate=rate)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class LedBarParam:
|
|
58
|
+
color: Color = Color.NONE
|
|
59
|
+
on_brightness_percent: int = None
|
|
60
|
+
off_brightness_percent: int = None
|
|
61
|
+
bar_percent: int = None
|
|
62
|
+
blink_time_ms: int = None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class Location:
|
|
67
|
+
location_type: LocationType
|
|
68
|
+
location_number: int
|
|
69
|
+
well: str
|
|
70
|
+
reference: LocationReference
|
|
71
|
+
offset: tuple[float] = (0, 0, 0)
|
|
72
|
+
|
|
73
|
+
# def to(
|
|
74
|
+
# self,
|
|
75
|
+
# location_number: int = None,
|
|
76
|
+
# well: str = None,
|
|
77
|
+
# reference: LocationReference = None,
|
|
78
|
+
# offset: tuple[float] = None,
|
|
79
|
+
# ):
|
|
80
|
+
# return Location(
|
|
81
|
+
# location_type=self.location_type,
|
|
82
|
+
# location_number=(
|
|
83
|
+
# location_number if location_number else self.location_number
|
|
84
|
+
# ),
|
|
85
|
+
# well=well if well else self.well,
|
|
86
|
+
# reference=reference if reference else self.reference,
|
|
87
|
+
# offset=offset if offset else self.offset,
|
|
88
|
+
# )
|
|
89
|
+
|
|
90
|
+
def __str__(self) -> str:
|
|
91
|
+
# inspect만으로는 원하는 형태로 만들기가 어려울 듯.
|
|
92
|
+
# result = " ".join(
|
|
93
|
+
# [
|
|
94
|
+
# f"{name}={value}"
|
|
95
|
+
# for name, value in inspect.getmembers(self)
|
|
96
|
+
# if "__" not in name and not inspect.isfunction(value)
|
|
97
|
+
# ]
|
|
98
|
+
# )
|
|
99
|
+
# return result
|
|
100
|
+
result = f"{self.location_type}"
|
|
101
|
+
if self.location_number:
|
|
102
|
+
result += f".{self.location_number}"
|
|
103
|
+
if self.well:
|
|
104
|
+
result += f" well={self.well}"
|
|
105
|
+
if self.reference:
|
|
106
|
+
result += f" reference={self.reference}"
|
|
107
|
+
if self.offset and self.offset != (0, 0, 0):
|
|
108
|
+
result += f" offset={self.offset}"
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
def __repr__(self) -> str:
|
|
112
|
+
return self.__str__()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def location(
|
|
116
|
+
location_type: LocationType = LocationType.DECK,
|
|
117
|
+
location_number: int = None,
|
|
118
|
+
well: str = None,
|
|
119
|
+
reference: LocationReference = None,
|
|
120
|
+
offset: tuple[float] = (0, 0, 0),
|
|
121
|
+
):
|
|
122
|
+
return Location(
|
|
123
|
+
location_type=location_type,
|
|
124
|
+
location_number=location_number,
|
|
125
|
+
well=well,
|
|
126
|
+
reference=reference,
|
|
127
|
+
offset=offset,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == "__main__":
|
|
132
|
+
flow_rate = FlowRate.from_rate()
|
|
133
|
+
print(flow_rate)
|
|
134
|
+
flow_rate = FlowRate.from_rate(0.12345)
|
|
135
|
+
print(flow_rate)
|
|
136
|
+
flow_rate = FlowRate.from_rate(0.012345)
|
|
137
|
+
print(flow_rate)
|
|
138
|
+
|
|
139
|
+
led_bar_param = LedBarParam()
|
|
140
|
+
print(led_bar_param)
|
|
141
|
+
|
|
142
|
+
loc = Location(
|
|
143
|
+
location_type=LocationType.DECK,
|
|
144
|
+
location_number=12,
|
|
145
|
+
well="a6",
|
|
146
|
+
reference=LocationReference.BOTTOM,
|
|
147
|
+
offset=(1, 2, 3),
|
|
148
|
+
)
|
|
149
|
+
print(loc)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
REQUIRED_FILES = [
|
|
6
|
+
# "deck.json",
|
|
7
|
+
"region.json",
|
|
8
|
+
"pipette.json",
|
|
9
|
+
"tip.json",
|
|
10
|
+
"labware.json",
|
|
11
|
+
"setup_data.toml",
|
|
12
|
+
"driver_param.toml",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Path:
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
required_files: list[str] = REQUIRED_FILES,
|
|
20
|
+
required_dirs: list[str] = [],
|
|
21
|
+
) -> None:
|
|
22
|
+
is_production = Path.is_production()
|
|
23
|
+
root_path = Path.get_root_path()
|
|
24
|
+
# print(f"root_path = {root_path}")
|
|
25
|
+
if is_production:
|
|
26
|
+
config_path = Path.get_config_path(root_path, required_files)
|
|
27
|
+
else:
|
|
28
|
+
config_path = Path.get_config_path(root_path, required_files, required_dirs)
|
|
29
|
+
self._config_path = config_path
|
|
30
|
+
logger.debug(f"root_path={root_path} config_path={config_path}")
|
|
31
|
+
# sypark platform 의존성 주입에 따라 경로 설정 변경 필요.
|
|
32
|
+
# self.DECK_PATH = os.path.join(config_path, "deck.json")
|
|
33
|
+
self.REGION_PATH = os.path.join(config_path, "region.json")
|
|
34
|
+
self.PIPETTE_PATH = os.path.join(config_path, "pipette.json")
|
|
35
|
+
self.TIP_PATH = os.path.join(config_path, "tip.json")
|
|
36
|
+
self.DECK_MODULE_PATH = os.path.join(config_path, "deck_module.json")
|
|
37
|
+
self.LABWARE_PATH = os.path.join(config_path, "labware.json")
|
|
38
|
+
self.SETUP_DATA_PATH = os.path.join(config_path, "setup_data.toml")
|
|
39
|
+
self.DRIVER_PARAM_PATH = os.path.join(config_path, "driver_param.toml")
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def config_path(self):
|
|
43
|
+
return self._config_path
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def is_production() -> bool:
|
|
47
|
+
"""PyInstaller에 의해 배포되었는지 여부"""
|
|
48
|
+
# return getattr(sys, "_MEIPASS", False)
|
|
49
|
+
return getattr(
|
|
50
|
+
sys, "frozen", False
|
|
51
|
+
) # cx_Freeze 같은 다른 패키징 도구에도 해당.
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def get_root_path() -> str:
|
|
55
|
+
if Path.is_production():
|
|
56
|
+
root_path = os.path.dirname(
|
|
57
|
+
os.path.abspath(sys.executable)
|
|
58
|
+
) # 실행 파일 경로.
|
|
59
|
+
else:
|
|
60
|
+
root_path = os.path.abspath(os.curdir) # 프로젝트 경로.
|
|
61
|
+
return root_path
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def get_config_path(
|
|
65
|
+
root_path: str, required_files: list[str], required_dirs: list[str] = []
|
|
66
|
+
):
|
|
67
|
+
for dirpath, dirnames, filenames in os.walk(root_path):
|
|
68
|
+
if all(required_dir in dirpath for required_dir in required_dirs) and all(
|
|
69
|
+
required_file in filenames for required_file in required_files
|
|
70
|
+
):
|
|
71
|
+
return dirpath
|
|
72
|
+
return None
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import sys, os
|
|
2
|
+
|
|
3
|
+
sys.path.append(os.path.abspath(os.curdir))
|
|
4
|
+
from ablelabs.neon.common.suitable.enums import Axis
|
|
5
|
+
|
|
6
|
+
DI_COUNT: int = 8
|
|
7
|
+
DO_COUNT: int = 8
|
|
8
|
+
|
|
9
|
+
PIPETTE_COUNT: int = 8
|
|
10
|
+
PIPETTE_NUMBERS = [i + 1 for i in range(PIPETTE_COUNT)]
|
|
11
|
+
|
|
12
|
+
AXIS_YS = [Axis.Y[n] for n in PIPETTE_NUMBERS]
|
|
13
|
+
AXIS_ZS = [Axis.Z[n] for n in PIPETTE_NUMBERS]
|
|
14
|
+
AXIS_PS = [Axis.P[n] for n in PIPETTE_NUMBERS]
|
|
15
|
+
|
|
16
|
+
Y_MIN_INTERVAL_MM = 9.0
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import sys, os
|
|
2
|
+
|
|
3
|
+
sys.path.append(os.path.abspath(os.curdir))
|
|
4
|
+
from ablelabs.neon.common.notable.enums import (
|
|
5
|
+
NUMBERING_ORDER,
|
|
6
|
+
Color,
|
|
7
|
+
RunStatus,
|
|
8
|
+
TaskStatus,
|
|
9
|
+
LabwareType,
|
|
10
|
+
Height,
|
|
11
|
+
Plunger,
|
|
12
|
+
)
|
|
13
|
+
from ablelabs.neon.utils.enum_types import Enum, auto, StrEnum, ItemGetable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LedLamp(Enum):
|
|
17
|
+
NONE = auto()
|
|
18
|
+
RED = auto()
|
|
19
|
+
GREEN = auto()
|
|
20
|
+
BLUE = auto()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Buzzer(Enum):
|
|
24
|
+
DONE = auto()
|
|
25
|
+
ERROR = auto()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Axis(StrEnum):
|
|
29
|
+
X = auto()
|
|
30
|
+
Y1 = auto()
|
|
31
|
+
Y2 = auto()
|
|
32
|
+
Y3 = auto()
|
|
33
|
+
Y4 = auto()
|
|
34
|
+
Y5 = auto()
|
|
35
|
+
Y6 = auto()
|
|
36
|
+
Y7 = auto()
|
|
37
|
+
Y8 = auto()
|
|
38
|
+
Z1 = auto()
|
|
39
|
+
Z2 = auto()
|
|
40
|
+
Z3 = auto()
|
|
41
|
+
Z4 = auto()
|
|
42
|
+
Z5 = auto()
|
|
43
|
+
Z6 = auto()
|
|
44
|
+
Z7 = auto()
|
|
45
|
+
Z8 = auto()
|
|
46
|
+
P1 = auto()
|
|
47
|
+
P2 = auto()
|
|
48
|
+
P3 = auto()
|
|
49
|
+
P4 = auto()
|
|
50
|
+
P5 = auto()
|
|
51
|
+
P6 = auto()
|
|
52
|
+
P7 = auto()
|
|
53
|
+
P8 = auto()
|
|
54
|
+
|
|
55
|
+
Y = ItemGetable[int](lambda number: Axis.from_str(f"{Axis.Y1[0]}{number}"))
|
|
56
|
+
Z = ItemGetable[int](lambda number: Axis.from_str(f"{Axis.Z1[0]}{number}"))
|
|
57
|
+
P = ItemGetable[int](lambda number: Axis.from_str(f"{Axis.P1[0]}{number}"))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class LocationType(StrEnum):
|
|
61
|
+
READY = auto()
|
|
62
|
+
DECK = auto()
|
|
63
|
+
TUBE_384 = auto()
|
|
64
|
+
TEST_TUBE = auto()
|
|
65
|
+
BALANCE = auto()
|
|
66
|
+
WASTE = auto()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class LocationReference(StrEnum):
|
|
70
|
+
TOP = auto()
|
|
71
|
+
TOP_JUST = auto()
|
|
72
|
+
BOTTOM = auto()
|
|
73
|
+
BOTTOM_JUST = auto()
|
|
74
|
+
LIQUID = auto() # for LLD only when aspirate
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class AssignmentStatus(StrEnum):
|
|
78
|
+
NOT_ASSIGNED = auto()
|
|
79
|
+
ASSIGNING = auto()
|
|
80
|
+
ASSIGNED = auto()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class MotionStr(StrEnum):
|
|
84
|
+
READY = auto()
|
|
85
|
+
INITIALIZE = auto()
|
|
86
|
+
PICK_UP_TIP = auto()
|
|
87
|
+
ASPIRATE = auto()
|
|
88
|
+
DISPENSE = auto()
|
|
89
|
+
DROP_TIP = auto()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class PipetteCalibrationType(StrEnum):
|
|
93
|
+
Tip_1000_DW = auto()
|
|
94
|
+
Tip_1000_ACN = auto()
|
|
95
|
+
Tip_200_ACN = auto()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
y1 = Axis.Y[1]
|
|
100
|
+
print(y1)
|