eric6/MicroPython/MicroPythonFileSystem.py

branch
micropython
changeset 7080
9a3adf033f90
parent 7077
3b7475b7a1ef
child 7081
ed510767c096
equal deleted inserted replaced
7079:d1564b590677 7080:9a3adf033f90
10 from __future__ import unicode_literals 10 from __future__ import unicode_literals
11 11
12 import ast 12 import ast
13 import time 13 import time
14 import stat 14 import stat
15 import os
15 16
16 from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread 17 from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread
17 18
18 from .MicroPythonSerialPort import MicroPythonSerialPort 19 from .MicroPythonSerialPort import MicroPythonSerialPort
19 20
32 <li>put: copy a file to the connected device</li> 33 <li>put: copy a file to the connected device</li>
33 <li>get: get a file from the connected device</li> 34 <li>get: get a file from the connected device</li>
34 <li>rm: remove a file from the connected device</li> 35 <li>rm: remove a file from the connected device</li>
35 <li>mkdir: create a new directory</li> 36 <li>mkdir: create a new directory</li>
36 <li>rmdir: remove an empty directory</li> 37 <li>rmdir: remove an empty directory</li>
38 <li>version: get version info about MicroPython</li>
37 </ul> 39 </ul>
38 """ 40 """
39 def __init__(self, parent=None): 41 def __init__(self, parent=None):
40 """ 42 """
41 Constructor 43 Constructor
301 ] 303 ]
302 out, err = self.__execute(commands) 304 out, err = self.__execute(commands)
303 if err: 305 if err:
304 raise IOError(self.__shortError(err)) 306 raise IOError(self.__shortError(err))
305 307
306 def put(self, hostFileName, deviceFileName): 308 def put(self, hostFileName, deviceFileName=None):
307 """ 309 """
308 Public method to copy a local file to the connected device. 310 Public method to copy a local file to the connected device.
309 311
310 @param hostFileName name of the file to be copied 312 @param hostFileName name of the file to be copied
311 @type str 313 @type str
313 @type str 315 @type str
314 @return flag indicating success 316 @return flag indicating success
315 @rtype bool 317 @rtype bool
316 @exception IOError raised to indicate an issue with the device 318 @exception IOError raised to indicate an issue with the device
317 """ 319 """
318 # TODO: not implemented yet 320 if not os.path.isfile(hostFileName):
319 321 raise IOError("No such file: {0}".format(hostFileName))
320 def get(self, deviceFileName, hostFileName): 322
323 with open(hostFileName, "rb") as hostFile:
324 content = hostFile.read()
325
326 if not deviceFileName:
327 deviceFileName = os.path.basename(hostFileName)
328
329 commands = [
330 "fd = open('{0}', 'wb')".format(deviceFileName),
331 "f = fd.write",
332 ]
333 while content:
334 chunk = content[:64]
335 commands.append("f(" + repr(chunk) + ")")
336 content = content[64:]
337 commands.append("fd.close()")
338
339 out, err = self.__execute(commands)
340 if err:
341 raise IOError(self.__shortError(err))
342 return True
343
344 def get(self, deviceFileName, hostFileName=None):
321 """ 345 """
322 Public method to copy a file from the connected device. 346 Public method to copy a file from the connected device.
323 347
324 @param deviceFileName name of the file to copy 348 @param deviceFileName name of the file to copy
325 @type str 349 @type str
327 @type str 351 @type str
328 @return flag indicating success 352 @return flag indicating success
329 @rtype bool 353 @rtype bool
330 @exception IOError raised to indicate an issue with the device 354 @exception IOError raised to indicate an issue with the device
331 """ 355 """
332 # TODO: not implemented yet 356 if not hostFileName:
357 hostFileName = deviceFileName
358
359 commands = [
360 "\n".join([
361 "try:",
362 " from microbit import uart as u",
363 "except ImportError:",
364 " try:",
365 " from machine import UART",
366 " u = UART(0, {0})".format(115200),
367 " except Exception:",
368 " try:",
369 " from sys import stdout as u",
370 " except Exception:",
371 " raise Exception('Could not find UART module in"
372 " device.')",
373 ]),
374 "f = open('{0}', 'rb')".format(deviceFileName),
375 "r = f.read",
376 "result = True",
377 "\n".join([
378 "while result:",
379 " result = r(32)"
380 " if result:",
381 " u.write(result)",
382 ]),
383 "f.close()",
384 ]
385 out, err = self.__execute(commands)
386 if err:
387 raise IOError(self.__shortError(err))
388
389 # write the received bytes to the local file
390 with open(hostFileName, "wb") as hostFile:
391 hostFile.write(out)
392 return True
393
394 # TODO: add rsync function
395
396 def version(self):
397 """
398 Public method to get the MicroPython version information of the
399 connected device.
400
401 @return dictionary containing the version information
402 @rtype dict
403 @exception ValueError raised to indicate that the device might not be
404 running MicroPython or there was an issue parsing the output
405 """
406 commands = [
407 "import os",
408 "print(os.uname())",
409 ]
410 try:
411 out, err = self.__execute(commands)
412 if err:
413 raise ValueError(self.__shortError(err))
414 except ValueError:
415 # just re-raise it
416 raise
417 except Exception:
418 # Raise a value error to indicate being unable to find something
419 # on the device that will return parseable information about the
420 # version. It doesn't matter what the error is, it just needs to
421 # report a failure with the expected ValueError exception.
422 raise ValueError("Unable to determine version information.")
423
424 rawOutput = out.decode("utf-8").strip()
425 rawOutput = rawOutput[1:-1]
426 items = rawOutput.split(",")
427 result = {}
428 for item in items:
429 key, value = item.strip().split("=")
430 result[key.strip()] = value.strip()[1:-1]
431 return result
432
433 def syncTime(self):
434 """
435 Public method to set the time of the connected device to the local
436 computer's time.
437
438 @exception IOError raised to indicate an issue with the device
439 """
440 now = time.localtime(time.time())
441 commands = [
442 "\n".join([
443 "def set_time(rtc_time):",
444 " rtc = None",
445 " try:", # Pyboard (it doesn't have machine.RTC())
446 " import pyb",
447 " rtc = pyb.RTC()",
448 " rtc.datetime(rtc_time)",
449 " except:",
450 " try:",
451 " import machine",
452 " rtc = machine.RTC()",
453 " try:", # ESP8266 uses rtc.datetime()
454 " rtc.datetime(rtc_time)",
455 " except:", # ESP32 uses rtc.init()
456 " rtc.init(rtc_time)",
457 " except:",
458 " pass",
459 ]),
460 "set_time({0})".format((now.tm_year, now.tm_mon, now.tm_mday,
461 now.tm_wday + 1, now.tm_hour, now.tm_min,
462 now.tm_sec, 0))
463 ]
464 out, err = self.__execute(commands)
465 if err:
466 raise IOError(self.__shortError(err))
333 467
334 468
335 class MicroPythonFileManager(QObject): 469 class MicroPythonFileManager(QObject):
336 """ 470 """
337 Class implementing an interface to the device file system commands with 471 Class implementing an interface to the device file system commands with
339 473
340 @signal longListFiles(result) emitted with a tuple of tuples containing the 474 @signal longListFiles(result) emitted with a tuple of tuples containing the
341 name, mode, size and time for each directory entry 475 name, mode, size and time for each directory entry
342 @signal currentDir(dirname) emitted to report the current directory of the 476 @signal currentDir(dirname) emitted to report the current directory of the
343 device 477 device
478 @signal getFileDone(deviceFile, localFile) emitted after the file was
479 fetched from the connected device and written to the local file system
480 @signal putFileDone(localFile, deviceFile) emitted after the file was
481 copied to the connected device
482 @signal deleteFileDone(deviceFile) emitted after the file has been deleted
483 on the connected device
344 484
345 @signal longListFilesFailed(exc) emitted with a failure message to indicate 485 @signal longListFilesFailed(exc) emitted with a failure message to indicate
346 a failed long listing operation 486 a failed long listing operation
347 @signal currentDirFailed(exc) emitted with a failure message to indicate 487 @signal currentDirFailed(exc) emitted with a failure message to indicate
348 that the current directory is not available 488 that the current directory is not available
489 @signal getFileFailed(exc) emitted with a failure message to indicate that
490 the file could not be fetched
491 @signal putFileFailed(exc) emitted with a failure message to indicate that
492 the file could not be copied
493 @signal deleteFileFailed(exc) emitted with a failure message to indicate
494 that the file could not be deleted on the device
349 """ 495 """
350 longListFiles = pyqtSignal(tuple) 496 longListFiles = pyqtSignal(tuple)
351 currentDir = pyqtSignal(str) 497 currentDir = pyqtSignal(str)
498 getFileDone = pyqtSignal(str, str)
499 putFileDone = pyqtSignal(str, str)
500 deleteFileDone = pyqtSignal(str)
352 501
353 longListFilesFailed = pyqtSignal(str) 502 longListFilesFailed = pyqtSignal(str)
354 currentDirFailed = pyqtSignal(str) 503 currentDirFailed = pyqtSignal(str)
504 getFileFailed = pyqtSignal(str)
505 putFileFailed = pyqtSignal(str)
506 deleteFileFailed = pyqtSignal(str)
355 507
356 def __init__(self, port, parent=None): 508 def __init__(self, port, parent=None):
357 """ 509 """
358 Constructor 510 Constructor
359 511
384 self.__serial.closeSerialLink() 536 self.__serial.closeSerialLink()
385 537
386 @pyqtSlot(str) 538 @pyqtSlot(str)
387 def lls(self, dirname): 539 def lls(self, dirname):
388 """ 540 """
389 Public method to get a long listing of the given directory. 541 Public slot to get a long listing of the given directory.
390 542
391 @param dirname name of the directory to list 543 @param dirname name of the directory to list
392 @type str 544 @type str
393 """ 545 """
394 try: 546 try:
403 self.longListFilesFailed.emit(str(exc)) 555 self.longListFilesFailed.emit(str(exc))
404 556
405 @pyqtSlot() 557 @pyqtSlot()
406 def pwd(self): 558 def pwd(self):
407 """ 559 """
408 Public method to get the current directory of the device. 560 Public slot to get the current directory of the device.
409 """ 561 """
410 try: 562 try:
411 pwd = self.__fs.pwd() 563 pwd = self.__fs.pwd()
412 self.currentDir.emit(pwd) 564 self.currentDir.emit(pwd)
413 except Exception as exc: 565 except Exception as exc:
414 self.currentDirFailed.emit(str(exc)) 566 self.currentDirFailed.emit(str(exc))
567
568 @pyqtSlot(str)
569 @pyqtSlot(str, str)
570 def get(self, deviceFileName, hostFileName=""):
571 """
572 Public slot to get a file from the connected device.
573
574 @param deviceFileName name of the file on the device
575 @type str
576 @param hostFileName name of the local file
577 @type str
578 """
579 if hostFileName and os.path.isdir(hostFileName):
580 # only a local directory was given
581 hostFileName = os.path.join(hostFileName,
582 os.path.basename(deviceFileName))
583 try:
584 self.__fs.get(deviceFileName, hostFileName)
585 self.getFileDone.emit(deviceFileName, hostFileName)
586 except Exception as exc:
587 self.getFileFailed.emit(str(exc))
588
589 @pyqtSlot(str)
590 @pyqtSlot(str, str)
591 def put(self, hostFileName, deviceFileName=""):
592 """
593 Public slot to put a file onto the device.
594
595 @param hostFileName name of the local file
596 @type str
597 @param deviceFileName name of the file on the connected device
598 @type str
599 """
600 try:
601 self.__fs.put(hostFileName, deviceFileName)
602 self.putFileDone(hostFileName, deviceFileName)
603 except Exception as exc:
604 self.putFileFailed(str(exc))
605
606 @pyqtSlot(str)
607 def delete(self, deviceFileName):
608 """
609 Public slot to delete a file on the device.
610
611 @param deviceFileName name of the file on the connected device
612 @type str
613 """
614 try:
615 self.__fs.rm(deviceFileName)
616 self.deleteFileDone.emit(deviceFileName)
617 except Exception as exc:
618 self.deleteFileFailed(str(exc))
415 619
416 ################################################################## 620 ##################################################################
417 ## Utility methods below 621 ## Utility methods below
418 ################################################################## 622 ##################################################################
419 623
440 @rtype str 644 @rtype str
441 """ 645 """
442 return stat.filemode(mode) 646 return stat.filemode(mode)
443 647
444 648
445 def decoratedName(name, mode): 649 def decoratedName(name, mode, isDir=False):
446 """ 650 """
447 Function to decorate the given name according to the given mode. 651 Function to decorate the given name according to the given mode.
448 652
449 @param name file or directory name 653 @param name file or directory name
450 @type str 654 @type str
451 @param mode mode value 655 @param mode mode value
452 @type int 656 @type int
657 @param isDir flag indicating that name is a directory
658 @type bool
453 @return decorated file or directory name 659 @return decorated file or directory name
454 @rtype str 660 @rtype str
455 """ 661 """
456 if stat.S_ISDIR(mode): 662 if stat.S_ISDIR(mode) or isDir:
457 # append a '/' for directories 663 # append a '/' for directories
458 return name + "/" 664 return name + "/"
459 else: 665 else:
460 # no change 666 # no change
461 return name 667 return name

eric ide

mercurial