|
|
@ -77,6 +77,9 @@ class Uploader: |
|
|
|
self.last_resp: Optional[UploadResponse] = None |
|
|
|
self.last_resp: Optional[UploadResponse] = None |
|
|
|
self.last_exc: Optional[Tuple[Exception, str]] = None |
|
|
|
self.last_exc: Optional[Tuple[Exception, str]] = None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.immediate_size = 0 |
|
|
|
|
|
|
|
self.immediate_count = 0 |
|
|
|
|
|
|
|
|
|
|
|
# stats for last successfully uploaded file |
|
|
|
# stats for last successfully uploaded file |
|
|
|
self.last_time = 0.0 |
|
|
|
self.last_time = 0.0 |
|
|
|
self.last_speed = 0.0 |
|
|
|
self.last_speed = 0.0 |
|
|
@ -85,10 +88,18 @@ class Uploader: |
|
|
|
self.immediate_folders = ["crash/", "boot/"] |
|
|
|
self.immediate_folders = ["crash/", "boot/"] |
|
|
|
self.immediate_priority = {"qlog": 0, "qlog.bz2": 0, "qcamera.ts": 1} |
|
|
|
self.immediate_priority = {"qlog": 0, "qlog.bz2": 0, "qcamera.ts": 1} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_upload_sort(self, name: str) -> int: |
|
|
|
|
|
|
|
if name in self.immediate_priority: |
|
|
|
|
|
|
|
return self.immediate_priority[name] |
|
|
|
|
|
|
|
return 1000 |
|
|
|
|
|
|
|
|
|
|
|
def list_upload_files(self) -> Iterator[Tuple[str, str, str]]: |
|
|
|
def list_upload_files(self) -> Iterator[Tuple[str, str, str]]: |
|
|
|
if not os.path.isdir(self.root): |
|
|
|
if not os.path.isdir(self.root): |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.immediate_size = 0 |
|
|
|
|
|
|
|
self.immediate_count = 0 |
|
|
|
|
|
|
|
|
|
|
|
for logname in listdir_by_creation(self.root): |
|
|
|
for logname in listdir_by_creation(self.root): |
|
|
|
path = os.path.join(self.root, logname) |
|
|
|
path = os.path.join(self.root, logname) |
|
|
|
try: |
|
|
|
try: |
|
|
@ -99,7 +110,7 @@ class Uploader: |
|
|
|
if any(name.endswith(".lock") for name in names): |
|
|
|
if any(name.endswith(".lock") for name in names): |
|
|
|
continue |
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
for name in sorted(names, key=lambda n: self.immediate_priority.get(n, 1000)): |
|
|
|
for name in sorted(names, key=self.get_upload_sort): |
|
|
|
key = os.path.join(logname, name) |
|
|
|
key = os.path.join(logname, name) |
|
|
|
fn = os.path.join(path, name) |
|
|
|
fn = os.path.join(path, name) |
|
|
|
# skip files already uploaded |
|
|
|
# skip files already uploaded |
|
|
@ -111,6 +122,13 @@ class Uploader: |
|
|
|
if is_uploaded: |
|
|
|
if is_uploaded: |
|
|
|
continue |
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
|
|
if name in self.immediate_priority: |
|
|
|
|
|
|
|
self.immediate_count += 1 |
|
|
|
|
|
|
|
self.immediate_size += os.path.getsize(fn) |
|
|
|
|
|
|
|
except OSError: |
|
|
|
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
yield name, key, fn |
|
|
|
yield name, key, fn |
|
|
|
|
|
|
|
|
|
|
|
def next_file_to_upload(self) -> Optional[Tuple[str, str, str]]: |
|
|
|
def next_file_to_upload(self) -> Optional[Tuple[str, str, str]]: |
|
|
@ -209,25 +227,18 @@ class Uploader: |
|
|
|
|
|
|
|
|
|
|
|
return success |
|
|
|
return success |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_msg(self): |
|
|
|
def step(self, network_type: int, metered: bool) -> bool: |
|
|
|
msg = messaging.new_message("uploaderState", valid=True) |
|
|
|
d = self.next_file_to_upload() |
|
|
|
us = msg.uploaderState |
|
|
|
if d is None: |
|
|
|
us.immediateQueueSize = int(self.immediate_size / 1e6) |
|
|
|
return True |
|
|
|
us.immediateQueueCount = self.immediate_count |
|
|
|
|
|
|
|
us.lastTime = self.last_time |
|
|
|
name, key, fn = d |
|
|
|
us.lastSpeed = self.last_speed |
|
|
|
|
|
|
|
us.lastFilename = self.last_filename |
|
|
|
# qlogs and bootlogs need to be compressed before uploading |
|
|
|
return msg |
|
|
|
if key.endswith(('qlog', 'rlog')) or (key.startswith('boot/') and not key.endswith('.bz2')): |
|
|
|
|
|
|
|
key += ".bz2" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return self.upload(name, key, fn, network_type, metered) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(exit_event: Optional[threading.Event] = None) -> None: |
|
|
|
def uploader_fn(exit_event: threading.Event) -> None: |
|
|
|
if exit_event is None: |
|
|
|
|
|
|
|
exit_event = threading.Event() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
try: |
|
|
|
set_core_affinity([0, 1, 2, 3]) |
|
|
|
set_core_affinity([0, 1, 2, 3]) |
|
|
|
except Exception: |
|
|
|
except Exception: |
|
|
@ -246,6 +257,7 @@ def main(exit_event: Optional[threading.Event] = None) -> None: |
|
|
|
cloudlog.warning("NVME not mounted") |
|
|
|
cloudlog.warning("NVME not mounted") |
|
|
|
|
|
|
|
|
|
|
|
sm = messaging.SubMaster(['deviceState']) |
|
|
|
sm = messaging.SubMaster(['deviceState']) |
|
|
|
|
|
|
|
pm = messaging.PubMaster(['uploaderState']) |
|
|
|
uploader = Uploader(dongle_id, Paths.log_root()) |
|
|
|
uploader = Uploader(dongle_id, Paths.log_root()) |
|
|
|
|
|
|
|
|
|
|
|
backoff = 0.1 |
|
|
|
backoff = 0.1 |
|
|
@ -258,8 +270,19 @@ def main(exit_event: Optional[threading.Event] = None) -> None: |
|
|
|
time.sleep(60 if offroad else 5) |
|
|
|
time.sleep(60 if offroad else 5) |
|
|
|
continue |
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
success = uploader.step(sm['deviceState'].networkType.raw, sm['deviceState'].networkMetered) |
|
|
|
d = uploader.next_file_to_upload() |
|
|
|
|
|
|
|
if d is None: # Nothing to upload |
|
|
|
|
|
|
|
if allow_sleep: |
|
|
|
|
|
|
|
time.sleep(60 if offroad else 5) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
name, key, fn = d |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# qlogs and bootlogs need to be compressed before uploading |
|
|
|
|
|
|
|
if key.endswith(('qlog', 'rlog')) or (key.startswith('boot/') and not key.endswith('.bz2')): |
|
|
|
|
|
|
|
key += ".bz2" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
success = uploader.upload(name, key, fn, sm['deviceState'].networkType.raw, sm['deviceState'].networkMetered) |
|
|
|
if success: |
|
|
|
if success: |
|
|
|
backoff = 0.1 |
|
|
|
backoff = 0.1 |
|
|
|
elif allow_sleep: |
|
|
|
elif allow_sleep: |
|
|
@ -267,6 +290,12 @@ def main(exit_event: Optional[threading.Event] = None) -> None: |
|
|
|
time.sleep(backoff + random.uniform(0, backoff)) |
|
|
|
time.sleep(backoff + random.uniform(0, backoff)) |
|
|
|
backoff = min(backoff*2, 120) |
|
|
|
backoff = min(backoff*2, 120) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pm.send("uploaderState", uploader.get_msg()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> None: |
|
|
|
|
|
|
|
uploader_fn(threading.Event()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
if __name__ == "__main__": |
|
|
|
main() |
|
|
|
main() |
|
|
|