8 (i.e. those devices not specifically supported yet). |
8 (i.e. those devices not specifically supported yet). |
9 """ |
9 """ |
10 |
10 |
11 import os |
11 import os |
12 |
12 |
13 from eric7 import Preferences |
13 from PyQt6.QtCore import QUrl, pyqtSlot |
|
14 from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest |
|
15 from PyQt6.QtWidgets import QMenu |
|
16 |
|
17 from eric7 import Globals, Preferences |
14 from eric7.EricWidgets import EricMessageBox |
18 from eric7.EricWidgets import EricMessageBox |
|
19 from eric7.EricWidgets.EricApplication import ericApp |
15 from eric7.SystemUtilities import FileSystemUtilities |
20 from eric7.SystemUtilities import FileSystemUtilities |
16 |
21 |
|
22 from ..MicroPythonWidget import HAS_QTCHART |
|
23 from . import FirmwareGithubUrls |
17 from .DeviceBase import BaseDevice |
24 from .DeviceBase import BaseDevice |
18 from .MicroPythonWidget import HAS_QTCHART |
|
19 |
25 |
20 |
26 |
21 class GenericMicroPythonDevice(BaseDevice): |
27 class GenericMicroPythonDevice(BaseDevice): |
22 """ |
28 """ |
23 Class implementing the device interface for generic MicroPython boards. |
29 Class implementing the device interface for generic MicroPython boards. |
120 @return tuple containing a flag indicating it is safe to start a |
128 @return tuple containing a flag indicating it is safe to start a |
121 File Manager and a reason why it cannot. |
129 File Manager and a reason why it cannot. |
122 @rtype tuple of (bool, str) |
130 @rtype tuple of (bool, str) |
123 """ |
131 """ |
124 return True, "" |
132 return True, "" |
|
133 |
|
134 def __createGenericMenu(self): |
|
135 """ |
|
136 Private method to create the Generic submenu. |
|
137 """ |
|
138 self.__genericMenu = QMenu(self.tr("Generic Device Functions")) |
|
139 |
|
140 self.__showMpyAct = self.__genericMenu.addAction( |
|
141 self.tr("Show MicroPython Versions"), self.__showFirmwareVersions |
|
142 ) |
|
143 self.__genericMenu.addSeparator() |
|
144 self.__bootloaderAct = self.__genericMenu.addAction( |
|
145 self.tr("Activate Bootloader"), self.__activateBootloader |
|
146 ) |
|
147 self.__genericMenu.addSeparator() |
|
148 self.__resetAct = self.__genericMenu.addAction( |
|
149 self.tr("Reset Device"), self.__resetDevice |
|
150 ) |
|
151 |
|
152 def addDeviceMenuEntries(self, menu): |
|
153 """ |
|
154 Public method to add device specific entries to the given menu. |
|
155 |
|
156 @param menu reference to the context menu |
|
157 @type QMenu |
|
158 """ |
|
159 connected = self.microPython.isConnected() |
|
160 |
|
161 self.__showMpyAct.setEnabled(connected) |
|
162 self.__bootloaderAct.setEnabled(connected) |
|
163 self.__resetAct.setEnabled(connected) |
|
164 |
|
165 menu.addMenu(self.__genericMenu) |
125 |
166 |
126 def supportsLocalFileAccess(self): |
167 def supportsLocalFileAccess(self): |
127 """ |
168 """ |
128 Public method to indicate file access via a local directory. |
169 Public method to indicate file access via a local directory. |
129 |
170 |
200 ).format(self.__deviceVolumeName), |
241 ).format(self.__deviceVolumeName), |
201 ) |
242 ) |
202 |
243 |
203 return super().getWorkspace() |
244 return super().getWorkspace() |
204 |
245 |
|
246 @pyqtSlot() |
|
247 def __activateBootloader(self): |
|
248 """ |
|
249 Private slot to switch the board into 'bootloader' mode. |
|
250 """ |
|
251 if self.microPython.isConnected(): |
|
252 self.microPython.deviceInterface().execute( |
|
253 "import machine\nmachine.bootloader()\n", mode=self._submitMode |
|
254 ) |
|
255 # simulate pressing the disconnect button |
|
256 self.microPython.on_connectButton_clicked() |
|
257 |
|
258 @pyqtSlot() |
|
259 def __showFirmwareVersions(self): |
|
260 """ |
|
261 Private slot to show the firmware version of the connected device and the |
|
262 available firmware version. |
|
263 """ |
|
264 if self.microPython.isConnected(): |
|
265 if self._deviceData["mpy_name"] != "micropython": |
|
266 EricMessageBox.critical( |
|
267 None, |
|
268 self.tr("Show MicroPython Versions"), |
|
269 self.tr( |
|
270 """The firmware of the connected device cannot be""" |
|
271 """ determined or the board does not run MicroPython.""" |
|
272 """ Aborting...""" |
|
273 ), |
|
274 ) |
|
275 else: |
|
276 ui = ericApp().getObject("UserInterface") |
|
277 request = QNetworkRequest(QUrl(FirmwareGithubUrls["micropython"])) |
|
278 reply = ui.networkAccessManager().head(request) |
|
279 reply.finished.connect(lambda: self.__firmwareVersionResponse(reply)) |
|
280 |
|
281 @pyqtSlot(QNetworkReply) |
|
282 def __firmwareVersionResponse(self, reply): |
|
283 """ |
|
284 Private slot handling the response of the latest version request. |
|
285 |
|
286 @param reply reference to the reply object |
|
287 @type QNetworkReply |
|
288 """ |
|
289 latestUrl = reply.url().toString() |
|
290 tag = latestUrl.rsplit("/", 1)[-1] |
|
291 while tag and not tag[0].isdecimal(): |
|
292 # get rid of leading non-decimal characters |
|
293 tag = tag[1:] |
|
294 latestVersion = Globals.versionToTuple(tag) |
|
295 |
|
296 if self._deviceData["mpy_version"] == "unknown": |
|
297 currentVersionStr = self.tr("unknown") |
|
298 currentVersion = (0, 0, 0) |
|
299 else: |
|
300 currentVersionStr = ( |
|
301 self._deviceData["mpy_variant_version"] |
|
302 if bool(self._deviceData["mpy_variant_version"]) |
|
303 else self._deviceData["mpy_version"] |
|
304 ) |
|
305 currentVersion = Globals.versionToTuple(currentVersionStr) |
|
306 |
|
307 msg = self.tr( |
|
308 "<h4>MicroPython Version Information</h4>" |
|
309 "<table>" |
|
310 "<tr><td>Installed:</td><td>{0}</td></tr>" |
|
311 "<tr><td>Available:</td><td>{1}</td></tr>" |
|
312 "{2}" |
|
313 "</table>" |
|
314 ).format( |
|
315 currentVersionStr, |
|
316 tag, |
|
317 self.tr("<tr><td>Variant:</td><td>{0}</td></tr>").format( |
|
318 self._deviceData["mpy_variant"] |
|
319 ) |
|
320 if self._deviceData["mpy_variant"] |
|
321 else "", |
|
322 ) |
|
323 if currentVersion < latestVersion: |
|
324 msg += self.tr("<p><b>Update available!</b></p>") |
|
325 |
|
326 EricMessageBox.information( |
|
327 None, |
|
328 self.tr("MicroPython Version"), |
|
329 msg, |
|
330 ) |
|
331 |
|
332 @pyqtSlot() |
|
333 def __resetDevice(self): |
|
334 """ |
|
335 Private slot to reset the connected device. |
|
336 """ |
|
337 self.microPython.deviceInterface().execute( |
|
338 "import machine\nmachine.reset()\n", mode=self._submitMode |
|
339 ) |
|
340 # simulate pressing the disconnect button |
|
341 self.microPython.on_connectButton_clicked() |
|
342 |
|
343 def getDocumentationUrl(self): |
|
344 """ |
|
345 Public method to get the device documentation URL. |
|
346 |
|
347 @return documentation URL of the device |
|
348 @rtype str |
|
349 """ |
|
350 return Preferences.getMicroPython("MicroPythonDocuUrl") |
|
351 |
|
352 def getFirmwareUrl(self): |
|
353 """ |
|
354 Public method to get the device firmware download URL. |
|
355 |
|
356 @return firmware download URL of the device |
|
357 @rtype str |
|
358 """ |
|
359 return Preferences.getMicroPython("MicroPythonFirmwareUrl") |
|
360 |
|
361 ################################################################## |
|
362 ## time related methods below |
|
363 ################################################################## |
|
364 |
|
365 def _getSetTimeCode(self): |
|
366 """ |
|
367 Protected method to get the device code to set the time. |
|
368 |
|
369 Note: This method must be implemented in the various device specific |
|
370 subclasses. |
|
371 |
|
372 @return code to be executed on the connected device to set the time |
|
373 @rtype str |
|
374 """ |
|
375 # rtc_time[0] - year 4 digit |
|
376 # rtc_time[1] - month 1..12 |
|
377 # rtc_time[2] - day 1..31 |
|
378 # rtc_time[3] - weekday 1..7 1=Monday |
|
379 # rtc_time[4] - hour 0..23 |
|
380 # rtc_time[5] - minute 0..59 |
|
381 # rtc_time[6] - second 0..59 |
|
382 # rtc_time[7] - yearday 1..366 |
|
383 # rtc_time[8] - isdst 0, 1, or -1 |
|
384 # |
|
385 # https://docs.micropython.org/en/latest/library/machine.RTC.html#machine-rtc |
|
386 return """ |
|
387 def set_time(rtc_time): |
|
388 try: |
|
389 import machine |
|
390 rtc = machine.RTC() |
|
391 rtc.datetime(rtc_time[:7] + (0,)) |
|
392 except Exception: |
|
393 pass |
|
394 """ |
|
395 |
205 |
396 |
206 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber): |
397 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber): |
207 """ |
398 """ |
208 Function to instantiate a MicroPython device object. |
399 Function to instantiate a MicroPython device object. |
209 |
400 |