|
51 | 51 |
|
52 | 52 |
|
53 | 53 | class _LoggingThread(threading.Thread): |
54 | | - def __init__(self, pipe, log_fn): |
| 54 | + def __init__(self, pipe, logger): |
55 | 55 | super().__init__() |
56 | 56 | self.pipe = pipe |
57 | | - self.log_fn = log_fn |
| 57 | + self.logger = logger |
58 | 58 |
|
59 | 59 | def run(self): |
60 | | - for line in self.pipe: |
61 | | - self.log_fn(line.decode("utf-8").replace("\n", "")) |
| 60 | + try: |
| 61 | + for line in self.pipe: |
| 62 | + if not line: # EOF reached |
| 63 | + break |
| 64 | + |
| 65 | + try: |
| 66 | + message = line.decode("utf-8").replace("\n", "") |
| 67 | + |
| 68 | + # Skip empty lines |
| 69 | + if not message.strip(): |
| 70 | + continue |
| 71 | + |
| 72 | + # Strip ANSI color codes used by MAVSDK |
| 73 | + # MAVSDK uses: \x1b[31m, \x1b[32m, \x1b[33m, \x1b[34m, \x1b[37m, \x1b[0m |
| 74 | + # Also handle \033[ variant (equivalent to \x1b[) |
| 75 | + for escape_seq in ['\x1b[', '\033[']: |
| 76 | + while escape_seq in message: |
| 77 | + start = message.find(escape_seq) |
| 78 | + if start == -1: |
| 79 | + break |
| 80 | + end = message.find('m', start) |
| 81 | + if end == -1: |
| 82 | + break |
| 83 | + message = message[:start] + message[end + 1:] |
| 84 | + |
| 85 | + # Parse mavsdk_server log level prefixes and map to Python logging levels |
| 86 | + # Format is: [timestamp|Level] message (filename:line) |
| 87 | + if "|Error] " in message: |
| 88 | + idx = message.find("|Error] ") + 8 |
| 89 | + self.logger.error(message[idx:].strip()) |
| 90 | + elif "|Warn ] " in message: |
| 91 | + idx = message.find("|Warn ] ") + 8 |
| 92 | + self.logger.warning(message[idx:].strip()) |
| 93 | + elif "|Info ] " in message: |
| 94 | + idx = message.find("|Info ] ") + 8 |
| 95 | + self.logger.info(message[idx:].strip()) |
| 96 | + elif "|Debug] " in message: |
| 97 | + idx = message.find("|Debug] ") + 8 |
| 98 | + self.logger.debug(message[idx:].strip()) |
| 99 | + else: |
| 100 | + # Default to debug for unprefixed messages |
| 101 | + self.logger.debug(message) |
| 102 | + except UnicodeDecodeError: |
| 103 | + # Skip lines that can't be decoded |
| 104 | + continue |
| 105 | + except (BrokenPipeError, OSError): |
| 106 | + # Subprocess has terminated, exit gracefully |
| 107 | + pass |
| 108 | + finally: |
| 109 | + # Ensure pipe is closed |
| 110 | + if self.pipe and not self.pipe.closed: |
| 111 | + self.pipe.close() |
62 | 112 |
|
63 | 113 | class System: |
64 | 114 | """ |
@@ -120,7 +170,7 @@ async def connect(self, system_address=None): |
120 | 170 |
|
121 | 171 | # add a delay to be sure resources have been freed and restart mavsdk_server |
122 | 172 | await asyncio.sleep(1) |
123 | | - |
| 173 | + |
124 | 174 |
|
125 | 175 | if self._mavsdk_server_address is None: |
126 | 176 | self._mavsdk_server_address = 'localhost' |
@@ -456,13 +506,14 @@ def _start_mavsdk_server(system_address, port, sysid, compid): |
456 | 506 | "--compid", str(compid)] |
457 | 507 | if system_address: |
458 | 508 | bin_path_and_args.append(system_address) |
| 509 | + |
459 | 510 | p = subprocess.Popen(bin_path_and_args, |
460 | 511 | shell=False, |
461 | 512 | stdout=subprocess.PIPE, |
462 | 513 | stderr=subprocess.STDOUT) |
463 | 514 |
|
464 | | - logger = logging.getLogger(__name__) |
465 | | - log_thread = _LoggingThread(p.stdout, logger.debug) |
| 515 | + logger = logging.getLogger('mavsdk_server') |
| 516 | + log_thread = _LoggingThread(p.stdout, logger) |
466 | 517 | log_thread.start() |
467 | 518 | except FileNotFoundError: |
468 | 519 | print(""" |
|
0 commit comments