- New implementation of esp.file_get that also works with binary files

- New communication protocol for downloaded chunks based on queues
  instead of events.
This commit is contained in:
Fabio Manganiello 2020-01-20 15:39:23 +01:00
parent 4b56431e2a
commit b484fcf9ed
2 changed files with 33 additions and 20 deletions

View file

@ -1,4 +1,5 @@
import base64 import base64
import io
import os import os
import threading import threading
@ -1532,22 +1533,31 @@ print([b for b in os.urandom({size})])
return self.execute(code, **kwargs).output return self.execute(code, **kwargs).output
@action @action
def file_get(self, file: str, **kwargs) -> str: def file_get(self, file: str, binary: bool = False, timeout: Optional[float] = 60.0, **kwargs) -> str:
""" """
Get the content of a file on the board. Get the content of a file on the board.
NOTE: It only works with non-binary files.
:param file: File name/path to get from the device. :param file: File name/path to get from the device.
:param kwargs: Parameters to pass to :meth:`platypush.plugins.esp.EspPlugin.execute`. :param binary: If True, then the base64-encoded content of the file will be returned.
:param timeout: File transfer timeout (default: one minute).
:param kwargs: Parameters to pass to :meth:`platypush.plugins.esp.EspPlugin.connect`.
""" """
file = self.string_quote(file) device = self._get_device(**kwargs)
code = ''' host = device['host']
import os port = device['port']
with open('{file}', 'r') as f: self.connect(host=host, port=port, password=device['password'])
print(f.read()) conn = self._get_connection(host=host, port=port)
'''.format(file=file)
return self.execute(code, **kwargs).output with io.BytesIO() as buffer:
conn.file_download(file, buffer, timeout=timeout)
data = buffer.getvalue()
if binary:
data = base64.encodebytes(data).decode()
else:
data = data.decode()
return data
@action @action
def file_upload(self, source: str, destination: Optional[str] = None, timeout: Optional[float] = 60.0, **kwargs): def file_upload(self, source: str, destination: Optional[str] = None, timeout: Optional[float] = 60.0, **kwargs):

View file

@ -1,5 +1,6 @@
import enum import enum
import logging import logging
import queue
import os import os
import re import re
import threading import threading
@ -50,7 +51,7 @@ class Connection:
self._file_transfer_ack_received = threading.Event() self._file_transfer_ack_received = threading.Event()
self._file_transfer_request_successful = True self._file_transfer_request_successful = True
self._file_transfer_successful = True self._file_transfer_successful = True
self._downloaded_chunk = None self._downloaded_chunks = queue.Queue()
self._password_requested = False self._password_requested = False
self._running_cmd = None self._running_cmd = None
self._received_echo = None self._received_echo = None
@ -203,7 +204,7 @@ class Connection:
def on_file_download_start(self): def on_file_download_start(self):
self.logger.info('Starting file download') self.logger.info('Starting file download')
self._file_transfer_successful = False self._file_transfer_successful = False
self._downloaded_chunk = None self._downloaded_chunks = queue.Queue()
self.state = self.State.DOWNLOADING_FILE self.state = self.State.DOWNLOADING_FILE
self._file_transfer_ack_received.clear() self._file_transfer_ack_received.clear()
self.ws.send(b'\x00', opcode=websocket.ABNF.OPCODE_BINARY) self.ws.send(b'\x00', opcode=websocket.ABNF.OPCODE_BINARY)
@ -218,10 +219,10 @@ class Connection:
if size == 0: if size == 0:
# End of file # End of file
self.logger.info('File download completed') self.logger.info('File download completed')
self._downloaded_chunk = None self._downloaded_chunks.put(None)
self.on_file_download_completed() self.on_file_download_completed()
else: else:
self._downloaded_chunk = data self._downloaded_chunks.put(data)
self.ws.send(b'\x00', opcode=websocket.ABNF.OPCODE_BINARY) self.ws.send(b'\x00', opcode=websocket.ABNF.OPCODE_BINARY)
self._download_chunk_ready.set() self._download_chunk_ready.set()
@ -237,15 +238,17 @@ class Connection:
self.state = self.State.SENDING_FILE_TRANSFER_RESPONSE self.state = self.State.SENDING_FILE_TRANSFER_RESPONSE
def get_downloaded_chunks(self, timeout: Optional[float] = None): def get_downloaded_chunks(self, timeout: Optional[float] = None):
block_ok = self._download_chunk_ready.wait(timeout=timeout) while True:
try:
while self._downloaded_chunk and block_ok: chunk = self._downloaded_chunks.get(timeout=timeout)
yield self._downloaded_chunk except queue.Empty:
block_ok = self._download_chunk_ready.wait(timeout=timeout)
if not block_ok:
self.on_timeout('File download timed out') self.on_timeout('File download timed out')
if chunk is None:
break
yield chunk
def wait_ready(self): def wait_ready(self):
connected = self._connected.wait(timeout=self.connect_timeout) connected = self._connected.wait(timeout=self.connect_timeout)
if not connected: if not connected: