diff -r 1a09700229e7 -r 9854647c8c5c src/eric7/MicroPython/Devices/DeviceBase.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/MicroPython/Devices/DeviceBase.py Mon Feb 13 17:49:52 2023 +0100 @@ -0,0 +1,362 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 - 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing some utility functions and the MicroPythonDevice base +class. +""" + +import contextlib +import copy +import os + +from PyQt6.QtCore import QObject, pyqtSlot +from PyQt6.QtWidgets import QInputDialog + +from eric7 import Preferences +from eric7.EricWidgets import EricMessageBox +from eric7.EricWidgets.EricApplication import ericApp + + +class BaseDevice(QObject): + """ + Base class for the more specific MicroPython devices. + """ + + def __init__(self, microPythonWidget, deviceType, parent=None): + """ + Constructor + + @param microPythonWidget reference to the main MicroPython widget + @type MicroPythonWidget + @param deviceType device type assigned to this device interface + @type str + @param parent reference to the parent object + @type QObject + """ + super().__init__(parent) + + self._deviceType = deviceType + self.microPython = microPythonWidget + self._deviceData = {} # dictionary with essential device data + + def setConnected(self, connected): + """ + Public method to set the connection state. + + Note: This method can be overwritten to perform actions upon connect + or disconnect of the device. + + @param connected connection state + @type bool + """ + self._deviceData = {} + + if connected: + with contextlib.suppress(OSError): + self._deviceData = self.microPython.commandsInterface().getDeviceData() + + def getDeviceType(self): + """ + Public method to get the device type. + + @return type of the device + @rtype str + """ + return self._deviceType + + def getDeviceData(self): + """ + Public method to get a copy of the determined device data. + + @return dictionary containing the essential device data + @rtype dict + """ + return copy.deepcopy(self._deviceData) + + def checkDeviceData(self): + """ + Public method to check the validity of the device data determined during + connecting the device. + + @return flag indicating valid device data + @rtype bool + """ + if bool(self._deviceData): + return True + else: + EricMessageBox.critical( + None, + self.tr("Show MicroPython Versions"), + self.tr( + """<p>The device data is not available. Try to connect to the""" + """ device again. Aborting...</p>""" + ).format(self.getDeviceType()), + ) + return False + + def setButtons(self): + """ + Public method to enable the supported action buttons. + """ + self.microPython.setActionButtons( + open=False, save=False, run=False, repl=False, files=False, chart=False + ) + + def forceInterrupt(self): + """ + Public method to determine the need for an interrupt when opening the + serial connection. + + @return flag indicating an interrupt is needed + @rtype bool + """ + return True + + def deviceName(self): + """ + Public method to get the name of the device. + + @return name of the device + @rtype str + """ + return self.tr("Unsupported Device") + + def canStartRepl(self): + """ + Public method to determine, if a REPL can be started. + + @return tuple containing a flag indicating it is safe to start a REPL + and a reason why it cannot. + @rtype tuple of (bool, str) + """ + return False, self.tr("REPL is not supported by this device.") + + def setRepl(self, on): + """ + Public method to set the REPL status and dependent status. + + @param on flag indicating the active status + @type bool + """ + pass + + def canStartPlotter(self): + """ + Public method to determine, if a Plotter can be started. + + @return tuple containing a flag indicating it is safe to start a + Plotter and a reason why it cannot. + @rtype tuple of (bool, str) + """ + return False, self.tr("Plotter is not supported by this device.") + + def setPlotter(self, on): + """ + Public method to set the Plotter status and dependent status. + + @param on flag indicating the active status + @type bool + """ + pass + + def canRunScript(self): + """ + Public method to determine, if a script can be executed. + + @return tuple containing a flag indicating it is safe to start a + Plotter and a reason why it cannot. + @rtype tuple of (bool, str) + """ + return False, self.tr("Running scripts is not supported by this device.") + + def runScript(self, script): + """ + Public method to run the given Python script. + + @param script script to be executed + @type str + """ + pass + + def canStartFileManager(self): + """ + Public method to determine, if a File Manager can be started. + + @return tuple containing a flag indicating it is safe to start a + File Manager and a reason why it cannot. + @rtype tuple of (bool, str) + """ + return False, self.tr("File Manager is not supported by this device.") + + def setFileManager(self, on): + """ + Public method to set the File Manager status and dependent status. + + @param on flag indicating the active status + @type bool + """ + pass + + def supportsLocalFileAccess(self): + """ + Public method to indicate file access via a local directory. + + @return flag indicating file access via local directory + @rtype bool + """ + return False # default + + def getWorkspace(self): + """ + Public method to get the workspace directory. + + @return workspace directory used for saving files + @rtype str + """ + return ( + Preferences.getMicroPython("MpyWorkspace") + or Preferences.getMultiProject("Workspace") + or os.path.expanduser("~") + ) + + def selectDeviceDirectory(self, deviceDirectories): + """ + Public method to select the device directory from a list of detected + ones. + + @param deviceDirectories list of directories to select from + @type list of str + @return selected directory or an empty string + @rtype str + """ + deviceDirectory, ok = QInputDialog.getItem( + None, + self.tr("Select Device Directory"), + self.tr("Select the directory for the connected device:"), + [""] + deviceDirectories, + 0, + False, + ) + if ok: + return deviceDirectory + else: + # user cancelled + return "" + + def sendCommands(self, commandsList): + """ + Public method to send a list of commands to the device. + + @param commandsList list of commands to be sent to the device + @type list of str + """ + rawOn = [ # sequence of commands to enter raw mode + b"\x02", # Ctrl-B: exit raw repl (just in case) + b"\r\x03\x03\x03", # Ctrl-C three times: interrupt any running + # program + b"\r\x01", # Ctrl-A: enter raw REPL + ] + newLine = [ + b'print("\\n")\r', + ] + commands = [c.encode("utf-8)") + b"\r" for c in commandsList] + commands.append(b"\r") + commands.append(b"\x04") + rawOff = [b"\x02", b"\x02"] + commandSequence = rawOn + newLine + commands + rawOff + self.microPython.commandsInterface().executeAsync(commandSequence) + + @pyqtSlot() + def handleDataFlood(self): + """ + Public slot handling a data floof from the device. + """ + pass + + def addDeviceMenuEntries(self, menu): + """ + Public method to add device specific entries to the given menu. + + @param menu reference to the context menu + @type QMenu + """ + pass + + def hasFlashMenuEntry(self): + """ + Public method to check, if the device has its own flash menu entry. + + @return flag indicating a specific flash menu entry + @rtype bool + """ + return False + + def hasTimeCommands(self): + """ + Public method to check, if the device supports time commands. + + The default returns True. + + @return flag indicating support for time commands + @rtype bool + """ + return True + + def hasDocumentationUrl(self): + """ + Public method to check, if the device has a configured documentation + URL. + + @return flag indicating a configured documentation URL + @rtype bool + """ + return bool(self.getDocumentationUrl()) + + def getDocumentationUrl(self): + """ + Public method to get the device documentation URL. + + @return documentation URL of the device + @rtype str + """ + return "" + + def hasFirmwareUrl(self): + """ + Public method to check, if the device has a configured firmware + download URL. + + @return flag indicating a configured firmware download URL + @rtype bool + """ + return bool(self.getFirmwareUrl()) + + def getFirmwareUrl(self): + """ + Public method to get the device firmware download URL. + + @return firmware download URL of the device + @rtype str + """ + return "" + + def downloadFirmware(self): + """ + Public method to download the device firmware. + """ + url = self.getFirmwareUrl() + if url: + ericApp().getObject("UserInterface").launchHelpViewer(url) + + def getDownloadMenuEntries(self): + """ + Public method to retrieve the entries for the downloads menu. + + @return list of tuples with menu text and URL to be opened for each + entry + @rtype list of tuple of (str, str) + """ + return []