src/eric7/MicroPython/MicroPythonCommandsInterface.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
10 import ast 10 import ast
11 import time 11 import time
12 import os 12 import os
13 13
14 from PyQt6.QtCore import ( 14 from PyQt6.QtCore import (
15 pyqtSlot, pyqtSignal, QObject, QThread, QTimer, QCoreApplication, 15 pyqtSlot,
16 QEventLoop 16 pyqtSignal,
17 QObject,
18 QThread,
19 QTimer,
20 QCoreApplication,
21 QEventLoop,
17 ) 22 )
18 23
19 from .MicroPythonSerialPort import MicroPythonSerialPort 24 from .MicroPythonSerialPort import MicroPythonSerialPort
20 25
21 import Preferences 26 import Preferences
22 27
23 28
24 class MicroPythonCommandsInterface(QObject): 29 class MicroPythonCommandsInterface(QObject):
25 """ 30 """
26 Class implementing some file system commands for MicroPython. 31 Class implementing some file system commands for MicroPython.
27 32
28 Commands are provided to perform operations on the file system of a 33 Commands are provided to perform operations on the file system of a
29 connected MicroPython device. Supported commands are: 34 connected MicroPython device. Supported commands are:
30 <ul> 35 <ul>
31 <li>ls: directory listing</li> 36 <li>ls: directory listing</li>
32 <li>lls: directory listing with meta data</li> 37 <li>lls: directory listing with meta data</li>
37 <li>rm: remove a file from the connected device</li> 42 <li>rm: remove a file from the connected device</li>
38 <li>rmrf: remove a file/directory recursively (like 'rm -rf' in bash) 43 <li>rmrf: remove a file/directory recursively (like 'rm -rf' in bash)
39 <li>mkdir: create a new directory</li> 44 <li>mkdir: create a new directory</li>
40 <li>rmdir: remove an empty directory</li> 45 <li>rmdir: remove an empty directory</li>
41 </ul> 46 </ul>
42 47
43 There are additional commands related to time and version. 48 There are additional commands related to time and version.
44 <ul> 49 <ul>
45 <li>version: get version info about MicroPython</li> 50 <li>version: get version info about MicroPython</li>
46 <li>getImplementation: get some implementation information</li> 51 <li>getImplementation: get some implementation information</li>
47 <li>syncTime: synchronize the time of the connected device</li> 52 <li>syncTime: synchronize the time of the connected device</li>
48 <li>showTime: show the current time of the connected device</li> 53 <li>showTime: show the current time of the connected device</li>
49 </ul> 54 </ul>
50 55
51 @signal executeAsyncFinished() emitted to indicate the end of an 56 @signal executeAsyncFinished() emitted to indicate the end of an
52 asynchronously executed list of commands (e.g. a script) 57 asynchronously executed list of commands (e.g. a script)
53 @signal dataReceived(data) emitted to send data received via the serial 58 @signal dataReceived(data) emitted to send data received via the serial
54 connection for further processing 59 connection for further processing
55 """ 60 """
61
56 executeAsyncFinished = pyqtSignal() 62 executeAsyncFinished = pyqtSignal()
57 dataReceived = pyqtSignal(bytes) 63 dataReceived = pyqtSignal(bytes)
58 64
59 def __init__(self, parent=None): 65 def __init__(self, parent=None):
60 """ 66 """
61 Constructor 67 Constructor
62 68
63 @param parent reference to the parent object 69 @param parent reference to the parent object
64 @type QObject 70 @type QObject
65 """ 71 """
66 super().__init__(parent) 72 super().__init__(parent)
67 73
68 self.__repl = parent 74 self.__repl = parent
69 75
70 self.__blockReadyRead = False 76 self.__blockReadyRead = False
71 77
72 self.__serial = MicroPythonSerialPort( 78 self.__serial = MicroPythonSerialPort(
73 timeout=Preferences.getMicroPython("SerialTimeout"), 79 timeout=Preferences.getMicroPython("SerialTimeout"), parent=self
74 parent=self) 80 )
75 self.__serial.readyRead.connect(self.__readSerial) 81 self.__serial.readyRead.connect(self.__readSerial)
76 82
77 @pyqtSlot() 83 @pyqtSlot()
78 def __readSerial(self): 84 def __readSerial(self):
79 """ 85 """
80 Private slot to read all available serial data and emit it with the 86 Private slot to read all available serial data and emit it with the
81 "dataReceived" signal for further processing. 87 "dataReceived" signal for further processing.
82 """ 88 """
83 if not self.__blockReadyRead: 89 if not self.__blockReadyRead:
84 data = bytes(self.__serial.readAll()) 90 data = bytes(self.__serial.readAll())
85 self.dataReceived.emit(data) 91 self.dataReceived.emit(data)
86 92
87 @pyqtSlot() 93 @pyqtSlot()
88 def connectToDevice(self, port): 94 def connectToDevice(self, port):
89 """ 95 """
90 Public slot to start the manager. 96 Public slot to start the manager.
91 97
92 @param port name of the port to be used 98 @param port name of the port to be used
93 @type str 99 @type str
94 @return flag indicating success 100 @return flag indicating success
95 @rtype bool 101 @rtype bool
96 """ 102 """
97 return self.__serial.openSerialLink(port) 103 return self.__serial.openSerialLink(port)
98 104
99 @pyqtSlot() 105 @pyqtSlot()
100 def disconnectFromDevice(self): 106 def disconnectFromDevice(self):
101 """ 107 """
102 Public slot to stop the thread. 108 Public slot to stop the thread.
103 """ 109 """
104 self.__serial.closeSerialLink() 110 self.__serial.closeSerialLink()
105 111
106 def isConnected(self): 112 def isConnected(self):
107 """ 113 """
108 Public method to get the connection status. 114 Public method to get the connection status.
109 115
110 @return flag indicating the connection status 116 @return flag indicating the connection status
111 @rtype bool 117 @rtype bool
112 """ 118 """
113 return self.__serial.isConnected() 119 return self.__serial.isConnected()
114 120
115 @pyqtSlot() 121 @pyqtSlot()
116 def handlePreferencesChanged(self): 122 def handlePreferencesChanged(self):
117 """ 123 """
118 Public slot to handle a change of the preferences. 124 Public slot to handle a change of the preferences.
119 """ 125 """
120 self.__serial.setTimeout(Preferences.getMicroPython("SerialTimeout")) 126 self.__serial.setTimeout(Preferences.getMicroPython("SerialTimeout"))
121 127
122 def write(self, data): 128 def write(self, data):
123 """ 129 """
124 Public method to write data to the connected device. 130 Public method to write data to the connected device.
125 131
126 @param data data to be written 132 @param data data to be written
127 @type bytes or bytearray 133 @type bytes or bytearray
128 """ 134 """
129 self.__serial.isConnected() and self.__serial.write(data) 135 self.__serial.isConnected() and self.__serial.write(data)
130 136
131 def __rawOn(self): 137 def __rawOn(self):
132 """ 138 """
133 Private method to switch the connected device to 'raw' mode. 139 Private method to switch the connected device to 'raw' mode.
134 140
135 Note: switching to raw mode is done with synchronous writes. 141 Note: switching to raw mode is done with synchronous writes.
136 142
137 @return flag indicating success 143 @return flag indicating success
138 @@rtype bool 144 @@rtype bool
139 """ 145 """
140 if not self.__serial: 146 if not self.__serial:
141 return False 147 return False
142 148
143 rawReplMessage = b"raw REPL; CTRL-B to exit\r\n>" 149 rawReplMessage = b"raw REPL; CTRL-B to exit\r\n>"
144 150
145 self.__serial.write(b"\x02") # end raw mode if required 151 self.__serial.write(b"\x02") # end raw mode if required
146 written = self.__serial.waitForBytesWritten(500) 152 written = self.__serial.waitForBytesWritten(500)
147 # time out after 500ms if device is not responding 153 # time out after 500ms if device is not responding
148 if not written: 154 if not written:
149 return False 155 return False
150 for _i in range(3): 156 for _i in range(3):
153 written = self.__serial.waitForBytesWritten(500) 159 written = self.__serial.waitForBytesWritten(500)
154 # time out after 500ms if device is not responding 160 # time out after 500ms if device is not responding
155 if not written: 161 if not written:
156 return False 162 return False
157 QThread.msleep(10) 163 QThread.msleep(10)
158 self.__serial.readAll() # read all data and discard it 164 self.__serial.readAll() # read all data and discard it
159 self.__serial.write(b"\r\x01") # send CTRL-A to enter raw mode 165 self.__serial.write(b"\r\x01") # send CTRL-A to enter raw mode
160 self.__serial.readUntil(rawReplMessage) 166 self.__serial.readUntil(rawReplMessage)
161 if self.__serial.hasTimedOut(): 167 if self.__serial.hasTimedOut():
162 # it timed out; try it again and than fail 168 # it timed out; try it again and than fail
163 self.__serial.write(b"\r\x01") # send CTRL-A again 169 self.__serial.write(b"\r\x01") # send CTRL-A again
164 self.__serial.readUntil(rawReplMessage) 170 self.__serial.readUntil(rawReplMessage)
165 if self.__serial.hasTimedOut(): 171 if self.__serial.hasTimedOut():
166 return False 172 return False
167 173
168 QCoreApplication.processEvents( 174 QCoreApplication.processEvents(
169 QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents) 175 QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents
170 self.__serial.readAll() # read all data and discard it 176 )
177 self.__serial.readAll() # read all data and discard it
171 return True 178 return True
172 179
173 def __rawOff(self): 180 def __rawOff(self):
174 """ 181 """
175 Private method to switch 'raw' mode off. 182 Private method to switch 'raw' mode off.
176 """ 183 """
177 if self.__serial: 184 if self.__serial:
178 self.__serial.write(b"\x02") # send CTRL-B to cancel raw mode 185 self.__serial.write(b"\x02") # send CTRL-B to cancel raw mode
179 self.__serial.readUntil(b">>> ") # read until Python prompt 186 self.__serial.readUntil(b">>> ") # read until Python prompt
180 self.__serial.readAll() # read all data and discard it 187 self.__serial.readAll() # read all data and discard it
181 188
182 def execute(self, commands): 189 def execute(self, commands):
183 """ 190 """
184 Public method to send commands to the connected device and return the 191 Public method to send commands to the connected device and return the
185 result. 192 result.
186 193
187 If no serial connection is available, empty results will be returned. 194 If no serial connection is available, empty results will be returned.
188 195
189 @param commands list of commands to be executed 196 @param commands list of commands to be executed
190 @type str 197 @type str
191 @return tuple containing stdout and stderr output of the device 198 @return tuple containing stdout and stderr output of the device
192 @rtype tuple of (bytes, bytes) 199 @rtype tuple of (bytes, bytes)
193 """ 200 """
194 if not self.__serial: 201 if not self.__serial:
195 return b"", b"" 202 return b"", b""
196 203
197 if not self.__serial.isConnected(): 204 if not self.__serial.isConnected():
198 return b"", b"Device not connected or not switched on." 205 return b"", b"Device not connected or not switched on."
199 206
200 result = bytearray() 207 result = bytearray()
201 err = b"" 208 err = b""
202 209
203 # switch on raw mode 210 # switch on raw mode
204 self.__blockReadyRead = True 211 self.__blockReadyRead = True
205 ok = self.__rawOn() 212 ok = self.__rawOn()
206 if not ok: 213 if not ok:
207 self.__blockReadyRead = False 214 self.__blockReadyRead = False
208 return ( 215 return (b"", b"Could not switch to raw mode. Is the device switched on?")
209 b"", 216
210 b"Could not switch to raw mode. Is the device switched on?"
211 )
212
213 # send commands 217 # send commands
214 QThread.msleep(10) 218 QThread.msleep(10)
215 for command in commands: 219 for command in commands:
216 if command: 220 if command:
217 commandBytes = command.encode("utf-8") 221 commandBytes = command.encode("utf-8")
218 self.__serial.write(commandBytes + b"\x04") 222 self.__serial.write(commandBytes + b"\x04")
219 QCoreApplication.processEvents( 223 QCoreApplication.processEvents(
220 QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents) 224 QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents
225 )
221 ok = self.__serial.readUntil(b"OK") 226 ok = self.__serial.readUntil(b"OK")
222 if ok != b"OK": 227 if ok != b"OK":
223 return ( 228 return (
224 b"", 229 b"",
225 "Expected 'OK', got '{0}', followed by '{1}'".format( 230 "Expected 'OK', got '{0}', followed by '{1}'".format(
226 ok, self.__serial.readAll()).encode("utf-8") 231 ok, self.__serial.readAll()
232 ).encode("utf-8"),
227 ) 233 )
228 234
229 # read until prompt 235 # read until prompt
230 response = self.__serial.readUntil(b"\x04>") 236 response = self.__serial.readUntil(b"\x04>")
231 if self.__serial.hasTimedOut(): 237 if self.__serial.hasTimedOut():
232 self.__blockReadyRead = False 238 self.__blockReadyRead = False
233 return b"", b"Timeout while processing commands." 239 return b"", b"Timeout while processing commands."
238 else: 244 else:
239 err = b"invalid response received: " + response 245 err = b"invalid response received: " + response
240 if err: 246 if err:
241 self.__blockReadyRead = False 247 self.__blockReadyRead = False
242 return b"", err 248 return b"", err
243 249
244 # switch off raw mode 250 # switch off raw mode
245 QThread.msleep(10) 251 QThread.msleep(10)
246 self.__rawOff() 252 self.__rawOff()
247 self.__blockReadyRead = False 253 self.__blockReadyRead = False
248 254
249 return bytes(result), err 255 return bytes(result), err
250 256
251 def executeAsync(self, commandsList): 257 def executeAsync(self, commandsList):
252 """ 258 """
253 Public method to execute a series of commands over a period of time 259 Public method to execute a series of commands over a period of time
254 without returning any result (asynchronous execution). 260 without returning any result (asynchronous execution).
255 261
256 @param commandsList list of commands to be execute on the device 262 @param commandsList list of commands to be execute on the device
257 @type list of bytes 263 @type list of bytes
258 """ 264 """
265
259 def remainingTask(commands): 266 def remainingTask(commands):
260 self.executeAsync(commands) 267 self.executeAsync(commands)
261 268
262 if commandsList: 269 if commandsList:
263 command = commandsList[0] 270 command = commandsList[0]
264 self.__serial.write(command) 271 self.__serial.write(command)
265 remainder = commandsList[1:] 272 remainder = commandsList[1:]
266 QTimer.singleShot(2, lambda: remainingTask(remainder)) 273 QTimer.singleShot(2, lambda: remainingTask(remainder))
267 else: 274 else:
268 self.executeAsyncFinished.emit() 275 self.executeAsyncFinished.emit()
269 276
270 def __shortError(self, error): 277 def __shortError(self, error):
271 """ 278 """
272 Private method to create a shortened error message. 279 Private method to create a shortened error message.
273 280
274 @param error verbose error message 281 @param error verbose error message
275 @type bytes 282 @type bytes
276 @return shortened error message 283 @return shortened error message
277 @rtype str 284 @rtype str
278 """ 285 """
281 try: 288 try:
282 return decodedError.split["\r\n"][-2] 289 return decodedError.split["\r\n"][-2]
283 except Exception: 290 except Exception:
284 return decodedError 291 return decodedError
285 return self.tr("Detected an error without indications.") 292 return self.tr("Detected an error without indications.")
286 293
287 ################################################################## 294 ##################################################################
288 ## Methods below implement the file system commands 295 ## Methods below implement the file system commands
289 ################################################################## 296 ##################################################################
290 297
291 def ls(self, dirname=""): 298 def ls(self, dirname=""):
292 """ 299 """
293 Public method to get a directory listing of the connected device. 300 Public method to get a directory listing of the connected device.
294 301
295 @param dirname name of the directory to be listed 302 @param dirname name of the directory to be listed
296 @type str 303 @type str
297 @return tuple containg the directory listing 304 @return tuple containg the directory listing
298 @rtype tuple of str 305 @rtype tuple of str
299 @exception OSError raised to indicate an issue with the device 306 @exception OSError raised to indicate an issue with the device
303 [ 310 [
304 "import os as __os_", 311 "import os as __os_",
305 "print(__os_.listdir())", 312 "print(__os_.listdir())",
306 "del __os_", 313 "del __os_",
307 ] 314 ]
308 if self.__repl.isMicrobit() else 315 if self.__repl.isMicrobit()
309 [ 316 else [
310 "import os as __os_", 317 "import os as __os_",
311 "print(__os_.listdir('{0}'))".format(dirname), 318 "print(__os_.listdir('{0}'))".format(dirname),
312 "del __os_", 319 "del __os_",
313 ] 320 ]
314 ) 321 )
315 out, err = self.execute(commands) 322 out, err = self.execute(commands)
316 if err: 323 if err:
317 raise OSError(self.__shortError(err)) 324 raise OSError(self.__shortError(err))
318 return ast.literal_eval(out.decode("utf-8")) 325 return ast.literal_eval(out.decode("utf-8"))
319 326
320 def lls(self, dirname="", fullstat=False, showHidden=False): 327 def lls(self, dirname="", fullstat=False, showHidden=False):
321 """ 328 """
322 Public method to get a long directory listing of the connected device 329 Public method to get a long directory listing of the connected device
323 including meta data. 330 including meta data.
324 331
325 @param dirname name of the directory to be listed 332 @param dirname name of the directory to be listed
326 @type str 333 @type str
327 @param fullstat flag indicating to return the full stat() tuple 334 @param fullstat flag indicating to return the full stat() tuple
328 @type bool 335 @type bool
329 @param showHidden flag indicating to show hidden files as well 336 @param showHidden flag indicating to show hidden files as well
337 """ 344 """
338 commands = ( 345 commands = (
339 # BBC micro:bit does not support directories 346 # BBC micro:bit does not support directories
340 [ 347 [
341 "import os as __os_", 348 "import os as __os_",
342 "\n".join([ 349 "\n".join(
343 "def is_visible(filename, showHidden):", 350 [
344 " return showHidden or " 351 "def is_visible(filename, showHidden):",
345 "(filename[0] != '.' and filename[-1] != '~')", 352 " return showHidden or "
346 ]), 353 "(filename[0] != '.' and filename[-1] != '~')",
347 "\n".join([ 354 ]
348 "def stat(filename):", 355 ),
349 " size = __os_.size(filename)", 356 "\n".join(
350 " return (0, 0, 0, 0, 0, 0, size, 0, 0, 0)" 357 [
351 ]), 358 "def stat(filename):",
352 "\n".join([ 359 " size = __os_.size(filename)",
353 "def listdir_stat(showHidden):", 360 " return (0, 0, 0, 0, 0, 0, size, 0, 0, 0)",
354 " files = __os_.listdir()", 361 ]
355 " return list((f, stat(f)) for f in files if" 362 ),
356 " is_visible(f,showHidden))", 363 "\n".join(
357 ]), 364 [
365 "def listdir_stat(showHidden):",
366 " files = __os_.listdir()",
367 " return list((f, stat(f)) for f in files if"
368 " is_visible(f,showHidden))",
369 ]
370 ),
358 "print(listdir_stat({0}))".format(showHidden), 371 "print(listdir_stat({0}))".format(showHidden),
359 "del __os_, stat, listdir_stat, is_visible", 372 "del __os_, stat, listdir_stat, is_visible",
360 ] 373 ]
361 if self.__repl.isMicrobit() else 374 if self.__repl.isMicrobit()
362 [ 375 else [
363 "import os as __os_", 376 "import os as __os_",
364 "\n".join([ 377 "\n".join(
365 "def is_visible(filename, showHidden):", 378 [
366 " return showHidden or " 379 "def is_visible(filename, showHidden):",
367 "(filename[0] != '.' and filename[-1] != '~')", 380 " return showHidden or "
368 ]), 381 "(filename[0] != '.' and filename[-1] != '~')",
369 "\n".join([ 382 ]
370 "def stat(filename):", 383 ),
371 " try:", 384 "\n".join(
372 " rstat = __os_.lstat(filename)", 385 [
373 " except:", 386 "def stat(filename):",
374 " rstat = __os_.stat(filename)", 387 " try:",
375 " return tuple(rstat)", 388 " rstat = __os_.lstat(filename)",
376 ]), 389 " except:",
377 "\n".join([ 390 " rstat = __os_.stat(filename)",
378 "def listdir_stat(dirname, showHidden):", 391 " return tuple(rstat)",
379 " try:", 392 ]
380 " files = __os_.listdir(dirname)", 393 ),
381 " except OSError:", 394 "\n".join(
382 " return []", 395 [
383 " if dirname in ('', '/'):", 396 "def listdir_stat(dirname, showHidden):",
384 " return list((f, stat(f)) for f in files if" 397 " try:",
385 " is_visible(f, showHidden))", 398 " files = __os_.listdir(dirname)",
386 " return list((f, stat(dirname + '/' + f))" 399 " except OSError:",
387 " for f in files if is_visible(f, showHidden))", 400 " return []",
388 ]), 401 " if dirname in ('', '/'):",
402 " return list((f, stat(f)) for f in files if"
403 " is_visible(f, showHidden))",
404 " return list((f, stat(dirname + '/' + f))"
405 " for f in files if is_visible(f, showHidden))",
406 ]
407 ),
389 "print(listdir_stat('{0}', {1}))".format(dirname, showHidden), 408 "print(listdir_stat('{0}', {1}))".format(dirname, showHidden),
390 "del __os_, stat, listdir_stat, is_visible", 409 "del __os_, stat, listdir_stat, is_visible",
391 ] 410 ]
392 ) 411 )
393 out, err = self.execute(commands) 412 out, err = self.execute(commands)
399 else: 418 else:
400 if fullstat: 419 if fullstat:
401 return fileslist 420 return fileslist
402 else: 421 else:
403 return [(f, (s[0], s[6], s[8])) for f, s in fileslist] 422 return [(f, (s[0], s[6], s[8])) for f, s in fileslist]
404 423
405 def cd(self, dirname): 424 def cd(self, dirname):
406 """ 425 """
407 Public method to change the current directory on the connected device. 426 Public method to change the current directory on the connected device.
408 427
409 @param dirname directory to change to 428 @param dirname directory to change to
410 @type str 429 @type str
411 @exception OSError raised to indicate an issue with the device 430 @exception OSError raised to indicate an issue with the device
412 """ 431 """
413 if dirname: 432 if dirname:
417 "del __os_", 436 "del __os_",
418 ] 437 ]
419 out, err = self.execute(commands) 438 out, err = self.execute(commands)
420 if err: 439 if err:
421 raise OSError(self.__shortError(err)) 440 raise OSError(self.__shortError(err))
422 441
423 def pwd(self): 442 def pwd(self):
424 """ 443 """
425 Public method to get the current directory of the connected device. 444 Public method to get the current directory of the connected device.
426 445
427 @return current directory 446 @return current directory
428 @rtype str 447 @rtype str
429 @exception OSError raised to indicate an issue with the device 448 @exception OSError raised to indicate an issue with the device
430 """ 449 """
431 if self.__repl.isMicrobit(): 450 if self.__repl.isMicrobit():
432 # BBC micro:bit does not support directories 451 # BBC micro:bit does not support directories
433 return "" 452 return ""
434 453
435 commands = [ 454 commands = [
436 "import os as __os_", 455 "import os as __os_",
437 "print(__os_.getcwd())", 456 "print(__os_.getcwd())",
438 "del __os_", 457 "del __os_",
439 ] 458 ]
440 out, err = self.execute(commands) 459 out, err = self.execute(commands)
441 if err: 460 if err:
442 raise OSError(self.__shortError(err)) 461 raise OSError(self.__shortError(err))
443 return out.decode("utf-8").strip() 462 return out.decode("utf-8").strip()
444 463
445 def rm(self, filename): 464 def rm(self, filename):
446 """ 465 """
447 Public method to remove a file from the connected device. 466 Public method to remove a file from the connected device.
448 467
449 @param filename name of the file to be removed 468 @param filename name of the file to be removed
450 @type str 469 @type str
451 @exception OSError raised to indicate an issue with the device 470 @exception OSError raised to indicate an issue with the device
452 """ 471 """
453 if filename: 472 if filename:
457 "del __os_", 476 "del __os_",
458 ] 477 ]
459 out, err = self.execute(commands) 478 out, err = self.execute(commands)
460 if err: 479 if err:
461 raise OSError(self.__shortError(err)) 480 raise OSError(self.__shortError(err))
462 481
463 def rmrf(self, name, recursive=False, force=False): 482 def rmrf(self, name, recursive=False, force=False):
464 """ 483 """
465 Public method to remove a file or directory recursively. 484 Public method to remove a file or directory recursively.
466 485
467 @param name of the file or directory to remove 486 @param name of the file or directory to remove
468 @type str 487 @type str
469 @param recursive flag indicating a recursive deletion 488 @param recursive flag indicating a recursive deletion
470 @type bool 489 @type bool
471 @param force flag indicating to ignore errors 490 @param force flag indicating to ignore errors
475 @exception OSError raised to indicate an issue with the device 494 @exception OSError raised to indicate an issue with the device
476 """ 495 """
477 if name: 496 if name:
478 commands = [ 497 commands = [
479 "import os as __os_", 498 "import os as __os_",
480 "\n".join([ 499 "\n".join(
481 "def remove_file(name, recursive=False, force=False):", 500 [
482 " try:", 501 "def remove_file(name, recursive=False, force=False):",
483 " mode = __os_.stat(name)[0]", 502 " try:",
484 " if mode & 0x4000 != 0:", 503 " mode = __os_.stat(name)[0]",
485 " if recursive:", 504 " if mode & 0x4000 != 0:",
486 " for file in __os_.listdir(name):", 505 " if recursive:",
487 " success = remove_file(" 506 " for file in __os_.listdir(name):",
488 "name + '/' + file, recursive, force)", 507 " success = remove_file("
489 " if not success and not force:", 508 "name + '/' + file, recursive, force)",
490 " return False", 509 " if not success and not force:",
491 " __os_.rmdir(name)", 510 " return False",
492 " else:", 511 " __os_.rmdir(name)",
493 " if not force:", 512 " else:",
494 " return False", 513 " if not force:",
495 " else:", 514 " return False",
496 " __os_.remove(name)", 515 " else:",
497 " except:", 516 " __os_.remove(name)",
498 " if not force:", 517 " except:",
499 " return False", 518 " if not force:",
500 " return True", 519 " return False",
501 ]), 520 " return True",
502 "print(remove_file('{0}', {1}, {2}))".format(name, recursive, 521 ]
503 force), 522 ),
523 "print(remove_file('{0}', {1}, {2}))".format(name, recursive, force),
504 "del __os_, remove_file", 524 "del __os_, remove_file",
505 ] 525 ]
506 out, err = self.execute(commands) 526 out, err = self.execute(commands)
507 if err: 527 if err:
508 raise OSError(self.__shortError(err)) 528 raise OSError(self.__shortError(err))
509 return ast.literal_eval(out.decode("utf-8")) 529 return ast.literal_eval(out.decode("utf-8"))
510 530
511 return False 531 return False
512 532
513 def mkdir(self, dirname): 533 def mkdir(self, dirname):
514 """ 534 """
515 Public method to create a new directory. 535 Public method to create a new directory.
516 536
517 @param dirname name of the directory to create 537 @param dirname name of the directory to create
518 @type str 538 @type str
519 @exception OSError raised to indicate an issue with the device 539 @exception OSError raised to indicate an issue with the device
520 """ 540 """
521 if dirname: 541 if dirname:
525 "del __os_", 545 "del __os_",
526 ] 546 ]
527 out, err = self.execute(commands) 547 out, err = self.execute(commands)
528 if err: 548 if err:
529 raise OSError(self.__shortError(err)) 549 raise OSError(self.__shortError(err))
530 550
531 def rmdir(self, dirname): 551 def rmdir(self, dirname):
532 """ 552 """
533 Public method to remove a directory. 553 Public method to remove a directory.
534 554
535 @param dirname name of the directory to be removed 555 @param dirname name of the directory to be removed
536 @type str 556 @type str
537 @exception OSError raised to indicate an issue with the device 557 @exception OSError raised to indicate an issue with the device
538 """ 558 """
539 if dirname: 559 if dirname:
543 "del __os_", 563 "del __os_",
544 ] 564 ]
545 out, err = self.execute(commands) 565 out, err = self.execute(commands)
546 if err: 566 if err:
547 raise OSError(self.__shortError(err)) 567 raise OSError(self.__shortError(err))
548 568
549 def put(self, hostFileName, deviceFileName=None): 569 def put(self, hostFileName, deviceFileName=None):
550 """ 570 """
551 Public method to copy a local file to the connected device. 571 Public method to copy a local file to the connected device.
552 572
553 @param hostFileName name of the file to be copied 573 @param hostFileName name of the file to be copied
554 @type str 574 @type str
555 @param deviceFileName name of the file to copy to 575 @param deviceFileName name of the file to copy to
556 @type str 576 @type str
557 @return flag indicating success 577 @return flag indicating success
558 @rtype bool 578 @rtype bool
559 @exception OSError raised to indicate an issue with the device 579 @exception OSError raised to indicate an issue with the device
560 """ 580 """
561 if not os.path.isfile(hostFileName): 581 if not os.path.isfile(hostFileName):
562 raise OSError("No such file: {0}".format(hostFileName)) 582 raise OSError("No such file: {0}".format(hostFileName))
563 583
564 with open(hostFileName, "rb") as hostFile: 584 with open(hostFileName, "rb") as hostFile:
565 content = hostFile.read() 585 content = hostFile.read()
566 # convert eol '\r' 586 # convert eol '\r'
567 content = content.replace(b"\r\n", b"\r") 587 content = content.replace(b"\r\n", b"\r")
568 content = content.replace(b"\n", b"\r") 588 content = content.replace(b"\n", b"\r")
569 589
570 if not deviceFileName: 590 if not deviceFileName:
571 deviceFileName = os.path.basename(hostFileName) 591 deviceFileName = os.path.basename(hostFileName)
572 592
573 commands = [ 593 commands = [
574 "fd = open('{0}', 'wb')".format(deviceFileName), 594 "fd = open('{0}', 'wb')".format(deviceFileName),
575 "f = fd.write", 595 "f = fd.write",
576 ] 596 ]
577 while content: 597 while content:
578 chunk = content[:64] 598 chunk = content[:64]
579 commands.append("f(" + repr(chunk) + ")") 599 commands.append("f(" + repr(chunk) + ")")
580 content = content[64:] 600 content = content[64:]
581 commands.extend([ 601 commands.extend(
582 "fd.close()", 602 [
583 "del f, fd", 603 "fd.close()",
584 ]) 604 "del f, fd",
585 605 ]
606 )
607
586 out, err = self.execute(commands) 608 out, err = self.execute(commands)
587 if err: 609 if err:
588 raise OSError(self.__shortError(err)) 610 raise OSError(self.__shortError(err))
589 return True 611 return True
590 612
591 def get(self, deviceFileName, hostFileName=None): 613 def get(self, deviceFileName, hostFileName=None):
592 """ 614 """
593 Public method to copy a file from the connected device. 615 Public method to copy a file from the connected device.
594 616
595 @param deviceFileName name of the file to copy 617 @param deviceFileName name of the file to copy
596 @type str 618 @type str
597 @param hostFileName name of the file to copy to 619 @param hostFileName name of the file to copy to
598 @type str 620 @type str
599 @return flag indicating success 621 @return flag indicating success
600 @rtype bool 622 @rtype bool
601 @exception OSError raised to indicate an issue with the device 623 @exception OSError raised to indicate an issue with the device
602 """ 624 """
603 if not hostFileName: 625 if not hostFileName:
604 hostFileName = deviceFileName 626 hostFileName = deviceFileName
605 627
606 commands = [ 628 commands = [
607 "\n".join([ 629 "\n".join(
608 "def send_data():", 630 [
609 " try:", 631 "def send_data():",
610 " from microbit import uart as u", 632 " try:",
611 " except ImportError:", 633 " from microbit import uart as u",
612 " try:", 634 " except ImportError:",
613 " from machine import UART", 635 " try:",
614 " u = UART(0, {0})".format(115200), 636 " from machine import UART",
615 " except Exception:", 637 " u = UART(0, {0})".format(115200),
616 " try:", 638 " except Exception:",
617 " from sys import stdout as u", 639 " try:",
618 " except Exception:", 640 " from sys import stdout as u",
619 " raise Exception('Could not find UART module" 641 " except Exception:",
620 " in device.')", 642 " raise Exception('Could not find UART module"
621 " f = open('{0}', 'rb')".format(deviceFileName), 643 " in device.')",
622 " r = f.read", 644 " f = open('{0}', 'rb')".format(deviceFileName),
623 " result = True", 645 " r = f.read",
624 " while result:", 646 " result = True",
625 " result = r(32)", 647 " while result:",
626 " if result:", 648 " result = r(32)",
627 " u.write(result)", 649 " if result:",
628 " f.close()", 650 " u.write(result)",
629 ]), 651 " f.close()",
652 ]
653 ),
630 "send_data()", 654 "send_data()",
631 ] 655 ]
632 out, err = self.execute(commands) 656 out, err = self.execute(commands)
633 if err: 657 if err:
634 raise OSError(self.__shortError(err)) 658 raise OSError(self.__shortError(err))
635 659
636 # write the received bytes to the local file 660 # write the received bytes to the local file
637 # convert eol to "\n" 661 # convert eol to "\n"
638 out = out.replace(b"\r\n", b"\n") 662 out = out.replace(b"\r\n", b"\n")
639 out = out.replace(b"\r", b"\n") 663 out = out.replace(b"\r", b"\n")
640 with open(hostFileName, "wb") as hostFile: 664 with open(hostFileName, "wb") as hostFile:
641 hostFile.write(out) 665 hostFile.write(out)
642 return True 666 return True
643 667
644 def fileSystemInfo(self): 668 def fileSystemInfo(self):
645 """ 669 """
646 Public method to obtain information about the currently mounted file 670 Public method to obtain information about the currently mounted file
647 systems. 671 systems.
648 672
649 @return tuple of tuples containing the file system name, the total 673 @return tuple of tuples containing the file system name, the total
650 size, the used size and the free size 674 size, the used size and the free size
651 @rtype tuple of tuples of (str, int, int, int) 675 @rtype tuple of tuples of (str, int, int, int)
652 @exception OSError raised to indicate an issue with the device 676 @exception OSError raised to indicate an issue with the device
653 """ 677 """
654 commands = [ 678 commands = [
655 "import os as __os_", 679 "import os as __os_",
656 "\n".join([ 680 "\n".join(
657 "def fsinfo():", 681 [
658 " infolist = []", 682 "def fsinfo():",
659 " info = __os_.statvfs('/')", 683 " infolist = []",
660 " if info[0] == 0:", 684 " info = __os_.statvfs('/')",
661 # assume it is just mount points 685 " if info[0] == 0:",
662 " fsnames = __os_.listdir('/')", 686 # assume it is just mount points
663 " for fs in fsnames:", 687 " fsnames = __os_.listdir('/')",
664 " fs = '/' + fs", 688 " for fs in fsnames:",
665 " infolist.append((fs, __os_.statvfs(fs)))", 689 " fs = '/' + fs",
666 " else:", 690 " infolist.append((fs, __os_.statvfs(fs)))",
667 " infolist.append(('/', info))", 691 " else:",
668 " return infolist", 692 " infolist.append(('/', info))",
669 ]), 693 " return infolist",
694 ]
695 ),
670 "print(fsinfo())", 696 "print(fsinfo())",
671 "del __os_, fsinfo", 697 "del __os_, fsinfo",
672 ] 698 ]
673 out, err = self.execute(commands) 699 out, err = self.execute(commands)
674 if err: 700 if err:
681 for fs, info in infolist: 707 for fs, info in infolist:
682 totalSize = info[2] * info[1] 708 totalSize = info[2] * info[1]
683 freeSize = info[4] * info[1] 709 freeSize = info[4] * info[1]
684 usedSize = totalSize - freeSize 710 usedSize = totalSize - freeSize
685 filesystemInfos.append((fs, totalSize, usedSize, freeSize)) 711 filesystemInfos.append((fs, totalSize, usedSize, freeSize))
686 712
687 return tuple(filesystemInfos) 713 return tuple(filesystemInfos)
688 714
689 ################################################################## 715 ##################################################################
690 ## non-filesystem related methods below 716 ## non-filesystem related methods below
691 ################################################################## 717 ##################################################################
692 718
693 def version(self): 719 def version(self):
694 """ 720 """
695 Public method to get the MicroPython version information of the 721 Public method to get the MicroPython version information of the
696 connected device. 722 connected device.
697 723
698 @return dictionary containing the version information 724 @return dictionary containing the version information
699 @rtype dict 725 @rtype dict
700 @exception OSError raised to indicate an issue with the device 726 @exception OSError raised to indicate an issue with the device
701 """ 727 """
702 commands = [ 728 commands = [
705 "del __os_", 731 "del __os_",
706 ] 732 ]
707 out, err = self.execute(commands) 733 out, err = self.execute(commands)
708 if err: 734 if err:
709 raise OSError(self.__shortError(err)) 735 raise OSError(self.__shortError(err))
710 736
711 rawOutput = out.decode("utf-8").strip() 737 rawOutput = out.decode("utf-8").strip()
712 rawOutput = rawOutput[1:-1] 738 rawOutput = rawOutput[1:-1]
713 items = rawOutput.split(",") 739 items = rawOutput.split(",")
714 result = {} 740 result = {}
715 for item in items: 741 for item in items:
716 key, value = item.strip().split("=") 742 key, value = item.strip().split("=")
717 result[key.strip()] = value.strip()[1:-1] 743 result[key.strip()] = value.strip()[1:-1]
718 return result 744 return result
719 745
720 def getImplementation(self): 746 def getImplementation(self):
721 """ 747 """
722 Public method to get some implementation information of the connected 748 Public method to get some implementation information of the connected
723 device. 749 device.
724 750
725 @return dictionary containing the implementation information 751 @return dictionary containing the implementation information
726 @rtype dict 752 @rtype dict
727 @exception OSError raised to indicate an issue with the device 753 @exception OSError raised to indicate an issue with the device
728 """ 754 """
729 commands = [ 755 commands = [
730 "import sys as __sys_", 756 "import sys as __sys_",
731 "res = {}", # __IGNORE_WARNING_M613__ 757 "res = {}", # __IGNORE_WARNING_M613__
732 "\n".join([ 758 "\n".join(
733 "try:", 759 [
734 " res['name'] = __sys_.implementation.name", 760 "try:",
735 "except AttributeError:", 761 " res['name'] = __sys_.implementation.name",
736 " res['name'] = 'unknown'", 762 "except AttributeError:",
737 ]), 763 " res['name'] = 'unknown'",
738 "\n".join([ 764 ]
739 "try:", 765 ),
740 " res['version'] = '.'.join((str(i) for i in" 766 "\n".join(
741 " __sys_.implementation.version))", 767 [
742 "except AttributeError:", 768 "try:",
743 " res['version'] = 'unknown'", 769 " res['version'] = '.'.join((str(i) for i in"
744 ]), 770 " __sys_.implementation.version))",
771 "except AttributeError:",
772 " res['version'] = 'unknown'",
773 ]
774 ),
745 "print(res)", 775 "print(res)",
746 "del res, __sys_", 776 "del res, __sys_",
747 ] 777 ]
748 out, err = self.execute(commands) 778 out, err = self.execute(commands)
749 if err: 779 if err:
750 raise OSError(self.__shortError(err)) 780 raise OSError(self.__shortError(err))
751 return ast.literal_eval(out.decode("utf-8")) 781 return ast.literal_eval(out.decode("utf-8"))
752 782
753 def getBoardInformation(self): 783 def getBoardInformation(self):
754 """ 784 """
755 Public method to get some information data of the connected board. 785 Public method to get some information data of the connected board.
756 786
757 @return dictionary containing the determined data 787 @return dictionary containing the determined data
758 @rtype dict 788 @rtype dict
759 @exception OSError raised to indicate an issue with the device 789 @exception OSError raised to indicate an issue with the device
760 """ 790 """
761 commands = [ 791 commands = [
762 "res = {}", # __IGNORE_WARNING_M613__ 792 "res = {}", # __IGNORE_WARNING_M613__
763
764 "import gc as __gc_", 793 "import gc as __gc_",
765 "__gc_.enable()", 794 "__gc_.enable()",
766 "__gc_.collect()", 795 "__gc_.collect()",
767 "mem_alloc = __gc_.mem_alloc()", 796 "mem_alloc = __gc_.mem_alloc()",
768 "mem_free = __gc_.mem_free()", 797 "mem_free = __gc_.mem_free()",
771 "res['mem_used_kb'] = mem_alloc / 1024.0", 800 "res['mem_used_kb'] = mem_alloc / 1024.0",
772 "res['mem_used_pc'] = mem_alloc / mem_total * 100.0", 801 "res['mem_used_pc'] = mem_alloc / mem_total * 100.0",
773 "res['mem_free_kb'] = mem_free / 1024.0", 802 "res['mem_free_kb'] = mem_free / 1024.0",
774 "res['mem_free_pc'] = mem_free / mem_total * 100.0", 803 "res['mem_free_pc'] = mem_free / mem_total * 100.0",
775 "del __gc_, mem_alloc, mem_free, mem_total", 804 "del __gc_, mem_alloc, mem_free, mem_total",
776
777 "import os as __os_", 805 "import os as __os_",
778 "uname = __os_.uname()", 806 "uname = __os_.uname()",
779 "res['sysname'] = uname.sysname", 807 "res['sysname'] = uname.sysname",
780 "res['nodename'] = uname.nodename", 808 "res['nodename'] = uname.nodename",
781 "res['release'] = uname.release", 809 "res['release'] = uname.release",
782 "res['version'] = uname.version", 810 "res['version'] = uname.version",
783 "res['machine'] = uname.machine", 811 "res['machine'] = uname.machine",
784
785 "import sys as __sys_", 812 "import sys as __sys_",
786 "res['py_platform'] = __sys_.platform", 813 "res['py_platform'] = __sys_.platform",
787 "res['py_version'] = __sys_.version", 814 "res['py_version'] = __sys_.version",
788 "\n".join([ 815 "\n".join(
789 "try:", 816 [
790 " res['mpy_name'] = __sys_.implementation.name", 817 "try:",
791 "except AttributeError:", 818 " res['mpy_name'] = __sys_.implementation.name",
792 " res['mpy_name'] = 'unknown'", 819 "except AttributeError:",
793 ]), 820 " res['mpy_name'] = 'unknown'",
794 "\n".join([ 821 ]
795 "try:", 822 ),
796 " res['mpy_version'] = '.'.join((str(i) for i in" 823 "\n".join(
797 " __sys_.implementation.version))", 824 [
798 "except AttributeError:", 825 "try:",
799 " res['mpy_version'] = 'unknown'", 826 " res['mpy_version'] = '.'.join((str(i) for i in"
800 ]), 827 " __sys_.implementation.version))",
801 828 "except AttributeError:",
829 " res['mpy_version'] = 'unknown'",
830 ]
831 ),
802 "stat_ = __os_.statvfs('/flash')", 832 "stat_ = __os_.statvfs('/flash')",
803 "res['flash_total_kb'] = stat_[2] * stat_[0] / 1024.0", 833 "res['flash_total_kb'] = stat_[2] * stat_[0] / 1024.0",
804 "res['flash_free_kb'] = stat_[3] * stat_[0] / 1024.0", 834 "res['flash_free_kb'] = stat_[3] * stat_[0] / 1024.0",
805 "res['flash_used_kb'] = res['flash_total_kb'] -" 835 "res['flash_used_kb'] = res['flash_total_kb'] -" " res['flash_free_kb']",
806 " res['flash_free_kb']",
807 "res['flash_free_pc'] = res['flash_free_kb'] /" 836 "res['flash_free_pc'] = res['flash_free_kb'] /"
808 " res['flash_total_kb'] * 100.0", 837 " res['flash_total_kb'] * 100.0",
809 "res['flash_used_pc'] = res['flash_used_kb'] /" 838 "res['flash_used_pc'] = res['flash_used_kb'] /"
810 " res['flash_total_kb'] * 100.0", 839 " res['flash_total_kb'] * 100.0",
811 840 "\n".join(
812 "\n".join([ 841 [
813 "try:", 842 "try:",
814 " import machine as __mc_", 843 " import machine as __mc_",
815 " res['mc_frequency_mhz'] = __mc_.freq() / 1000000.0", 844 " res['mc_frequency_mhz'] = __mc_.freq() / 1000000.0",
816 " res['mc_id'] = ':'.join(['{0:X}'.format(x)" 845 " res['mc_id'] = ':'.join(['{0:X}'.format(x)"
817 " for x in __mc_.unique_id()])", 846 " for x in __mc_.unique_id()])",
818 " del __mc_", 847 " del __mc_",
819 "except ImportError:", 848 "except ImportError:",
820 "\n".join([ 849 "\n".join(
821 " try:", 850 [
822 " import microcontroller as __mc_", 851 " try:",
823 " res['mc_frequency_mhz'] = __mc_.cpu.frequency" 852 " import microcontroller as __mc_",
824 " / 1000000.0", 853 " res['mc_frequency_mhz'] = __mc_.cpu.frequency"
825 " res['mc_temp_c'] = __mc_.cpu.temperature", 854 " / 1000000.0",
826 " res['mc_id'] = ':'.join(['{0:X}'.format(x)" 855 " res['mc_temp_c'] = __mc_.cpu.temperature",
827 " for x in __mc_.cpu.uid])", 856 " res['mc_id'] = ':'.join(['{0:X}'.format(x)"
828 " del __mc_", 857 " for x in __mc_.cpu.uid])",
829 " except ImportError:", 858 " del __mc_",
830 " res['mc_frequency'] = None", 859 " except ImportError:",
831 " res['mc_temp'] = None", 860 " res['mc_frequency'] = None",
832 ]), 861 " res['mc_temp'] = None",
833 ]), 862 ]
834 863 ),
835 "\n".join([ 864 ]
836 "try:", 865 ),
837 " import ulab as __ulab_", 866 "\n".join(
838 " res['ulab'] = __ulab_.__version__", 867 [
839 " del __ulab_", 868 "try:",
840 "except ImportError:", 869 " import ulab as __ulab_",
841 " res['ulab'] = None", 870 " res['ulab'] = __ulab_.__version__",
842 ]), 871 " del __ulab_",
843 872 "except ImportError:",
873 " res['ulab'] = None",
874 ]
875 ),
844 "print(res)", 876 "print(res)",
845 "del res, stat_, __os_, __sys_", 877 "del res, stat_, __os_, __sys_",
846 ] 878 ]
847 out, err = self.execute(commands) 879 out, err = self.execute(commands)
848 if err: 880 if err:
849 raise OSError(self.__shortError(err)) 881 raise OSError(self.__shortError(err))
850 return ast.literal_eval(out.decode("utf-8")) 882 return ast.literal_eval(out.decode("utf-8"))
851 883
852 def syncTime(self, deviceType): 884 def syncTime(self, deviceType):
853 """ 885 """
854 Public method to set the time of the connected device to the local 886 Public method to set the time of the connected device to the local
855 computer's time. 887 computer's time.
856 888
857 @param deviceType type of board to sync time to 889 @param deviceType type of board to sync time to
858 @type str 890 @type str
859 @exception OSError raised to indicate an issue with the device 891 @exception OSError raised to indicate an issue with the device
860 """ 892 """
861 # rtc_time[0] - year 4 digit 893 # rtc_time[0] - year 4 digit
872 # The pyb.RTC.datetime function takes the arguments in the 904 # The pyb.RTC.datetime function takes the arguments in the
873 # order: (year, month, day, weekday, hour, minute, second, 905 # order: (year, month, day, weekday, hour, minute, second,
874 # subseconds) 906 # subseconds)
875 # http://docs.micropython.org/en/latest/library/pyb.RTC.html 907 # http://docs.micropython.org/en/latest/library/pyb.RTC.html
876 # #pyb.RTC.datetime 908 # #pyb.RTC.datetime
877 set_time = "\n".join([ 909 set_time = "\n".join(
878 "def set_time(rtc_time):", 910 [
879 " import pyb", 911 "def set_time(rtc_time):",
880 " rtc = pyb.RTC()", 912 " import pyb",
881 " rtc.datetime(rtc_time[:7] + (0,))", 913 " rtc = pyb.RTC()",
882 ]) 914 " rtc.datetime(rtc_time[:7] + (0,))",
915 ]
916 )
883 elif deviceType == "esp": 917 elif deviceType == "esp":
884 # The machine.RTC documentation was incorrect and doesn't agree 918 # The machine.RTC documentation was incorrect and doesn't agree
885 # with the code, so no link is presented here. The order of the 919 # with the code, so no link is presented here. The order of the
886 # arguments is the same as the pyboard except for LoBo MPy. 920 # arguments is the same as the pyboard except for LoBo MPy.
887 set_time = "\n".join([ 921 set_time = "\n".join(
888 "def set_time(rtc_time):", 922 [
889 " import machine", 923 "def set_time(rtc_time):",
890 " rtc = machine.RTC()", 924 " import machine",
891 " try:", # ESP8266 may use rtc.datetime() 925 " rtc = machine.RTC()",
892 " rtc.datetime(rtc_time[:7] + (0,))", 926 " try:", # ESP8266 may use rtc.datetime()
893 " except Exception:", # ESP32 uses rtc.init() 927 " rtc.datetime(rtc_time[:7] + (0,))",
894 " import os", 928 " except Exception:", # ESP32 uses rtc.init()
895 " if 'LoBo' in os.uname()[0]:", # LoBo MPy 929 " import os",
896 " clock_time = rtc_time[:3] +" 930 " if 'LoBo' in os.uname()[0]:", # LoBo MPy
897 " rtc_time[4:7] + (rtc_time[3], rtc_time[7])", 931 " clock_time = rtc_time[:3] +"
898 " else:", 932 " rtc_time[4:7] + (rtc_time[3], rtc_time[7])",
899 " clock_time = rtc_time[:7] + (0,)", 933 " else:",
900 " rtc.init(clock_time)", 934 " clock_time = rtc_time[:7] + (0,)",
901 ]) 935 " rtc.init(clock_time)",
936 ]
937 )
902 elif deviceType == "circuitpython": 938 elif deviceType == "circuitpython":
903 set_time = "\n".join([ 939 set_time = "\n".join(
904 "def set_time(rtc_time):", 940 [
905 " import rtc", 941 "def set_time(rtc_time):",
906 " import time", 942 " import rtc",
907 " clock = rtc.RTC()", 943 " import time",
908 " clock_time = rtc_time[:3] + rtc_time[4:7] + (rtc_time[3]," 944 " clock = rtc.RTC()",
909 " rtc_time[7], rtc_time[8])", 945 " clock_time = rtc_time[:3] + rtc_time[4:7] + (rtc_time[3],"
910 " clock.datetime = time.struct_time(clock_time)", 946 " rtc_time[7], rtc_time[8])",
911 ]) 947 " clock.datetime = time.struct_time(clock_time)",
948 ]
949 )
912 elif deviceType in ("bbc_microbit", "calliope"): 950 elif deviceType in ("bbc_microbit", "calliope"):
913 # BBC micro:bit and Calliope mini don't support time commands 951 # BBC micro:bit and Calliope mini don't support time commands
914 return 952 return
915 elif deviceType == "rp2040": 953 elif deviceType == "rp2040":
916 # Raspberry Pi Pico (RP2040) - machine.RTC doesn't exist 954 # Raspberry Pi Pico (RP2040) - machine.RTC doesn't exist
917 set_time = "\n".join([ 955 set_time = "\n".join(
918 "def set_time(rtc_time):", 956 [
919 " setup_0 = rtc_time[0] << 12 | rtc_time[1] << 8 |" 957 "def set_time(rtc_time):",
920 " rtc_time[2]", 958 " setup_0 = rtc_time[0] << 12 | rtc_time[1] << 8 |"
921 " setup_1 = (rtc_time[3] % 7) << 24 | rtc_time[4] << 16 |" 959 " rtc_time[2]",
922 " rtc_time[5] << 8 | rtc_time[6]", 960 " setup_1 = (rtc_time[3] % 7) << 24 | rtc_time[4] << 16 |"
923 " machine.mem32[0x4005c004] = setup_0", 961 " rtc_time[5] << 8 | rtc_time[6]",
924 " machine.mem32[0x4005c008] = setup_1", 962 " machine.mem32[0x4005c004] = setup_0",
925 " machine.mem32[0x4005c00c] |= 0x10", 963 " machine.mem32[0x4005c008] = setup_1",
926 ]) 964 " machine.mem32[0x4005c00c] |= 0x10",
965 ]
966 )
927 elif deviceType == "pycom": 967 elif deviceType == "pycom":
928 # PyCom's machine.RTC takes its arguments in a slightly 968 # PyCom's machine.RTC takes its arguments in a slightly
929 # different order than the official machine.RTC. 969 # different order than the official machine.RTC.
930 # (year, month, day, hour, minute, second[, microsecond[, 970 # (year, month, day, hour, minute, second[, microsecond[,
931 # tzinfo]]) 971 # tzinfo]])
932 # https://docs.pycom.io/firmwareapi/pycom/machine/rtc/ 972 # https://docs.pycom.io/firmwareapi/pycom/machine/rtc/
933 # #rtc-init-datetime-none-source-rtc-internal-rc 973 # #rtc-init-datetime-none-source-rtc-internal-rc
934 set_time = "\n".join([ 974 set_time = "\n".join(
935 "def set_time(rtc_time):", 975 [
936 " import pycom", 976 "def set_time(rtc_time):",
937 " rtc_time2 = rtc_time[:3] + rtc_time[4:7]", 977 " import pycom",
938 " import machine", 978 " rtc_time2 = rtc_time[:3] + rtc_time[4:7]",
939 " rtc = machine.RTC()", 979 " import machine",
940 " rtc.init(rtc_time2)", 980 " rtc = machine.RTC()",
941 ]) 981 " rtc.init(rtc_time2)",
982 ]
983 )
942 else: 984 else:
943 # no set_time() support for generic boards 985 # no set_time() support for generic boards
944 return 986 return
945 987
946 now = time.localtime(time.time()) 988 now = time.localtime(time.time())
947 commands = [ 989 commands = [
948 set_time, 990 set_time,
949 "set_time({0})".format(( 991 "set_time({0})".format(
950 now.tm_year, now.tm_mon, now.tm_mday, now.tm_wday + 1, 992 (
951 now.tm_hour, now.tm_min, now.tm_sec, now.tm_yday, now.tm_isdst 993 now.tm_year,
952 )), 994 now.tm_mon,
995 now.tm_mday,
996 now.tm_wday + 1,
997 now.tm_hour,
998 now.tm_min,
999 now.tm_sec,
1000 now.tm_yday,
1001 now.tm_isdst,
1002 )
1003 ),
953 "del set_time", 1004 "del set_time",
954 ] 1005 ]
955 out, err = self.execute(commands) 1006 out, err = self.execute(commands)
956 if err: 1007 if err:
957 raise OSError(self.__shortError(err)) 1008 raise OSError(self.__shortError(err))
958 1009
959 def getTime(self): 1010 def getTime(self):
960 """ 1011 """
961 Public method to get the current time of the device. 1012 Public method to get the current time of the device.
962 1013
963 @return time of the device 1014 @return time of the device
964 @rtype str 1015 @rtype str
965 @exception OSError raised to indicate an issue with the device 1016 @exception OSError raised to indicate an issue with the device
966 """ 1017 """
967 commands = [ 1018 commands = [
968 "\n".join([ 1019 "\n".join(
969 "try:", 1020 [
970 " import rtc as __rtc_", 1021 "try:",
971 " print('{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'" 1022 " import rtc as __rtc_",
972 ".format(*__rtc_.RTC().datetime[:6]))", 1023 " print('{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'"
973 " del __rtc_", 1024 ".format(*__rtc_.RTC().datetime[:6]))",
974 "except:", 1025 " del __rtc_",
975 " import time as __time_", 1026 "except:",
976 " try:", 1027 " import time as __time_",
977 " print(__time_.strftime('%Y-%m-%d %H:%M:%S'," 1028 " try:",
978 # __IGNORE_WARNING_M601__ 1029 " print(__time_.strftime('%Y-%m-%d %H:%M:%S',"
979 " __time_.localtime()))", 1030 # __IGNORE_WARNING_M601__
980 " except AttributeError:", 1031 " __time_.localtime()))",
981 " tm = __time_.localtime()", 1032 " except AttributeError:",
982 " print('{0:04d}-{1:02d}-{2:02d}" 1033 " tm = __time_.localtime()",
983 " {3:02d}:{4:02d}:{5:02d}'" 1034 " print('{0:04d}-{1:02d}-{2:02d}"
984 ".format(tm[0], tm[1], tm[2], tm[3], tm[4], tm[5]))", 1035 " {3:02d}:{4:02d}:{5:02d}'"
985 " del tm", 1036 ".format(tm[0], tm[1], tm[2], tm[3], tm[4], tm[5]))",
986 " del __time_" 1037 " del tm",
987 ]), 1038 " del __time_",
1039 ]
1040 ),
988 ] 1041 ]
989 out, err = self.execute(commands) 1042 out, err = self.execute(commands)
990 if err: 1043 if err:
991 if b"NotImplementedError" in err: 1044 if b"NotImplementedError" in err:
992 return "&lt;unsupported&gt; &lt;unsupported&gt;" 1045 return "&lt;unsupported&gt; &lt;unsupported&gt;"

eric ide

mercurial