from asyncio import Future, StreamReader, StreamWriter, Task, shield import asyncio import functools import operator import struct from typing import Awaitable, Dict, Optional, Self from dataclasses import dataclass MSP_MAGIC_OUT = b"$R<" MSP_MAGIC_IN = b"$R>" MSP_CODE_POLL = 3 def make_msp(code: int, data: bytes = b'') -> bytes: ret = bytearray(MSP_MAGIC_OUT) if len(data) > 255: raise ValueError() ret.append(len(data)) ret.append(code) ret += data checksum = functools.reduce(operator.xor, map(int, ret[len(MSP_MAGIC_OUT):])) ret.append(checksum) return bytes(ret) async def read_msp(r: StreamReader) -> tuple[int, bytes]: magic_pos = 0 while magic_pos < len(MSP_MAGIC_IN): b, = await r.readexactly(1) if b == MSP_MAGIC_IN[magic_pos]: magic_pos += 1 else: magic_pos = 0 print('Unexpected byte from RCBenchmark serial') size, = await r.readexactly(1) code, = await r.readexactly(1) data = await r.readexactly(size) checksum, = await r.readexactly(1) _ = checksum return code, data def make_poll(esc_pwm: int) -> bytes: data = struct.pack('HHHH', esc_pwm, 0, 0, 0) # return make_msp(MSP_CODE_POLL, data) return data # esc_voltage, esc_current, esc_power, load_thrust, load_left, rot_e, rot_o, temp0, temp1, temp2, basic_data_flag, acc_x, acc_y, acc_z, vibration, raw_pressure_p, raw_pressure_t, load_right, pro_data_flag = struct.unpack_from(' Self: unpacked = struct.unpack_from(' Awaitable[bytes]: if code in self.pending_requests: raise RuntimeError('Duplicate request') f = Future() def future_done(_): del self.pending_requests[code] self.pending_requests[code] = f f.add_done_callback(future_done) self.w.write(make_msp(code, data)) return shield(f) async def do_poll(self, esc_pwm: int) -> PollResponse: resp = await self.do_request(MSP_CODE_POLL, make_poll(esc_pwm)) return PollResponse.from_bytes(resp)