nedo-vision-worker 1.3.7__py3-none-any.whl → 1.3.9__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 +1 -1
- nedo_vision_worker/bootstrap.py +51 -0
- nedo_vision_worker/cli.py +44 -193
- nedo_vision_worker/config/ConfigurationManager.py +101 -106
- nedo_vision_worker/config/ConfigurationManagerInterface.py +12 -0
- nedo_vision_worker/config/DummyConfigurationManager.py +52 -0
- nedo_vision_worker/services/ConnectionInfoClient.py +1 -1
- nedo_vision_worker/services/GrpcClientBase.py +1 -0
- nedo_vision_worker/util/CorruptedImageValidator.py +55 -0
- nedo_vision_worker/worker/DatasetFrameWorker.py +4 -0
- nedo_vision_worker/worker/RabbitMQListener.py +0 -4
- nedo_vision_worker/worker_service.py +13 -96
- {nedo_vision_worker-1.3.7.dist-info → nedo_vision_worker-1.3.9.dist-info}/METADATA +2 -1
- {nedo_vision_worker-1.3.7.dist-info → nedo_vision_worker-1.3.9.dist-info}/RECORD +17 -15
- nedo_vision_worker/initializer/AppInitializer.py +0 -138
- nedo_vision_worker/initializer/__init__.py +0 -1
- {nedo_vision_worker-1.3.7.dist-info → nedo_vision_worker-1.3.9.dist-info}/WHEEL +0 -0
- {nedo_vision_worker-1.3.7.dist-info → nedo_vision_worker-1.3.9.dist-info}/entry_points.txt +0 -0
- {nedo_vision_worker-1.3.7.dist-info → nedo_vision_worker-1.3.9.dist-info}/top_level.txt +0 -0
nedo_vision_worker/__init__.py
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from nedo_vision_worker.config.ConfigurationManager import ConfigurationManager
|
|
3
|
+
from nedo_vision_worker.database.DatabaseManager import set_storage_path, DatabaseManager
|
|
4
|
+
from nedo_vision_worker.worker_service import WorkerService
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def start_worker(
|
|
8
|
+
*,
|
|
9
|
+
server_host: str,
|
|
10
|
+
server_port: int,
|
|
11
|
+
token: str,
|
|
12
|
+
system_usage_interval: int,
|
|
13
|
+
rtmp_server: str,
|
|
14
|
+
storage_path: str,
|
|
15
|
+
log_level: str = "INFO",
|
|
16
|
+
) -> WorkerService:
|
|
17
|
+
# Logging (only once, force allowed)
|
|
18
|
+
logging.basicConfig(
|
|
19
|
+
level=getattr(logging, log_level),
|
|
20
|
+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
21
|
+
force=True,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger("nedo-worker")
|
|
25
|
+
|
|
26
|
+
# Storage & DB
|
|
27
|
+
set_storage_path(storage_path)
|
|
28
|
+
DatabaseManager.init_databases()
|
|
29
|
+
|
|
30
|
+
# Configuration
|
|
31
|
+
configuration_manager = ConfigurationManager(
|
|
32
|
+
worker_token=token,
|
|
33
|
+
server_host=server_host,
|
|
34
|
+
server_port=server_port,
|
|
35
|
+
rtmp_server_url=rtmp_server,
|
|
36
|
+
logger=logging.getLogger("configuration_manager"),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
service = WorkerService(
|
|
40
|
+
configuration_manager=configuration_manager,
|
|
41
|
+
server_host=server_host,
|
|
42
|
+
token=token,
|
|
43
|
+
system_usage_interval=system_usage_interval,
|
|
44
|
+
rtmp_server=rtmp_server,
|
|
45
|
+
storage_path=storage_path,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
logger.info("🚀 Starting Nedo Vision Worker Service")
|
|
49
|
+
service.run()
|
|
50
|
+
|
|
51
|
+
return service
|
nedo_vision_worker/cli.py
CHANGED
|
@@ -1,224 +1,75 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import signal
|
|
3
3
|
import sys
|
|
4
|
-
import traceback
|
|
5
4
|
import logging
|
|
6
5
|
from typing import NoReturn
|
|
7
6
|
|
|
8
|
-
from .
|
|
9
|
-
|
|
7
|
+
from nedo_vision_worker.bootstrap import start_worker
|
|
8
|
+
from nedo_vision_worker import __version__
|
|
10
9
|
|
|
11
10
|
class NedoWorkerCLI:
|
|
12
|
-
"""Main CLI application for Nedo Vision Worker Service."""
|
|
13
|
-
|
|
14
11
|
def __init__(self):
|
|
15
12
|
self.logger = logging.getLogger(__name__)
|
|
16
13
|
self._setup_signal_handlers()
|
|
17
|
-
|
|
14
|
+
self._service = None
|
|
15
|
+
|
|
18
16
|
def _setup_signal_handlers(self) -> None:
|
|
19
|
-
"""Set up signal handlers for graceful shutdown."""
|
|
20
17
|
signal.signal(signal.SIGINT, self._signal_handler)
|
|
21
18
|
signal.signal(signal.SIGTERM, self._signal_handler)
|
|
22
|
-
|
|
19
|
+
|
|
23
20
|
def _signal_handler(self, signum: int, frame) -> NoReturn:
|
|
24
|
-
"
|
|
25
|
-
self.
|
|
21
|
+
self.logger.info("🛑 Shutdown signal received")
|
|
22
|
+
if self._service and hasattr(self._service, "stop"):
|
|
23
|
+
self._service.stop()
|
|
26
24
|
sys.exit(0)
|
|
27
|
-
|
|
25
|
+
|
|
28
26
|
def create_parser(self) -> argparse.ArgumentParser:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
parser.add_argument(
|
|
50
|
-
"--version",
|
|
51
|
-
action="version",
|
|
52
|
-
version="nedo-vision-worker 1.2.1"
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
subparsers = parser.add_subparsers(
|
|
56
|
-
dest='command',
|
|
57
|
-
help='Available commands',
|
|
58
|
-
required=True
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
self._add_doctor_command(subparsers)
|
|
62
|
-
self._add_run_command(subparsers)
|
|
63
|
-
|
|
27
|
+
parser = argparse.ArgumentParser(description="Nedo Vision Worker Service")
|
|
28
|
+
|
|
29
|
+
parser.add_argument("--version", action="version", version=f"nedo-vision-worker {__version__}")
|
|
30
|
+
|
|
31
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
32
|
+
|
|
33
|
+
subparsers.add_parser("doctor", help="Check system dependencies")
|
|
34
|
+
|
|
35
|
+
run = subparsers.add_parser("run", help="Start worker")
|
|
36
|
+
|
|
37
|
+
run.add_argument("--token", required=True)
|
|
38
|
+
run.add_argument("--server-host", default="be.vision.sindika.co.id")
|
|
39
|
+
run.add_argument("--server-port", type=int, default=50051)
|
|
40
|
+
run.add_argument("--rtmp-server", default="rtmp://live.vision.sindika.co.id:1935/live")
|
|
41
|
+
run.add_argument("--storage-path", default="data")
|
|
42
|
+
run.add_argument("--system-usage-interval", type=int, default=30)
|
|
43
|
+
run.add_argument("--log-level", default="INFO")
|
|
44
|
+
|
|
64
45
|
return parser
|
|
65
|
-
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
def _add_run_command(self, subparsers) -> None:
|
|
75
|
-
"""Add the run command with its arguments."""
|
|
76
|
-
run_parser = subparsers.add_parser(
|
|
77
|
-
'run',
|
|
78
|
-
help='Start the worker service',
|
|
79
|
-
description='Start the Nedo Vision Worker Service'
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
run_parser.add_argument(
|
|
83
|
-
"--token",
|
|
84
|
-
required=True,
|
|
85
|
-
help="Authentication token for the worker (obtained from frontend)"
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
run_parser.add_argument(
|
|
89
|
-
"--server-host",
|
|
90
|
-
default="be.vision.sindika.co.id",
|
|
91
|
-
help="Server hostname for communication (default: %(default)s)"
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
run_parser.add_argument(
|
|
95
|
-
"--server-port",
|
|
96
|
-
type=int,
|
|
97
|
-
default=50051,
|
|
98
|
-
help="Server port for gRPC communication (default: %(default)s)"
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
run_parser.add_argument(
|
|
102
|
-
"--rtmp-server",
|
|
103
|
-
default="rtmp://live.vision.sindika.co.id:1935/live",
|
|
104
|
-
help="RTMP server URL for video streaming (default: %(default)s)"
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
run_parser.add_argument(
|
|
108
|
-
"--storage-path",
|
|
109
|
-
default="data",
|
|
110
|
-
help="Storage path for databases and files (default: %(default)s)"
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
run_parser.add_argument(
|
|
114
|
-
"--system-usage-interval",
|
|
115
|
-
type=int,
|
|
116
|
-
default=30,
|
|
117
|
-
metavar="SECONDS",
|
|
118
|
-
help="System usage reporting interval in seconds (default: %(default)s)"
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
run_parser.add_argument(
|
|
122
|
-
"--log-level",
|
|
123
|
-
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
124
|
-
default="INFO",
|
|
125
|
-
help="Set the logging level (default: %(default)s)"
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
def run_doctor_command(self) -> int:
|
|
129
|
-
"""Execute the doctor command."""
|
|
130
|
-
try:
|
|
131
|
-
from .doctor import main as doctor_main
|
|
46
|
+
|
|
47
|
+
def run(self) -> int:
|
|
48
|
+
parser = self.create_parser()
|
|
49
|
+
args = parser.parse_args()
|
|
50
|
+
|
|
51
|
+
if args.command == "doctor":
|
|
52
|
+
from nedo_vision_worker.doctor import main as doctor_main
|
|
132
53
|
return doctor_main()
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
except Exception as e:
|
|
137
|
-
self.logger.error(f"Doctor command failed: {e}")
|
|
138
|
-
return 1
|
|
139
|
-
|
|
140
|
-
def run_worker_service(self, args: argparse.Namespace) -> int:
|
|
141
|
-
"""Start and run the worker service."""
|
|
142
|
-
# Configure logging
|
|
143
|
-
logging.basicConfig(
|
|
144
|
-
level=getattr(logging, args.log_level),
|
|
145
|
-
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
try:
|
|
149
|
-
# Create the worker service
|
|
150
|
-
service = WorkerService(
|
|
54
|
+
|
|
55
|
+
if args.command == "run":
|
|
56
|
+
self._service = start_worker(
|
|
151
57
|
server_host=args.server_host,
|
|
58
|
+
server_port=args.server_port,
|
|
152
59
|
token=args.token,
|
|
153
60
|
system_usage_interval=args.system_usage_interval,
|
|
154
61
|
rtmp_server=args.rtmp_server,
|
|
155
62
|
storage_path=args.storage_path,
|
|
156
|
-
|
|
63
|
+
log_level=args.log_level,
|
|
157
64
|
)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
# Start the service
|
|
163
|
-
service.run()
|
|
164
|
-
|
|
165
|
-
# Keep the service running
|
|
166
|
-
self._wait_for_service(service)
|
|
167
|
-
|
|
168
|
-
return 0
|
|
169
|
-
|
|
170
|
-
except KeyboardInterrupt:
|
|
171
|
-
self.logger.info("Shutdown requested by user")
|
|
172
|
-
return 0
|
|
173
|
-
except Exception as e:
|
|
174
|
-
self.logger.error(f"Service failed: {e}")
|
|
175
|
-
if args.log_level == "DEBUG":
|
|
176
|
-
traceback.print_exc()
|
|
177
|
-
return 1
|
|
178
|
-
|
|
179
|
-
def _log_startup_info(self, args: argparse.Namespace) -> None:
|
|
180
|
-
"""Log service startup information."""
|
|
181
|
-
self.logger.info("🚀 Starting Nedo Vision Worker Service...")
|
|
182
|
-
self.logger.info(f"🌐 Server: {args.server_host}")
|
|
183
|
-
self.logger.info(f"🔑 Token: {args.token[:8]}{'*' * (len(args.token) - 8)}")
|
|
184
|
-
self.logger.info(f"⏱️ System Usage Interval: {args.system_usage_interval}s")
|
|
185
|
-
self.logger.info(f"📡 RTMP Server: {args.rtmp_server}")
|
|
186
|
-
self.logger.info(f"💾 Storage Path: {args.storage_path}")
|
|
187
|
-
self.logger.info("Press Ctrl+C to stop the service")
|
|
188
|
-
|
|
189
|
-
def _wait_for_service(self, service: WorkerService) -> None:
|
|
190
|
-
"""Wait for the service to run and handle shutdown."""
|
|
191
|
-
import time
|
|
192
|
-
|
|
193
|
-
try:
|
|
194
|
-
while getattr(service, 'running', False):
|
|
195
|
-
time.sleep(1)
|
|
196
|
-
except KeyboardInterrupt:
|
|
197
|
-
self.logger.info("🛑 Shutdown requested...")
|
|
198
|
-
finally:
|
|
199
|
-
if hasattr(service, 'stop'):
|
|
200
|
-
service.stop()
|
|
201
|
-
self.logger.info("✅ Service stopped successfully")
|
|
202
|
-
|
|
203
|
-
def run(self) -> int:
|
|
204
|
-
"""Main entry point for the CLI application."""
|
|
205
|
-
parser = self.create_parser()
|
|
206
|
-
args = parser.parse_args()
|
|
207
|
-
|
|
208
|
-
if args.command == 'doctor':
|
|
209
|
-
return self.run_doctor_command()
|
|
210
|
-
elif args.command == 'run':
|
|
211
|
-
return self.run_worker_service(args)
|
|
212
|
-
else:
|
|
213
|
-
parser.print_help()
|
|
214
|
-
return 1
|
|
65
|
+
signal.pause() # wait for signal
|
|
66
|
+
|
|
67
|
+
return 0
|
|
215
68
|
|
|
216
69
|
|
|
217
70
|
def main() -> int:
|
|
218
|
-
|
|
219
|
-
cli = NedoWorkerCLI()
|
|
220
|
-
return cli.run()
|
|
71
|
+
return NedoWorkerCLI().run()
|
|
221
72
|
|
|
222
73
|
|
|
223
74
|
if __name__ == "__main__":
|
|
224
|
-
sys.exit(main())
|
|
75
|
+
sys.exit(main())
|
|
@@ -1,84 +1,138 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import grpc
|
|
3
|
+
from typing import Optional
|
|
2
4
|
from ..models.config import ConfigEntity # ORM model for server_config
|
|
3
5
|
from ..database.DatabaseManager import DatabaseManager # DatabaseManager for managing sessions
|
|
4
6
|
|
|
7
|
+
from ..util.HardwareID import HardwareID
|
|
8
|
+
from ..services.ConnectionInfoClient import ConnectionInfoClient
|
|
9
|
+
from .ConfigurationManagerInterface import ConfigurationManagerInterface
|
|
5
10
|
|
|
6
|
-
class ConfigurationManager:
|
|
11
|
+
class ConfigurationManager(ConfigurationManagerInterface):
|
|
7
12
|
"""
|
|
8
|
-
A class to manage
|
|
13
|
+
A class to manage local and remote configuration stored in the 'config' database.
|
|
9
14
|
"""
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
def init_database():
|
|
13
|
-
"""
|
|
14
|
-
Initialize the 'config' database and create the `server_config` table if it doesn't exist.
|
|
15
|
-
"""
|
|
16
|
+
def __init__(self, worker_token: str, server_host: str, server_port: int, rtmp_server_url: str, logger: logging.Logger):
|
|
16
17
|
try:
|
|
17
|
-
|
|
18
|
+
self._logger = logger
|
|
19
|
+
self._worker_token = worker_token
|
|
20
|
+
self._server_host = server_host
|
|
21
|
+
self._server_port = server_port
|
|
22
|
+
self._rtmp_server_url = rtmp_server_url
|
|
23
|
+
|
|
24
|
+
self._initialize_remote_configuration()
|
|
25
|
+
|
|
18
26
|
logging.info("✅ [APP] Configuration database initialized successfully.")
|
|
19
27
|
except Exception as e:
|
|
20
28
|
logging.exception("❌ [APP] Failed to initialize the configuration database.")
|
|
21
29
|
raise RuntimeError("Database initialization failed.") from e
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def is_config_initialized() -> bool:
|
|
30
|
+
|
|
31
|
+
def get_config(self, key: str) -> str:
|
|
25
32
|
"""
|
|
26
|
-
|
|
33
|
+
Retrieve the value of a specific configuration key from the 'config' database.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
key (str): The configuration key.
|
|
27
37
|
|
|
28
38
|
Returns:
|
|
29
|
-
|
|
39
|
+
str: The configuration value, or None if the key does not exist.
|
|
30
40
|
"""
|
|
41
|
+
if not key or not isinstance(key, str):
|
|
42
|
+
raise ValueError("⚠️ The 'key' must be a non-empty string.")
|
|
43
|
+
|
|
31
44
|
session = None
|
|
32
45
|
try:
|
|
33
46
|
session = DatabaseManager.get_session("config")
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
logging.info(f"🔍 [APP] Retrieving configuration key: {key}")
|
|
48
|
+
config = session.query(ConfigEntity).filter_by(key=key).first()
|
|
49
|
+
if config:
|
|
50
|
+
logging.info(f"✅ [APP] Configuration key '{key}' retrieved successfully.")
|
|
51
|
+
return config.value
|
|
52
|
+
else:
|
|
53
|
+
logging.warning(f"⚠️ [APP] Configuration key '{key}' not found.")
|
|
54
|
+
return ""
|
|
36
55
|
except Exception as e:
|
|
37
|
-
logging.exception("❌ [APP] Failed to
|
|
38
|
-
|
|
56
|
+
logging.exception(f"❌ [APP] Failed to retrieve configuration key '{key}': {e}")
|
|
57
|
+
raise RuntimeError(f"Failed to retrieve configuration key '{key}'") from e
|
|
39
58
|
finally:
|
|
40
59
|
if session:
|
|
41
60
|
session.close()
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def set_config(key: str, value: str):
|
|
61
|
+
|
|
62
|
+
def get_all_configs(self) -> Optional[dict]:
|
|
45
63
|
"""
|
|
46
|
-
|
|
64
|
+
Retrieve all configuration key-value pairs from the 'config' database.
|
|
47
65
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
value (str): The configuration value.
|
|
66
|
+
Returns:
|
|
67
|
+
dict: A dictionary of all configuration key-value pairs.
|
|
51
68
|
"""
|
|
52
|
-
if not key or not isinstance(key, str):
|
|
53
|
-
raise ValueError("⚠️ [APP] The 'key' must be a non-empty string.")
|
|
54
|
-
if not isinstance(value, str):
|
|
55
|
-
raise ValueError("⚠️ [APP] The 'value' must be a string.")
|
|
56
|
-
|
|
57
69
|
session = None
|
|
58
70
|
try:
|
|
59
71
|
session = DatabaseManager.get_session("config")
|
|
60
|
-
logging.info(
|
|
61
|
-
|
|
62
|
-
if
|
|
63
|
-
logging.info(
|
|
64
|
-
|
|
72
|
+
logging.info("🔍 [APP] Retrieving all configuration keys.")
|
|
73
|
+
configs = session.query(ConfigEntity).all()
|
|
74
|
+
if configs:
|
|
75
|
+
logging.info("✅ [APP] All configuration keys retrieved successfully.")
|
|
76
|
+
return {config.key: config.value for config in configs}
|
|
65
77
|
else:
|
|
66
|
-
logging.info(
|
|
67
|
-
|
|
68
|
-
session.add(new_config)
|
|
69
|
-
session.commit()
|
|
70
|
-
logging.info(f"✅ [APP] Configuration key '{key}' set successfully.")
|
|
78
|
+
logging.info("⚠️ [APP] No configuration keys found.")
|
|
79
|
+
return None
|
|
71
80
|
except Exception as e:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
logging.exception(f"❌ [APP] Failed to set configuration key '{key}': {e}")
|
|
75
|
-
raise RuntimeError(f"Failed to set configuration key '{key}'") from e
|
|
81
|
+
logging.exception("❌ [APP] Failed to retrieve all configuration keys.")
|
|
82
|
+
raise RuntimeError("Failed to retrieve all configuration keys.") from e
|
|
76
83
|
finally:
|
|
77
84
|
if session:
|
|
78
85
|
session.close()
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
def _initialize_remote_configuration(self):
|
|
88
|
+
"""
|
|
89
|
+
Initialize the application configuration using the provided token
|
|
90
|
+
and saving configuration data locally.
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
# Get hardware ID
|
|
94
|
+
hardware_id = HardwareID.get_unique_id()
|
|
95
|
+
|
|
96
|
+
self._logger.info(f"🖥️ [APP] Detected Hardware ID: {hardware_id}")
|
|
97
|
+
self._logger.info(f"🌐 [APP] Using Server Host: {self._server_host}")
|
|
98
|
+
|
|
99
|
+
# Check if token is provided
|
|
100
|
+
if not self._worker_token:
|
|
101
|
+
raise ValueError("Token is required for worker initialization. Please provide a token obtained from the frontend.")
|
|
102
|
+
|
|
103
|
+
# Get connection info using the ConnectionInfoClient
|
|
104
|
+
connection_client = ConnectionInfoClient(self._server_host, self._server_port, self._worker_token)
|
|
105
|
+
connection_result = connection_client.get_connection_info()
|
|
106
|
+
|
|
107
|
+
if not connection_result["success"]:
|
|
108
|
+
logging.error(f"Device connection info failed: {connection_result['message']}")
|
|
109
|
+
raise ValueError(f"Initializing remote config failed, reason: {connection_result['message']}")
|
|
110
|
+
|
|
111
|
+
worker_id = connection_result.get('id')
|
|
112
|
+
if not worker_id:
|
|
113
|
+
raise ValueError("No worker_id returned from connection info!")
|
|
114
|
+
|
|
115
|
+
self._set_config_batch({
|
|
116
|
+
"worker_id": worker_id,
|
|
117
|
+
"server_host": self._server_host,
|
|
118
|
+
"rtmp_server": self._rtmp_server_url,
|
|
119
|
+
"server_port": str(self._server_port),
|
|
120
|
+
"token": self._worker_token,
|
|
121
|
+
"rabbitmq_host": connection_result['rabbitmq_host'],
|
|
122
|
+
"rabbitmq_port": str(connection_result['rabbitmq_port']),
|
|
123
|
+
"rabbitmq_username": connection_result['rabbitmq_username'],
|
|
124
|
+
"rabbitmq_password": connection_result['rabbitmq_password']
|
|
125
|
+
})
|
|
126
|
+
self._print_config()
|
|
127
|
+
|
|
128
|
+
except ValueError as ve:
|
|
129
|
+
logging.error(f"Validation error: {ve}")
|
|
130
|
+
except grpc.RpcError as ge:
|
|
131
|
+
logging.error(f"Grpc Error: {ge}")
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logging.error(f"Unexpected error during initialization: {e}")
|
|
134
|
+
|
|
135
|
+
def _set_config_batch(self, configs: dict):
|
|
82
136
|
"""
|
|
83
137
|
Set or update multiple configuration key-value pairs in the 'config' database in a batch operation.
|
|
84
138
|
|
|
@@ -116,71 +170,12 @@ class ConfigurationManager:
|
|
|
116
170
|
if session:
|
|
117
171
|
session.close()
|
|
118
172
|
|
|
119
|
-
|
|
120
|
-
def get_config(key: str) -> str:
|
|
121
|
-
"""
|
|
122
|
-
Retrieve the value of a specific configuration key from the 'config' database.
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
key (str): The configuration key.
|
|
126
|
-
|
|
127
|
-
Returns:
|
|
128
|
-
str: The configuration value, or None if the key does not exist.
|
|
129
|
-
"""
|
|
130
|
-
if not key or not isinstance(key, str):
|
|
131
|
-
raise ValueError("⚠️ The 'key' must be a non-empty string.")
|
|
132
|
-
|
|
133
|
-
session = None
|
|
134
|
-
try:
|
|
135
|
-
session = DatabaseManager.get_session("config")
|
|
136
|
-
logging.info(f"🔍 [APP] Retrieving configuration key: {key}")
|
|
137
|
-
config = session.query(ConfigEntity).filter_by(key=key).first()
|
|
138
|
-
if config:
|
|
139
|
-
logging.info(f"✅ [APP] Configuration key '{key}' retrieved successfully.")
|
|
140
|
-
return config.value
|
|
141
|
-
else:
|
|
142
|
-
logging.warning(f"⚠️ [APP] Configuration key '{key}' not found.")
|
|
143
|
-
return None
|
|
144
|
-
except Exception as e:
|
|
145
|
-
logging.exception(f"❌ [APP] Failed to retrieve configuration key '{key}': {e}")
|
|
146
|
-
raise RuntimeError(f"Failed to retrieve configuration key '{key}'") from e
|
|
147
|
-
finally:
|
|
148
|
-
if session:
|
|
149
|
-
session.close()
|
|
150
|
-
|
|
151
|
-
@staticmethod
|
|
152
|
-
def get_all_configs() -> dict:
|
|
153
|
-
"""
|
|
154
|
-
Retrieve all configuration key-value pairs from the 'config' database.
|
|
155
|
-
|
|
156
|
-
Returns:
|
|
157
|
-
dict: A dictionary of all configuration key-value pairs.
|
|
158
|
-
"""
|
|
159
|
-
session = None
|
|
160
|
-
try:
|
|
161
|
-
session = DatabaseManager.get_session("config")
|
|
162
|
-
logging.info("🔍 [APP] Retrieving all configuration keys.")
|
|
163
|
-
configs = session.query(ConfigEntity).all()
|
|
164
|
-
if configs:
|
|
165
|
-
logging.info("✅ [APP] All configuration keys retrieved successfully.")
|
|
166
|
-
return {config.key: config.value for config in configs}
|
|
167
|
-
else:
|
|
168
|
-
logging.info("⚠️ [APP] No configuration keys found.")
|
|
169
|
-
return {}
|
|
170
|
-
except Exception as e:
|
|
171
|
-
logging.exception("❌ [APP] Failed to retrieve all configuration keys.")
|
|
172
|
-
raise RuntimeError("Failed to retrieve all configuration keys.") from e
|
|
173
|
-
finally:
|
|
174
|
-
if session:
|
|
175
|
-
session.close()
|
|
176
|
-
|
|
177
|
-
@staticmethod
|
|
178
|
-
def print_config():
|
|
173
|
+
def _print_config(self):
|
|
179
174
|
"""
|
|
180
175
|
Print all configuration key-value pairs to the console.
|
|
181
176
|
"""
|
|
182
177
|
try:
|
|
183
|
-
configs =
|
|
178
|
+
configs = self.get_all_configs()
|
|
184
179
|
if configs:
|
|
185
180
|
print("📄 Current Configuration:")
|
|
186
181
|
for key, value in configs.items():
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from .ConfigurationManagerInterface import ConfigurationManagerInterface
|
|
5
|
+
|
|
6
|
+
class DummyConfigurationManager(ConfigurationManagerInterface):
|
|
7
|
+
"""
|
|
8
|
+
A class to manage local and remote configuration stored in the 'config' database.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
worker_id: str,
|
|
14
|
+
worker_token: str,
|
|
15
|
+
server_host: str,
|
|
16
|
+
server_port: str,
|
|
17
|
+
rtmp_server_url: str,
|
|
18
|
+
rabbitmq_host: str,
|
|
19
|
+
rabbitmq_port: str,
|
|
20
|
+
rabbitmq_username: str,
|
|
21
|
+
rabbitmq_password: str,
|
|
22
|
+
logger: logging.Logger
|
|
23
|
+
):
|
|
24
|
+
self._logger = logger
|
|
25
|
+
self._worker_id = worker_id
|
|
26
|
+
self._worker_token = worker_token
|
|
27
|
+
self._server_host = server_host
|
|
28
|
+
self._server_port = server_port
|
|
29
|
+
self._rtmp_server_url = rtmp_server_url
|
|
30
|
+
self._rabbitmq_host = rabbitmq_host
|
|
31
|
+
self._rabbitmq_port = rabbitmq_port
|
|
32
|
+
self._rabbitmq_username = rabbitmq_username
|
|
33
|
+
self._rabbitmq_password = rabbitmq_password
|
|
34
|
+
|
|
35
|
+
self._config = {
|
|
36
|
+
"worker_id": self._worker_id,
|
|
37
|
+
"token": self._worker_token,
|
|
38
|
+
"server_host": self._server_host,
|
|
39
|
+
"server_port": str(self._server_port),
|
|
40
|
+
"rtmp_server": self._rtmp_server_url,
|
|
41
|
+
"rabbitmq_host": self._rabbitmq_host,
|
|
42
|
+
"rabbitmq_port": self._rabbitmq_port,
|
|
43
|
+
"rabbitmq_username": self._rabbitmq_username,
|
|
44
|
+
"rabbitmq_password": self._rabbitmq_password,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_config(self, key: str) -> str:
|
|
49
|
+
return self._config[key]
|
|
50
|
+
|
|
51
|
+
def get_all_configs(self) -> Optional[dict]:
|
|
52
|
+
return self._config
|
|
@@ -41,6 +41,7 @@ class GrpcClientBase:
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
def __init__(self, server_host: str, server_port: int = 50051, max_retries: int = 3):
|
|
44
|
+
self.stub = None
|
|
44
45
|
self.server_address = f"{server_host}:{server_port}"
|
|
45
46
|
self.channel: Optional[grpc.Channel] = None
|
|
46
47
|
self.connected = False
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from PIL import Image
|
|
2
|
+
import io
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
# Tunables (adjust once, then forget)
|
|
6
|
+
MIN_IMAGE_KB = 10
|
|
7
|
+
GRAY_RATIO_THRESHOLD = 0.55
|
|
8
|
+
GRAY_MIN = 90
|
|
9
|
+
GRAY_MAX = 210
|
|
10
|
+
GRAY_DELTA = 4
|
|
11
|
+
|
|
12
|
+
def validate_image_gray_area(image_bytes: bytes) -> bool:
|
|
13
|
+
print(len(image_bytes))
|
|
14
|
+
# ---- Stage 1: size check (O(1)) ----
|
|
15
|
+
if len(image_bytes) < (MIN_IMAGE_KB * 1024):
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
# ---- Stage 2: structural integrity (cheap) ----
|
|
19
|
+
try:
|
|
20
|
+
with Image.open(io.BytesIO(image_bytes)) as img:
|
|
21
|
+
img.verify()
|
|
22
|
+
except Exception:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
# ---- Stage 3: partial pixel decode (cropped, fast) ----
|
|
26
|
+
try:
|
|
27
|
+
with Image.open(io.BytesIO(image_bytes)) as img:
|
|
28
|
+
img = img.convert("RGB")
|
|
29
|
+
|
|
30
|
+
w, h = img.size
|
|
31
|
+
if w == 0 or h == 0:
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
# Sample bottom 35% where corruption usually appears
|
|
35
|
+
crop_y = int(h * 0.65)
|
|
36
|
+
img_crop = img.crop((0, crop_y, w, h))
|
|
37
|
+
|
|
38
|
+
# Downscale aggressively (huge speedup)
|
|
39
|
+
img_small = img_crop.resize((w // 8, h // 8))
|
|
40
|
+
|
|
41
|
+
arr = np.asarray(img_small, dtype=np.uint8)
|
|
42
|
+
|
|
43
|
+
r, g, b = arr[..., 0], arr[..., 1], arr[..., 2]
|
|
44
|
+
|
|
45
|
+
gray_mask = (
|
|
46
|
+
(np.abs(r - g) < GRAY_DELTA) &
|
|
47
|
+
(np.abs(r - b) < GRAY_DELTA) &
|
|
48
|
+
(r > GRAY_MIN) & (r < GRAY_MAX)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
gray_ratio = gray_mask.mean()
|
|
52
|
+
return gray_ratio < GRAY_RATIO_THRESHOLD
|
|
53
|
+
|
|
54
|
+
except Exception:
|
|
55
|
+
return False
|
|
@@ -7,6 +7,7 @@ from typing import Dict
|
|
|
7
7
|
from ..services.WorkerSourcePipelineClient import WorkerSourcePipelineClient
|
|
8
8
|
from ..services.GrpcClientManager import GrpcClientManager
|
|
9
9
|
from ..repositories.DatasetSourceRepository import DatasetSourceRepository
|
|
10
|
+
from ..util.CorruptedImageValidator import validate_image_gray_area
|
|
10
11
|
|
|
11
12
|
logger = logging.getLogger(__name__)
|
|
12
13
|
|
|
@@ -96,6 +97,9 @@ class DatasetSourceThread:
|
|
|
96
97
|
try:
|
|
97
98
|
# Get frame from source
|
|
98
99
|
frame_bytes = self._get_frame_from_source(self.dataset_source.worker_source_url)
|
|
100
|
+
if not validate_image_gray_area(frame_bytes):
|
|
101
|
+
logger.warning(f"⚠️ [APP] Detected gray area corruption in image while processing {self.dataset_source.dataset_name} (ID: {self.dataset_source.id})")
|
|
102
|
+
return True
|
|
99
103
|
|
|
100
104
|
if frame_bytes:
|
|
101
105
|
# Generate unique filename
|
|
@@ -88,10 +88,6 @@ class RabbitMQListener:
|
|
|
88
88
|
self.exchange_name = exchange_name
|
|
89
89
|
self.queue_name = queue_name
|
|
90
90
|
|
|
91
|
-
if self.listener_thread and self.listener_thread.is_alive():
|
|
92
|
-
logger.warning("⚠️ [APP] RabbitMQ listener is already running.")
|
|
93
|
-
return
|
|
94
|
-
|
|
95
91
|
# Clean up any dead thread reference
|
|
96
92
|
if self.listener_thread and not self.listener_thread.is_alive():
|
|
97
93
|
self.listener_thread = None
|
|
@@ -9,14 +9,9 @@ try:
|
|
|
9
9
|
except RuntimeError:
|
|
10
10
|
pass
|
|
11
11
|
|
|
12
|
-
from .initializer.AppInitializer import AppInitializer
|
|
13
12
|
from .worker.WorkerManager import WorkerManager
|
|
14
|
-
from .config.
|
|
15
|
-
from .util.HardwareID import HardwareID
|
|
13
|
+
from .config.ConfigurationManagerInterface import ConfigurationManagerInterface
|
|
16
14
|
from .services.GrpcClientBase import set_auth_failure_callback
|
|
17
|
-
from .database.DatabaseManager import set_storage_path
|
|
18
|
-
from . import models
|
|
19
|
-
|
|
20
15
|
|
|
21
16
|
class WorkerService:
|
|
22
17
|
"""
|
|
@@ -26,8 +21,9 @@ class WorkerService:
|
|
|
26
21
|
|
|
27
22
|
def __init__(
|
|
28
23
|
self,
|
|
24
|
+
configuration_manager: ConfigurationManagerInterface,
|
|
29
25
|
server_host: str = "be.vision.sindika.co.id",
|
|
30
|
-
token: str =
|
|
26
|
+
token: str = "",
|
|
31
27
|
system_usage_interval: int = 30,
|
|
32
28
|
rtmp_server: str = "rtmp://live.vision.sindika.co.id:1935/live",
|
|
33
29
|
storage_path: str = "data",
|
|
@@ -44,9 +40,8 @@ class WorkerService:
|
|
|
44
40
|
storage_path: Storage path for databases and files (default: 'data')
|
|
45
41
|
server_port: gRPC server port (default: 50051)
|
|
46
42
|
"""
|
|
47
|
-
# Set the global storage path before any database operations
|
|
48
|
-
set_storage_path(storage_path)
|
|
49
43
|
|
|
44
|
+
self._configuration_manager = configuration_manager
|
|
50
45
|
self.logger = self._setup_logging()
|
|
51
46
|
self.worker_manager = None
|
|
52
47
|
self.running = False
|
|
@@ -89,84 +84,6 @@ class WorkerService:
|
|
|
89
84
|
|
|
90
85
|
return logging.getLogger(__name__)
|
|
91
86
|
|
|
92
|
-
def _initialize_configuration(self):
|
|
93
|
-
"""Initialize the application configuration."""
|
|
94
|
-
self.logger.info("🚀 [APP] Initializing application...")
|
|
95
|
-
|
|
96
|
-
# Initialize database
|
|
97
|
-
ConfigurationManager.init_database()
|
|
98
|
-
|
|
99
|
-
# Load all configurations at once
|
|
100
|
-
config = ConfigurationManager.get_all_configs()
|
|
101
|
-
|
|
102
|
-
# Use the server_host parameter directly
|
|
103
|
-
server_host = self.server_host
|
|
104
|
-
self.logger.info(f"🌐 [APP] Using server host: {server_host}")
|
|
105
|
-
|
|
106
|
-
# Check if configuration exists
|
|
107
|
-
if not config:
|
|
108
|
-
self.logger.info("⚙️ [APP] Configuration not found. Performing first-time setup...")
|
|
109
|
-
|
|
110
|
-
# Get hardware ID
|
|
111
|
-
hardware_id = HardwareID.get_unique_id()
|
|
112
|
-
|
|
113
|
-
self.logger.info(f"🖥️ [APP] Detected Hardware ID: {hardware_id}")
|
|
114
|
-
self.logger.info(f"🌐 [APP] Using Server Host: {server_host}")
|
|
115
|
-
|
|
116
|
-
# Check if token is provided
|
|
117
|
-
if not self.token:
|
|
118
|
-
raise ValueError("Token is required for worker initialization. Please provide a token obtained from the frontend.")
|
|
119
|
-
|
|
120
|
-
# Initialize with token
|
|
121
|
-
AppInitializer.initialize_configuration(hardware_id, server_host, self.token)
|
|
122
|
-
|
|
123
|
-
# Set server_port in config for first-time setup
|
|
124
|
-
ConfigurationManager.set_config("server_port", str(self.server_port))
|
|
125
|
-
|
|
126
|
-
# Get configuration
|
|
127
|
-
config = ConfigurationManager.get_all_configs()
|
|
128
|
-
else:
|
|
129
|
-
# Check if server_host, server_port, or token has changed and update if needed
|
|
130
|
-
config_updated = False
|
|
131
|
-
|
|
132
|
-
if config['server_host'] != server_host:
|
|
133
|
-
ConfigurationManager.set_config("server_host", server_host)
|
|
134
|
-
config_updated = True
|
|
135
|
-
self.logger.info(f"✅ [APP] Updated server host to: {server_host}")
|
|
136
|
-
|
|
137
|
-
# Check if server_port has changed and update if needed
|
|
138
|
-
if str(config.get('server_port')) != str(self.server_port):
|
|
139
|
-
ConfigurationManager.set_config("server_port", str(self.server_port))
|
|
140
|
-
config_updated = True
|
|
141
|
-
self.logger.info(f"✅ [APP] Updated server port to: {self.server_port}")
|
|
142
|
-
|
|
143
|
-
# Check if token has changed and update if needed
|
|
144
|
-
if self.token and config.get('token') != self.token:
|
|
145
|
-
ConfigurationManager.set_config("token", self.token)
|
|
146
|
-
config_updated = True
|
|
147
|
-
self.logger.info("✅ [APP] Updated authentication token")
|
|
148
|
-
|
|
149
|
-
if config_updated:
|
|
150
|
-
config = ConfigurationManager.get_all_configs()
|
|
151
|
-
self.logger.info("✅ [APP] Configuration updated successfully")
|
|
152
|
-
else:
|
|
153
|
-
self.logger.info("✅ [APP] Configuration found. No changes needed.")
|
|
154
|
-
|
|
155
|
-
# Always fetch connection info on startup to check for updates
|
|
156
|
-
self.logger.info("🔄 [APP] Checking for connection info updates...")
|
|
157
|
-
token_to_use = self.token if self.token else config.get('token')
|
|
158
|
-
if token_to_use:
|
|
159
|
-
AppInitializer.update_connection_info(server_host, self.server_port, token_to_use)
|
|
160
|
-
# Reload config after potential updates
|
|
161
|
-
config = ConfigurationManager.get_all_configs()
|
|
162
|
-
else:
|
|
163
|
-
self.logger.warning("⚠️ [APP] No token available to fetch connection info updates")
|
|
164
|
-
|
|
165
|
-
# Add runtime parameters to config
|
|
166
|
-
config['rtmp_server'] = self.rtmp_server
|
|
167
|
-
|
|
168
|
-
return config
|
|
169
|
-
|
|
170
87
|
def initialize(self) -> bool:
|
|
171
88
|
"""
|
|
172
89
|
Initialize the worker service components.
|
|
@@ -178,9 +95,9 @@ class WorkerService:
|
|
|
178
95
|
self.logger.info("Worker service initialization started")
|
|
179
96
|
|
|
180
97
|
# Initialize configuration
|
|
181
|
-
self.config = self.
|
|
182
|
-
|
|
183
|
-
if
|
|
98
|
+
self.config = self._configuration_manager.get_all_configs()
|
|
99
|
+
print(self.config)
|
|
100
|
+
if self.config is None:
|
|
184
101
|
raise RuntimeError("Failed to initialize configuration")
|
|
185
102
|
|
|
186
103
|
# Initialize WorkerManager
|
|
@@ -275,12 +192,12 @@ def main():
|
|
|
275
192
|
args = parser.parse_args()
|
|
276
193
|
|
|
277
194
|
# Create and run worker service
|
|
278
|
-
service = WorkerService(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
)
|
|
283
|
-
service.run()
|
|
195
|
+
# service = WorkerService(
|
|
196
|
+
# server_host=args.server_host,
|
|
197
|
+
# server_port=args.server_port,
|
|
198
|
+
# system_usage_interval=args.system_usage_interval
|
|
199
|
+
# )
|
|
200
|
+
# service.run()
|
|
284
201
|
|
|
285
202
|
|
|
286
203
|
if __name__ == "__main__":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nedo-vision-worker
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.9
|
|
4
4
|
Summary: Nedo Vision Worker Service Library for AI Vision Processing
|
|
5
5
|
Author-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
|
|
6
6
|
Maintainer-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
|
|
@@ -40,6 +40,7 @@ Requires-Dist: protobuf>=3.20.0
|
|
|
40
40
|
Requires-Dist: psutil>=5.9.0
|
|
41
41
|
Requires-Dist: requests>=2.28.0
|
|
42
42
|
Requires-Dist: SQLAlchemy>=1.4.0
|
|
43
|
+
Requires-Dist: Pillow>=12.1.0
|
|
43
44
|
Requires-Dist: pynvml>=11.4.1; platform_system != "Darwin" or platform_machine != "arm64"
|
|
44
45
|
Provides-Extra: opencv
|
|
45
46
|
Requires-Dist: opencv-python>=4.6.0; platform_machine not in "aarch64 armv7l" and extra == "opencv"
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
nedo_vision_worker/__init__.py,sha256=
|
|
2
|
-
nedo_vision_worker/
|
|
1
|
+
nedo_vision_worker/__init__.py,sha256=vQPFkMA2dVYlXMWnsaaP0bx0guzxL0prnYAA2hObGUE,203
|
|
2
|
+
nedo_vision_worker/bootstrap.py,sha256=CUwNtb_mf1VGwO1-ujai7ZNN0ojiO3XQzV50JEwmS3U,1430
|
|
3
|
+
nedo_vision_worker/cli.py,sha256=L0hrjSdU4yfoC-KuxkYJ3orpwH_dWzdmGMTHzjOj5MM,2539
|
|
3
4
|
nedo_vision_worker/doctor.py,sha256=wNkpe8gLVd76Y_ViyK2h1ZFdqeSl37MnzZN5frWKu30,48410
|
|
4
|
-
nedo_vision_worker/worker_service.py,sha256=
|
|
5
|
-
nedo_vision_worker/config/ConfigurationManager.py,sha256=
|
|
5
|
+
nedo_vision_worker/worker_service.py,sha256=4SoWqjuz7lQKByU48-yBLV0CeTmX9LIJ5uIc-vmn6ck,7318
|
|
6
|
+
nedo_vision_worker/config/ConfigurationManager.py,sha256=NQD6kXmBrahuMIEhvUr1zrcB60HlQxTmWvBY1dD28PI,8392
|
|
7
|
+
nedo_vision_worker/config/ConfigurationManagerInterface.py,sha256=doV1Qv6e2edL5Llu-JOF_vhmB87iG-yTXL_GYmlvv9M,270
|
|
8
|
+
nedo_vision_worker/config/DummyConfigurationManager.py,sha256=J1Ur5AD4YKl6CmdJ78SufSw5gdTQURUQtww61FxIoks,1685
|
|
6
9
|
nedo_vision_worker/config/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
7
10
|
nedo_vision_worker/database/DatabaseManager.py,sha256=j2koXo1fnMmAyQnY4sv4txfZR8qIzrPyev-sQ4HBaOQ,9478
|
|
8
11
|
nedo_vision_worker/database/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
9
|
-
nedo_vision_worker/initializer/AppInitializer.py,sha256=6UVdjiuayziPYZ7JkQ436z7-9sHj7J3jtp6lfQsu-DU,5698
|
|
10
|
-
nedo_vision_worker/initializer/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
11
12
|
nedo_vision_worker/models/__init__.py,sha256=6ZH2W1Jcy4o6xBPqFPcyxRU2UJ5Zvw_kfO38yLLGtHA,796
|
|
12
13
|
nedo_vision_worker/models/ai_model.py,sha256=9muyZL9AxtX417-tYUiw8bgvFPtqdXgEAq-hm_mLxGY,2277
|
|
13
14
|
nedo_vision_worker/models/auth.py,sha256=hBMchk6ATy8Wc3fUWLpYRbeNiteb43t2hIub0cCFap8,456
|
|
@@ -48,11 +49,11 @@ nedo_vision_worker/repositories/WorkerSourcePipelineRepository.py,sha256=xfmEvgn
|
|
|
48
49
|
nedo_vision_worker/repositories/WorkerSourceRepository.py,sha256=AhAJLAacMFdsOgtQNiu7Pahl1DAGI0T1THHeUlKwQJc,2385
|
|
49
50
|
nedo_vision_worker/repositories/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
50
51
|
nedo_vision_worker/services/AIModelClient.py,sha256=lxRNax6FR-pV0G1NpJnlaqjbQeu3kRolIUNSw1RkoZA,15406
|
|
51
|
-
nedo_vision_worker/services/ConnectionInfoClient.py,sha256=
|
|
52
|
+
nedo_vision_worker/services/ConnectionInfoClient.py,sha256=udvcKVjZaWfmMbC3HM1cybU5fz9ZidnJGnHj6OSr0uQ,2226
|
|
52
53
|
nedo_vision_worker/services/DatasetSourceClient.py,sha256=O5a7onxFl0z47zXaMXWxHAMPuuc-i_vzkd2w5fwrukc,3319
|
|
53
54
|
nedo_vision_worker/services/DirectDeviceToRTMPStreamer.py,sha256=Ypcc0fh1WiUMkICN_KRRAvGmeGkgF8jQ06gfiRYReW4,24747
|
|
54
55
|
nedo_vision_worker/services/FileToRTMPServer.py,sha256=0hY5pmeAzLw_d3uPR2Qp6gSAYb4rJHiAunuNe08OvkM,2870
|
|
55
|
-
nedo_vision_worker/services/GrpcClientBase.py,sha256=
|
|
56
|
+
nedo_vision_worker/services/GrpcClientBase.py,sha256=RkJDGRsXu5HalMDR8cOsIaoFf5tA_cLTkh5euBPyo2M,6852
|
|
56
57
|
nedo_vision_worker/services/GrpcClientManager.py,sha256=DLXekmxlQogLo8V9-TNDXtyHT_UG-BaggqwsIups55k,5568
|
|
57
58
|
nedo_vision_worker/services/GrpcConnection.py,sha256=UNjaUC4ZcXuteHQx8AAAL5ymYkT1OpoIvyCYPUc3tCI,4915
|
|
58
59
|
nedo_vision_worker/services/ImageUploadClient.py,sha256=T353YsRfm74G7Mh-eWr5nvdQHXTfpKwHJFmNW8HyjT8,3019
|
|
@@ -70,6 +71,7 @@ nedo_vision_worker/services/WorkerSourcePipelineClient.py,sha256=9qj65uBujCVoUE3
|
|
|
70
71
|
nedo_vision_worker/services/WorkerSourceUpdater.py,sha256=RUNkiL1FRrKkW4UDm4i4i8ShgJy__MhTTrNlfouDAZc,8892
|
|
71
72
|
nedo_vision_worker/services/WorkerStatusClient.py,sha256=7kC5EZjEBwWtHOE6UQ29OPCpYnv_6HSuH7Tc0alK_2Q,2531
|
|
72
73
|
nedo_vision_worker/services/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
74
|
+
nedo_vision_worker/util/CorruptedImageValidator.py,sha256=F58L-thVFiz_sfi84L6xc9y2njrEuiO2d9AwzSe03hk,1529
|
|
73
75
|
nedo_vision_worker/util/EncoderSelector.py,sha256=-9lZwVmiKzJr1cELeuCXi-jRonty2bpociZq4KDScmA,3399
|
|
74
76
|
nedo_vision_worker/util/FFmpegUtil.py,sha256=QnQrzurmllzGb7SlAAYCrzKBUblweoFU-0h-X-32IYg,1829
|
|
75
77
|
nedo_vision_worker/util/HardwareID.py,sha256=rSW8-6stm7rjXEdkYGqXMUn56gyw62YiWnSwZQVCCLM,4315
|
|
@@ -83,19 +85,19 @@ nedo_vision_worker/worker/CoreActionWorker.py,sha256=lb7zPY3yui6I3F4rX4Ii7JwpWZa
|
|
|
83
85
|
nedo_vision_worker/worker/DataSenderWorker.py,sha256=TpMLhjpuuW-70TP6BzKpR9irPqVr9dDyFsBAA5s2N3U,8784
|
|
84
86
|
nedo_vision_worker/worker/DataSyncWorker.py,sha256=LmDPt2J1frmXwuR46L6b0MjlFOHfgG-4_0MGQa78zF4,6288
|
|
85
87
|
nedo_vision_worker/worker/DatasetFrameSender.py,sha256=1SFYj8LJFNi-anBTapsbq8U_NGMM7mnoMKg9NeFAHys,8087
|
|
86
|
-
nedo_vision_worker/worker/DatasetFrameWorker.py,sha256=
|
|
88
|
+
nedo_vision_worker/worker/DatasetFrameWorker.py,sha256=gVgMEnwZsLUsnOIml4XJGTBk5YkB0cjDYEp0X14OMHQ,19255
|
|
87
89
|
nedo_vision_worker/worker/PPEDetectionManager.py,sha256=sXeOjvhCzi4oUhDZwH5-8DSxI9b__Jp0de8QksgaYGw,6063
|
|
88
90
|
nedo_vision_worker/worker/PipelineActionWorker.py,sha256=xgvryjKtEsMj4BKqWzDIaK_lFny-DfMCj5Y2DxHnWww,5651
|
|
89
91
|
nedo_vision_worker/worker/PipelineImageWorker.py,sha256=J8VBUG0cwcH3qOJp2zTl30B-XhmPFyvJLjxitKJYq0E,5642
|
|
90
92
|
nedo_vision_worker/worker/PipelinePreviewWorker.py,sha256=owFiBbktcOZkdImQeykZSeBIR2-mpt6HNkmYIkLRKzE,6397
|
|
91
|
-
nedo_vision_worker/worker/RabbitMQListener.py,sha256=
|
|
93
|
+
nedo_vision_worker/worker/RabbitMQListener.py,sha256=Gwn7VpRg0fMZ0fva98eOnTzZ4HPFf2i_ZCUgwfTdyYQ,6768
|
|
92
94
|
nedo_vision_worker/worker/RestrictedAreaManager.py,sha256=Pz2M9KPSMa1QPXZeYhyN9ih4eX6wmkNS_ZPu5KrvNxY,5927
|
|
93
95
|
nedo_vision_worker/worker/SystemUsageManager.py,sha256=mkh4sT-HkIEY1CJHMEG6LP9ATu39YXvLRLyf995OkoQ,5315
|
|
94
96
|
nedo_vision_worker/worker/VideoStreamWorker.py,sha256=5n6v1PNO7IB-jj_McALLkUP-cBjJoIEw4UiSAs3vTb0,7606
|
|
95
97
|
nedo_vision_worker/worker/WorkerManager.py,sha256=2bxXi19fp3p1qjYBStYRdVVgko8dnevXx1_M_sqH5og,5521
|
|
96
98
|
nedo_vision_worker/worker/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
97
|
-
nedo_vision_worker-1.3.
|
|
98
|
-
nedo_vision_worker-1.3.
|
|
99
|
-
nedo_vision_worker-1.3.
|
|
100
|
-
nedo_vision_worker-1.3.
|
|
101
|
-
nedo_vision_worker-1.3.
|
|
99
|
+
nedo_vision_worker-1.3.9.dist-info/METADATA,sha256=nTYIlYmWee5VO9kjFPGbcsrNaYcamNkKc89ZzwKzF-o,14758
|
|
100
|
+
nedo_vision_worker-1.3.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
101
|
+
nedo_vision_worker-1.3.9.dist-info/entry_points.txt,sha256=LrglS-8nCi8C_PL_pa6uxdgCe879hBETHDVXAckvs-8,60
|
|
102
|
+
nedo_vision_worker-1.3.9.dist-info/top_level.txt,sha256=vgilhlkyD34YsEKkaBabmhIpcKSvF3XpzD2By68L-XI,19
|
|
103
|
+
nedo_vision_worker-1.3.9.dist-info/RECORD,,
|
|
@@ -1,138 +0,0 @@
|
|
|
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}")
|
|
79
|
-
|
|
80
|
-
@staticmethod
|
|
81
|
-
def update_connection_info(server_host: str, server_port: int, token: str):
|
|
82
|
-
"""
|
|
83
|
-
Fetch and update connection information (RabbitMQ credentials) from the server.
|
|
84
|
-
This should be called on startup to ensure credentials are up-to-date.
|
|
85
|
-
|
|
86
|
-
Args:
|
|
87
|
-
server_host: The server hostname or IP address
|
|
88
|
-
server_port: The gRPC server port
|
|
89
|
-
token: Authentication token for the worker
|
|
90
|
-
|
|
91
|
-
Returns:
|
|
92
|
-
bool: True if update was successful, False otherwise
|
|
93
|
-
"""
|
|
94
|
-
try:
|
|
95
|
-
# Validate server host
|
|
96
|
-
AppInitializer.validate_server_host(server_host)
|
|
97
|
-
|
|
98
|
-
# Get connection info using the ConnectionInfoClient
|
|
99
|
-
connection_client = ConnectionInfoClient(server_host, server_port, token)
|
|
100
|
-
connection_result = connection_client.get_connection_info()
|
|
101
|
-
|
|
102
|
-
if not connection_result["success"]:
|
|
103
|
-
logging.error(f"Failed to fetch connection info: {connection_result['message']}")
|
|
104
|
-
return False
|
|
105
|
-
|
|
106
|
-
# Check if any RabbitMQ credentials have changed
|
|
107
|
-
current_config = ConfigurationManager.get_all_configs()
|
|
108
|
-
config_updated = False
|
|
109
|
-
|
|
110
|
-
rabbitmq_fields = {
|
|
111
|
-
'rabbitmq_host': connection_result['rabbitmq_host'],
|
|
112
|
-
'rabbitmq_port': str(connection_result['rabbitmq_port']),
|
|
113
|
-
'rabbitmq_username': connection_result['rabbitmq_username'],
|
|
114
|
-
'rabbitmq_password': connection_result['rabbitmq_password']
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
for field, new_value in rabbitmq_fields.items():
|
|
118
|
-
if current_config.get(field) != new_value:
|
|
119
|
-
ConfigurationManager.set_config(field, new_value)
|
|
120
|
-
config_updated = True
|
|
121
|
-
logging.info(f"✅ [APP] Updated {field}")
|
|
122
|
-
|
|
123
|
-
if config_updated:
|
|
124
|
-
logging.info("✅ [APP] RabbitMQ connection info updated successfully")
|
|
125
|
-
else:
|
|
126
|
-
logging.info("✅ [APP] RabbitMQ connection info is up-to-date")
|
|
127
|
-
|
|
128
|
-
return True
|
|
129
|
-
|
|
130
|
-
except ValueError as ve:
|
|
131
|
-
logging.error(f"Validation error: {ve}")
|
|
132
|
-
return False
|
|
133
|
-
except grpc.RpcError as ge:
|
|
134
|
-
logging.error(f"gRPC Error: {ge}")
|
|
135
|
-
return False
|
|
136
|
-
except Exception as e:
|
|
137
|
-
logging.error(f"Unexpected error updating connection info: {e}")
|
|
138
|
-
return False
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|