Merge pull request #573 from ypomortsev/yegor

HFP: Fix reading multiple AT commands from a single data packet
This commit is contained in:
zxzxwu
2024-10-23 13:23:58 +08:00
committed by GitHub
2 changed files with 76 additions and 41 deletions

View File

@@ -795,29 +795,32 @@ class HfProtocol(pyee.EventEmitter):
# Append to the read buffer. # Append to the read buffer.
self.read_buffer.extend(data) self.read_buffer.extend(data)
# Locate header and trailer. while self.read_buffer:
header = self.read_buffer.find(b'\r\n') # Locate header and trailer.
trailer = self.read_buffer.find(b'\r\n', header + 2) header = self.read_buffer.find(b'\r\n')
if header == -1 or trailer == -1: trailer = self.read_buffer.find(b'\r\n', header + 2)
return if header == -1 or trailer == -1:
return
# Isolate the AT response code and parameters. # Isolate the AT response code and parameters.
raw_response = self.read_buffer[header + 2 : trailer] raw_response = self.read_buffer[header + 2 : trailer]
response = AtResponse.parse_from(raw_response) response = AtResponse.parse_from(raw_response)
logger.debug(f"<<< {raw_response.decode()}") logger.debug(f"<<< {raw_response.decode()}")
# Consume the response bytes. # Consume the response bytes.
self.read_buffer = self.read_buffer[trailer + 2 :] self.read_buffer = self.read_buffer[trailer + 2 :]
# Forward the received code to the correct queue. # Forward the received code to the correct queue.
if self.command_lock.locked() and ( if self.command_lock.locked() and (
response.code in STATUS_CODES or response.code in RESPONSE_CODES response.code in STATUS_CODES or response.code in RESPONSE_CODES
): ):
self.response_queue.put_nowait(response) self.response_queue.put_nowait(response)
elif response.code in UNSOLICITED_CODES: elif response.code in UNSOLICITED_CODES:
self.unsolicited_queue.put_nowait(response) self.unsolicited_queue.put_nowait(response)
else: else:
logger.warning(f"dropping unexpected response with code '{response.code}'") logger.warning(
f"dropping unexpected response with code '{response.code}'"
)
async def execute_command( async def execute_command(
self, self,
@@ -1244,31 +1247,32 @@ class AgProtocol(pyee.EventEmitter):
# Append to the read buffer. # Append to the read buffer.
self.read_buffer.extend(data) self.read_buffer.extend(data)
# Locate the trailer. while self.read_buffer:
trailer = self.read_buffer.find(b'\r') # Locate the trailer.
if trailer == -1: trailer = self.read_buffer.find(b'\r')
return if trailer == -1:
return
# Isolate the AT response code and parameters. # Isolate the AT response code and parameters.
raw_command = self.read_buffer[:trailer] raw_command = self.read_buffer[:trailer]
command = AtCommand.parse_from(raw_command) command = AtCommand.parse_from(raw_command)
logger.debug(f"<<< {raw_command.decode()}") logger.debug(f"<<< {raw_command.decode()}")
# Consume the response bytes. # Consume the response bytes.
self.read_buffer = self.read_buffer[trailer + 1 :] self.read_buffer = self.read_buffer[trailer + 1 :]
if command.sub_code == AtCommand.SubCode.TEST: if command.sub_code == AtCommand.SubCode.TEST:
handler_name = f'_on_{command.code.lower()}_test' handler_name = f'_on_{command.code.lower()}_test'
elif command.sub_code == AtCommand.SubCode.READ: elif command.sub_code == AtCommand.SubCode.READ:
handler_name = f'_on_{command.code.lower()}_read' handler_name = f'_on_{command.code.lower()}_read'
else: else:
handler_name = f'_on_{command.code.lower()}' handler_name = f'_on_{command.code.lower()}'
if handler := getattr(self, handler_name, None): if handler := getattr(self, handler_name, None):
handler(*command.parameters) handler(*command.parameters)
else: else:
logger.warning('Handler %s not found', handler_name) logger.warning('Handler %s not found', handler_name)
self.send_response('ERROR') self.send_response('ERROR')
def send_response(self, response: str) -> None: def send_response(self, response: str) -> None:
"""Sends an AT response.""" """Sends an AT response."""

View File

@@ -569,6 +569,37 @@ async def test_sco_setup():
await asyncio.gather(*sco_disconnection_futures) await asyncio.gather(*sco_disconnection_futures)
# -----------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_hf_batched_response(
hfp_connections: Tuple[hfp.HfProtocol, hfp.AgProtocol]
):
hf, ag = hfp_connections
ag.dlc.write(b'\r\n+BIND: (1,2)\r\n\r\nOK\r\n')
await hf.execute_command("AT+BIND=?", response_type=hfp.AtResponseType.SINGLE)
# -----------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_ag_batched_commands(
hfp_connections: Tuple[hfp.HfProtocol, hfp.AgProtocol]
):
hf, ag = hfp_connections
answer_future = asyncio.get_running_loop().create_future()
ag.on('answer', lambda: answer_future.set_result(None))
hang_up_future = asyncio.get_running_loop().create_future()
ag.on('hang_up', lambda: hang_up_future.set_result(None))
hf.dlc.write(b'ATA\rAT+CHUP\r')
await answer_future
await hang_up_future
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
async def run(): async def run():
await test_slc() await test_slc()