nedo-vision-worker 1.1.2__py3-none-any.whl → 1.2.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.
Files changed (43) hide show
  1. nedo_vision_worker/__init__.py +1 -1
  2. nedo_vision_worker/cli.py +197 -168
  3. nedo_vision_worker/database/DatabaseManager.py +3 -3
  4. nedo_vision_worker/doctor.py +1066 -386
  5. nedo_vision_worker/models/ai_model.py +35 -2
  6. nedo_vision_worker/protos/AIModelService_pb2.py +12 -10
  7. nedo_vision_worker/protos/AIModelService_pb2_grpc.py +1 -1
  8. nedo_vision_worker/protos/DatasetSourceService_pb2.py +2 -2
  9. nedo_vision_worker/protos/DatasetSourceService_pb2_grpc.py +1 -1
  10. nedo_vision_worker/protos/HumanDetectionService_pb2.py +2 -2
  11. nedo_vision_worker/protos/HumanDetectionService_pb2_grpc.py +1 -1
  12. nedo_vision_worker/protos/PPEDetectionService_pb2.py +2 -2
  13. nedo_vision_worker/protos/PPEDetectionService_pb2_grpc.py +1 -1
  14. nedo_vision_worker/protos/VisionWorkerService_pb2.py +2 -2
  15. nedo_vision_worker/protos/VisionWorkerService_pb2_grpc.py +1 -1
  16. nedo_vision_worker/protos/WorkerSourcePipelineService_pb2.py +2 -2
  17. nedo_vision_worker/protos/WorkerSourcePipelineService_pb2_grpc.py +1 -1
  18. nedo_vision_worker/protos/WorkerSourceService_pb2.py +2 -2
  19. nedo_vision_worker/protos/WorkerSourceService_pb2_grpc.py +1 -1
  20. nedo_vision_worker/services/AIModelClient.py +184 -160
  21. nedo_vision_worker/services/DirectDeviceToRTMPStreamer.py +534 -0
  22. nedo_vision_worker/services/GrpcClientBase.py +142 -108
  23. nedo_vision_worker/services/PPEDetectionClient.py +0 -7
  24. nedo_vision_worker/services/RestrictedAreaClient.py +0 -5
  25. nedo_vision_worker/services/SharedDirectDeviceClient.py +278 -0
  26. nedo_vision_worker/services/SharedVideoStreamServer.py +315 -0
  27. nedo_vision_worker/services/SystemWideDeviceCoordinator.py +236 -0
  28. nedo_vision_worker/services/VideoSharingDaemon.py +832 -0
  29. nedo_vision_worker/services/VideoStreamClient.py +30 -13
  30. nedo_vision_worker/services/WorkerSourceClient.py +1 -1
  31. nedo_vision_worker/services/WorkerSourcePipelineClient.py +28 -6
  32. nedo_vision_worker/services/WorkerSourceUpdater.py +30 -3
  33. nedo_vision_worker/util/VideoProbeUtil.py +222 -15
  34. nedo_vision_worker/worker/DataSyncWorker.py +1 -0
  35. nedo_vision_worker/worker/PipelineImageWorker.py +1 -1
  36. nedo_vision_worker/worker/VideoStreamWorker.py +27 -3
  37. nedo_vision_worker/worker/WorkerManager.py +2 -29
  38. nedo_vision_worker/worker_service.py +24 -11
  39. {nedo_vision_worker-1.1.2.dist-info → nedo_vision_worker-1.2.0.dist-info}/METADATA +1 -3
  40. {nedo_vision_worker-1.1.2.dist-info → nedo_vision_worker-1.2.0.dist-info}/RECORD +43 -38
  41. {nedo_vision_worker-1.1.2.dist-info → nedo_vision_worker-1.2.0.dist-info}/WHEEL +0 -0
  42. {nedo_vision_worker-1.1.2.dist-info → nedo_vision_worker-1.2.0.dist-info}/entry_points.txt +0 -0
  43. {nedo_vision_worker-1.1.2.dist-info → nedo_vision_worker-1.2.0.dist-info}/top_level.txt +0 -0
@@ -6,5 +6,5 @@ A library for running worker agents in the Nedo Vision platform.
6
6
 
7
7
  from .worker_service import WorkerService
8
8
 
9
- __version__ = "1.1.2"
9
+ __version__ = "1.2.0"
10
10
  __all__ = ["WorkerService"]
nedo_vision_worker/cli.py CHANGED
@@ -3,193 +3,222 @@ import signal
3
3
  import sys
4
4
  import traceback
5
5
  import logging
6
+ from typing import NoReturn
6
7
 
7
8
  from .worker_service import WorkerService
8
9
 
9
10
 
10
- def signal_handler(signum, frame):
11
- """Handle system signals for graceful shutdown"""
12
- logging.info(f"Received signal {signum}, shutting down...")
13
- sys.exit(0)
14
-
15
-
16
- def main():
17
- """Main CLI entry point."""
18
- parser = argparse.ArgumentParser(
19
- description="Nedo Vision Worker Service Library CLI",
20
- formatter_class=argparse.RawDescriptionHelpFormatter,
21
- epilog="""
11
+ class NedoWorkerCLI:
12
+ """Main CLI application for Nedo Vision Worker Service."""
13
+
14
+ def __init__(self):
15
+ self.logger = logging.getLogger(__name__)
16
+ self._setup_signal_handlers()
17
+
18
+ def _setup_signal_handlers(self) -> None:
19
+ """Set up signal handlers for graceful shutdown."""
20
+ signal.signal(signal.SIGINT, self._signal_handler)
21
+ signal.signal(signal.SIGTERM, self._signal_handler)
22
+
23
+ def _signal_handler(self, signum: int, frame) -> NoReturn:
24
+ """Handle system signals for graceful shutdown."""
25
+ self.logger.info(f"Received signal {signum}, shutting down...")
26
+ sys.exit(0)
27
+
28
+ def create_parser(self) -> argparse.ArgumentParser:
29
+ """Create and configure the argument parser."""
30
+ parser = argparse.ArgumentParser(
31
+ description="Nedo Vision Worker Service",
32
+ formatter_class=argparse.RawDescriptionHelpFormatter,
33
+ epilog="""
22
34
  Examples:
23
35
  # Check system dependencies and requirements
24
36
  nedo-worker doctor
25
37
 
26
- # Start worker service with required parameters
27
- nedo-worker run --token your-token-here --rtmp-server rtmp://server.com:1935/live
28
-
29
- # Start with custom server host
30
- nedo-worker run --token your-token-here --rtmp-server rtmp://server.com:1935/live --server-host custom.server.com
31
-
32
- # Start with custom storage path
33
- nedo-worker run --token your-token-here --rtmp-server rtmp://server.com:1935/live --storage-path /path/to/storage
34
- """
35
- )
36
-
37
- # Add subcommands
38
- subparsers = parser.add_subparsers(dest='command', help='Available commands')
39
-
40
- # Doctor command
41
- doctor_parser = subparsers.add_parser(
42
- 'doctor',
43
- help='Check system dependencies and requirements',
44
- description='Run diagnostic checks for FFmpeg, OpenCV, gRPC and other dependencies'
45
- )
46
-
47
- # Run command
48
- run_parser = subparsers.add_parser(
49
- 'run',
50
- help='Start the worker service',
51
- description='Start the Nedo Vision Worker Service'
52
- )
53
-
54
- run_parser.add_argument(
55
- "--server-host",
56
- default="be.vision.sindika.co.id",
57
- help="Server hostname for communication (default: be.vision.sindika.co.id)"
58
- )
59
-
60
- run_parser.add_argument(
61
- "--token",
62
- required=True,
63
- help="Authentication token for the worker (obtained from frontend)"
64
- )
65
-
66
- run_parser.add_argument(
67
- "--system-usage-interval",
68
- type=int,
69
- default=30,
70
- help="System usage reporting interval in seconds (default: 30)"
71
- )
72
-
73
- run_parser.add_argument(
74
- "--rtmp-server",
75
- required=True,
76
- help="RTMP server URL for video streaming (e.g., rtmp://server.com:1935/live)"
77
- )
78
-
79
- run_parser.add_argument(
80
- "--storage-path",
81
- default="data",
82
- help="Storage path for databases and files (default: data)"
83
- )
84
-
85
- # Add legacy arguments for backward compatibility (when no subcommand is used)
86
- parser.add_argument(
87
- "--token",
88
- help="(Legacy) Authentication token for the worker (obtained from frontend)"
89
- )
90
-
91
- parser.add_argument(
92
- "--server-host",
93
- default="be.vision.sindika.co.id",
94
- help="(Legacy) Server hostname for communication (default: be.vision.sindika.co.id)"
95
- )
96
-
97
- parser.add_argument(
98
- "--system-usage-interval",
99
- type=int,
100
- default=30,
101
- help="(Legacy) System usage reporting interval in seconds (default: 30)"
102
- )
103
-
104
- parser.add_argument(
105
- "--rtmp-server",
106
- help="(Legacy) RTMP server URL for video streaming (e.g., rtmp://server.com:1935/live)"
107
- )
108
-
109
- parser.add_argument(
110
- "--storage-path",
111
- default="data",
112
- help="(Legacy) Storage path for databases and files (default: data)"
113
- )
114
-
115
- parser.add_argument(
116
- "--version",
117
- action="version",
118
- version="nedo-vision-worker 1.1.2"
119
- )
120
-
121
- parser.add_argument(
122
- "--doctor",
123
- action="store_true",
124
- help="(Deprecated) Run system diagnostics - use 'nedo-worker doctor' instead"
125
- )
126
-
127
- args = parser.parse_args()
128
-
129
- # Handle subcommands
130
- if args.command == 'doctor':
131
- from .doctor import main as doctor_main
132
- sys.exit(doctor_main())
133
- elif args.command == 'run':
134
- run_worker_service(args)
135
- elif args.doctor: # Legacy mode - deprecated --doctor flag
136
- print("⚠️ Warning: Using deprecated --doctor flag. Use 'nedo-worker doctor' instead.")
137
- from .doctor import main as doctor_main
138
- sys.exit(doctor_main())
139
- elif args.token and args.rtmp_server: # Legacy mode - if token and rtmp_server are provided without subcommand
140
- print("⚠️ Warning: Using legacy command format. Consider using 'nedo-worker run --token ... --rtmp-server ...' instead.")
141
- run_worker_service(args)
142
- else:
143
- # If no subcommand provided and no token, show help
144
- parser.print_help()
145
- sys.exit(1)
146
-
38
+ # Start worker service
39
+ nedo-worker run --token your-token-here
147
40
 
148
- def run_worker_service(args):
149
- """Run the worker service with the provided arguments."""
150
- # Set up signal handlers for graceful shutdown
151
- signal.signal(signal.SIGINT, signal_handler)
152
- signal.signal(signal.SIGTERM, signal_handler)
153
-
154
- logger = logging.getLogger(__name__)
41
+ # Start with custom configuration
42
+ nedo-worker run --token your-token-here \\
43
+ --rtmp-server rtmp://custom.server.com:1935/live \\
44
+ --server-host custom.server.com \\
45
+ --storage-path /custom/storage/path
46
+ """
47
+ )
48
+
49
+ parser.add_argument(
50
+ "--version",
51
+ action="version",
52
+ version="nedo-vision-worker 1.2.0"
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
+
64
+ return parser
65
+
66
+ def _add_doctor_command(self, subparsers) -> None:
67
+ """Add the doctor command."""
68
+ subparsers.add_parser(
69
+ 'doctor',
70
+ help='Check system dependencies and requirements',
71
+ description='Run diagnostic checks for FFmpeg, OpenCV, gRPC and other dependencies'
72
+ )
155
73
 
156
- try:
157
- # Create and start the worker service
158
- service = WorkerService(
159
- server_host=args.server_host,
160
- token=args.token,
161
- system_usage_interval=args.system_usage_interval,
162
- rtmp_server=args.rtmp_server,
163
- storage_path=args.storage_path
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)"
164
99
  )
165
100
 
166
- logger.info("🚀 Starting Nedo Vision Worker Service...")
167
- logger.info(f"🌐 Server: {args.server_host}")
168
- logger.info(f"🔑 Token: {args.token[:8]}...")
169
- logger.info(f"⏱️ System Usage Interval: {args.system_usage_interval}s")
170
- logger.info(f"📡 RTMP Server: {args.rtmp_server}")
171
- logger.info(f"💾 Storage Path: {args.storage_path}")
172
- logger.info("Press Ctrl+C to stop the service")
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
132
+ return doctor_main()
133
+ except ImportError as e:
134
+ self.logger.error(f"Failed to import doctor module: {e}")
135
+ return 1
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
+ )
173
147
 
174
- # Start the service
175
- service.run()
148
+ try:
149
+ # Create the worker service
150
+ service = WorkerService(
151
+ server_host=args.server_host,
152
+ token=args.token,
153
+ system_usage_interval=args.system_usage_interval,
154
+ rtmp_server=args.rtmp_server,
155
+ storage_path=args.storage_path,
156
+ server_port=args.server_port
157
+ )
158
+
159
+ # Log startup information
160
+ self._log_startup_info(args)
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
176
192
 
177
- # Keep the service running
178
193
  try:
179
194
  while getattr(service, 'running', False):
180
- import time
181
195
  time.sleep(1)
182
196
  except KeyboardInterrupt:
183
- logger.info("\n🛑 Shutdown requested...")
197
+ self.logger.info("🛑 Shutdown requested...")
184
198
  finally:
185
- service.stop()
186
- logger.info("✅ Service stopped successfully")
187
-
188
- except Exception as e:
189
- logger.error(f"❌ Error: {e}")
190
- traceback.print_exc()
191
- sys.exit(1)
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
215
+
216
+
217
+ def main() -> int:
218
+ """Main CLI entry point."""
219
+ cli = NedoWorkerCLI()
220
+ return cli.run()
192
221
 
193
222
 
194
223
  if __name__ == "__main__":
195
- main()
224
+ sys.exit(main())
@@ -20,13 +20,13 @@ _storage_path = None # Global storage path
20
20
  def set_storage_path(storage_path: str):
21
21
  """Set the global storage path for the application."""
22
22
  global _storage_path
23
- _storage_path = Path(storage_path)
23
+ _storage_path = Path(storage_path).resolve() # Convert to absolute path
24
24
 
25
25
  def _get_storage_paths():
26
26
  """Get storage paths using the configured storage path."""
27
27
  global _storage_path
28
28
  if _storage_path is None:
29
- _storage_path = Path("data") # Default fallback
29
+ _storage_path = Path("data").resolve() # Default fallback as absolute path
30
30
 
31
31
  return {
32
32
  "db": _storage_path / "sqlite",
@@ -38,7 +38,7 @@ def get_storage_path(subdir: str = None) -> Path:
38
38
  """Get a storage path for a specific subdirectory."""
39
39
  global _storage_path
40
40
  if _storage_path is None:
41
- _storage_path = Path("data") # Default fallback
41
+ _storage_path = Path("data").resolve() # Default fallback as absolute path
42
42
 
43
43
  if subdir:
44
44
  return _storage_path / subdir