nedo-vision-worker 1.2.8__py3-none-any.whl → 1.3.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 +1 -1
- nedo_vision_worker/repositories/PPEDetectionRepository.py +40 -7
- nedo_vision_worker/repositories/RestrictedAreaRepository.py +39 -6
- nedo_vision_worker/services/PPEDetectionClient.py +27 -8
- nedo_vision_worker/services/RestrictedAreaClient.py +24 -3
- nedo_vision_worker/worker/DataSenderWorker.py +52 -3
- nedo_vision_worker/worker/PPEDetectionManager.py +31 -3
- nedo_vision_worker/worker/RestrictedAreaManager.py +32 -3
- {nedo_vision_worker-1.2.8.dist-info → nedo_vision_worker-1.3.0.dist-info}/METADATA +1 -1
- {nedo_vision_worker-1.2.8.dist-info → nedo_vision_worker-1.3.0.dist-info}/RECORD +13 -13
- {nedo_vision_worker-1.2.8.dist-info → nedo_vision_worker-1.3.0.dist-info}/WHEEL +0 -0
- {nedo_vision_worker-1.2.8.dist-info → nedo_vision_worker-1.3.0.dist-info}/entry_points.txt +0 -0
- {nedo_vision_worker-1.2.8.dist-info → nedo_vision_worker-1.3.0.dist-info}/top_level.txt +0 -0
nedo_vision_worker/__init__.py
CHANGED
|
@@ -19,19 +19,22 @@ class PPEDetectionRepository:
|
|
|
19
19
|
os.makedirs(self.storage_dir, exist_ok=True)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def
|
|
22
|
+
def get_latest_detections(self, limit: int = 50) -> list:
|
|
23
23
|
"""
|
|
24
|
-
Retrieves the latest
|
|
24
|
+
Retrieves the latest PPE detections ordered by the 'created_at' timestamp.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
limit (int): Maximum number of detections to retrieve. Default is 50.
|
|
25
28
|
|
|
26
29
|
Returns:
|
|
27
|
-
list: A list of
|
|
30
|
+
list: A list of PPE detection dictionaries.
|
|
28
31
|
"""
|
|
29
32
|
try:
|
|
30
33
|
latest_detections = (
|
|
31
34
|
self.session.query(PPEDetectionEntity)
|
|
32
35
|
.options(joinedload(PPEDetectionEntity.ppe_labels))
|
|
33
36
|
.order_by(asc(PPEDetectionEntity.created_at))
|
|
34
|
-
.limit(
|
|
37
|
+
.limit(limit)
|
|
35
38
|
.all()
|
|
36
39
|
)
|
|
37
40
|
|
|
@@ -67,9 +70,22 @@ class PPEDetectionRepository:
|
|
|
67
70
|
|
|
68
71
|
except SQLAlchemyError as e:
|
|
69
72
|
self.session.rollback()
|
|
70
|
-
logging.error(f"❌ Database error while retrieving
|
|
73
|
+
logging.error(f"❌ Database error while retrieving detections: {e}")
|
|
71
74
|
return []
|
|
72
75
|
|
|
76
|
+
def get_total_pending_count(self) -> int:
|
|
77
|
+
"""
|
|
78
|
+
Returns the total count of pending PPE detections in the database.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
int: Total count of pending detections.
|
|
82
|
+
"""
|
|
83
|
+
try:
|
|
84
|
+
return self.session.query(PPEDetectionEntity).count()
|
|
85
|
+
except SQLAlchemyError as e:
|
|
86
|
+
logging.error(f"❌ Error counting pending detections: {e}")
|
|
87
|
+
return 0
|
|
88
|
+
|
|
73
89
|
def delete_records_from_db(self, detection_data: list):
|
|
74
90
|
"""
|
|
75
91
|
Deletes PPE detection records from the database based on detection data.
|
|
@@ -77,6 +93,10 @@ class PPEDetectionRepository:
|
|
|
77
93
|
Args:
|
|
78
94
|
detection_data (list): List of dictionaries containing the detection data.
|
|
79
95
|
"""
|
|
96
|
+
if not detection_data:
|
|
97
|
+
logging.info("No detection data provided for deletion.")
|
|
98
|
+
return
|
|
99
|
+
|
|
80
100
|
try:
|
|
81
101
|
# Extract person_id from detection data to delete the corresponding records
|
|
82
102
|
person_ids_to_delete = [data['person_id'] for data in detection_data]
|
|
@@ -97,10 +117,23 @@ class PPEDetectionRepository:
|
|
|
97
117
|
if not os.path.isabs(image_path):
|
|
98
118
|
image_path = str(get_storage_path("files") / Path(image_path).relative_to("data/files"))
|
|
99
119
|
if os.path.exists(image_path):
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
try:
|
|
121
|
+
os.remove(image_path)
|
|
122
|
+
except OSError as e:
|
|
123
|
+
logging.warning(f"Failed to delete image file {image_path}: {e}")
|
|
102
124
|
else:
|
|
103
125
|
logging.warning(f"Image file not found for detection {detection.id}: {image_path}")
|
|
126
|
+
|
|
127
|
+
# Delete the image tile file if it exists
|
|
128
|
+
image_tile_path = detection.image_tile_path
|
|
129
|
+
if image_tile_path:
|
|
130
|
+
if not os.path.isabs(image_tile_path):
|
|
131
|
+
image_tile_path = str(get_storage_path("files") / Path(image_tile_path).relative_to("data/files"))
|
|
132
|
+
if os.path.exists(image_tile_path):
|
|
133
|
+
try:
|
|
134
|
+
os.remove(image_tile_path)
|
|
135
|
+
except OSError as e:
|
|
136
|
+
logging.warning(f"Failed to delete image tile file {image_tile_path}: {e}")
|
|
104
137
|
|
|
105
138
|
# Delete the detection record
|
|
106
139
|
self.session.delete(detection)
|
|
@@ -16,9 +16,12 @@ class RestrictedAreaRepository:
|
|
|
16
16
|
self.session: Session = self.db_manager.get_session("default")
|
|
17
17
|
os.makedirs(self.storage_dir, exist_ok=True)
|
|
18
18
|
|
|
19
|
-
def
|
|
19
|
+
def get_latest_violations(self, limit: int = 50) -> list:
|
|
20
20
|
"""
|
|
21
|
-
Retrieves the latest
|
|
21
|
+
Retrieves the latest restricted area violations ordered by the 'created_at' timestamp.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
limit (int): Maximum number of violations to retrieve. Default is 50.
|
|
22
25
|
|
|
23
26
|
Returns:
|
|
24
27
|
list: A list of dictionaries representing restricted area violations.
|
|
@@ -27,7 +30,7 @@ class RestrictedAreaRepository:
|
|
|
27
30
|
latest_violations = (
|
|
28
31
|
self.session.query(RestrictedAreaViolationEntity)
|
|
29
32
|
.order_by(asc(RestrictedAreaViolationEntity.created_at))
|
|
30
|
-
.limit(
|
|
33
|
+
.limit(limit)
|
|
31
34
|
.all()
|
|
32
35
|
)
|
|
33
36
|
|
|
@@ -51,9 +54,22 @@ class RestrictedAreaRepository:
|
|
|
51
54
|
|
|
52
55
|
except SQLAlchemyError as e:
|
|
53
56
|
self.session.rollback()
|
|
54
|
-
logging.error(f"❌ Database error while retrieving
|
|
57
|
+
logging.error(f"❌ Database error while retrieving violations: {e}")
|
|
55
58
|
return []
|
|
56
59
|
|
|
60
|
+
def get_total_pending_count(self) -> int:
|
|
61
|
+
"""
|
|
62
|
+
Returns the total count of pending restricted area violations in the database.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
int: Total count of pending violations.
|
|
66
|
+
"""
|
|
67
|
+
try:
|
|
68
|
+
return self.session.query(RestrictedAreaViolationEntity).count()
|
|
69
|
+
except SQLAlchemyError as e:
|
|
70
|
+
logging.error(f"❌ Error counting pending violations: {e}")
|
|
71
|
+
return 0
|
|
72
|
+
|
|
57
73
|
def delete_records_from_db(self, violation_data: list):
|
|
58
74
|
"""
|
|
59
75
|
Deletes restricted area violation records from the database based on the provided data.
|
|
@@ -61,6 +77,10 @@ class RestrictedAreaRepository:
|
|
|
61
77
|
Args:
|
|
62
78
|
violation_data (list): List of dictionaries containing the violation data.
|
|
63
79
|
"""
|
|
80
|
+
if not violation_data:
|
|
81
|
+
logging.info("No violation data provided for deletion.")
|
|
82
|
+
return
|
|
83
|
+
|
|
64
84
|
try:
|
|
65
85
|
person_ids_to_delete = [data['person_id'] for data in violation_data]
|
|
66
86
|
|
|
@@ -75,11 +95,24 @@ class RestrictedAreaRepository:
|
|
|
75
95
|
if not os.path.isabs(image_path):
|
|
76
96
|
image_path = str(get_storage_path("restricted_violations") / Path(image_path).relative_to("data/restricted_violations"))
|
|
77
97
|
if os.path.exists(image_path):
|
|
78
|
-
|
|
79
|
-
|
|
98
|
+
try:
|
|
99
|
+
os.remove(image_path)
|
|
100
|
+
except OSError as e:
|
|
101
|
+
logging.warning(f"Failed to delete image file {image_path}: {e}")
|
|
80
102
|
else:
|
|
81
103
|
logging.warning(f"Image file not found for violation {violation.id}: {image_path}")
|
|
82
104
|
|
|
105
|
+
# Delete the image tile file if it exists
|
|
106
|
+
image_tile_path = violation.image_tile_path
|
|
107
|
+
if image_tile_path:
|
|
108
|
+
if not os.path.isabs(image_tile_path):
|
|
109
|
+
image_tile_path = str(get_storage_path("restricted_violations") / Path(image_tile_path).relative_to("data/restricted_violations"))
|
|
110
|
+
if os.path.exists(image_tile_path):
|
|
111
|
+
try:
|
|
112
|
+
os.remove(image_tile_path)
|
|
113
|
+
except OSError as e:
|
|
114
|
+
logging.warning(f"Failed to delete image tile file {image_tile_path}: {e}")
|
|
115
|
+
|
|
83
116
|
self.session.delete(violation)
|
|
84
117
|
|
|
85
118
|
self.session.commit()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
2
3
|
from .GrpcClientBase import GrpcClientBase
|
|
3
4
|
from ..protos.PPEDetectionService_pb2_grpc import PPEDetectionGRPCServiceStub
|
|
4
5
|
from ..protos.PPEDetectionService_pb2 import UpsertPPEDetectionBatchRequest, UpsertPPEDetectionRequest, PPEDetectionLabelRequest
|
|
@@ -56,11 +57,23 @@ class PPEDetectionClient(GrpcClientBase):
|
|
|
56
57
|
return {"success": False, "message": "gRPC connection is not established."}
|
|
57
58
|
|
|
58
59
|
try:
|
|
59
|
-
# Prepare the list of UpsertPPEDetectionRequest messages
|
|
60
60
|
ppe_detection_requests = []
|
|
61
|
+
valid_records = []
|
|
62
|
+
invalid_records = []
|
|
63
|
+
|
|
61
64
|
for data in detection_data:
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
if not os.path.exists(data['image']) or not os.path.exists(data['image_tile']):
|
|
66
|
+
logger.warning(f"⚠️ Missing image files for person_id {data.get('person_id')}")
|
|
67
|
+
invalid_records.append(data)
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
image_binary = self.read_image_as_binary(data['image'])
|
|
72
|
+
image_tile_binary = self.read_image_as_binary(data['image_tile'])
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.error(f"❌ Error reading images for person_id {data.get('person_id')}: {e}")
|
|
75
|
+
invalid_records.append(data)
|
|
76
|
+
continue
|
|
64
77
|
|
|
65
78
|
ppe_detection_labels = [
|
|
66
79
|
PPEDetectionLabelRequest(
|
|
@@ -73,11 +86,11 @@ class PPEDetectionClient(GrpcClientBase):
|
|
|
73
86
|
)
|
|
74
87
|
for label in data['ppe_detection_labels']
|
|
75
88
|
]
|
|
76
|
-
|
|
89
|
+
|
|
77
90
|
request = UpsertPPEDetectionRequest(
|
|
78
91
|
person_id=data['person_id'],
|
|
79
92
|
worker_id=worker_id,
|
|
80
|
-
worker_source_id=
|
|
93
|
+
worker_source_id=data['worker_source_id'],
|
|
81
94
|
image=image_binary,
|
|
82
95
|
image_tile=image_tile_binary,
|
|
83
96
|
worker_timestamp=data['worker_timestamp'],
|
|
@@ -85,18 +98,24 @@ class PPEDetectionClient(GrpcClientBase):
|
|
|
85
98
|
token=token
|
|
86
99
|
)
|
|
87
100
|
ppe_detection_requests.append(request)
|
|
101
|
+
valid_records.append(data)
|
|
102
|
+
|
|
103
|
+
if invalid_records:
|
|
104
|
+
logger.info(f"🧹 Deleting {len(invalid_records)} invalid PPE detection records")
|
|
105
|
+
self.repository.delete_records_from_db(invalid_records)
|
|
106
|
+
|
|
107
|
+
if not ppe_detection_requests:
|
|
108
|
+
return {"success": True, "message": "No valid detections to send"}
|
|
88
109
|
|
|
89
|
-
# Create the UpsertPPEDetectionBatchRequest
|
|
90
110
|
batch_request = UpsertPPEDetectionBatchRequest(
|
|
91
111
|
ppe_detection_requests=ppe_detection_requests,
|
|
92
112
|
token=token
|
|
93
113
|
)
|
|
94
114
|
|
|
95
|
-
# Call the UpsertBatch RPC
|
|
96
115
|
response = self.handle_rpc(self.stub.UpsertBatch, batch_request)
|
|
97
116
|
|
|
98
117
|
if response and response.success:
|
|
99
|
-
self.repository.delete_records_from_db(
|
|
118
|
+
self.repository.delete_records_from_db(valid_records)
|
|
100
119
|
return {"success": True, "message": response.message}
|
|
101
120
|
|
|
102
121
|
return {"success": False, "message": response.message if response else "Unknown error"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
2
3
|
from ..repositories.RestrictedAreaRepository import RestrictedAreaRepository
|
|
3
4
|
from .GrpcClientBase import GrpcClientBase
|
|
4
5
|
from ..protos.HumanDetectionService_pb2_grpc import HumanDetectionGRPCServiceStub
|
|
@@ -60,10 +61,22 @@ class RestrictedAreaClient(GrpcClientBase):
|
|
|
60
61
|
|
|
61
62
|
try:
|
|
62
63
|
human_detection_requests = []
|
|
64
|
+
valid_records = []
|
|
65
|
+
invalid_records = []
|
|
63
66
|
|
|
64
67
|
for data in violation_data:
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
if not os.path.exists(data['image']) or not os.path.exists(data['image_tile']):
|
|
69
|
+
logger.warning(f"⚠️ Missing image files for person_id {data.get('person_id')}")
|
|
70
|
+
invalid_records.append(data)
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
image_binary = self.read_image_as_binary(data['image'])
|
|
75
|
+
image_tile_binary = self.read_image_as_binary(data['image_tile'])
|
|
76
|
+
except Exception as e:
|
|
77
|
+
logger.error(f"❌ Error reading images for person_id {data.get('person_id')}: {e}")
|
|
78
|
+
invalid_records.append(data)
|
|
79
|
+
continue
|
|
67
80
|
|
|
68
81
|
request = UpsertHumanDetectionRequest(
|
|
69
82
|
person_id=data['person_id'],
|
|
@@ -81,6 +94,14 @@ class RestrictedAreaClient(GrpcClientBase):
|
|
|
81
94
|
)
|
|
82
95
|
|
|
83
96
|
human_detection_requests.append(request)
|
|
97
|
+
valid_records.append(data)
|
|
98
|
+
|
|
99
|
+
if invalid_records:
|
|
100
|
+
logger.info(f"🧹 Deleting {len(invalid_records)} invalid violation records")
|
|
101
|
+
self.repository.delete_records_from_db(invalid_records)
|
|
102
|
+
|
|
103
|
+
if not human_detection_requests:
|
|
104
|
+
return {"success": True, "message": "No valid violations to send"}
|
|
84
105
|
|
|
85
106
|
batch_request = UpsertHumanDetectionBatchRequest(
|
|
86
107
|
human_detection_requests=human_detection_requests,
|
|
@@ -90,7 +111,7 @@ class RestrictedAreaClient(GrpcClientBase):
|
|
|
90
111
|
response = self.handle_rpc(self.stub.UpsertBatch, batch_request)
|
|
91
112
|
|
|
92
113
|
if response and response.success:
|
|
93
|
-
self.repository.delete_records_from_db(
|
|
114
|
+
self.repository.delete_records_from_db(valid_records)
|
|
94
115
|
return {"success": True, "message": response.message}
|
|
95
116
|
|
|
96
117
|
return {"success": False, "message": response.message if response else "Unknown error"}
|
|
@@ -118,14 +118,63 @@ class DataSenderWorker:
|
|
|
118
118
|
try:
|
|
119
119
|
while not self.stop_event.is_set():
|
|
120
120
|
self.system_usage_manager.process_system_usage()
|
|
121
|
-
|
|
122
|
-
self.
|
|
121
|
+
|
|
122
|
+
ppe_pending = self._send_with_adaptive_interval(
|
|
123
|
+
self.ppe_detection_manager.send_ppe_detection_batch,
|
|
124
|
+
self.ppe_detection_manager.ppe_detection_repo.get_total_pending_count
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
violation_pending = self._send_with_adaptive_interval(
|
|
128
|
+
self.restricted_area_manager.send_violation_batch,
|
|
129
|
+
self.restricted_area_manager.repo.get_total_pending_count
|
|
130
|
+
)
|
|
131
|
+
|
|
123
132
|
self._process_image_upload()
|
|
124
133
|
self._process_dataset_frames()
|
|
125
|
-
|
|
134
|
+
|
|
135
|
+
sleep_time = self._calculate_sleep_interval(ppe_pending, violation_pending)
|
|
136
|
+
if sleep_time < self.send_interval and (ppe_pending + violation_pending) > 0:
|
|
137
|
+
logger.debug(f"⚡ [APP] Catch-up mode: {ppe_pending + violation_pending} pending, sleeping {sleep_time}s")
|
|
138
|
+
time.sleep(sleep_time)
|
|
126
139
|
except Exception as e:
|
|
127
140
|
logger.error("🚨 [APP] Unexpected error in main worker loop.", exc_info=True)
|
|
128
141
|
|
|
142
|
+
def _send_with_adaptive_interval(self, send_func, count_func):
|
|
143
|
+
"""Send data with adaptive interval based on backlog."""
|
|
144
|
+
try:
|
|
145
|
+
pending = count_func()
|
|
146
|
+
if pending > 0:
|
|
147
|
+
send_func()
|
|
148
|
+
return pending
|
|
149
|
+
except Exception as e:
|
|
150
|
+
logger.error(f"🚨 [APP] Error in adaptive sending: {e}", exc_info=True)
|
|
151
|
+
return 0
|
|
152
|
+
|
|
153
|
+
def _calculate_sleep_interval(self, ppe_pending: int, violation_pending: int) -> float:
|
|
154
|
+
"""
|
|
155
|
+
Calculate optimal sleep interval based on pending data.
|
|
156
|
+
Conservative approach to prevent server overload.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
ppe_pending: Number of pending PPE detections
|
|
160
|
+
violation_pending: Number of pending violations
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
float: Sleep interval in seconds
|
|
164
|
+
"""
|
|
165
|
+
total_pending = ppe_pending + violation_pending
|
|
166
|
+
|
|
167
|
+
if total_pending == 0:
|
|
168
|
+
return self.send_interval
|
|
169
|
+
elif total_pending < 50:
|
|
170
|
+
return self.send_interval
|
|
171
|
+
elif total_pending < 200:
|
|
172
|
+
return 3.0
|
|
173
|
+
elif total_pending < 500:
|
|
174
|
+
return 2.0
|
|
175
|
+
else:
|
|
176
|
+
return 1.5
|
|
177
|
+
|
|
129
178
|
def _run_worker_source_updater(self):
|
|
130
179
|
"""Dedicated loop for updating worker sources at a different interval."""
|
|
131
180
|
try:
|
|
@@ -50,14 +50,42 @@ class PPEDetectionManager:
|
|
|
50
50
|
|
|
51
51
|
logger.info("📡 [APP] PPE detection monitoring started.")
|
|
52
52
|
|
|
53
|
+
def _calculate_batch_size(self, pending_count: int) -> int:
|
|
54
|
+
"""
|
|
55
|
+
Calculates optimal batch size based on pending items.
|
|
56
|
+
Conservative limits to prevent server overload.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
pending_count (int): Number of pending detections
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
int: Optimal batch size
|
|
63
|
+
"""
|
|
64
|
+
if pending_count < 20:
|
|
65
|
+
return 10
|
|
66
|
+
elif pending_count < 100:
|
|
67
|
+
return 30
|
|
68
|
+
elif pending_count < 500:
|
|
69
|
+
return 50
|
|
70
|
+
else:
|
|
71
|
+
return 100
|
|
53
72
|
|
|
54
73
|
def send_ppe_detection_batch(self):
|
|
55
|
-
"""Sends a batch of collected PPE detection data to the server."""
|
|
74
|
+
"""Sends a batch of collected PPE detection data to the server with dynamic batch sizing."""
|
|
56
75
|
try:
|
|
57
|
-
|
|
76
|
+
pending_count = self.ppe_detection_repo.get_total_pending_count()
|
|
77
|
+
|
|
78
|
+
if pending_count == 0:
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
batch_size = self._calculate_batch_size(pending_count)
|
|
82
|
+
self.ppe_detection_data = self.ppe_detection_repo.get_latest_detections(batch_size)
|
|
83
|
+
|
|
58
84
|
if not self.ppe_detection_data:
|
|
59
85
|
return
|
|
60
86
|
|
|
87
|
+
logger.info(f"📤 [APP] Sending {len(self.ppe_detection_data)} PPE detections ({pending_count} pending)")
|
|
88
|
+
|
|
61
89
|
response = self.ppe_detection_client.send_upsert_batch(
|
|
62
90
|
worker_id=self.worker_id,
|
|
63
91
|
worker_source_id=self.worker_source_id,
|
|
@@ -66,7 +94,7 @@ class PPEDetectionManager:
|
|
|
66
94
|
)
|
|
67
95
|
|
|
68
96
|
if response.get("success"):
|
|
69
|
-
logger.info("✅ [APP] Successfully sent PPE
|
|
97
|
+
logger.info(f"✅ [APP] Successfully sent {len(self.ppe_detection_data)} PPE detections")
|
|
70
98
|
self.ppe_detection_data.clear()
|
|
71
99
|
else:
|
|
72
100
|
logger.error(f"❌ [APP] Failed to send PPE detection batch: {response.get('message')}")
|
|
@@ -50,13 +50,42 @@ class RestrictedAreaManager:
|
|
|
50
50
|
|
|
51
51
|
logger.info("📡 [APP] Restricted area violation monitoring started.")
|
|
52
52
|
|
|
53
|
+
def _calculate_batch_size(self, pending_count: int) -> int:
|
|
54
|
+
"""
|
|
55
|
+
Calculates optimal batch size based on pending items.
|
|
56
|
+
Conservative limits to prevent server overload.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
pending_count (int): Number of pending violations
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
int: Optimal batch size
|
|
63
|
+
"""
|
|
64
|
+
if pending_count < 20:
|
|
65
|
+
return 10
|
|
66
|
+
elif pending_count < 100:
|
|
67
|
+
return 30
|
|
68
|
+
elif pending_count < 500:
|
|
69
|
+
return 50
|
|
70
|
+
else:
|
|
71
|
+
return 100
|
|
72
|
+
|
|
53
73
|
def send_violation_batch(self):
|
|
54
|
-
"""Sends a batch of collected violation data to the server."""
|
|
74
|
+
"""Sends a batch of collected violation data to the server with dynamic batch sizing."""
|
|
55
75
|
try:
|
|
56
|
-
|
|
76
|
+
pending_count = self.repo.get_total_pending_count()
|
|
77
|
+
|
|
78
|
+
if pending_count == 0:
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
batch_size = self._calculate_batch_size(pending_count)
|
|
82
|
+
self.violations_data = self.repo.get_latest_violations(batch_size)
|
|
83
|
+
|
|
57
84
|
if not self.violations_data:
|
|
58
85
|
return
|
|
59
86
|
|
|
87
|
+
logger.info(f"📤 [APP] Sending {len(self.violations_data)} violations ({pending_count} pending)")
|
|
88
|
+
|
|
60
89
|
response = self.client.send_upsert_batch(
|
|
61
90
|
worker_id=self.worker_id,
|
|
62
91
|
worker_source_id=self.worker_source_id,
|
|
@@ -65,7 +94,7 @@ class RestrictedAreaManager:
|
|
|
65
94
|
)
|
|
66
95
|
|
|
67
96
|
if response.get("success"):
|
|
68
|
-
logger.info("✅ [APP] Successfully sent
|
|
97
|
+
logger.info(f"✅ [APP] Successfully sent {len(self.violations_data)} violations")
|
|
69
98
|
self.violations_data.clear()
|
|
70
99
|
else:
|
|
71
100
|
logger.error(f"❌ [APP] Failed to send restricted area violation batch: {response.get('message')}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nedo-vision-worker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
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>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
nedo_vision_worker/__init__.py,sha256=
|
|
1
|
+
nedo_vision_worker/__init__.py,sha256=lM4SwuHDgc4TdI91GAS_FS--pQKK2RFKliCp8sJY1-o,203
|
|
2
2
|
nedo_vision_worker/cli.py,sha256=ddWspJmSgVkcUYvRdkvTtMNuMTDvNCqLLuMVU9KE3Ik,7457
|
|
3
3
|
nedo_vision_worker/doctor.py,sha256=wNkpe8gLVd76Y_ViyK2h1ZFdqeSl37MnzZN5frWKu30,48410
|
|
4
4
|
nedo_vision_worker/worker_service.py,sha256=9zz8hKwDwqwpfS0KPQfftGJtRci0uj_wiwcr_TGf-E0,11039
|
|
@@ -40,8 +40,8 @@ nedo_vision_worker/protos/WorkerSourceService_pb2_grpc.py,sha256=23Zj3WSogfhmp_Y
|
|
|
40
40
|
nedo_vision_worker/protos/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
41
41
|
nedo_vision_worker/repositories/AIModelRepository.py,sha256=35cQ2EnJvMdArewumPrVIaiwueGy6cXU_g5J1Qcj6_Y,1415
|
|
42
42
|
nedo_vision_worker/repositories/DatasetSourceRepository.py,sha256=MOUbXY1-7WUPhY15aMpdXl6rl0afSvu3Cq8jNtp9nzM,8595
|
|
43
|
-
nedo_vision_worker/repositories/PPEDetectionRepository.py,sha256=
|
|
44
|
-
nedo_vision_worker/repositories/RestrictedAreaRepository.py,sha256=
|
|
43
|
+
nedo_vision_worker/repositories/PPEDetectionRepository.py,sha256=WYPrmMs3wSsaALR4ixsgMNOUbHbmYDGPbjMQdjSnIE4,6161
|
|
44
|
+
nedo_vision_worker/repositories/RestrictedAreaRepository.py,sha256=nk8DV5dkBfAm2AT8wY9la6yv6Q7pFi00YXLbvofqNkc,5158
|
|
45
45
|
nedo_vision_worker/repositories/WorkerSourcePipelineDebugRepository.py,sha256=kOlVEnPOoDRZdZIm8uWXlc89GMvBPI-36QyKecX7ucE,3350
|
|
46
46
|
nedo_vision_worker/repositories/WorkerSourcePipelineDetectionRepository.py,sha256=cbgg_7p0eNUIgCHoPDZBaRZ1b2Y68p_dfSxpvuGMtRE,1773
|
|
47
47
|
nedo_vision_worker/repositories/WorkerSourcePipelineRepository.py,sha256=xfmEvgnyt-DdfSApGyFfy0H0dXjFFkjeo4LMr0fVFXU,10053
|
|
@@ -56,9 +56,9 @@ nedo_vision_worker/services/GrpcClientBase.py,sha256=hPyxOGw3aGSW1FhmY3wp3Iq8U1M
|
|
|
56
56
|
nedo_vision_worker/services/GrpcClientManager.py,sha256=DLXekmxlQogLo8V9-TNDXtyHT_UG-BaggqwsIups55k,5568
|
|
57
57
|
nedo_vision_worker/services/GrpcConnection.py,sha256=UNjaUC4ZcXuteHQx8AAAL5ymYkT1OpoIvyCYPUc3tCI,4915
|
|
58
58
|
nedo_vision_worker/services/ImageUploadClient.py,sha256=T353YsRfm74G7Mh-eWr5nvdQHXTfpKwHJFmNW8HyjT8,3019
|
|
59
|
-
nedo_vision_worker/services/PPEDetectionClient.py,sha256=
|
|
59
|
+
nedo_vision_worker/services/PPEDetectionClient.py,sha256=3oE_Y0Avw_kcWKgTA7qmChq32woW7JerWefrCxllUYU,5172
|
|
60
60
|
nedo_vision_worker/services/RTSPtoRTMPStreamer.py,sha256=LtfrWDHNcm-Ky6nZLnFCF8xgqIm7VQmsWIenK2yKNfo,3804
|
|
61
|
-
nedo_vision_worker/services/RestrictedAreaClient.py,sha256=
|
|
61
|
+
nedo_vision_worker/services/RestrictedAreaClient.py,sha256=TD2Y5UJ0NjXgNFNgwS5ze4P-5jWwgrP0j0Nlyyd7-6Q,4844
|
|
62
62
|
nedo_vision_worker/services/SharedDirectDeviceClient.py,sha256=dylMhqpMsfK_UKLWIVL-ApJRP4g-NCP_55xvlGYBiwo,10760
|
|
63
63
|
nedo_vision_worker/services/SharedVideoStreamServer.py,sha256=WMKVxkzMoyfbgYiJ0fQOT-Ujz9btz6FLlaDP738yfoY,11601
|
|
64
64
|
nedo_vision_worker/services/SystemUsageClient.py,sha256=Yf77dooQeNh6CDL5FkWVrX9543OVz1wc3exCAg6GlWw,3273
|
|
@@ -80,22 +80,22 @@ nedo_vision_worker/util/SystemMonitor.py,sha256=2kkqj9mOlywAS2fHdN1TaIXSXvCApcIH
|
|
|
80
80
|
nedo_vision_worker/util/VideoProbeUtil.py,sha256=cF-vJ7hIDlXfEJby2a0s9tqwkPGVz_6B3Vv4D5pMmIw,12876
|
|
81
81
|
nedo_vision_worker/util/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
82
82
|
nedo_vision_worker/worker/CoreActionWorker.py,sha256=lb7zPY3yui6I3F4rX4Ii7JwpWZahLEO72rh3iWOgFmg,5441
|
|
83
|
-
nedo_vision_worker/worker/DataSenderWorker.py,sha256=
|
|
83
|
+
nedo_vision_worker/worker/DataSenderWorker.py,sha256=BNA5mL2guBVp0O5MKULEWd78dY32YjiyWZX7ECFjVFc,9089
|
|
84
84
|
nedo_vision_worker/worker/DataSyncWorker.py,sha256=LmDPt2J1frmXwuR46L6b0MjlFOHfgG-4_0MGQa78zF4,6288
|
|
85
85
|
nedo_vision_worker/worker/DatasetFrameSender.py,sha256=1SFYj8LJFNi-anBTapsbq8U_NGMM7mnoMKg9NeFAHys,8087
|
|
86
86
|
nedo_vision_worker/worker/DatasetFrameWorker.py,sha256=Hh_wZuMjwovxsEKFqXSuTRin9eYRBZCbcFKm3CKLMbE,19335
|
|
87
|
-
nedo_vision_worker/worker/PPEDetectionManager.py,sha256=
|
|
87
|
+
nedo_vision_worker/worker/PPEDetectionManager.py,sha256=sO0ygzUKNNkUsrv-QJk9b-r9QnTw80VsniD5--OQJi0,4569
|
|
88
88
|
nedo_vision_worker/worker/PipelineActionWorker.py,sha256=xgvryjKtEsMj4BKqWzDIaK_lFny-DfMCj5Y2DxHnWww,5651
|
|
89
89
|
nedo_vision_worker/worker/PipelineImageWorker.py,sha256=J8VBUG0cwcH3qOJp2zTl30B-XhmPFyvJLjxitKJYq0E,5642
|
|
90
90
|
nedo_vision_worker/worker/PipelinePreviewWorker.py,sha256=owFiBbktcOZkdImQeykZSeBIR2-mpt6HNkmYIkLRKzE,6397
|
|
91
91
|
nedo_vision_worker/worker/RabbitMQListener.py,sha256=9gR49MDplgpyb-D5HOH0K77-DJQFvhS2E7biL92SjSU,6950
|
|
92
|
-
nedo_vision_worker/worker/RestrictedAreaManager.py,sha256=
|
|
92
|
+
nedo_vision_worker/worker/RestrictedAreaManager.py,sha256=Y739KzbPzm2UF1a9oT3YEk7trfKqQjGUQ6MTwEDMkj4,4463
|
|
93
93
|
nedo_vision_worker/worker/SystemUsageManager.py,sha256=mkh4sT-HkIEY1CJHMEG6LP9ATu39YXvLRLyf995OkoQ,5315
|
|
94
94
|
nedo_vision_worker/worker/VideoStreamWorker.py,sha256=5n6v1PNO7IB-jj_McALLkUP-cBjJoIEw4UiSAs3vTb0,7606
|
|
95
95
|
nedo_vision_worker/worker/WorkerManager.py,sha256=2bxXi19fp3p1qjYBStYRdVVgko8dnevXx1_M_sqH5og,5521
|
|
96
96
|
nedo_vision_worker/worker/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
97
|
-
nedo_vision_worker-1.
|
|
98
|
-
nedo_vision_worker-1.
|
|
99
|
-
nedo_vision_worker-1.
|
|
100
|
-
nedo_vision_worker-1.
|
|
101
|
-
nedo_vision_worker-1.
|
|
97
|
+
nedo_vision_worker-1.3.0.dist-info/METADATA,sha256=MiQg-EA4V0jEqMkrupbyhwLvIDD1UfTpQ_cub90gh8E,14728
|
|
98
|
+
nedo_vision_worker-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
99
|
+
nedo_vision_worker-1.3.0.dist-info/entry_points.txt,sha256=LrglS-8nCi8C_PL_pa6uxdgCe879hBETHDVXAckvs-8,60
|
|
100
|
+
nedo_vision_worker-1.3.0.dist-info/top_level.txt,sha256=vgilhlkyD34YsEKkaBabmhIpcKSvF3XpzD2By68L-XI,19
|
|
101
|
+
nedo_vision_worker-1.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|