py-alaska 0.1.0__py3-none-any.whl
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.
- py_alaska/SmBlock.py +263 -0
- py_alaska/__init__.py +63 -0
- py_alaska/div_logo.png +0 -0
- py_alaska/gconfig.py +1241 -0
- py_alaska/imi_camera.py +391 -0
- py_alaska/tab_camera.py +730 -0
- py_alaska/task_manager.py +661 -0
- py_alaska/task_monitor.py +1533 -0
- py_alaska/task_performance.py +550 -0
- py_alaska/task_signal.py +238 -0
- py_alaska-0.1.0.dist-info/METADATA +263 -0
- py_alaska-0.1.0.dist-info/RECORD +15 -0
- py_alaska-0.1.0.dist-info/WHEEL +5 -0
- py_alaska-0.1.0.dist-info/licenses/LICENSE +21 -0
- py_alaska-0.1.0.dist-info/top_level.txt +1 -0
py_alaska/SmBlock.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""
|
|
2
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
║ ALASK v2.0 Project ║
|
|
4
|
+
║ SmBlock - Shared Memory Block Allocator ║
|
|
5
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
6
|
+
║ Version : 1.0.0 ║
|
|
7
|
+
║ Date : 2026-02-01 ║
|
|
8
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
9
|
+
║ Description: ║
|
|
10
|
+
║ 프로세스 간 공유 메모리를 사용한 이미지 블록 풀 관리자. ║
|
|
11
|
+
║ Free List 알고리즘으로 효율적인 O(1) 블록 할당/해제 수행. ║
|
|
12
|
+
║ ║
|
|
13
|
+
║ Memory Layout: ║
|
|
14
|
+
║ [Index Buffer: int16[N]] + [Data Buffer: uint8[N][H][W][C]] + [Free Head] ║
|
|
15
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from multiprocessing.shared_memory import SharedMemory
|
|
19
|
+
from typing import Tuple, Optional
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SmBlock:
|
|
24
|
+
"""프로세스 간 공유 메모리 이미지 블록 풀 관리자."""
|
|
25
|
+
|
|
26
|
+
__slots__ = ('_name', '_shape', '_maxsize', '_item_size', '_index_size',
|
|
27
|
+
'_is_new', '_shm', '_index_buffer', '_data_buffer', '_free_head',
|
|
28
|
+
'_lock', '_last_alloc')
|
|
29
|
+
|
|
30
|
+
def __init__(self, name: str, shape: Tuple[int, ...], maxsize: int,
|
|
31
|
+
create: bool = False, lock=None):
|
|
32
|
+
"""SmBlock 초기화.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
name: 공유 메모리 이름 (시스템 전역 고유)
|
|
36
|
+
shape: 이미지 형태 (height, width, channels)
|
|
37
|
+
maxsize: 최대 블록 수 (풀 크기)
|
|
38
|
+
create: True=새 공유 메모리 생성, False=기존에 연결
|
|
39
|
+
lock: multiprocessing.Lock 동시성 제어용
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
FileExistsError: create=True인데 이미 존재할 때
|
|
43
|
+
FileNotFoundError: create=False인데 존재하지 않을 때
|
|
44
|
+
"""
|
|
45
|
+
self._name = name
|
|
46
|
+
self._shape = shape
|
|
47
|
+
self._maxsize = maxsize
|
|
48
|
+
self._lock = lock
|
|
49
|
+
self._is_new = create
|
|
50
|
+
self._last_alloc = -1
|
|
51
|
+
|
|
52
|
+
# 크기 계산
|
|
53
|
+
self._item_size = int(np.prod(shape)) # H * W * C
|
|
54
|
+
self._index_size = 2 * maxsize # sizeof(int16) * N
|
|
55
|
+
|
|
56
|
+
# 총 공유 메모리 크기
|
|
57
|
+
total_size = self._index_size + (self._item_size * maxsize) + 2
|
|
58
|
+
|
|
59
|
+
# 공유 메모리 생성 또는 연결
|
|
60
|
+
if create:
|
|
61
|
+
self._shm = SharedMemory(name=name, create=True, size=total_size)
|
|
62
|
+
self._setup_buffers()
|
|
63
|
+
self._initialize_free_list()
|
|
64
|
+
else:
|
|
65
|
+
self._shm = SharedMemory(name=name, create=False)
|
|
66
|
+
self._setup_buffers()
|
|
67
|
+
|
|
68
|
+
def _setup_buffers(self):
|
|
69
|
+
"""numpy 배열로 공유 메모리 영역 매핑."""
|
|
70
|
+
buf = self._shm.buf
|
|
71
|
+
n = self._maxsize
|
|
72
|
+
|
|
73
|
+
# Index Buffer: offset 0, size 2*N
|
|
74
|
+
self._index_buffer = np.ndarray(
|
|
75
|
+
(n,), dtype=np.int16,
|
|
76
|
+
buffer=buf[0:self._index_size]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Data Buffer: offset 2*N, size N*H*W*C
|
|
80
|
+
data_offset = self._index_size
|
|
81
|
+
data_size = self._item_size * n
|
|
82
|
+
self._data_buffer = np.ndarray(
|
|
83
|
+
(n,) + self._shape, dtype=np.uint8,
|
|
84
|
+
buffer=buf[data_offset:data_offset + data_size]
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Free Head: offset 2*N + N*H*W*C, size 2
|
|
88
|
+
free_head_offset = data_offset + data_size
|
|
89
|
+
self._free_head = np.ndarray(
|
|
90
|
+
(1,), dtype=np.int16,
|
|
91
|
+
buffer=buf[free_head_offset:free_head_offset + 2]
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def _initialize_free_list(self):
|
|
95
|
+
"""Free List 초기화 (생성 시에만 호출)."""
|
|
96
|
+
n = self._maxsize
|
|
97
|
+
# 각 블록이 다음 블록을 가리키도록 설정
|
|
98
|
+
for i in range(n - 1):
|
|
99
|
+
self._index_buffer[i] = i + 1
|
|
100
|
+
self._index_buffer[n - 1] = -1 # 마지막 블록은 끝 표시
|
|
101
|
+
self._free_head[0] = 0 # 첫 번째 free 블록
|
|
102
|
+
|
|
103
|
+
def malloc(self, image: np.ndarray) -> int:
|
|
104
|
+
"""이미지를 풀에 할당하고 인덱스 반환. O(1) 시간복잡도.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
image: 저장할 이미지 (dtype=uint8, shape=생성자의 shape과 동일)
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
할당된 블록 인덱스 (0 ~ maxsize-1), 풀이 가득 차면 -1
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
ValueError: 이미지 shape이 맞지 않을 때
|
|
114
|
+
"""
|
|
115
|
+
if image.shape != self._shape:
|
|
116
|
+
raise ValueError(f"Shape mismatch: expected {self._shape}, got {image.shape}")
|
|
117
|
+
|
|
118
|
+
with self._lock:
|
|
119
|
+
head = self._free_head[0]
|
|
120
|
+
if head == -1:
|
|
121
|
+
return -1 # 풀이 가득 참
|
|
122
|
+
|
|
123
|
+
# Free list에서 블록 분리
|
|
124
|
+
next_free = self._index_buffer[head]
|
|
125
|
+
self._free_head[0] = next_free
|
|
126
|
+
self._index_buffer[head] = -1 # 사용 중 표시
|
|
127
|
+
|
|
128
|
+
# 데이터 복사
|
|
129
|
+
self._data_buffer[head][:] = image
|
|
130
|
+
self._last_alloc = head
|
|
131
|
+
return int(head)
|
|
132
|
+
|
|
133
|
+
def malloc2(self, image: np.ndarray) -> int:
|
|
134
|
+
"""마지막 할당 위치 다음부터 순차 탐색하여 빈 공간 할당.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
image: 저장할 이미지
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
할당된 블록 인덱스 또는 -1
|
|
141
|
+
"""
|
|
142
|
+
if image.shape != self._shape:
|
|
143
|
+
raise ValueError(f"Shape mismatch: expected {self._shape}, got {image.shape}")
|
|
144
|
+
|
|
145
|
+
with self._lock:
|
|
146
|
+
n = self._maxsize
|
|
147
|
+
start = (self._last_alloc + 1) % n
|
|
148
|
+
|
|
149
|
+
# 순차 탐색으로 빈 블록 찾기
|
|
150
|
+
for offset in range(n):
|
|
151
|
+
idx = (start + offset) % n
|
|
152
|
+
if self._index_buffer[idx] != -1 or idx == self._free_head[0]:
|
|
153
|
+
# 빈 블록 발견 - Free list에서 제거
|
|
154
|
+
if idx == self._free_head[0]:
|
|
155
|
+
# head인 경우
|
|
156
|
+
self._free_head[0] = self._index_buffer[idx]
|
|
157
|
+
else:
|
|
158
|
+
# 중간 노드인 경우 - 이전 노드 찾아서 연결
|
|
159
|
+
prev = self._free_head[0]
|
|
160
|
+
while prev != -1 and self._index_buffer[prev] != idx:
|
|
161
|
+
prev = self._index_buffer[prev]
|
|
162
|
+
if prev != -1:
|
|
163
|
+
self._index_buffer[prev] = self._index_buffer[idx]
|
|
164
|
+
|
|
165
|
+
self._index_buffer[idx] = -1 # 사용 중 표시
|
|
166
|
+
self._data_buffer[idx][:] = image
|
|
167
|
+
self._last_alloc = idx
|
|
168
|
+
return idx
|
|
169
|
+
|
|
170
|
+
return -1 # 풀이 가득 참
|
|
171
|
+
|
|
172
|
+
def mfree(self, index: int):
|
|
173
|
+
"""할당된 블록을 해제하고 Free List에 반환.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
index: 해제할 블록 인덱스
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
ValueError: 이미 비어있는 블록을 해제할 때
|
|
180
|
+
IndexError: 유효하지 않은 인덱스
|
|
181
|
+
"""
|
|
182
|
+
if index < 0 or index >= self._maxsize:
|
|
183
|
+
raise IndexError(f"Index {index} out of range [0, {self._maxsize})")
|
|
184
|
+
|
|
185
|
+
with self._lock:
|
|
186
|
+
if self._index_buffer[index] != -1:
|
|
187
|
+
raise ValueError(f"Block {index} is already free")
|
|
188
|
+
|
|
189
|
+
# Free list의 head로 추가 (스택 방식)
|
|
190
|
+
self._index_buffer[index] = self._free_head[0]
|
|
191
|
+
self._free_head[0] = index
|
|
192
|
+
|
|
193
|
+
def get(self, index: int) -> np.ndarray:
|
|
194
|
+
"""할당된 블록의 이미지 데이터 반환.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
index: 읽을 블록 인덱스
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
이미지 데이터 (shape=생성자의 shape)
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
ValueError: 비어있는 블록에 접근할 때
|
|
204
|
+
IndexError: 유효하지 않은 인덱스
|
|
205
|
+
"""
|
|
206
|
+
if index < 0 or index >= self._maxsize:
|
|
207
|
+
raise IndexError(f"Index {index} out of range [0, {self._maxsize})")
|
|
208
|
+
|
|
209
|
+
with self._lock:
|
|
210
|
+
if self._index_buffer[index] != -1:
|
|
211
|
+
raise ValueError(f"Block {index} is free (not allocated)")
|
|
212
|
+
|
|
213
|
+
return self._data_buffer[index].copy()
|
|
214
|
+
|
|
215
|
+
def size(self) -> int:
|
|
216
|
+
"""풀의 최대 크기(블록 수) 반환."""
|
|
217
|
+
return self._maxsize
|
|
218
|
+
|
|
219
|
+
def count(self) -> int:
|
|
220
|
+
"""현재 사용 가능한(비어있는) 블록 수 반환."""
|
|
221
|
+
with self._lock:
|
|
222
|
+
cnt = 0
|
|
223
|
+
idx = self._free_head[0]
|
|
224
|
+
while idx != -1:
|
|
225
|
+
cnt += 1
|
|
226
|
+
idx = self._index_buffer[idx]
|
|
227
|
+
return cnt
|
|
228
|
+
|
|
229
|
+
def close(self):
|
|
230
|
+
"""공유 메모리 연결 해제 및 정리."""
|
|
231
|
+
self._shm.close()
|
|
232
|
+
if self._is_new:
|
|
233
|
+
self._shm.unlink()
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def name(self) -> str:
|
|
237
|
+
return self._name
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def shape(self) -> Tuple[int, ...]:
|
|
241
|
+
return self._shape
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def maxsize(self) -> int:
|
|
245
|
+
return self._maxsize
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def item_size(self) -> int:
|
|
249
|
+
return self._item_size
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def index_size(self) -> int:
|
|
253
|
+
return self._index_size
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def is_new(self) -> bool:
|
|
257
|
+
return self._is_new
|
|
258
|
+
|
|
259
|
+
def __str__(self) -> str:
|
|
260
|
+
return f"SmBlock[ {self._name}, {self._shape}, {self._maxsize} ]"
|
|
261
|
+
|
|
262
|
+
def __repr__(self) -> str:
|
|
263
|
+
return self.__str__()
|
py_alaska/__init__.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
║ ALASKA v2.0 로봇 얼라인 비전 시스템 ║
|
|
4
|
+
║ Advanced Lightweight Asynchronous Service Kernel ║
|
|
5
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
6
|
+
║ Project : ALASKA v2.0 ║
|
|
7
|
+
║ Company : 동일비전소유 ║
|
|
8
|
+
║ Version : 2.0.0 ║
|
|
9
|
+
║ Date : 2026-01-31 ║
|
|
10
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
11
|
+
║ Modules: ║
|
|
12
|
+
║ - task_manager : TaskManager, TaskClass, TaskInfo, TaskWorker, RmiClient║
|
|
13
|
+
║ - task_monitor : TaskMonitor (HTTP-based monitoring) ║
|
|
14
|
+
║ - task_signal : Signal, SignalBroker, SignalClient ║
|
|
15
|
+
║ - task_performance: MethodPerformance, TaskPerformance, SystemPerformance ║
|
|
16
|
+
║ - gconfig : Global configuration management ║
|
|
17
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from .task_manager import TaskManager, TaskClass, TaskInfo, TaskWorker, RmiClient
|
|
21
|
+
from .task_monitor import TaskMonitor
|
|
22
|
+
from .task_signal import Signal, SignalBroker, SignalClient
|
|
23
|
+
from .task_performance import (
|
|
24
|
+
MethodPerformance,
|
|
25
|
+
TaskPerformance,
|
|
26
|
+
SystemPerformance,
|
|
27
|
+
TimingRecorder,
|
|
28
|
+
PerformanceCollector,
|
|
29
|
+
)
|
|
30
|
+
from .gconfig import (
|
|
31
|
+
gconfig,
|
|
32
|
+
GConfig,
|
|
33
|
+
ConfigError,
|
|
34
|
+
ConfigFileNotFoundError,
|
|
35
|
+
ConfigPathNotFoundError,
|
|
36
|
+
ConfigLockTimeoutError,
|
|
37
|
+
ConfigValidationError,
|
|
38
|
+
ConfigParseError,
|
|
39
|
+
ConfigIntegrityError,
|
|
40
|
+
ConfigSecurityError,
|
|
41
|
+
ConfigStaleLockError,
|
|
42
|
+
ConfigMandatoryFieldError,
|
|
43
|
+
ConfigRecoveryError,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
__version__ = "0.1.0"
|
|
47
|
+
__all__ = [
|
|
48
|
+
# Task Manager
|
|
49
|
+
"TaskManager", "TaskClass", "TaskInfo", "TaskWorker", "RmiClient",
|
|
50
|
+
# Task Monitor
|
|
51
|
+
"TaskMonitor",
|
|
52
|
+
# Task Signal
|
|
53
|
+
"Signal", "SignalBroker", "SignalClient",
|
|
54
|
+
# Task Performance
|
|
55
|
+
"MethodPerformance", "TaskPerformance", "SystemPerformance",
|
|
56
|
+
"TimingRecorder", "PerformanceCollector",
|
|
57
|
+
# GConfig
|
|
58
|
+
"gconfig", "GConfig",
|
|
59
|
+
"ConfigError", "ConfigFileNotFoundError", "ConfigPathNotFoundError",
|
|
60
|
+
"ConfigLockTimeoutError", "ConfigValidationError", "ConfigParseError",
|
|
61
|
+
"ConfigIntegrityError", "ConfigSecurityError", "ConfigStaleLockError",
|
|
62
|
+
"ConfigMandatoryFieldError", "ConfigRecoveryError",
|
|
63
|
+
]
|
py_alaska/div_logo.png
ADDED
|
Binary file
|