1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing the device interface class for generic MicroPython devices
8(i.e. those devices not specifically supported yet).
9"""
10
11import os
12
13from E5Gui import E5MessageBox
14
15from .MicroPythonDevices import MicroPythonDevice
16from .MicroPythonWidget import HAS_QTCHART
17
18import Preferences
19import Utilities
20
21
22class GenericMicroPythonDevice(MicroPythonDevice):
23    """
24    Class implementing the device interface for generic MicroPython boards.
25    """
26    def __init__(self, microPythonWidget, deviceType, vid, pid, parent=None):
27        """
28        Constructor
29
30        @param microPythonWidget reference to the main MicroPython widget
31        @type MicroPythonWidget
32        @param deviceType device type assigned to this device interface
33        @type str
34        @param vid vendor ID
35        @type int
36        @param pid product ID
37        @type int
38        @param parent reference to the parent object
39        @type QObject
40        """
41        super().__init__(
42            microPythonWidget, deviceType, parent)
43
44        self.__directAccess = False
45        self.__deviceVolumeName = ""
46        self.__workspace = ""
47        self.__deviceName = ""
48
49        for deviceData in Preferences.getMicroPython("ManualDevices"):
50            if (
51                deviceData["vid"] == vid and
52                deviceData["pid"] == pid
53            ):
54                self.__deviceVolumeName = deviceData["data_volume"]
55                self.__directAccess = bool(deviceData["data_volume"])
56                self.__deviceName = deviceData["description"]
57
58                if self.__directAccess:
59                    self.__workspace = self.__findWorkspace()
60
61    def setButtons(self):
62        """
63        Public method to enable the supported action buttons.
64        """
65        super().setButtons()
66        self.microPython.setActionButtons(
67            run=True, repl=True, files=True, chart=HAS_QTCHART)
68
69        if self.__directAccess and self.__deviceVolumeMounted():
70            self.microPython.setActionButtons(open=True, save=True)
71
72    def deviceName(self):
73        """
74        Public method to get the name of the device.
75
76        @return name of the device
77        @rtype str
78        """
79        return self.__deviceName
80
81    def canStartRepl(self):
82        """
83        Public method to determine, if a REPL can be started.
84
85        @return tuple containing a flag indicating it is safe to start a REPL
86            and a reason why it cannot.
87        @rtype tuple of (bool, str)
88        """
89        return True, ""
90
91    def canStartPlotter(self):
92        """
93        Public method to determine, if a Plotter can be started.
94
95        @return tuple containing a flag indicating it is safe to start a
96            Plotter and a reason why it cannot.
97        @rtype tuple of (bool, str)
98        """
99        return True, ""
100
101    def canRunScript(self):
102        """
103        Public method to determine, if a script can be executed.
104
105        @return tuple containing a flag indicating it is safe to start a
106            Plotter and a reason why it cannot.
107        @rtype tuple of (bool, str)
108        """
109        return True, ""
110
111    def runScript(self, script):
112        """
113        Public method to run the given Python script.
114
115        @param script script to be executed
116        @type str
117        """
118        pythonScript = script.split("\n")
119        self.sendCommands(pythonScript)
120
121    def canStartFileManager(self):
122        """
123        Public method to determine, if a File Manager can be started.
124
125        @return tuple containing a flag indicating it is safe to start a
126            File Manager and a reason why it cannot.
127        @rtype tuple of (bool, str)
128        """
129        return True, ""
130
131    def supportsLocalFileAccess(self):
132        """
133        Public method to indicate file access via a local directory.
134
135        @return flag indicating file access via local directory
136        @rtype bool
137        """
138        return self.__deviceVolumeMounted()
139
140    def __deviceVolumeMounted(self):
141        """
142        Private method to check, if the device volume is mounted.
143
144        @return flag indicated a mounted device
145        @rtype bool
146        """
147        if self.__workspace and not os.path.exists(self.__workspace):
148            self.__workspace = ""       # reset
149
150        return (
151            self.__directAccess and
152            self.__deviceVolumeName in self.getWorkspace(silent=True)
153        )
154
155    def getWorkspace(self, silent=False):
156        """
157        Public method to get the workspace directory.
158
159        @param silent flag indicating silent operations
160        @type bool
161        @return workspace directory used for saving files
162        @rtype str
163        """
164        if self.__directAccess:
165            if self.__workspace:
166                # return cached entry
167                return self.__workspace
168            else:
169                self.__workspace = self.__findWorkspace(silent=silent)
170                return self.__workspace
171        else:
172            return super().getWorkspace()
173
174    def __findWorkspace(self, silent=False):
175        """
176        Private method to find the workspace directory.
177
178        @param silent flag indicating silent operations
179        @type bool
180        @return workspace directory used for saving files
181        @rtype str
182        """
183        # Attempts to find the path on the filesystem that represents the
184        # plugged in board.
185        deviceDirectories = Utilities.findVolume(self.__deviceVolumeName,
186                                                 findAll=True)
187
188        if deviceDirectories:
189            if len(deviceDirectories) == 1:
190                return deviceDirectories[0]
191            else:
192                return self.selectDeviceDirectory(deviceDirectories)
193        else:
194            # return the default workspace and give the user a warning (unless
195            # silent mode is selected)
196            if not silent:
197                E5MessageBox.warning(
198                    self.microPython,
199                    self.tr("Workspace Directory"),
200                    self.tr("Python files for this generic board can be"
201                            " edited in place, if the device volume is locally"
202                            " available. A volume named '{0}' was not found."
203                            " In place editing will not be available."
204                            ).format(self.__deviceVolumeName)
205                )
206
207            return super().getWorkspace()
208