forked from platypush/platypush
- 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:
parent
4b56431e2a
commit
b484fcf9ed
2 changed files with 33 additions and 20 deletions
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue