nedo-vision-worker 1.0.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.
- nedo_vision_worker/__init__.py +10 -0
- nedo_vision_worker/cli.py +195 -0
- nedo_vision_worker/config/ConfigurationManager.py +196 -0
- nedo_vision_worker/config/__init__.py +1 -0
- nedo_vision_worker/database/DatabaseManager.py +219 -0
- nedo_vision_worker/database/__init__.py +1 -0
- nedo_vision_worker/doctor.py +453 -0
- nedo_vision_worker/initializer/AppInitializer.py +78 -0
- nedo_vision_worker/initializer/__init__.py +1 -0
- nedo_vision_worker/models/__init__.py +15 -0
- nedo_vision_worker/models/ai_model.py +29 -0
- nedo_vision_worker/models/auth.py +14 -0
- nedo_vision_worker/models/config.py +9 -0
- nedo_vision_worker/models/dataset_source.py +30 -0
- nedo_vision_worker/models/logs.py +9 -0
- nedo_vision_worker/models/ppe_detection.py +39 -0
- nedo_vision_worker/models/ppe_detection_label.py +20 -0
- nedo_vision_worker/models/restricted_area_violation.py +20 -0
- nedo_vision_worker/models/user.py +10 -0
- nedo_vision_worker/models/worker_source.py +19 -0
- nedo_vision_worker/models/worker_source_pipeline.py +21 -0
- nedo_vision_worker/models/worker_source_pipeline_config.py +24 -0
- nedo_vision_worker/models/worker_source_pipeline_debug.py +15 -0
- nedo_vision_worker/models/worker_source_pipeline_detection.py +14 -0
- nedo_vision_worker/protos/AIModelService_pb2.py +46 -0
- nedo_vision_worker/protos/AIModelService_pb2_grpc.py +140 -0
- nedo_vision_worker/protos/DatasetSourceService_pb2.py +46 -0
- nedo_vision_worker/protos/DatasetSourceService_pb2_grpc.py +140 -0
- nedo_vision_worker/protos/HumanDetectionService_pb2.py +44 -0
- nedo_vision_worker/protos/HumanDetectionService_pb2_grpc.py +140 -0
- nedo_vision_worker/protos/PPEDetectionService_pb2.py +46 -0
- nedo_vision_worker/protos/PPEDetectionService_pb2_grpc.py +140 -0
- nedo_vision_worker/protos/VisionWorkerService_pb2.py +72 -0
- nedo_vision_worker/protos/VisionWorkerService_pb2_grpc.py +471 -0
- nedo_vision_worker/protos/WorkerSourcePipelineService_pb2.py +64 -0
- nedo_vision_worker/protos/WorkerSourcePipelineService_pb2_grpc.py +312 -0
- nedo_vision_worker/protos/WorkerSourceService_pb2.py +50 -0
- nedo_vision_worker/protos/WorkerSourceService_pb2_grpc.py +183 -0
- nedo_vision_worker/protos/__init__.py +1 -0
- nedo_vision_worker/repositories/AIModelRepository.py +44 -0
- nedo_vision_worker/repositories/DatasetSourceRepository.py +150 -0
- nedo_vision_worker/repositories/PPEDetectionRepository.py +112 -0
- nedo_vision_worker/repositories/RestrictedAreaRepository.py +88 -0
- nedo_vision_worker/repositories/WorkerSourcePipelineDebugRepository.py +90 -0
- nedo_vision_worker/repositories/WorkerSourcePipelineDetectionRepository.py +48 -0
- nedo_vision_worker/repositories/WorkerSourcePipelineRepository.py +174 -0
- nedo_vision_worker/repositories/WorkerSourceRepository.py +46 -0
- nedo_vision_worker/repositories/__init__.py +1 -0
- nedo_vision_worker/services/AIModelClient.py +362 -0
- nedo_vision_worker/services/ConnectionInfoClient.py +57 -0
- nedo_vision_worker/services/DatasetSourceClient.py +88 -0
- nedo_vision_worker/services/FileToRTMPServer.py +78 -0
- nedo_vision_worker/services/GrpcClientBase.py +155 -0
- nedo_vision_worker/services/GrpcClientManager.py +141 -0
- nedo_vision_worker/services/ImageUploadClient.py +82 -0
- nedo_vision_worker/services/PPEDetectionClient.py +108 -0
- nedo_vision_worker/services/RTSPtoRTMPStreamer.py +98 -0
- nedo_vision_worker/services/RestrictedAreaClient.py +100 -0
- nedo_vision_worker/services/SystemUsageClient.py +77 -0
- nedo_vision_worker/services/VideoStreamClient.py +161 -0
- nedo_vision_worker/services/WorkerSourceClient.py +215 -0
- nedo_vision_worker/services/WorkerSourcePipelineClient.py +393 -0
- nedo_vision_worker/services/WorkerSourceUpdater.py +134 -0
- nedo_vision_worker/services/WorkerStatusClient.py +65 -0
- nedo_vision_worker/services/__init__.py +1 -0
- nedo_vision_worker/util/HardwareID.py +104 -0
- nedo_vision_worker/util/ImageUploader.py +92 -0
- nedo_vision_worker/util/Networking.py +94 -0
- nedo_vision_worker/util/PlatformDetector.py +50 -0
- nedo_vision_worker/util/SystemMonitor.py +299 -0
- nedo_vision_worker/util/VideoProbeUtil.py +120 -0
- nedo_vision_worker/util/__init__.py +1 -0
- nedo_vision_worker/worker/CoreActionWorker.py +125 -0
- nedo_vision_worker/worker/DataSenderWorker.py +168 -0
- nedo_vision_worker/worker/DataSyncWorker.py +143 -0
- nedo_vision_worker/worker/DatasetFrameSender.py +208 -0
- nedo_vision_worker/worker/DatasetFrameWorker.py +412 -0
- nedo_vision_worker/worker/PPEDetectionManager.py +86 -0
- nedo_vision_worker/worker/PipelineActionWorker.py +129 -0
- nedo_vision_worker/worker/PipelineImageWorker.py +116 -0
- nedo_vision_worker/worker/RabbitMQListener.py +170 -0
- nedo_vision_worker/worker/RestrictedAreaManager.py +85 -0
- nedo_vision_worker/worker/SystemUsageManager.py +111 -0
- nedo_vision_worker/worker/VideoStreamWorker.py +139 -0
- nedo_vision_worker/worker/WorkerManager.py +155 -0
- nedo_vision_worker/worker/__init__.py +1 -0
- nedo_vision_worker/worker_service.py +264 -0
- nedo_vision_worker-1.0.0.dist-info/METADATA +563 -0
- nedo_vision_worker-1.0.0.dist-info/RECORD +92 -0
- nedo_vision_worker-1.0.0.dist-info/WHEEL +5 -0
- nedo_vision_worker-1.0.0.dist-info/entry_points.txt +2 -0
- nedo_vision_worker-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Nedo Vision Worker Service Doctor
|
|
4
|
+
|
|
5
|
+
This module provides diagnostic capabilities to check system requirements
|
|
6
|
+
and dependencies for the Nedo Vision Worker Service.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import platform
|
|
12
|
+
import shutil
|
|
13
|
+
import os
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def check_python_version():
|
|
18
|
+
"""Check if Python version meets requirements."""
|
|
19
|
+
print("🐍 Checking Python version...")
|
|
20
|
+
|
|
21
|
+
version = sys.version_info
|
|
22
|
+
min_version = (3, 8)
|
|
23
|
+
|
|
24
|
+
if version >= min_version:
|
|
25
|
+
print(f" ✅ Python {version.major}.{version.minor}.{version.micro} (meets requirement >= {min_version[0]}.{min_version[1]})")
|
|
26
|
+
return True
|
|
27
|
+
else:
|
|
28
|
+
print(f" ❌ Python {version.major}.{version.minor}.{version.micro} (requires >= {min_version[0]}.{min_version[1]})")
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def check_ffmpeg():
|
|
33
|
+
"""Check if FFmpeg is installed and accessible."""
|
|
34
|
+
print("🎬 Checking FFmpeg...")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
# Check if ffmpeg is in PATH
|
|
38
|
+
ffmpeg_path = shutil.which("ffmpeg")
|
|
39
|
+
if not ffmpeg_path:
|
|
40
|
+
print(" ❌ FFmpeg not found in PATH")
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
# Check ffmpeg version
|
|
44
|
+
result = subprocess.run(
|
|
45
|
+
["ffmpeg", "-version"],
|
|
46
|
+
capture_output=True,
|
|
47
|
+
text=True,
|
|
48
|
+
timeout=10
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if result.returncode == 0:
|
|
52
|
+
# Extract version from output
|
|
53
|
+
version_line = result.stdout.split('\n')[0]
|
|
54
|
+
print(f" ✅ {version_line}")
|
|
55
|
+
print(f" 📍 Location: {ffmpeg_path}")
|
|
56
|
+
return True
|
|
57
|
+
else:
|
|
58
|
+
print(" ❌ FFmpeg found but failed to get version")
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
except subprocess.TimeoutExpired:
|
|
62
|
+
print(" ❌ FFmpeg check timed out")
|
|
63
|
+
return False
|
|
64
|
+
except Exception as e:
|
|
65
|
+
print(f" ❌ Error checking FFmpeg: {e}")
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def check_opencv():
|
|
70
|
+
"""Check if OpenCV is properly installed."""
|
|
71
|
+
print("👁️ Checking OpenCV...")
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
import cv2
|
|
75
|
+
version = cv2.__version__
|
|
76
|
+
build_info = cv2.getBuildInformation()
|
|
77
|
+
|
|
78
|
+
print(f" ✅ OpenCV {version} installed")
|
|
79
|
+
|
|
80
|
+
# Check OpenCV build configuration
|
|
81
|
+
if "CUDA" in build_info:
|
|
82
|
+
print(" 🚀 OpenCV built with CUDA support")
|
|
83
|
+
if "OpenMP" in build_info:
|
|
84
|
+
print(" ⚡ OpenCV built with OpenMP support")
|
|
85
|
+
|
|
86
|
+
# Check for platform-specific optimizations
|
|
87
|
+
machine = platform.machine()
|
|
88
|
+
if machine in ["aarch64", "armv7l", "arm64"]:
|
|
89
|
+
if "NEON" in build_info:
|
|
90
|
+
print(" 🎯 OpenCV built with ARM NEON optimizations")
|
|
91
|
+
else:
|
|
92
|
+
print(" ⚠️ OpenCV may not have ARM optimizations")
|
|
93
|
+
|
|
94
|
+
# Test basic functionality
|
|
95
|
+
import numpy as np
|
|
96
|
+
test_img = np.zeros((100, 100, 3), dtype=np.uint8)
|
|
97
|
+
|
|
98
|
+
# Test encoding/decoding
|
|
99
|
+
_, encoded = cv2.imencode('.jpg', test_img)
|
|
100
|
+
decoded = cv2.imdecode(encoded, cv2.IMREAD_COLOR)
|
|
101
|
+
|
|
102
|
+
if decoded is not None:
|
|
103
|
+
print(" ✅ OpenCV basic functionality working")
|
|
104
|
+
return True
|
|
105
|
+
else:
|
|
106
|
+
print(" ❌ OpenCV encoding/decoding test failed")
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
except ImportError:
|
|
110
|
+
print(" ❌ OpenCV not installed")
|
|
111
|
+
return False
|
|
112
|
+
except Exception as e:
|
|
113
|
+
print(f" ❌ OpenCV test failed: {e}")
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def check_grpc():
|
|
118
|
+
"""Check if gRPC is properly installed."""
|
|
119
|
+
print("🌐 Checking gRPC...")
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
import grpc
|
|
123
|
+
print(f" ✅ gRPC installed")
|
|
124
|
+
|
|
125
|
+
# Test basic gRPC functionality
|
|
126
|
+
from grpc import StatusCode
|
|
127
|
+
print(" ✅ gRPC basic imports working")
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
except ImportError:
|
|
131
|
+
print(" ❌ gRPC not installed")
|
|
132
|
+
return False
|
|
133
|
+
except Exception as e:
|
|
134
|
+
print(f" ❌ gRPC test failed: {e}")
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def check_pynvml():
|
|
139
|
+
"""Check if pynvml (NVIDIA management) is available."""
|
|
140
|
+
print("🎮 Checking NVIDIA GPU support...")
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
import pynvml
|
|
144
|
+
pynvml.nvmlInit()
|
|
145
|
+
|
|
146
|
+
device_count = pynvml.nvmlDeviceGetCount()
|
|
147
|
+
if device_count > 0:
|
|
148
|
+
for i in range(device_count):
|
|
149
|
+
handle = pynvml.nvmlDeviceGetHandleByIndex(i)
|
|
150
|
+
name = pynvml.nvmlDeviceGetName(handle)
|
|
151
|
+
# Handle both string and bytes return types for compatibility
|
|
152
|
+
if isinstance(name, bytes):
|
|
153
|
+
name = name.decode('utf-8')
|
|
154
|
+
|
|
155
|
+
# Get additional GPU information
|
|
156
|
+
try:
|
|
157
|
+
memory_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
|
|
158
|
+
memory_total_gb = memory_info.total / (1024**3)
|
|
159
|
+
print(f" ✅ GPU {i}: {name}")
|
|
160
|
+
print(f" 💾 Memory: {memory_total_gb:.1f} GB")
|
|
161
|
+
|
|
162
|
+
# Check for Jetson-specific GPUs
|
|
163
|
+
if "tegra" in name.lower() or "jetson" in name.lower():
|
|
164
|
+
print(" 🚀 Jetson GPU detected")
|
|
165
|
+
|
|
166
|
+
# Check compute capability for deep learning
|
|
167
|
+
try:
|
|
168
|
+
major, minor = pynvml.nvmlDeviceGetCudaComputeCapability(handle)
|
|
169
|
+
print(f" 🔢 Compute Capability: {major}.{minor}")
|
|
170
|
+
if major >= 6: # Pascal architecture or newer
|
|
171
|
+
print(" ✅ GPU supports modern deep learning frameworks")
|
|
172
|
+
else:
|
|
173
|
+
print(" ⚠️ GPU may have limited deep learning support")
|
|
174
|
+
except:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
except Exception as e:
|
|
178
|
+
print(f" ✅ GPU {i}: {name} (limited info: {e})")
|
|
179
|
+
|
|
180
|
+
return True
|
|
181
|
+
else:
|
|
182
|
+
print(" ⚠️ pynvml installed but no NVIDIA GPUs detected")
|
|
183
|
+
# Check if we're on ARM and might have integrated GPU
|
|
184
|
+
if platform.machine() in ["aarch64", "armv7l", "arm64"]:
|
|
185
|
+
print(" 💡 ARM device may use integrated GPU (Mali, Adreno, etc.)")
|
|
186
|
+
return True # Not an error, just no NVIDIA GPU
|
|
187
|
+
|
|
188
|
+
except ImportError:
|
|
189
|
+
print(" ❌ pynvml not installed")
|
|
190
|
+
print(" 💡 Install with: pip install pynvml")
|
|
191
|
+
return False
|
|
192
|
+
except Exception as e:
|
|
193
|
+
print(f" ⚠️ GPU check failed: {e}")
|
|
194
|
+
# More helpful error messages for common issues
|
|
195
|
+
if "driver" in str(e).lower():
|
|
196
|
+
print(" 💡 NVIDIA drivers may not be installed")
|
|
197
|
+
elif "nvml" in str(e).lower():
|
|
198
|
+
print(" 💡 NVIDIA Management Library not available")
|
|
199
|
+
return True # Not critical for service operation
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def check_storage_permissions():
|
|
203
|
+
"""Check if we can create storage directories."""
|
|
204
|
+
print("💾 Checking storage permissions...")
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
# Test default storage path
|
|
208
|
+
test_path = Path("data") / "test_permissions"
|
|
209
|
+
test_path.mkdir(parents=True, exist_ok=True)
|
|
210
|
+
|
|
211
|
+
# Test file creation
|
|
212
|
+
test_file = test_path / "test.txt"
|
|
213
|
+
test_file.write_text("test")
|
|
214
|
+
|
|
215
|
+
# Test file reading
|
|
216
|
+
content = test_file.read_text()
|
|
217
|
+
|
|
218
|
+
# Cleanup
|
|
219
|
+
test_file.unlink()
|
|
220
|
+
test_path.rmdir()
|
|
221
|
+
|
|
222
|
+
if content == "test":
|
|
223
|
+
print(" ✅ Storage read/write permissions OK")
|
|
224
|
+
return True
|
|
225
|
+
else:
|
|
226
|
+
print(" ❌ Storage read/write test failed")
|
|
227
|
+
return False
|
|
228
|
+
|
|
229
|
+
except Exception as e:
|
|
230
|
+
print(f" ❌ Storage permission check failed: {e}")
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def check_network_connectivity():
|
|
235
|
+
"""Check basic network connectivity."""
|
|
236
|
+
print("🌐 Checking network connectivity...")
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
import socket
|
|
240
|
+
|
|
241
|
+
# Test DNS resolution
|
|
242
|
+
socket.gethostbyname("be.vision.sindika.co.id")
|
|
243
|
+
print(" ✅ DNS resolution working (be.vision.sindika.co.id)")
|
|
244
|
+
|
|
245
|
+
# Test basic socket connectivity
|
|
246
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
247
|
+
sock.settimeout(5)
|
|
248
|
+
result = sock.connect_ex(("be.vision.sindika.co.id", 50051))
|
|
249
|
+
sock.close()
|
|
250
|
+
|
|
251
|
+
if result == 0:
|
|
252
|
+
print(" ✅ Network connectivity to be.vision.sindika.co.id:50051 OK")
|
|
253
|
+
return True
|
|
254
|
+
else:
|
|
255
|
+
print(" ⚠️ Cannot connect to be.vision.sindika.co.id:50051 (may be normal if service is down)")
|
|
256
|
+
return True # Not critical for initial setup
|
|
257
|
+
|
|
258
|
+
except Exception as e:
|
|
259
|
+
print(f" ⚠️ Network check failed: {e}")
|
|
260
|
+
return True # Not critical for service installation
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def check_platform_compatibility():
|
|
264
|
+
"""Check platform-specific compatibility."""
|
|
265
|
+
print("🔧 Checking platform compatibility...")
|
|
266
|
+
|
|
267
|
+
system = platform.system()
|
|
268
|
+
machine = platform.machine()
|
|
269
|
+
|
|
270
|
+
# Check for known compatible platforms
|
|
271
|
+
compatible_platforms = {
|
|
272
|
+
"Linux": ["x86_64", "aarch64", "armv7l", "arm64"],
|
|
273
|
+
"Windows": ["AMD64", "x86_64"],
|
|
274
|
+
"Darwin": ["x86_64", "arm64"] # macOS Intel and Apple Silicon
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if system in compatible_platforms:
|
|
278
|
+
if machine in compatible_platforms[system]:
|
|
279
|
+
print(f" ✅ Platform {system}/{machine} is supported")
|
|
280
|
+
|
|
281
|
+
# Special handling for ARM devices
|
|
282
|
+
if machine in ["aarch64", "armv7l", "arm64"]:
|
|
283
|
+
print(" 📱 ARM-based device detected")
|
|
284
|
+
if "tegra" in platform.platform().lower():
|
|
285
|
+
print(" 🚀 NVIDIA Jetson device detected")
|
|
286
|
+
elif "raspberry" in platform.platform().lower():
|
|
287
|
+
print(" 🍓 Raspberry Pi device detected")
|
|
288
|
+
|
|
289
|
+
return True
|
|
290
|
+
else:
|
|
291
|
+
print(f" ⚠️ Architecture {machine} may have limited support on {system}")
|
|
292
|
+
return True # Still allow execution
|
|
293
|
+
else:
|
|
294
|
+
print(f" ⚠️ Platform {system} may have limited support")
|
|
295
|
+
return True # Still allow execution
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def print_system_info():
|
|
299
|
+
"""Print basic system information."""
|
|
300
|
+
print("💻 System Information:")
|
|
301
|
+
print(f" 🖥️ OS: {platform.system()} {platform.release()}")
|
|
302
|
+
print(f" 🏗️ Architecture: {platform.machine()}")
|
|
303
|
+
print(f" 🐍 Python: {platform.python_version()}")
|
|
304
|
+
print(f" 📍 Python executable: {sys.executable}")
|
|
305
|
+
|
|
306
|
+
# Additional platform details
|
|
307
|
+
try:
|
|
308
|
+
import os
|
|
309
|
+
if hasattr(os, 'uname'):
|
|
310
|
+
uname = os.uname()
|
|
311
|
+
print(f" 🔧 Kernel: {uname.sysname} {uname.release}")
|
|
312
|
+
except:
|
|
313
|
+
pass
|
|
314
|
+
|
|
315
|
+
# Check for containerized environment
|
|
316
|
+
if Path("/.dockerenv").exists():
|
|
317
|
+
print(" 🐳 Running in Docker container")
|
|
318
|
+
elif os.environ.get("KUBERNETES_SERVICE_HOST"):
|
|
319
|
+
print(" ☸️ Running in Kubernetes pod")
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def print_installation_help():
|
|
323
|
+
"""Print installation help for missing dependencies."""
|
|
324
|
+
print("\n📋 Installation Help:")
|
|
325
|
+
print("=" * 50)
|
|
326
|
+
|
|
327
|
+
system = platform.system()
|
|
328
|
+
machine = platform.machine()
|
|
329
|
+
|
|
330
|
+
print("\n🎬 FFmpeg Installation:")
|
|
331
|
+
if system == "Windows":
|
|
332
|
+
print(" • Using Chocolatey: choco install ffmpeg")
|
|
333
|
+
print(" • Using winget: winget install FFmpeg")
|
|
334
|
+
print(" • Manual: Download from https://ffmpeg.org/download.html")
|
|
335
|
+
elif system == "Darwin": # macOS
|
|
336
|
+
print(" • Using Homebrew: brew install ffmpeg")
|
|
337
|
+
print(" • Using MacPorts: sudo port install ffmpeg")
|
|
338
|
+
else: # Linux
|
|
339
|
+
print(" • Ubuntu/Debian: sudo apt update && sudo apt install ffmpeg")
|
|
340
|
+
print(" • CentOS/RHEL: sudo yum install ffmpeg")
|
|
341
|
+
print(" • Fedora: sudo dnf install ffmpeg")
|
|
342
|
+
print(" • Alpine: apk add ffmpeg")
|
|
343
|
+
|
|
344
|
+
# ARM-specific guidance
|
|
345
|
+
if machine in ["aarch64", "armv7l", "arm64"]:
|
|
346
|
+
print(" • For ARM devices:")
|
|
347
|
+
print(" - Jetson: Usually pre-installed with JetPack")
|
|
348
|
+
print(" - Raspberry Pi: sudo apt install ffmpeg")
|
|
349
|
+
print(" - Build from source for optimal performance")
|
|
350
|
+
|
|
351
|
+
print("\n🐍 Python Dependencies:")
|
|
352
|
+
print(" • Install all: pip install -r requirements.txt")
|
|
353
|
+
print(" • Or install individually:")
|
|
354
|
+
print(" - pip install opencv-python")
|
|
355
|
+
print(" - pip install grpcio")
|
|
356
|
+
print(" - pip install pynvml")
|
|
357
|
+
|
|
358
|
+
# Platform-specific OpenCV guidance
|
|
359
|
+
if machine in ["aarch64", "armv7l", "arm64"]:
|
|
360
|
+
print("\n🔧 ARM-Specific Notes:")
|
|
361
|
+
print(" • OpenCV: Consider opencv-python-headless for servers")
|
|
362
|
+
print(" • Jetson: Use opencv built with JetPack for GPU acceleration")
|
|
363
|
+
print(" • Build from source for optimal ARM performance")
|
|
364
|
+
print(" • For Jetson: Install with 'sudo apt install python3-opencv'")
|
|
365
|
+
|
|
366
|
+
print("\n🎮 GPU Support:")
|
|
367
|
+
if system == "Linux" and machine in ["aarch64", "armv7l"]:
|
|
368
|
+
print(" • Jetson devices:")
|
|
369
|
+
print(" - Install JetPack SDK from NVIDIA")
|
|
370
|
+
print(" - Verify with: sudo /usr/bin/tegrastats")
|
|
371
|
+
print(" - Check CUDA: nvcc --version")
|
|
372
|
+
else:
|
|
373
|
+
print(" • NVIDIA GPU (Optional):")
|
|
374
|
+
print(" - Install NVIDIA drivers from https://www.nvidia.com/drivers/")
|
|
375
|
+
print(" - Install CUDA toolkit if needed")
|
|
376
|
+
print(" - pynvml should work automatically if drivers are installed")
|
|
377
|
+
|
|
378
|
+
print("\n☁️ Cloud/Container Deployment:")
|
|
379
|
+
print(" • Docker: Use nvidia/cuda base images for GPU support")
|
|
380
|
+
print(" • Cloud: Ensure GPU instances have proper drivers")
|
|
381
|
+
print(" • Kubernetes: Use nvidia.com/gpu resource limits")
|
|
382
|
+
print(" • AWS: Use Deep Learning AMI or ECS GPU instances")
|
|
383
|
+
print(" • GCP: Use AI Platform or GPU-enabled Compute instances")
|
|
384
|
+
|
|
385
|
+
print("\n📦 Package Installation Tips:")
|
|
386
|
+
print(" • Use virtual environments: python -m venv venv")
|
|
387
|
+
print(" • Update pip: pip install --upgrade pip")
|
|
388
|
+
print(" • For ARM: pip install --extra-index-url https://www.piwheels.org/simple/")
|
|
389
|
+
print(" • Build tools: sudo apt install build-essential python3-dev")
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def main():
|
|
393
|
+
"""Run all diagnostic checks."""
|
|
394
|
+
print("🏥 Nedo Vision Worker Service Doctor")
|
|
395
|
+
print("=" * 50)
|
|
396
|
+
|
|
397
|
+
# Print system info
|
|
398
|
+
print_system_info()
|
|
399
|
+
print()
|
|
400
|
+
|
|
401
|
+
# Run all checks
|
|
402
|
+
checks = [
|
|
403
|
+
("Platform Compatibility", check_platform_compatibility),
|
|
404
|
+
("Python Version", check_python_version),
|
|
405
|
+
("FFmpeg", check_ffmpeg),
|
|
406
|
+
("OpenCV", check_opencv),
|
|
407
|
+
("gRPC", check_grpc),
|
|
408
|
+
("NVIDIA GPU Support", check_pynvml),
|
|
409
|
+
("Storage Permissions", check_storage_permissions),
|
|
410
|
+
("Network Connectivity", check_network_connectivity),
|
|
411
|
+
]
|
|
412
|
+
|
|
413
|
+
results = []
|
|
414
|
+
print("🔍 Running Diagnostic Checks:")
|
|
415
|
+
print("-" * 30)
|
|
416
|
+
|
|
417
|
+
for name, check_func in checks:
|
|
418
|
+
try:
|
|
419
|
+
result = check_func()
|
|
420
|
+
results.append((name, result))
|
|
421
|
+
except Exception as e:
|
|
422
|
+
print(f" ❌ {name} check failed with exception: {e}")
|
|
423
|
+
results.append((name, False))
|
|
424
|
+
print()
|
|
425
|
+
|
|
426
|
+
# Summary
|
|
427
|
+
print("📊 Summary:")
|
|
428
|
+
print("-" * 20)
|
|
429
|
+
|
|
430
|
+
passed = 0
|
|
431
|
+
failed = 0
|
|
432
|
+
|
|
433
|
+
for name, result in results:
|
|
434
|
+
if result:
|
|
435
|
+
print(f" ✅ {name}")
|
|
436
|
+
passed += 1
|
|
437
|
+
else:
|
|
438
|
+
print(f" ❌ {name}")
|
|
439
|
+
failed += 1
|
|
440
|
+
|
|
441
|
+
print(f"\n🎯 Results: {passed} passed, {failed} failed")
|
|
442
|
+
|
|
443
|
+
if failed > 0:
|
|
444
|
+
print("\n⚠️ Some checks failed. See installation help below:")
|
|
445
|
+
print_installation_help()
|
|
446
|
+
return 1
|
|
447
|
+
else:
|
|
448
|
+
print("\n🎉 All checks passed! Your system is ready for Nedo Vision Worker Service.")
|
|
449
|
+
return 0
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
if __name__ == "__main__":
|
|
453
|
+
sys.exit(main())
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
import uuid
|
|
4
|
+
import grpc
|
|
5
|
+
from ..config.ConfigurationManager import ConfigurationManager
|
|
6
|
+
from ..util.PlatformDetector import PlatformDetector
|
|
7
|
+
from ..util.Networking import Networking
|
|
8
|
+
from ..services.ConnectionInfoClient import ConnectionInfoClient
|
|
9
|
+
from ..database.DatabaseManager import DatabaseManager
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AppInitializer:
|
|
13
|
+
@staticmethod
|
|
14
|
+
def validate_uuid(value):
|
|
15
|
+
"""Validate if the provided value is a valid UUID."""
|
|
16
|
+
try:
|
|
17
|
+
uuid.UUID(value)
|
|
18
|
+
return value
|
|
19
|
+
except ValueError:
|
|
20
|
+
raise ValueError(f"Invalid device ID format: {value}. Must be a valid UUID.")
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def validate_server_host(value):
|
|
24
|
+
"""Validate if the server host is a valid domain name or IP address."""
|
|
25
|
+
domain_regex = (
|
|
26
|
+
r"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*"
|
|
27
|
+
r"([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"
|
|
28
|
+
)
|
|
29
|
+
ip_regex = (
|
|
30
|
+
r"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\."
|
|
31
|
+
r"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\."
|
|
32
|
+
r"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\."
|
|
33
|
+
r"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
|
|
34
|
+
)
|
|
35
|
+
if re.match(domain_regex, value) or re.match(ip_regex, value):
|
|
36
|
+
return value
|
|
37
|
+
raise ValueError(f"Invalid server host: {value}. Must be a valid domain or IP address.")
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def initialize_configuration(device_id: str, server_host: str, token: str):
|
|
41
|
+
"""
|
|
42
|
+
Initialize the application configuration using the provided token
|
|
43
|
+
and saving configuration data locally.
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
# Validate inputs
|
|
47
|
+
AppInitializer.validate_uuid(device_id)
|
|
48
|
+
AppInitializer.validate_server_host(server_host)
|
|
49
|
+
|
|
50
|
+
# Get connection info using the ConnectionInfoClient
|
|
51
|
+
connection_client = ConnectionInfoClient(server_host, 50051, token)
|
|
52
|
+
connection_result = connection_client.get_connection_info()
|
|
53
|
+
|
|
54
|
+
if not connection_result["success"]:
|
|
55
|
+
logging.error(f"Device connection info failed: {connection_result['message']}")
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
worker_id = connection_result.get('id')
|
|
59
|
+
if not worker_id:
|
|
60
|
+
raise ValueError("No worker_id returned from connection info!")
|
|
61
|
+
|
|
62
|
+
ConfigurationManager.set_config_batch({
|
|
63
|
+
"worker_id": worker_id,
|
|
64
|
+
"server_host": server_host,
|
|
65
|
+
"token": token,
|
|
66
|
+
"rabbitmq_host": connection_result['rabbitmq_host'],
|
|
67
|
+
"rabbitmq_port": str(connection_result['rabbitmq_port']),
|
|
68
|
+
"rabbitmq_username": connection_result['rabbitmq_username'],
|
|
69
|
+
"rabbitmq_password": connection_result['rabbitmq_password']
|
|
70
|
+
})
|
|
71
|
+
ConfigurationManager.print_config()
|
|
72
|
+
|
|
73
|
+
except ValueError as ve:
|
|
74
|
+
logging.error(f"Validation error: {ve}")
|
|
75
|
+
except grpc.RpcError as ge:
|
|
76
|
+
logging.error(f"Grpc Error: {ge}")
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logging.error(f"Unexpected error during initialization: {e}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Import all models to ensure they are registered with SQLAlchemy Base registry
|
|
2
|
+
from .ai_model import AIModelEntity
|
|
3
|
+
from .auth import AuthEntity
|
|
4
|
+
from .config import ConfigEntity
|
|
5
|
+
from .dataset_source import DatasetSourceEntity
|
|
6
|
+
from .logs import LogEntity
|
|
7
|
+
from .ppe_detection import PPEDetectionEntity
|
|
8
|
+
from .ppe_detection_label import PPEDetectionLabelEntity
|
|
9
|
+
from .restricted_area_violation import RestrictedAreaViolationEntity
|
|
10
|
+
from .user import UserEntity
|
|
11
|
+
from .worker_source import WorkerSourceEntity
|
|
12
|
+
from .worker_source_pipeline import WorkerSourcePipelineEntity
|
|
13
|
+
from .worker_source_pipeline_config import WorkerSourcePipelineConfigEntity
|
|
14
|
+
from .worker_source_pipeline_debug import WorkerSourcePipelineDebugEntity
|
|
15
|
+
from .worker_source_pipeline_detection import WorkerSourcePipelineDetectionEntity
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from sqlalchemy import Column, String, DateTime
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from ..database.DatabaseManager import Base
|
|
5
|
+
|
|
6
|
+
class AIModelEntity(Base):
|
|
7
|
+
__tablename__ = "ai_model"
|
|
8
|
+
__bind_key__ = "default"
|
|
9
|
+
|
|
10
|
+
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
|
|
11
|
+
file = Column(String, nullable=False)
|
|
12
|
+
type = Column(String, nullable=False)
|
|
13
|
+
name = Column(String, nullable=False)
|
|
14
|
+
version = Column(String, nullable=False)
|
|
15
|
+
download_status = Column(String, nullable=True, default="completed") # pending, downloading, completed, failed
|
|
16
|
+
last_download_attempt = Column(DateTime, nullable=True)
|
|
17
|
+
download_error = Column(String, nullable=True)
|
|
18
|
+
|
|
19
|
+
def __repr__(self):
|
|
20
|
+
return (
|
|
21
|
+
f"<AIModelEntity(id={self.id}, name={self.name}, type={self.type}, "
|
|
22
|
+
f"file={self.file}, version={self.version})>"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def __str__(self):
|
|
26
|
+
return (
|
|
27
|
+
f"AIModelEntity(id={self.id}, name={self.name}, type={self.type}, "
|
|
28
|
+
f"file={self.file}, version={self.version}, status={self.download_status})"
|
|
29
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from sqlalchemy import Column, String, Integer
|
|
2
|
+
from ..database.DatabaseManager import Base
|
|
3
|
+
|
|
4
|
+
class AuthEntity(Base):
|
|
5
|
+
__tablename__ = "auth"
|
|
6
|
+
id = Column(Integer, primary_key=True)
|
|
7
|
+
username = Column(String, nullable=False)
|
|
8
|
+
password = Column(String, nullable=False)
|
|
9
|
+
|
|
10
|
+
def __repr__(self):
|
|
11
|
+
return f"<AuthEntity(id={self.id}, username={self.username})>"
|
|
12
|
+
|
|
13
|
+
def to_dict(self):
|
|
14
|
+
return {"id": self.id, "username": self.username}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from sqlalchemy import Column, String, Integer
|
|
2
|
+
from ..database.DatabaseManager import Base
|
|
3
|
+
|
|
4
|
+
class DatasetSourceEntity(Base):
|
|
5
|
+
__tablename__ = "dataset_sources"
|
|
6
|
+
__bind_key__ = "default"
|
|
7
|
+
|
|
8
|
+
id = Column(String, primary_key=True)
|
|
9
|
+
dataset_id = Column(String, nullable=False)
|
|
10
|
+
worker_source_id = Column(String, nullable=False)
|
|
11
|
+
sampling_interval = Column(Integer, nullable=False)
|
|
12
|
+
dataset_name = Column(String, nullable=False)
|
|
13
|
+
worker_source_name = Column(String, nullable=False)
|
|
14
|
+
worker_source_url = Column(String, nullable=False)
|
|
15
|
+
|
|
16
|
+
def __repr__(self):
|
|
17
|
+
return (
|
|
18
|
+
f"<DatasetSourceEntity(id={self.id}, dataset_id={self.dataset_id}, "
|
|
19
|
+
f"worker_source_id={self.worker_source_id}, sampling_interval={self.sampling_interval}, "
|
|
20
|
+
f"dataset_name={self.dataset_name}, worker_source_name={self.worker_source_name}, "
|
|
21
|
+
f"worker_source_url={self.worker_source_url})>"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def __str__(self):
|
|
25
|
+
return (
|
|
26
|
+
f"DatasetSourceEntity(id={self.id}, dataset_id={self.dataset_id}, "
|
|
27
|
+
f"worker_source_id={self.worker_source_id}, sampling_interval={self.sampling_interval}, "
|
|
28
|
+
f"dataset_name={self.dataset_name}, worker_source_name={self.worker_source_name}, "
|
|
29
|
+
f"worker_source_url={self.worker_source_url})"
|
|
30
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from sqlalchemy import Column, String, Integer
|
|
2
|
+
from ..database.DatabaseManager import Base
|
|
3
|
+
|
|
4
|
+
class LogEntity(Base):
|
|
5
|
+
__tablename__ = "logs"
|
|
6
|
+
__bind_key__ = "logging"
|
|
7
|
+
|
|
8
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
9
|
+
message = Column(String, nullable=False)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
import datetime
|
|
3
|
+
from sqlalchemy import Column, String, ForeignKey, DateTime, Float, Integer
|
|
4
|
+
from sqlalchemy.orm import relationship
|
|
5
|
+
from ..database.DatabaseManager import Base
|
|
6
|
+
|
|
7
|
+
class PPEDetectionEntity(Base):
|
|
8
|
+
__tablename__ = "ppe_detections"
|
|
9
|
+
__bind_key__ = "default"
|
|
10
|
+
|
|
11
|
+
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
|
|
12
|
+
worker_id = Column(String, nullable=False)
|
|
13
|
+
worker_source_id = Column(String, nullable=False)
|
|
14
|
+
person_id = Column(String, nullable=False)
|
|
15
|
+
image_path = Column(String, nullable=False)
|
|
16
|
+
image_tile_path = Column(String, nullable=False)
|
|
17
|
+
detection_count = Column(Integer, nullable=False, default=0) # Tracks total detections before saving
|
|
18
|
+
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
|
19
|
+
b_box_x1 = Column(Float, nullable=False)
|
|
20
|
+
b_box_y1 = Column(Float, nullable=False)
|
|
21
|
+
b_box_x2 = Column(Float, nullable=False)
|
|
22
|
+
b_box_y2 = Column(Float, nullable=False)
|
|
23
|
+
|
|
24
|
+
ppe_labels = relationship("PPEDetectionLabelEntity", back_populates="detection")
|
|
25
|
+
|
|
26
|
+
def __repr__(self):
|
|
27
|
+
return (
|
|
28
|
+
f"<PPEDetectionEntity(id={self.id}, worker_id={self.worker_id}, "
|
|
29
|
+
f"worker_source_id={self.worker_source_id}, person_id={self.person_id}, "
|
|
30
|
+
f"image_path={self.image_path}, detection_count={self.detection_count}, "
|
|
31
|
+
f"created_at={self.created_at})>"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def __str__(self):
|
|
35
|
+
return (
|
|
36
|
+
f"PPEDetectionEntity(id={self.id}, worker_id={self.worker_id}, "
|
|
37
|
+
f"worker_source_id={self.worker_source_id}, person_id={self.person_id}, "
|
|
38
|
+
f"detection_count={self.detection_count}, created_at={self.created_at})"
|
|
39
|
+
)
|