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.
Files changed (92) hide show
  1. ablelabs-0.0.66/PKG-INFO +16 -0
  2. ablelabs-0.0.66/ablelabs/__init__.py +0 -0
  3. ablelabs-0.0.66/ablelabs/neon/__init__.py +0 -0
  4. ablelabs-0.0.66/ablelabs/neon/common/__init__.py +0 -0
  5. ablelabs-0.0.66/ablelabs/neon/common/alma/__init__.py +0 -0
  6. ablelabs-0.0.66/ablelabs/neon/common/alma/constants.py +13 -0
  7. ablelabs-0.0.66/ablelabs/neon/common/alma/enums.py +65 -0
  8. ablelabs-0.0.66/ablelabs/neon/common/alma/structs.py +71 -0
  9. ablelabs-0.0.66/ablelabs/neon/common/notable/__init__.py +0 -0
  10. ablelabs-0.0.66/ablelabs/neon/common/notable/constants.py +13 -0
  11. ablelabs-0.0.66/ablelabs/neon/common/notable/enums.py +105 -0
  12. ablelabs-0.0.66/ablelabs/neon/common/notable/kribb/__init__.py +0 -0
  13. ablelabs-0.0.66/ablelabs/neon/common/notable/kribb/structs.py +33 -0
  14. ablelabs-0.0.66/ablelabs/neon/common/notable/structs.py +149 -0
  15. ablelabs-0.0.66/ablelabs/neon/common/path.py +72 -0
  16. ablelabs-0.0.66/ablelabs/neon/common/suitable/__init__.py +0 -0
  17. ablelabs-0.0.66/ablelabs/neon/common/suitable/constants.py +16 -0
  18. ablelabs-0.0.66/ablelabs/neon/common/suitable/enums.py +100 -0
  19. ablelabs-0.0.66/ablelabs/neon/common/suitable/structs.py +53 -0
  20. ablelabs-0.0.66/ablelabs/neon/controllers/__init__.py +0 -0
  21. ablelabs-0.0.66/ablelabs/neon/controllers/alma/__init__.py +0 -0
  22. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/__init__.py +0 -0
  23. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/axis_api.py +64 -0
  24. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/deck_module.py +15 -0
  25. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/deck_modules/__init__.py +0 -0
  26. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/deck_modules/heater.py +24 -0
  27. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/deck_modules/inspector.py +36 -0
  28. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/get_api.py +15 -0
  29. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/robot_api.py +99 -0
  30. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/robot_router.py +245 -0
  31. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/set_api.py +15 -0
  32. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/time_api.py +16 -0
  33. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/upper_module.py +27 -0
  34. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/upper_modules/__init__.py +0 -0
  35. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/upper_modules/gripper.py +62 -0
  36. ablelabs-0.0.66/ablelabs/neon/controllers/alma/api/upper_modules/pipette.py +74 -0
  37. ablelabs-0.0.66/ablelabs/neon/controllers/alma/sample.py +263 -0
  38. ablelabs-0.0.66/ablelabs/neon/controllers/notable/__init__.py +0 -0
  39. ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/__init__.py +0 -0
  40. ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/axis_api.py +64 -0
  41. ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/get_api.py +31 -0
  42. ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/motion_api.py +103 -0
  43. ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/robot_api.py +76 -0
  44. ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/robot_router.py +190 -0
  45. ablelabs-0.0.66/ablelabs/neon/controllers/notable/api/set_api.py +37 -0
  46. ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/__init__.py +0 -0
  47. ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/__init__.py +0 -0
  48. ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/motion_api.py +31 -0
  49. ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/optic_api.py +77 -0
  50. ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/robot_api.py +56 -0
  51. ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/robot_router.py +276 -0
  52. ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/api/set_api.py +21 -0
  53. ablelabs-0.0.66/ablelabs/neon/controllers/notable/kribb/sample.py +357 -0
  54. ablelabs-0.0.66/ablelabs/neon/controllers/notable/sample.py +209 -0
  55. ablelabs-0.0.66/ablelabs/neon/controllers/notable/sample_en.py +189 -0
  56. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/__init__.py +0 -0
  57. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/__init__.py +0 -0
  58. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/__init__.py +0 -0
  59. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/axis_api.py +64 -0
  60. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/get_api.py +15 -0
  61. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/motion_api.py +126 -0
  62. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/robot_api.py +406 -0
  63. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/robot_router.py +297 -0
  64. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/set_api.py +34 -0
  65. ablelabs-0.0.66/ablelabs/neon/controllers/suitable/sara/api/time_api.py +106 -0
  66. ablelabs-0.0.66/ablelabs/neon/models/__init__.py +0 -0
  67. ablelabs-0.0.66/ablelabs/neon/utils/__init__.py +0 -0
  68. ablelabs-0.0.66/ablelabs/neon/utils/decorators.py +292 -0
  69. ablelabs-0.0.66/ablelabs/neon/utils/device_utils.py +18 -0
  70. ablelabs-0.0.66/ablelabs/neon/utils/dict_utils.py +31 -0
  71. ablelabs-0.0.66/ablelabs/neon/utils/enum_types.py +46 -0
  72. ablelabs-0.0.66/ablelabs/neon/utils/excel_utils.py +325 -0
  73. ablelabs-0.0.66/ablelabs/neon/utils/format_conversion.py +154 -0
  74. ablelabs-0.0.66/ablelabs/neon/utils/location_conversion.py +252 -0
  75. ablelabs-0.0.66/ablelabs/neon/utils/long_polling_getter.py +82 -0
  76. ablelabs-0.0.66/ablelabs/neon/utils/math/__init__.py +0 -0
  77. ablelabs-0.0.66/ablelabs/neon/utils/math/interpolator.py +24 -0
  78. ablelabs-0.0.66/ablelabs/neon/utils/math/linear_map.py +35 -0
  79. ablelabs-0.0.66/ablelabs/neon/utils/network/__init__.py +0 -0
  80. ablelabs-0.0.66/ablelabs/neon/utils/network/base_tcp.py +105 -0
  81. ablelabs-0.0.66/ablelabs/neon/utils/network/messenger.py +325 -0
  82. ablelabs-0.0.66/ablelabs/neon/utils/network/tcp_client.py +37 -0
  83. ablelabs-0.0.66/ablelabs/neon/utils/network/tcp_server.py +88 -0
  84. ablelabs-0.0.66/ablelabs/neon/utils/work_node.py +189 -0
  85. ablelabs-0.0.66/ablelabs.egg-info/PKG-INFO +16 -0
  86. ablelabs-0.0.66/ablelabs.egg-info/SOURCES.txt +90 -0
  87. ablelabs-0.0.66/ablelabs.egg-info/dependency_links.txt +1 -0
  88. ablelabs-0.0.66/ablelabs.egg-info/not-zip-safe +1 -0
  89. ablelabs-0.0.66/ablelabs.egg-info/requires.txt +7 -0
  90. ablelabs-0.0.66/ablelabs.egg-info/top_level.txt +1 -0
  91. ablelabs-0.0.66/setup.cfg +4 -0
  92. ablelabs-0.0.66/setup.py +27 -0
@@ -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
@@ -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()
@@ -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
@@ -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)