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/task_signal.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""
|
|
2
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
║ ALASK v2.0 Project ║
|
|
4
|
+
║ Task Signal - Process-Safe Signal Communication Module ║
|
|
5
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
6
|
+
║ Version : 2.0.0 ║
|
|
7
|
+
║ Date : 2026-01-30 ║
|
|
8
|
+
╠══════════════════════════════════════════════════════════════════════════════╣
|
|
9
|
+
║ Classes: ║
|
|
10
|
+
║ - Signal : Dataclass representing a signal message ║
|
|
11
|
+
║ - SignalBroker : Central broker managing queues and subscriptions ║
|
|
12
|
+
║ - SignalClient : Client wrapper for signal emission and reception ║
|
|
13
|
+
║ ║
|
|
14
|
+
║ Functions: ║
|
|
15
|
+
║ - _is_picklable : Check if an object can be pickled for IPC ║
|
|
16
|
+
║ ║
|
|
17
|
+
║ Notes (Thread/Process Mixed Mode): ║
|
|
18
|
+
║ 1. Handlers cannot be passed between processes - register locally ║
|
|
19
|
+
║ 2. Data must be picklable (no lambdas, sockets, file handles) ║
|
|
20
|
+
║ 3. Process mode requires SyncManager ║
|
|
21
|
+
║ 4. Thread-only mode uses mgr=None for lightweight operation ║
|
|
22
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
from typing import Any, Dict, List, Callable, Optional, TYPE_CHECKING
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
|
+
import threading
|
|
29
|
+
import queue
|
|
30
|
+
import time
|
|
31
|
+
import uuid
|
|
32
|
+
import pickle
|
|
33
|
+
import traceback
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from multiprocessing.managers import SyncManager
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _is_picklable(obj: Any) -> bool:
|
|
40
|
+
"""객체가 pickle 가능한지 확인"""
|
|
41
|
+
try:
|
|
42
|
+
pickle.dumps(obj)
|
|
43
|
+
return True
|
|
44
|
+
except (pickle.PicklingError, TypeError, AttributeError):
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class Signal:
|
|
50
|
+
name: str
|
|
51
|
+
source: str
|
|
52
|
+
data: Any = None
|
|
53
|
+
signal_id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
|
|
54
|
+
timestamp: float = field(default_factory=time.time)
|
|
55
|
+
|
|
56
|
+
def validate_for_ipc(self) -> bool:
|
|
57
|
+
"""IPC(프로세스 간 통신)에 사용 가능한지 확인"""
|
|
58
|
+
return _is_picklable(self.data)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SignalBroker:
|
|
62
|
+
"""시그널 브로커 - Thread/Process 혼용 지원
|
|
63
|
+
|
|
64
|
+
mgr=None: 스레드 전용 모드 (경량, 빠름)
|
|
65
|
+
mgr=Manager(): 프로세스 간 통신 모드 (IPC)
|
|
66
|
+
"""
|
|
67
|
+
__slots__ = ('_mgr', '_queues', '_subscribers', '_lock', '_mode')
|
|
68
|
+
|
|
69
|
+
def __init__(self, mgr: Optional[SyncManager] = None):
|
|
70
|
+
self._mgr = mgr
|
|
71
|
+
self._mode = 'process' if mgr else 'thread'
|
|
72
|
+
self._queues: Dict[str, Any] = {}
|
|
73
|
+
self._subscribers: Dict[str, List[str]] = {}
|
|
74
|
+
self._lock = threading.Lock()
|
|
75
|
+
|
|
76
|
+
def register(self, task_id: str) -> Any:
|
|
77
|
+
"""태스크 큐 등록 (모드에 따라 적절한 큐 타입 생성)"""
|
|
78
|
+
queues = self._queues
|
|
79
|
+
with self._lock:
|
|
80
|
+
if task_id not in queues:
|
|
81
|
+
if self._mgr:
|
|
82
|
+
queues[task_id] = self._mgr.Queue()
|
|
83
|
+
else:
|
|
84
|
+
queues[task_id] = queue.Queue()
|
|
85
|
+
return queues[task_id]
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def mode(self) -> str:
|
|
89
|
+
"""현재 모드 반환 ('thread' 또는 'process')"""
|
|
90
|
+
return self._mode
|
|
91
|
+
|
|
92
|
+
def unregister(self, task_id: str):
|
|
93
|
+
with self._lock:
|
|
94
|
+
self._queues.pop(task_id, None)
|
|
95
|
+
for subs in self._subscribers.values():
|
|
96
|
+
if task_id in subs:
|
|
97
|
+
subs.remove(task_id)
|
|
98
|
+
|
|
99
|
+
def subscribe(self, signal_name: str, task_id: str):
|
|
100
|
+
subs = self._subscribers
|
|
101
|
+
with self._lock:
|
|
102
|
+
if signal_name not in subs:
|
|
103
|
+
subs[signal_name] = []
|
|
104
|
+
lst = subs[signal_name]
|
|
105
|
+
if task_id not in lst:
|
|
106
|
+
lst.append(task_id)
|
|
107
|
+
|
|
108
|
+
def unsubscribe(self, signal_name: str, task_id: str):
|
|
109
|
+
with self._lock:
|
|
110
|
+
lst = self._subscribers.get(signal_name)
|
|
111
|
+
if lst and task_id in lst:
|
|
112
|
+
lst.remove(task_id)
|
|
113
|
+
|
|
114
|
+
def emit(self, signal: Signal, validate: bool = False) -> bool:
|
|
115
|
+
"""시그널 발행
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
signal: 발행할 시그널
|
|
119
|
+
validate: True이면 IPC 모드에서 pickle 가능 여부 검증
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
성공 여부
|
|
123
|
+
"""
|
|
124
|
+
if validate and self._mode == 'process' and not signal.validate_for_ipc():
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
with self._lock:
|
|
128
|
+
lst = self._subscribers.get(signal.name)
|
|
129
|
+
subscribers = lst[:] if lst else []
|
|
130
|
+
queues = self._queues
|
|
131
|
+
q_map = {tid: queues.get(tid) for tid in subscribers}
|
|
132
|
+
data = signal.__dict__
|
|
133
|
+
sent = False
|
|
134
|
+
for tid, q in q_map.items():
|
|
135
|
+
if q:
|
|
136
|
+
try:
|
|
137
|
+
q.put(data, block=False)
|
|
138
|
+
sent = True
|
|
139
|
+
except queue.Full:
|
|
140
|
+
print(f"[SignalBroker] Queue full for task '{tid}', signal '{signal.name}' dropped")
|
|
141
|
+
except Exception as e:
|
|
142
|
+
print(f"[SignalBroker] emit error to '{tid}': {e}")
|
|
143
|
+
return sent
|
|
144
|
+
|
|
145
|
+
def emit_to(self, task_id: str, signal: Signal, validate: bool = False) -> bool:
|
|
146
|
+
"""특정 태스크에 시그널 발행
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
task_id: 대상 태스크 ID
|
|
150
|
+
signal: 발행할 시그널
|
|
151
|
+
validate: True이면 IPC 모드에서 pickle 가능 여부 검증
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
성공 여부
|
|
155
|
+
"""
|
|
156
|
+
if validate and self._mode == 'process' and not signal.validate_for_ipc():
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
with self._lock:
|
|
160
|
+
q = self._queues.get(task_id)
|
|
161
|
+
if q:
|
|
162
|
+
try:
|
|
163
|
+
q.put(signal.__dict__, block=False)
|
|
164
|
+
return True
|
|
165
|
+
except queue.Full:
|
|
166
|
+
print(f"[SignalBroker] Queue full for task '{task_id}', signal '{signal.name}' dropped")
|
|
167
|
+
except Exception as e:
|
|
168
|
+
print(f"[SignalBroker] emit_to error '{task_id}': {e}")
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
def get_subscribers(self, signal_name: str) -> List[str]:
|
|
172
|
+
with self._lock:
|
|
173
|
+
lst = self._subscribers.get(signal_name)
|
|
174
|
+
return lst[:] if lst else []
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class SignalClient:
|
|
178
|
+
__slots__ = ('_broker', '_task_id', '_queue', '_handlers', '_running', '_thread')
|
|
179
|
+
|
|
180
|
+
def __init__(self, broker: SignalBroker, task_id: str):
|
|
181
|
+
self._broker = broker
|
|
182
|
+
self._task_id = task_id
|
|
183
|
+
self._queue = broker.register(task_id)
|
|
184
|
+
self._handlers: Dict[str, List[Callable]] = {}
|
|
185
|
+
self._running = False
|
|
186
|
+
self._thread = None
|
|
187
|
+
|
|
188
|
+
def start(self):
|
|
189
|
+
self._running = True
|
|
190
|
+
self._thread = threading.Thread(target=self._recv_loop, daemon=True)
|
|
191
|
+
self._thread.start()
|
|
192
|
+
|
|
193
|
+
def stop(self):
|
|
194
|
+
self._running = False
|
|
195
|
+
self._broker.unregister(self._task_id)
|
|
196
|
+
|
|
197
|
+
def _recv_loop(self):
|
|
198
|
+
q, handlers = self._queue, self._handlers
|
|
199
|
+
while self._running:
|
|
200
|
+
try:
|
|
201
|
+
data = q.get(timeout=0.5)
|
|
202
|
+
signal = Signal(**data) if isinstance(data, dict) else data
|
|
203
|
+
lst = handlers.get(signal.name)
|
|
204
|
+
if lst:
|
|
205
|
+
for handler in lst:
|
|
206
|
+
try:
|
|
207
|
+
handler(signal)
|
|
208
|
+
except Exception as e:
|
|
209
|
+
tb = traceback.format_exc()
|
|
210
|
+
print(f"[SignalClient:{self._task_id}] Handler error for '{signal.name}':\n{tb}")
|
|
211
|
+
except queue.Empty:
|
|
212
|
+
continue
|
|
213
|
+
except Exception as e:
|
|
214
|
+
print(f"[SignalClient:{self._task_id}] recv_loop error: {e}")
|
|
215
|
+
|
|
216
|
+
def on(self, signal_name: str, handler: Callable[[Signal], None]):
|
|
217
|
+
h = self._handlers
|
|
218
|
+
if signal_name not in h:
|
|
219
|
+
h[signal_name] = []
|
|
220
|
+
h[signal_name].append(handler)
|
|
221
|
+
self._broker.subscribe(signal_name, self._task_id)
|
|
222
|
+
|
|
223
|
+
def off(self, signal_name: str, handler: Callable[[Signal], None] = None):
|
|
224
|
+
lst = self._handlers.get(signal_name)
|
|
225
|
+
if lst:
|
|
226
|
+
if handler:
|
|
227
|
+
if handler in lst:
|
|
228
|
+
lst.remove(handler)
|
|
229
|
+
else:
|
|
230
|
+
lst.clear()
|
|
231
|
+
if not self._handlers.get(signal_name):
|
|
232
|
+
self._broker.unsubscribe(signal_name, self._task_id)
|
|
233
|
+
|
|
234
|
+
def emit(self, signal_name: str, data: Any = None):
|
|
235
|
+
self._broker.emit(Signal(name=signal_name, source=self._task_id, data=data))
|
|
236
|
+
|
|
237
|
+
def emit_to(self, task_id: str, signal_name: str, data: Any = None):
|
|
238
|
+
self._broker.emit_to(task_id, Signal(name=signal_name, source=self._task_id, data=data))
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py-alaska
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ALASKA - Multiprocess Task Management Framework for Python
|
|
5
|
+
Author-email: DivisionVision <info@division.co.kr>
|
|
6
|
+
Maintainer-email: DivisionVision <info@division.co.kr>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/divisionvision/alaska
|
|
9
|
+
Project-URL: Documentation, https://github.com/divisionvision/alaska#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/divisionvision/alaska.git
|
|
11
|
+
Project-URL: Issues, https://github.com/divisionvision/alaska/issues
|
|
12
|
+
Keywords: multiprocess,task,rmi,ipc,monitoring,shared-memory
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: numpy>=1.20.0
|
|
28
|
+
Provides-Extra: monitor
|
|
29
|
+
Requires-Dist: psutil>=5.8.0; extra == "monitor"
|
|
30
|
+
Provides-Extra: camera
|
|
31
|
+
Requires-Dist: PySide6>=6.0.0; extra == "camera"
|
|
32
|
+
Provides-Extra: all
|
|
33
|
+
Requires-Dist: psutil>=5.8.0; extra == "all"
|
|
34
|
+
Requires-Dist: PySide6>=6.0.0; extra == "all"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# ALASKA
|
|
43
|
+
|
|
44
|
+
**A**dvanced **L**ightweight **A**synchronous **S**ervice **K**ernel for **A**pplications
|
|
45
|
+
|
|
46
|
+
[](https://badge.fury.io/py/py-alaska)
|
|
47
|
+
[](https://pypi.org/project/py-alaska/)
|
|
48
|
+
[](https://opensource.org/licenses/MIT)
|
|
49
|
+
|
|
50
|
+
A Python framework for building multiprocess task management systems with RMI (Remote Method Invocation), shared memory, and real-time monitoring.
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- **Multiprocess Task Management**: Run tasks as separate processes or threads
|
|
55
|
+
- **RMI (Remote Method Invocation)**: Call methods across processes seamlessly
|
|
56
|
+
- **Shared Memory (SmBlock)**: Zero-copy image/data sharing between processes
|
|
57
|
+
- **Signal/Broker Pattern**: Pub/sub messaging between tasks
|
|
58
|
+
- **Web Monitoring Dashboard**: Real-time HTTP-based monitoring UI
|
|
59
|
+
- **Performance Metrics**: IPC/FUNC timing statistics with sliding window
|
|
60
|
+
- **Auto-restart**: Automatic task recovery on failure
|
|
61
|
+
- **JSON Configuration**: Flexible configuration with injection support
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Basic installation
|
|
67
|
+
pip install py-alaska
|
|
68
|
+
|
|
69
|
+
# With monitoring support (psutil)
|
|
70
|
+
pip install py-alaska[monitor]
|
|
71
|
+
|
|
72
|
+
# With camera/GUI support (PySide6)
|
|
73
|
+
pip install py-alaska[camera]
|
|
74
|
+
|
|
75
|
+
# Full installation
|
|
76
|
+
pip install py-alaska[all]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick Start
|
|
80
|
+
|
|
81
|
+
### 1. Define a Task
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from py_alaska import TaskClass
|
|
85
|
+
|
|
86
|
+
@TaskClass(name="my_task", mode="process", restart=True)
|
|
87
|
+
class MyTask:
|
|
88
|
+
def __init__(self):
|
|
89
|
+
self.task = None # Injected by framework
|
|
90
|
+
self.counter = 0
|
|
91
|
+
|
|
92
|
+
def increment(self, value: int) -> int:
|
|
93
|
+
"""RMI method: can be called from other tasks"""
|
|
94
|
+
self.counter += value
|
|
95
|
+
return self.counter
|
|
96
|
+
|
|
97
|
+
def get_count(self) -> int:
|
|
98
|
+
"""RMI method: query current count"""
|
|
99
|
+
return self.counter
|
|
100
|
+
|
|
101
|
+
def task_loop(self):
|
|
102
|
+
"""Main loop: runs continuously"""
|
|
103
|
+
while not self.task.should_stop():
|
|
104
|
+
# Do work here
|
|
105
|
+
pass
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2. Create Configuration (config.json)
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"app_info": {
|
|
113
|
+
"name": "MyApp",
|
|
114
|
+
"version": "1.0.0",
|
|
115
|
+
"id": "myapp_001"
|
|
116
|
+
},
|
|
117
|
+
"task_config": {
|
|
118
|
+
"_monitor": {
|
|
119
|
+
"port": 7000,
|
|
120
|
+
"exit_hook": true
|
|
121
|
+
},
|
|
122
|
+
"worker/my_task": {
|
|
123
|
+
"counter": 0
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 3. Run the Application
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from py_alaska import TaskManager, gconfig
|
|
133
|
+
import my_task # Import to register TaskClass
|
|
134
|
+
|
|
135
|
+
def main():
|
|
136
|
+
gconfig.load("config.json")
|
|
137
|
+
|
|
138
|
+
manager = TaskManager(gconfig)
|
|
139
|
+
manager.start_all()
|
|
140
|
+
|
|
141
|
+
# Access via RMI
|
|
142
|
+
worker = manager.get_client("worker")
|
|
143
|
+
result = worker.increment(10)
|
|
144
|
+
print(f"Counter: {result}")
|
|
145
|
+
|
|
146
|
+
# Web monitor at http://localhost:7000
|
|
147
|
+
import time
|
|
148
|
+
time.sleep(3600)
|
|
149
|
+
|
|
150
|
+
manager.stop_all()
|
|
151
|
+
|
|
152
|
+
if __name__ == "__main__":
|
|
153
|
+
main()
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Architecture
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
160
|
+
│ TaskManager │
|
|
161
|
+
├─────────────────────────────────────────────────────────────┤
|
|
162
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
163
|
+
│ │ Task A │ │ Task B │ │ Task C │ │
|
|
164
|
+
│ │ (Process) │ │ (Process) │ │ (Thread) │ │
|
|
165
|
+
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
|
166
|
+
│ │ │ │ │
|
|
167
|
+
│ └────────────────┼────────────────┘ │
|
|
168
|
+
│ │ │
|
|
169
|
+
│ ┌─────┴─────┐ │
|
|
170
|
+
│ │ RMI Bus │ │
|
|
171
|
+
│ │ (Queue) │ │
|
|
172
|
+
│ └─────┬─────┘ │
|
|
173
|
+
│ │ │
|
|
174
|
+
│ ┌─────┴─────┐ │
|
|
175
|
+
│ │ SmBlock │ │
|
|
176
|
+
│ │ (Shared) │ │
|
|
177
|
+
│ └───────────┘ │
|
|
178
|
+
├─────────────────────────────────────────────────────────────┤
|
|
179
|
+
│ TaskMonitor │
|
|
180
|
+
│ HTTP :7000 │
|
|
181
|
+
└─────────────────────────────────────────────────────────────┘
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Core Components
|
|
185
|
+
|
|
186
|
+
| Component | Description |
|
|
187
|
+
|-----------|-------------|
|
|
188
|
+
| `TaskManager` | Main orchestrator for all tasks |
|
|
189
|
+
| `TaskClass` | Decorator to define a task |
|
|
190
|
+
| `RmiClient` | Client for calling remote methods |
|
|
191
|
+
| `SmBlock` | Shared memory block pool for zero-copy data sharing |
|
|
192
|
+
| `Signal/SignalBroker` | Pub/sub messaging system |
|
|
193
|
+
| `TaskMonitor` | HTTP-based web monitoring dashboard |
|
|
194
|
+
| `GConfig` | Global configuration management |
|
|
195
|
+
|
|
196
|
+
## API Reference
|
|
197
|
+
|
|
198
|
+
### TaskClass Decorator
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
@TaskClass(
|
|
202
|
+
name="task_name", # Unique task identifier
|
|
203
|
+
mode="process", # "process" or "thread"
|
|
204
|
+
restart=True, # Auto-restart on failure
|
|
205
|
+
restart_delay=3.0, # Delay before restart (seconds)
|
|
206
|
+
)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### RMI Methods
|
|
210
|
+
|
|
211
|
+
Any public method in a TaskClass becomes an RMI method:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
# In Task A
|
|
215
|
+
def calculate(self, x: int, y: int) -> int:
|
|
216
|
+
return x + y
|
|
217
|
+
|
|
218
|
+
# From Task B or main process
|
|
219
|
+
client = manager.get_client("task_a")
|
|
220
|
+
result = client.calculate(10, 20) # Returns 30
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### SmBlock (Shared Memory)
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
# Configuration
|
|
227
|
+
"_smblock": {
|
|
228
|
+
"image_pool": {"shape": [1024, 1024, 3], "maxsize": 100}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# In task
|
|
232
|
+
index = self.smblock.malloc() # Allocate block
|
|
233
|
+
image = self.smblock.get(index) # Get numpy array
|
|
234
|
+
image[:] = frame # Write data
|
|
235
|
+
self.smblock.mfree(index) # Release block
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Monitoring
|
|
239
|
+
|
|
240
|
+
Access the web dashboard at `http://localhost:7000` (configurable port).
|
|
241
|
+
|
|
242
|
+
**Features:**
|
|
243
|
+
- Real-time task status (alive/stopped)
|
|
244
|
+
- RMI call statistics (count, timing)
|
|
245
|
+
- CPU/Memory usage per task
|
|
246
|
+
- SmBlock pool utilization
|
|
247
|
+
- Configuration editor
|
|
248
|
+
- Performance metrics (IPC/FUNC time)
|
|
249
|
+
|
|
250
|
+
## Requirements
|
|
251
|
+
|
|
252
|
+
- Python >= 3.8
|
|
253
|
+
- numpy >= 1.20.0
|
|
254
|
+
- psutil >= 5.8.0 (optional, for monitoring)
|
|
255
|
+
- PySide6 >= 6.0.0 (optional, for camera/GUI)
|
|
256
|
+
|
|
257
|
+
## License
|
|
258
|
+
|
|
259
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
260
|
+
|
|
261
|
+
## Contributing
|
|
262
|
+
|
|
263
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
py_alaska/SmBlock.py,sha256=pxnKHQmFqcFCZZQF-A__GBBKf4kus0VACdLfsA0Dm7M,10476
|
|
2
|
+
py_alaska/__init__.py,sha256=q4_ibJZWWvtUD7d2vt9RrsmViLTO55X_A_nDf_o3I6c,3355
|
|
3
|
+
py_alaska/div_logo.png,sha256=RKsuiBP7vFWBq1EEMrOEYkwuDzia3nuDMMDfKjzXUVI,26020
|
|
4
|
+
py_alaska/gconfig.py,sha256=6MCnGcjNNEJAjZRMjO06wx67B8D7NBzlJYS9JrXMkJA,43544
|
|
5
|
+
py_alaska/imi_camera.py,sha256=pPt6VTFqPD9U2YPABhalnsq9uFIV6iZkmJCvf8YQauM,15652
|
|
6
|
+
py_alaska/tab_camera.py,sha256=6Rj9n2eMp6LHXTZ8bm0eyoMLkOJDi2eLvPEVYevt5D0,31126
|
|
7
|
+
py_alaska/task_manager.py,sha256=gS6lY3A8g3Qhvk1maYaRPoQRpX-B9iXi_pJyuZP6wl0,28793
|
|
8
|
+
py_alaska/task_monitor.py,sha256=8cFy6i0NxK7hOCePJ4QpuN38hDNL_ey8mtAaClKuyn8,78213
|
|
9
|
+
py_alaska/task_performance.py,sha256=v11o2gOPt_rMKFIXe8qB7qCNRzILxCehvVytem6TWJs,22859
|
|
10
|
+
py_alaska/task_signal.py,sha256=EHlShzxKW6lgpBZ5CSL7W221-Ja9P0o1i5mxaj94-MY,9771
|
|
11
|
+
py_alaska-0.1.0.dist-info/licenses/LICENSE,sha256=wr6kC3HOkdD_s1IPebIBnK6ZnQyf7p-NtJrM_lK9LLk,1092
|
|
12
|
+
py_alaska-0.1.0.dist-info/METADATA,sha256=xD6vBFWynTtgv6nvm6DD8m-YPzgMdZUhfD0FrQNmGOI,9369
|
|
13
|
+
py_alaska-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
14
|
+
py_alaska-0.1.0.dist-info/top_level.txt,sha256=XIUr32XlSdVUarOxE-TaRvlKJitSLRJjqSsWRbw_wOg,10
|
|
15
|
+
py_alaska-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DivisionVision
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
py_alaska
|