nedo-vision-worker 1.3.1__tar.gz → 1.3.3__tar.gz

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 (107) hide show
  1. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/PKG-INFO +1 -1
  2. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/__init__.py +1 -1
  3. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/PPEDetectionManager.py +65 -15
  4. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/RestrictedAreaManager.py +63 -15
  5. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker.egg-info/PKG-INFO +1 -1
  6. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/README.md +0 -0
  7. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/cli.py +0 -0
  8. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/config/ConfigurationManager.py +0 -0
  9. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/config/__init__.py +0 -0
  10. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/database/DatabaseManager.py +0 -0
  11. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/database/__init__.py +0 -0
  12. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/doctor.py +0 -0
  13. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/initializer/AppInitializer.py +0 -0
  14. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/initializer/__init__.py +0 -0
  15. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/__init__.py +0 -0
  16. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/ai_model.py +0 -0
  17. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/auth.py +0 -0
  18. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/config.py +0 -0
  19. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/dataset_source.py +0 -0
  20. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/logs.py +0 -0
  21. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/ppe_detection.py +0 -0
  22. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/ppe_detection_label.py +0 -0
  23. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/restricted_area_violation.py +0 -0
  24. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/user.py +0 -0
  25. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/worker_source.py +0 -0
  26. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/worker_source_pipeline.py +0 -0
  27. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/worker_source_pipeline_config.py +0 -0
  28. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/worker_source_pipeline_debug.py +0 -0
  29. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/models/worker_source_pipeline_detection.py +0 -0
  30. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/AIModelService_pb2.py +0 -0
  31. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/AIModelService_pb2_grpc.py +0 -0
  32. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/DatasetSourceService_pb2.py +0 -0
  33. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/DatasetSourceService_pb2_grpc.py +0 -0
  34. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/HumanDetectionService_pb2.py +0 -0
  35. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/HumanDetectionService_pb2_grpc.py +0 -0
  36. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/PPEDetectionService_pb2.py +0 -0
  37. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/PPEDetectionService_pb2_grpc.py +0 -0
  38. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/VisionWorkerService_pb2.py +0 -0
  39. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/VisionWorkerService_pb2_grpc.py +0 -0
  40. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/WorkerSourcePipelineService_pb2.py +0 -0
  41. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/WorkerSourcePipelineService_pb2_grpc.py +0 -0
  42. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/WorkerSourceService_pb2.py +0 -0
  43. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/WorkerSourceService_pb2_grpc.py +0 -0
  44. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/protos/__init__.py +0 -0
  45. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/repositories/AIModelRepository.py +0 -0
  46. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/repositories/DatasetSourceRepository.py +0 -0
  47. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/repositories/PPEDetectionRepository.py +0 -0
  48. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/repositories/RestrictedAreaRepository.py +0 -0
  49. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/repositories/WorkerSourcePipelineDebugRepository.py +0 -0
  50. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/repositories/WorkerSourcePipelineDetectionRepository.py +0 -0
  51. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/repositories/WorkerSourcePipelineRepository.py +0 -0
  52. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/repositories/WorkerSourceRepository.py +0 -0
  53. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/repositories/__init__.py +0 -0
  54. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/AIModelClient.py +0 -0
  55. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/ConnectionInfoClient.py +0 -0
  56. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/DatasetSourceClient.py +0 -0
  57. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/DirectDeviceToRTMPStreamer.py +0 -0
  58. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/FileToRTMPServer.py +0 -0
  59. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/GrpcClientBase.py +0 -0
  60. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/GrpcClientManager.py +0 -0
  61. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/GrpcConnection.py +0 -0
  62. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/ImageUploadClient.py +0 -0
  63. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/PPEDetectionClient.py +0 -0
  64. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/RTSPtoRTMPStreamer.py +0 -0
  65. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/RestrictedAreaClient.py +0 -0
  66. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/SharedDirectDeviceClient.py +0 -0
  67. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/SharedVideoStreamServer.py +0 -0
  68. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/SystemUsageClient.py +0 -0
  69. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/SystemWideDeviceCoordinator.py +0 -0
  70. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/VideoSharingDaemon.py +0 -0
  71. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/VideoStreamClient.py +0 -0
  72. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/WorkerSourceClient.py +0 -0
  73. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/WorkerSourcePipelineClient.py +0 -0
  74. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/WorkerSourceUpdater.py +0 -0
  75. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/WorkerStatusClient.py +0 -0
  76. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/services/__init__.py +0 -0
  77. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/util/EncoderSelector.py +0 -0
  78. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/util/FFmpegUtil.py +0 -0
  79. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/util/HardwareID.py +0 -0
  80. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/util/ImageUploader.py +0 -0
  81. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/util/Networking.py +0 -0
  82. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/util/PlatformDetector.py +0 -0
  83. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/util/SystemMonitor.py +0 -0
  84. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/util/VideoProbeUtil.py +0 -0
  85. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/util/__init__.py +0 -0
  86. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/CoreActionWorker.py +0 -0
  87. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/DataSenderWorker.py +0 -0
  88. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/DataSyncWorker.py +0 -0
  89. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/DatasetFrameSender.py +0 -0
  90. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/DatasetFrameWorker.py +0 -0
  91. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/PipelineActionWorker.py +0 -0
  92. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/PipelineImageWorker.py +0 -0
  93. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/PipelinePreviewWorker.py +0 -0
  94. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/RabbitMQListener.py +0 -0
  95. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/SystemUsageManager.py +0 -0
  96. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/VideoStreamWorker.py +0 -0
  97. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/WorkerManager.py +0 -0
  98. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker/__init__.py +0 -0
  99. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker/worker_service.py +0 -0
  100. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker.egg-info/SOURCES.txt +0 -0
  101. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker.egg-info/dependency_links.txt +0 -0
  102. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker.egg-info/entry_points.txt +0 -0
  103. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker.egg-info/requires.txt +0 -0
  104. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/nedo_vision_worker.egg-info/top_level.txt +0 -0
  105. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/pyproject.toml +0 -0
  106. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/setup.cfg +0 -0
  107. {nedo_vision_worker-1.3.1 → nedo_vision_worker-1.3.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nedo-vision-worker
3
- Version: 1.3.1
3
+ Version: 1.3.3
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>
@@ -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.3.1"
9
+ __version__ = "1.3.3"
10
10
  __all__ = ["WorkerService"]
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import os
2
3
  import threading
3
4
  import time
4
5
  from ..services.PPEDetectionClient import PPEDetectionClient
@@ -50,41 +51,90 @@ class PPEDetectionManager:
50
51
 
51
52
  logger.info("📡 [APP] PPE detection monitoring started.")
52
53
 
53
- def _calculate_batch_size(self, pending_count: int) -> int:
54
+ def _calculate_fetch_size(self, pending_count: int) -> int:
54
55
  """
55
- Calculates optimal batch size based on pending items.
56
- Limited to 50 to stay within 50MB gRPC message size limit.
56
+ Dynamically calculates how many records to fetch based on pending count.
57
57
 
58
58
  Args:
59
59
  pending_count (int): Number of pending detections
60
60
 
61
61
  Returns:
62
- int: Optimal batch size (max 50)
62
+ int: Number of records to fetch
63
63
  """
64
- if pending_count < 20:
65
- return 10
66
- elif pending_count < 100:
67
- return 20
68
- elif pending_count < 500:
69
- return 30
64
+ if pending_count <= 50:
65
+ return min(pending_count, 10)
66
+ elif pending_count <= 200:
67
+ return min(pending_count, 30)
68
+ elif pending_count <= 1000:
69
+ return min(pending_count, 50)
70
70
  else:
71
- return 50
71
+ return min(pending_count, 100)
72
+
73
+ def _calculate_batch_by_size(self, all_detections: list, max_size_mb: int = 40) -> list:
74
+ """
75
+ Calculates batch based on actual image file sizes to stay within gRPC limit.
76
+
77
+ Args:
78
+ all_detections (list): All pending detections
79
+ max_size_mb (int): Maximum batch size in MB (default 40MB for 50MB limit with margin)
80
+
81
+ Returns:
82
+ list: Detections that fit within size limit
83
+ """
84
+ import os
85
+
86
+ batch = []
87
+ total_size = 0
88
+ max_size_bytes = max_size_mb * 1024 * 1024
89
+
90
+ for detection in all_detections:
91
+ try:
92
+ image_size = os.path.getsize(detection['image']) if os.path.exists(detection['image']) else 0
93
+ tile_size = os.path.getsize(detection['image_tile']) if os.path.exists(detection['image_tile']) else 0
94
+ detection_size = image_size + tile_size
95
+
96
+ if total_size + detection_size > max_size_bytes:
97
+ if batch:
98
+ break
99
+ else:
100
+ logger.warning(f"⚠️ Single detection exceeds {max_size_mb}MB, skipping")
101
+ continue
102
+
103
+ batch.append(detection)
104
+ total_size += detection_size
105
+
106
+ except Exception as e:
107
+ logger.error(f"❌ Error checking file size: {e}")
108
+ continue
109
+
110
+ return batch
72
111
 
73
112
  def send_ppe_detection_batch(self):
74
- """Sends a batch of collected PPE detection data to the server with dynamic batch sizing."""
113
+ """Sends a batch of collected PPE detection data to the server with dynamic size-based batching."""
75
114
  try:
76
115
  pending_count = self.ppe_detection_repo.get_total_pending_count()
77
116
 
78
117
  if pending_count == 0:
79
118
  return
80
119
 
81
- batch_size = self._calculate_batch_size(pending_count)
82
- self.ppe_detection_data = self.ppe_detection_repo.get_latest_detections(batch_size)
120
+ fetch_size = self._calculate_fetch_size(pending_count)
121
+ all_detections = self.ppe_detection_repo.get_latest_detections(fetch_size)
122
+
123
+ if not all_detections:
124
+ return
125
+
126
+ self.ppe_detection_data = self._calculate_batch_by_size(all_detections)
83
127
 
84
128
  if not self.ppe_detection_data:
129
+ logger.warning("⚠️ [APP] No valid detections within size limit")
85
130
  return
86
131
 
87
- logger.info(f"📤 [APP] Sending {len(self.ppe_detection_data)} PPE detections ({pending_count} pending)")
132
+ batch_size_mb = sum(
133
+ os.path.getsize(d['image']) + os.path.getsize(d['image_tile'])
134
+ for d in self.ppe_detection_data if os.path.exists(d['image']) and os.path.exists(d['image_tile'])
135
+ ) / (1024 * 1024)
136
+
137
+ logger.info(f"📤 [APP] Sending {len(self.ppe_detection_data)} PPE detections (~{batch_size_mb:.1f}MB, {pending_count} pending)")
88
138
 
89
139
  response = self.ppe_detection_client.send_upsert_batch(
90
140
  worker_id=self.worker_id,
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import os
2
3
  import threading
3
4
  import time
4
5
 
@@ -50,41 +51,88 @@ class RestrictedAreaManager:
50
51
 
51
52
  logger.info("📡 [APP] Restricted area violation monitoring started.")
52
53
 
53
- def _calculate_batch_size(self, pending_count: int) -> int:
54
+ def _calculate_fetch_size(self, pending_count: int) -> int:
54
55
  """
55
- Calculates optimal batch size based on pending items.
56
- Limited to 50 to stay within 50MB gRPC message size limit.
56
+ Dynamically calculates how many records to fetch based on pending count.
57
57
 
58
58
  Args:
59
59
  pending_count (int): Number of pending violations
60
60
 
61
61
  Returns:
62
- int: Optimal batch size (max 50)
62
+ int: Number of records to fetch
63
63
  """
64
- if pending_count < 20:
65
- return 10
66
- elif pending_count < 100:
67
- return 20
68
- elif pending_count < 500:
69
- return 30
64
+ if pending_count <= 50:
65
+ return min(pending_count, 10)
66
+ elif pending_count <= 200:
67
+ return min(pending_count, 30)
68
+ elif pending_count <= 1000:
69
+ return min(pending_count, 50)
70
70
  else:
71
- return 50
71
+ return min(pending_count, 100)
72
+
73
+ def _calculate_batch_by_size(self, all_violations: list, max_size_mb: int = 40) -> list:
74
+ """
75
+ Calculates batch based on actual image file sizes to stay within gRPC limit.
76
+
77
+ Args:
78
+ all_violations (list): All pending violations
79
+ max_size_mb (int): Maximum batch size in MB (default 40MB for 50MB limit with margin)
80
+
81
+ Returns:
82
+ list: Violations that fit within size limit
83
+ """
84
+ batch = []
85
+ total_size = 0
86
+ max_size_bytes = max_size_mb * 1024 * 1024
87
+
88
+ for violation in all_violations:
89
+ try:
90
+ image_size = os.path.getsize(violation['image']) if os.path.exists(violation['image']) else 0
91
+ tile_size = os.path.getsize(violation['image_tile']) if os.path.exists(violation['image_tile']) else 0
92
+ violation_size = image_size + tile_size
93
+
94
+ if total_size + violation_size > max_size_bytes:
95
+ if batch:
96
+ break
97
+ else:
98
+ logger.warning(f"⚠️ Single violation exceeds {max_size_mb}MB, skipping")
99
+ continue
100
+
101
+ batch.append(violation)
102
+ total_size += violation_size
103
+
104
+ except Exception as e:
105
+ logger.error(f"❌ Error checking file size: {e}")
106
+ continue
107
+
108
+ return batch
72
109
 
73
110
  def send_violation_batch(self):
74
- """Sends a batch of collected violation data to the server with dynamic batch sizing."""
111
+ """Sends a batch of collected violation data to the server with dynamic size-based batching."""
75
112
  try:
76
113
  pending_count = self.repo.get_total_pending_count()
77
114
 
78
115
  if pending_count == 0:
79
116
  return
80
117
 
81
- batch_size = self._calculate_batch_size(pending_count)
82
- self.violations_data = self.repo.get_latest_violations(batch_size)
118
+ fetch_size = self._calculate_fetch_size(pending_count)
119
+ all_violations = self.repo.get_latest_violations(fetch_size)
120
+
121
+ if not all_violations:
122
+ return
123
+
124
+ self.violations_data = self._calculate_batch_by_size(all_violations)
83
125
 
84
126
  if not self.violations_data:
127
+ logger.warning("⚠️ [APP] No valid violations within size limit")
85
128
  return
86
129
 
87
- logger.info(f"📤 [APP] Sending {len(self.violations_data)} violations ({pending_count} pending)")
130
+ batch_size_mb = sum(
131
+ os.path.getsize(v['image']) + os.path.getsize(v['image_tile'])
132
+ for v in self.violations_data if os.path.exists(v['image']) and os.path.exists(v['image_tile'])
133
+ ) / (1024 * 1024)
134
+
135
+ logger.info(f"📤 [APP] Sending {len(self.violations_data)} violations (~{batch_size_mb:.1f}MB, {pending_count} pending)")
88
136
 
89
137
  response = self.client.send_upsert_batch(
90
138
  worker_id=self.worker_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nedo-vision-worker
3
- Version: 1.3.1
3
+ Version: 1.3.3
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>