2022-12-31 01:56:48 +01:00
#!/usr/bin/env python
# Primadiag SAS - Paris
# Author: Gino D.
# Copyright (c) 2023 Primadiag
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE
2022-12-30 23:33:59 +01:00
import os
2022-12-29 16:37:55 +01:00
import sys
import time
2022-12-30 23:33:59 +01:00
import json
2022-12-31 03:04:11 +01:00
import signal
2023-01-02 11:12:01 +01:00
import tempfile
2022-12-29 17:20:41 +01:00
import binascii
2022-12-30 23:33:59 +01:00
import textwrap
2023-01-05 16:56:52 +01:00
import mpy_cross
2023-01-02 11:12:01 +01:00
import subprocess
2022-12-29 17:20:41 +01:00
2022-12-31 03:36:55 +01:00
from serial import Serial
2023-01-04 18:28:31 +01:00
from dotenv import dotenv_values
2023-01-04 15:40:22 +01:00
from typing import List , Optional , Tuple , Union
2022-12-29 17:20:41 +01:00
2023-01-04 18:28:31 +01:00
BUFFER_SIZE : int = 256
2022-12-31 16:17:29 +01:00
2023-01-04 00:54:46 +01:00
class Tab ( ) :
colsize : Tuple = ( )
def __init__ ( self , * length : int ) :
self . colsize = length
def blank ( self , text : str = ' - ' ) :
print ( text * sum ( self . colsize ) )
2023-01-05 16:56:52 +01:00
def print ( self , * texts : str , truncate : bool = False ) :
def coltext ( text : str , padding_length : int = 0 , max_length : int = 20 , truncate : bool = False ) - > str :
2023-01-04 15:40:22 +01:00
text = str ( text )
2023-01-05 16:56:52 +01:00
if len ( text ) > = max_length :
if truncate :
text = text [ : ( max_length - 4 ) if max_length > 5 else max_length ] + ' ... '
else :
words = [ ]
lines = [ ]
for word in text . split ( ' ' ) :
if sum ( [ len ( w ) + 1 for w in words ] ) > = max_length :
if len ( lines ) == 0 :
lines . append ( ' ' . join ( words ) + ' \n ' )
else :
lines . append ( ( ' ' * ( padding_length - 1 ) ) + ' ' . join ( words ) + ' \n ' )
words = [ ]
words . append ( word )
lines . append ( ( ' ' * ( padding_length - 1 ) ) + ' ' . join ( words ) )
text = ' ' . join ( lines )
2023-01-04 00:54:46 +01:00
2023-01-05 16:56:52 +01:00
return text + ' ' * ( max_length - len ( text ) )
2023-01-04 00:54:46 +01:00
line = ' '
2023-01-05 16:56:52 +01:00
padding_length = 0
2023-01-04 00:54:46 +01:00
for i , text in enumerate ( texts [ : len ( self . colsize ) ] ) :
2023-01-05 16:56:52 +01:00
line + = coltext ( text , padding_length , self . colsize [ i ] , truncate )
padding_length + = self . colsize [ i ]
2023-01-04 00:54:46 +01:00
print ( line )
def labels ( self , * texts : str , blank_text : str = ' - ' ) :
2023-01-04 15:40:22 +01:00
self . blank ( blank_text )
2023-01-04 00:54:46 +01:00
self . print ( * texts )
self . blank ( blank_text )
2022-12-31 01:56:48 +01:00
2023-01-05 16:56:52 +01:00
# tab = Tab(4, 11, 60)
# tab.print('[*]', 'Notice how even though the input was a set and a tuple', 'Notice how even though the input was a set and a tuple, the output is a list because sorted() returns a new list by definition')
# tab.print('[*]', 'abcdefghij', 'The returned object can be cast to a new type if it needs to match the input type. Be careful if attempting to cast the resulting list back to a set, as a set by definition is unordered')
# exit()
2022-12-31 03:36:55 +01:00
class Telnet :
def __init__ ( self , IP : str , login : str , password : str ) :
import telnetlib
from collections import deque
self . tn = telnetlib . Telnet ( IP , timeout = 15 )
self . fifo = deque ( )
if b ' Login as: ' in self . tn . read_until ( b ' Login as: ' ) :
self . tn . write ( bytes ( login , ' ascii ' ) + b ' \r \n ' )
if b ' Password: ' in self . tn . read_until ( b ' Password: ' ) :
time . sleep ( 0.2 )
self . tn . write ( bytes ( password , ' ascii ' ) + b ' \r \n ' )
if b ' for more information. ' in self . tn . read_until ( b ' Type " help() " for more information. ' ) :
return
raise Exception ( ' Failed to establish a Telnet connection with the board ' )
def __del__ ( self ) :
self . close ( )
def close ( self ) :
try :
self . tn . close ( )
except :
pass
def read ( self , size : int = 1 ) - > bytes :
timeout = 0
while len ( self . fifo ) < size and not timeout > = 8 :
timeout = 0
data = self . tn . read_eager ( )
if len ( data ) :
self . fifo . extend ( data )
timeout = 0
else :
timeout + = 1
time . sleep ( 0.25 )
data = b ' '
while len ( data ) < size and len ( self . fifo ) > 0 :
data + = bytes ( [ self . fifo . popleft ( ) ] )
return data
def write ( self , data : bytes ) :
self . tn . write ( data )
return len ( data )
def inWaiting ( self ) - > int :
n_waiting = len ( self . fifo )
if not n_waiting :
data = self . tn . read_eager ( )
self . fifo . extend ( data )
n_waiting = len ( data )
return n_waiting
2022-12-31 01:56:48 +01:00
class Pyboard ( object ) :
2022-12-31 15:08:59 +01:00
i : int = 0
2022-12-31 03:36:55 +01:00
serial : Union [ Serial , Telnet ]
def __init__ ( self , device : str , baudrate : int = 115200 , login : str = ' ' , password : str = ' ' ) :
is_telnet = device and device . count ( ' . ' ) == 3
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
for _ in range ( 0 , 3 ) :
try :
2022-12-31 03:36:55 +01:00
if is_telnet :
self . serial = Telnet ( device , login , password )
else :
self . serial = Serial ( device , baudrate = baudrate , interCharTimeout = 1 )
2022-12-30 23:33:59 +01:00
break
except :
time . sleep ( 1 )
else :
2023-01-04 18:28:31 +01:00
raise Exception ( f ' Failed to access device: { device } ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
def close ( self ) :
self . serial . close ( )
2022-12-29 17:20:41 +01:00
2023-01-04 00:54:46 +01:00
def transfer_status ( self ) - > int :
2022-12-31 15:08:59 +01:00
arrows = [ ' ◜ ' , ' ◝ ' , ' ◞ ' , ' ◟ ' ]
2023-01-04 00:54:46 +01:00
sys . stdout . write ( f ' [∘] Transfering... { arrows [ self . i ] } \r ' )
2022-12-31 15:08:59 +01:00
sys . stdout . flush ( )
self . i = ( self . i + 1 ) % 4
2022-12-30 23:33:59 +01:00
def stdout_write_bytes ( self , data : str ) :
sys . stdout . buffer . write ( data . replace ( b ' \x04 ' , b ' ' ) )
sys . stdout . buffer . flush ( )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
def send_ok ( self ) - > bytes :
self . serial . write ( b ' \x04 ' )
return b ' OK '
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
def send_ctrl_a ( self ) - > bytes :
# ctrl-A: enter raw REPL
self . serial . write ( b ' \x01 ' )
return b ' raw REPL; CTRL-B to exit \r \n > '
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
def send_ctrl_b ( self ) :
# ctrl-B: exit raw REPL
self . serial . write ( b ' \x02 ' )
2022-12-29 17:20:41 +01:00
2022-12-31 04:10:28 +01:00
def send_ctrl_c ( self ) - > bytes :
2022-12-30 23:33:59 +01:00
# ctrl-C twice: interrupt any running program
2022-12-31 04:10:28 +01:00
for _ in range ( 0 , 2 ) :
2022-12-30 23:33:59 +01:00
self . serial . write ( b ' \x03 ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
return b ' raw REPL; CTRL-B to exit \r \n '
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
def send_ctrl_d ( self ) - > bytes :
# ctrl-D: soft reset
self . serial . write ( b ' \x04 ' )
return b ' soft reboot \r \n '
2022-12-29 17:20:41 +01:00
2022-12-31 15:25:19 +01:00
def read_until ( self , delimiter : bytes , stream_output : bool = False , show_status : bool = False ) - > Optional [ bytes ] :
2022-12-30 23:33:59 +01:00
data = self . serial . read ( 1 )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
if stream_output :
self . stdout_write_bytes ( data )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
timeout = 0
max_len = len ( delimiter )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
while not timeout > = 1000 :
if data . endswith ( delimiter ) :
return data
elif self . serial . inWaiting ( ) > 0 :
timeout = 0
stream_data = self . serial . read ( 1 )
data + = stream_data
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
if stream_output :
self . stdout_write_bytes ( stream_data )
data = data [ - max_len : ]
2022-12-31 15:25:19 +01:00
elif show_status :
2023-01-04 00:54:46 +01:00
self . transfer_status ( )
2022-12-30 23:33:59 +01:00
else :
timeout + = 1
2022-12-31 16:17:29 +01:00
time . sleep ( 0.0001 )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
def __enter__ ( self ) :
self . send_ctrl_c ( )
2022-12-29 17:20:41 +01:00
n = self . serial . inWaiting ( )
2022-12-30 23:33:59 +01:00
2022-12-29 17:20:41 +01:00
while n > 0 :
self . serial . read ( n )
n = self . serial . inWaiting ( )
2022-12-30 23:33:59 +01:00
for _ in range ( 0 , 5 ) :
if self . read_until ( self . send_ctrl_a ( ) ) :
2022-12-29 17:20:41 +01:00
break
2022-12-30 23:33:59 +01:00
time . sleep ( 0.01 )
2022-12-29 17:20:41 +01:00
else :
2022-12-30 23:33:59 +01:00
raise Exception ( ' REPL: could not enter ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
if not self . read_until ( self . send_ctrl_d ( ) ) :
raise Exception ( ' REPL: could not soft reboot ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
if not self . read_until ( self . send_ctrl_c ( ) ) :
raise Exception ( ' REPL: could not interrupt after soft reboot ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
return self . __terminal
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
def __exit__ ( self , a , b , c ) :
self . send_ctrl_b ( )
2022-12-29 17:20:41 +01:00
2022-12-31 15:31:00 +01:00
def __terminal ( self , command : str , stream_output : bool = False ) - > Optional [ str ] :
2022-12-30 23:33:59 +01:00
command = textwrap . dedent ( command )
# send input
if not isinstance ( command , bytes ) :
command = bytes ( command , encoding = ' utf8 ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
if not self . read_until ( b ' > ' ) :
raise Exception ( ' Terminal: prompt has been lost ' )
2022-12-29 17:20:41 +01:00
2022-12-31 01:56:48 +01:00
for i in range ( 0 , len ( command ) , BUFFER_SIZE ) :
2022-12-31 15:25:19 +01:00
if not stream_output :
2023-01-04 00:54:46 +01:00
self . transfer_status ( )
2022-12-31 15:25:19 +01:00
2022-12-31 01:56:48 +01:00
self . serial . write ( command [ i : min ( i + BUFFER_SIZE , len ( command ) ) ] )
2022-12-31 16:17:29 +01:00
time . sleep ( 0.0001 )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
if not self . read_until ( self . send_ok ( ) ) :
raise Exception ( ' Terminal: could not execute command ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
# catch output
2022-12-31 15:31:00 +01:00
data = self . read_until ( b ' \x04 ' , stream_output = stream_output , show_status = not stream_output )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
if not data :
raise Exception ( ' Terminal: timeout waiting for first EOF reception ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
exception = self . read_until ( b ' \x04 ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
if not exception :
raise Exception ( ' Terminal: timeout waiting for second EOF reception ' )
2022-12-29 20:08:50 +01:00
2022-12-30 23:33:59 +01:00
data , exception = ( data [ : - 1 ] . decode ( ' utf-8 ' ) , exception [ : - 1 ] . decode ( ' utf-8 ' ) )
2022-12-29 20:08:50 +01:00
2022-12-30 23:33:59 +01:00
if exception :
2022-12-29 20:08:50 +01:00
reason = ' Traceback (most recent call last): '
2022-12-30 23:33:59 +01:00
traceback = exception . split ( reason )
2022-12-29 20:08:50 +01:00
if len ( traceback ) == 2 :
for call in traceback [ 1 ] [ : - 2 ] . split ( ' \\ r \\ n ' ) :
reason + = f ' { call } \n '
2023-01-04 00:54:46 +01:00
raise Exception ( f ' { " - " * 45 } \n { reason . strip ( ) } ' )
2022-12-30 23:33:59 +01:00
else :
2023-01-04 00:54:46 +01:00
raise Exception ( f ' { " - " * 45 } \n { exception } ' )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
return data . strip ( )
2022-12-29 17:20:41 +01:00
2023-01-04 15:40:22 +01:00
class FileSystem ( object ) :
2023-01-04 00:54:46 +01:00
pyboard : Pyboard
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
def __init__ ( self , pyboard : Pyboard ) :
2023-01-04 00:54:46 +01:00
self . pyboard = pyboard
2022-12-30 23:33:59 +01:00
2022-12-31 01:56:48 +01:00
def checksum ( self , source : str , data : str ) - > str :
2022-12-30 23:33:59 +01:00
output = self . terminal ( f """
def checksum ( data ) :
v = 21
2023-01-05 16:56:52 +01:00
for c in data . decode ( ' utf-8 ' ) :
2022-12-30 23:33:59 +01:00
v ^ = ord ( c )
return v
2023-01-05 16:56:52 +01:00
with open ( ' {source} ' , ' rb ' ) as fh :
2022-12-30 23:33:59 +01:00
print ( checksum ( fh . read ( ) ) )
""" )
if isinstance ( data , bytes ) :
data = data . decode ( ' utf-8 ' )
v = 21
for c in data :
v ^ = ord ( c )
2023-01-04 00:54:46 +01:00
if int ( v ) == int ( output ) :
return ' OK '
return f ' { v } != { output } '
2022-12-30 23:33:59 +01:00
def get ( self , filename : str ) - > Tuple [ bytes , bool ] :
if not filename . startswith ( ' / ' ) :
filename = ' / ' + filename
output = self . terminal ( f """
2022-12-29 17:20:41 +01:00
import sys
import ubinascii
with open ( ' {filename} ' , ' rb ' ) as infile :
while True :
2022-12-31 01:56:48 +01:00
result = infile . read ( { BUFFER_SIZE } )
2022-12-29 17:20:41 +01:00
if result == b ' ' :
break
2022-12-30 23:33:59 +01:00
sys . stdout . write ( ubinascii . hexlify ( result ) )
""" )
output = binascii . unhexlify ( output )
2023-01-04 18:28:31 +01:00
2023-01-05 16:56:52 +01:00
if filename . endswith ( ' .mpy ' ) :
return ( output , ' ??? ' )
2022-12-30 23:33:59 +01:00
return ( output , self . checksum ( filename , output ) )
2022-12-29 17:20:41 +01:00
2022-12-31 01:56:48 +01:00
def download ( self , source : str , destination : str ) - > str :
2022-12-30 23:33:59 +01:00
output , checksum = self . get ( source )
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
with open ( destination , ' wb ' ) as fh :
fh . write ( output )
2022-12-31 01:56:48 +01:00
return checksum
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
def put ( self , filename : str , data : bytes ) - > Tuple [ str , bool ] :
if not filename . startswith ( ' / ' ) :
filename = ' / ' + filename
2022-12-29 17:20:41 +01:00
2022-12-30 23:33:59 +01:00
if not isinstance ( data , bytes ) :
data = bytes ( data , encoding = ' utf8 ' )
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
try :
2022-12-29 16:37:55 +01:00
if os . path . dirname ( filename ) :
2022-12-30 23:33:59 +01:00
self . mkdir ( os . path . dirname ( filename ) )
2022-12-29 16:37:55 +01:00
2023-01-04 00:54:46 +01:00
with self . pyboard as terminal :
2022-12-30 23:33:59 +01:00
size = len ( data )
2023-01-05 16:56:52 +01:00
terminal ( f """ fh = open( ' { filename } ' , ' wb ' ) """ )
2022-12-29 16:37:55 +01:00
2022-12-31 01:56:48 +01:00
for i in range ( 0 , size , BUFFER_SIZE ) :
chunk_size = min ( BUFFER_SIZE , size - i )
2022-12-30 23:33:59 +01:00
chunk = repr ( data [ i : i + chunk_size ] )
terminal ( f """ fh.write( { chunk } ) """ )
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
terminal ( """ fh.close() """ )
except Exception as e :
raise e
2022-12-29 16:37:55 +01:00
2023-01-05 16:56:52 +01:00
if filename . endswith ( ' .mpy ' ) :
return ( filename , ' ??? ' )
2022-12-30 23:33:59 +01:00
return ( filename , self . checksum ( filename , data ) )
2022-12-29 16:37:55 +01:00
2022-12-31 01:56:48 +01:00
def upload ( self , source : str , destination : str ) - > str :
2022-12-30 23:33:59 +01:00
with open ( source , ' rb ' ) as fh :
2022-12-31 01:56:48 +01:00
_ , checksum = self . put ( destination , fh . read ( ) )
return checksum
2022-12-29 20:08:50 +01:00
2022-12-31 01:56:48 +01:00
def ls ( self , dirname : str = ' / ' ) - > Tuple [ int , List , str ] :
2023-01-04 18:28:31 +01:00
dirname = dirname . strip ( ' ./ ' ) . strip ( ' / ' )
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
output = self . terminal ( f """
try :
import os
import json
except ImportError :
import uos as os
import ujson as json
def ls ( dirname ) :
2022-12-31 01:56:48 +01:00
e = [ ]
s = os . stat ( dirname )
if s [ 0 ] == 0x4000 :
2023-01-05 16:56:52 +01:00
if not dirname . endswith ( ' / ' ) :
dirname + = ' / '
e . append ( ( dirname , - 1 ) )
2022-12-31 01:56:48 +01:00
for t in os . ilistdir ( dirname ) :
2023-01-04 18:28:31 +01:00
if dirname . startswith ( ' / ' ) :
dirname = dirname [ 1 : ]
2022-12-31 01:56:48 +01:00
if t [ 1 ] == 0x4000 :
e . extend ( ls ( dirname + t [ 0 ] + ' / ' ) )
else :
e . append ( ( dirname + t [ 0 ] , os . stat ( dirname + t [ 0 ] ) [ 6 ] ) )
else :
e . append ( ( dirname , s [ 6 ] ) )
2023-01-05 16:56:52 +01:00
return sorted ( e )
2022-12-31 01:56:48 +01:00
try :
s = 1
2023-01-05 16:56:52 +01:00
r = ls ( ' {dirname} ' )
2022-12-31 01:56:48 +01:00
x = ' '
except Exception as e :
s = 0
2023-01-05 16:56:52 +01:00
r = [ ( ' {dirname} ' , - 2 ) ]
2022-12-31 01:56:48 +01:00
x = str ( e )
2022-12-29 16:37:55 +01:00
2022-12-31 01:56:48 +01:00
print ( json . dumps ( [ s , r , x ] ) )
2022-12-30 23:33:59 +01:00
""" )
return json . loads ( output )
2022-12-29 16:37:55 +01:00
2022-12-31 01:56:48 +01:00
def rm ( self , filename : str ) - > Tuple [ int , List , str ] :
2022-12-30 23:33:59 +01:00
if not filename . startswith ( ' / ' ) :
filename = ' / ' + filename
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
output = self . terminal ( f """
try :
import os
import json
except ImportError :
import uos as os
import ujson as json
def ls ( dirname ) :
2022-12-31 01:56:48 +01:00
e = [ ]
2023-01-05 16:56:52 +01:00
if not dirname . endswith ( ' / ' ) :
dirname + = ' / '
2022-12-30 23:33:59 +01:00
for t in os . ilistdir ( dirname ) :
2023-01-04 18:28:31 +01:00
if dirname . startswith ( ' / ' ) :
dirname = dirname [ 1 : ]
2022-12-30 23:33:59 +01:00
if t [ 1 ] == 0x4000 :
2022-12-31 01:56:48 +01:00
e . append ( ( dirname + t [ 0 ] + ' / ' , - 1 ) )
e . extend ( ls ( dirname + t [ 0 ] + ' / ' ) )
2022-12-30 23:33:59 +01:00
else :
2022-12-31 01:56:48 +01:00
e . append ( ( dirname + t [ 0 ] , os . stat ( dirname + t [ 0 ] ) [ 6 ] ) )
2023-01-05 16:56:52 +01:00
return sorted ( e , reverse = True )
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
def rm ( filename ) :
r = [ ]
if os . stat ( filename ) [ 0 ] == 0x4000 :
2022-12-31 01:56:48 +01:00
e = ls ( filename )
2022-12-31 03:04:11 +01:00
if filename != ' / ' :
2023-01-05 16:56:52 +01:00
e . append ( ( filename . strip ( ' / ' ) , - 1 ) )
2022-12-31 01:56:48 +01:00
for f , s in e :
if not s == - 1 :
try :
os . remove ( f )
r . append ( ( f , 1 , ' ' ) )
except Exception as e :
r . append ( ( f , 0 , str ( e ) ) )
for f , s in e :
if s == - 1 :
try :
os . rmdir ( f )
r . append ( ( f , 1 , ' ' ) )
except Exception as e :
r . append ( ( f , 0 , str ( e ) ) )
2022-12-30 23:33:59 +01:00
else :
try :
os . remove ( filename )
2022-12-31 01:56:48 +01:00
r . append ( ( filename , 1 , ' ' ) )
2022-12-30 23:33:59 +01:00
except Exception as e :
r . append ( ( filename , 0 , str ( e ) ) )
return r
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
try :
2022-12-31 01:56:48 +01:00
s = 1
2023-01-05 16:56:52 +01:00
r = rm ( ' {filename} ' )
2022-12-31 01:56:48 +01:00
x = ' '
2022-12-30 23:33:59 +01:00
except Exception as e :
2022-12-31 01:56:48 +01:00
s = 0
2023-01-05 16:56:52 +01:00
r = [ ( ' {filename} ' , 0 , str ( e ) ) ]
2022-12-31 01:56:48 +01:00
x = str ( e )
print ( json . dumps ( [ s , r , x ] ) )
2022-12-30 23:33:59 +01:00
""" )
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
return json . loads ( output )
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
def mkdir ( self , dirname : str ) - > List :
if not dirname . startswith ( ' / ' ) :
dirname = ' / ' + dirname
if dirname . endswith ( ' / ' ) :
dirname = dirname [ : - 1 ]
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
output = self . terminal ( f """
try :
import os
import json
except ImportError :
import uos as os
import ujson as json
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
r = [ ]
d = [ ]
2023-01-05 16:56:52 +01:00
for zd in str ( ' {dirname} ' ) . split ( ' / ' ) :
2022-12-30 23:33:59 +01:00
if not zd :
continue
d . append ( zd )
2023-01-05 16:56:52 +01:00
zd = ' / ' . join ( d )
2022-12-30 23:33:59 +01:00
try :
os . mkdir ( zd )
2023-01-05 16:56:52 +01:00
r . append ( ( ' / ' + zd , 1 ) )
2022-12-30 23:33:59 +01:00
except Exception as e :
if str ( e ) . find ( ' EEXIST ' ) :
2023-01-05 16:56:52 +01:00
r . append ( ( ' / ' + zd , 1 ) )
2022-12-30 23:33:59 +01:00
else :
2023-01-05 16:56:52 +01:00
r . append ( ( ' / ' + zd , 0 , str ( e ) ) )
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
print ( json . dumps ( r ) )
""" )
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
return json . loads ( output )
2022-12-29 16:37:55 +01:00
2022-12-30 23:33:59 +01:00
def launch ( self , filename : str ) :
try :
self . terminal ( f """
2023-01-05 16:56:52 +01:00
with open ( ' {filename} ' , ' r ' ) as fh :
2022-12-30 23:33:59 +01:00
exec ( fh . read ( ) )
""" , stream_output=True)
except Exception as e :
print ( str ( e ) )
2022-12-29 16:37:55 +01:00
2022-12-31 15:31:00 +01:00
def terminal ( self , command : str , stream_output : bool = False ) - > str :
2023-01-04 00:54:46 +01:00
with self . pyboard as terminal :
2022-12-30 23:33:59 +01:00
try :
2022-12-31 15:31:00 +01:00
return terminal ( command , stream_output = stream_output )
2022-12-30 23:33:59 +01:00
except Exception as e :
raise e
2022-12-29 16:37:55 +01:00
2022-12-31 01:56:48 +01:00
class Picowatch ( object ) :
2023-01-05 16:56:52 +01:00
def __init__ ( self , pyboard : Pyboard ) :
2023-01-04 15:40:22 +01:00
self . filesystem = FileSystem ( pyboard )
2022-12-31 04:10:28 +01:00
signal . signal ( signal . SIGINT , lambda a , b : self . interupt ( ) )
def interupt ( self ) :
2023-01-04 15:40:22 +01:00
self . filesystem . pyboard . send_ctrl_c ( )
2022-12-31 04:10:28 +01:00
def terminal ( self , command : str ) :
2023-01-04 15:40:22 +01:00
self . filesystem . terminal ( command , stream_output = True )
2022-12-31 01:56:48 +01:00
2023-01-05 16:56:52 +01:00
def internal_ls ( self , filepath : str ) :
queue = [ ]
if filepath == ' / ' :
filepath = os . getcwd ( ) . replace ( os . sep , ' / ' )
else :
filepath = os . path . join ( os . getcwd ( ) , filepath . strip ( ' ./ ' ) . strip ( ' / ' ) ) . replace ( os . sep , ' / ' )
if os . path . isfile ( filepath ) :
queue = [ ( filepath , os . stat ( filepath ) [ 6 ] ) ]
elif os . path . isdir ( filepath ) :
def ls ( dirname : str ) :
e = [ ]
if os . path . isdir ( dirname ) :
if not dirname . endswith ( ' / ' ) :
dirname + = ' / '
for filename in os . listdir ( dirname ) :
if filename . startswith ( ' . ' ) :
continue
filename = dirname + filename
if os . path . isdir ( filename ) :
e . extend ( ls ( filename ) )
else :
e . append ( ( filename , os . stat ( filename ) [ 6 ] ) )
return e
queue = ls ( filepath )
return queue
2023-01-04 15:40:22 +01:00
def listing ( self , filepath : str = ' / ' ) :
filepath = filepath . strip ( ' ./ ' )
2023-01-04 18:28:31 +01:00
tab = Tab ( 4 , 30 , 15 , 100 )
2023-01-04 15:40:22 +01:00
tab . labels ( ' [ ] ' , ' Filename ' , ' Size (kb) ' , ' Exception ' )
status , output , exception = self . filesystem . ls ( filepath )
2022-12-31 01:56:48 +01:00
if status :
2023-01-04 15:40:22 +01:00
for filename , size in output :
2022-12-31 01:56:48 +01:00
if size == - 1 :
2023-01-04 15:40:22 +01:00
tab . print ( ' [*] ' , filename , ' - ' )
2022-12-31 01:56:48 +01:00
else :
2023-01-04 18:28:31 +01:00
tab . print ( ' [*] ' , filename , f ' { round ( size / 1024 , 2 ) } kb ' )
2022-12-31 01:56:48 +01:00
else :
2023-01-04 15:40:22 +01:00
tab . print ( ' [?] ' , filepath , ' ' , str ( exception ) )
2022-12-31 01:56:48 +01:00
2023-01-04 15:40:22 +01:00
def contents ( self , filename : str ) :
filename = filename . strip ( ' ./ ' ) . strip ( ' / ' )
content , _ = self . filesystem . get ( filename )
print ( ' - ' * 50 )
2022-12-31 03:04:11 +01:00
2023-01-04 15:40:22 +01:00
for ln in content . decode ( ' utf-8 ' ) . split ( ' \n ' ) :
print ( ln )
2022-12-31 03:04:11 +01:00
def upload ( self , filepath : str ) :
2023-01-05 16:56:52 +01:00
tab = Tab ( 4 , 30 , 15 , 15 , 100 )
tab . labels ( ' [ ] ' , ' Filename ' , ' Size (kb) ' , ' Checksum ' , ' Exception ' )
2022-12-31 03:04:11 +01:00
2023-01-05 16:56:52 +01:00
for source , size in self . internal_ls ( filepath ) :
destination = source . replace ( os . getcwd ( ) . replace ( os . sep , ' / ' ) , ' ' ) . strip ( ' / ' )
2023-01-04 15:40:22 +01:00
2022-12-31 03:04:11 +01:00
try :
2023-01-05 16:56:52 +01:00
tab . print ( ' [↑] ' , destination , f ' { round ( size / 1024 , 2 ) } kb ' , self . filesystem . upload ( source , destination ) )
2022-12-31 03:04:11 +01:00
except Exception as e :
2023-01-05 16:56:52 +01:00
tab . print ( ' [?] ' , destination , ' ' , ' ' , str ( e ) )
2022-12-31 01:56:48 +01:00
def download ( self , filepath : str ) :
2023-01-04 18:28:31 +01:00
tab = Tab ( 4 , 30 , 15 , 100 )
2023-01-04 18:36:15 +01:00
tab . labels ( ' [ ] ' , ' Filename ' , ' Checksum ' , ' Exception ' )
2023-01-05 16:56:52 +01:00
status , output , exception = self . filesystem . ls ( filepath . strip ( ' ./ ' ) . strip ( ' / ' ) )
2022-12-29 16:37:55 +01:00
2022-12-31 01:56:48 +01:00
if status :
for remote , size in output :
if size == - 1 :
2023-01-05 16:56:52 +01:00
os . makedirs ( os . path . join ( os . getcwd ( ) , remote ) , 777 , exist_ok = True )
2022-12-29 17:20:41 +01:00
2022-12-31 01:56:48 +01:00
for remote , size in output :
2023-01-05 16:56:52 +01:00
destination = os . path . join ( os . getcwd ( ) , remote ) . replace ( os . sep , ' / ' )
2023-01-04 18:28:31 +01:00
2022-12-31 01:56:48 +01:00
if not size == - 1 :
try :
2023-01-04 18:28:31 +01:00
tab . print ( ' [↓] ' , remote , self . filesystem . download ( remote , destination ) )
2022-12-31 01:56:48 +01:00
except Exception as e :
2023-01-04 15:40:22 +01:00
tab . print ( ' [?] ' , remote , ' ' , str ( e ) )
2022-12-31 01:56:48 +01:00
else :
2023-01-04 00:54:46 +01:00
tab . print ( ' [?] ' , filepath , ' ' , exception )
2022-12-31 01:56:48 +01:00
def delete ( self , filepath : str ) :
2023-01-04 18:28:31 +01:00
tab = Tab ( 4 , 30 , 100 )
2023-01-04 15:40:22 +01:00
tab . labels ( ' [ ] ' , ' Filename ' , ' Exception ' )
2023-01-05 16:56:52 +01:00
status , output , exception = self . filesystem . rm ( filepath . strip ( ' ./ ' ) )
2022-12-31 01:56:48 +01:00
if status :
2023-01-04 15:40:22 +01:00
for filename , checked , exception in output :
2022-12-31 01:56:48 +01:00
if checked :
2023-01-04 15:40:22 +01:00
tab . print ( ' [-] ' , filename )
2022-12-31 01:56:48 +01:00
else :
2023-01-04 15:40:22 +01:00
tab . print ( ' [?] ' , filename , exception )
2022-12-31 01:56:48 +01:00
else :
2023-01-04 00:54:46 +01:00
tab . print ( ' [?] ' , filepath , exception )
2023-01-02 11:12:01 +01:00
2023-01-04 15:40:22 +01:00
def compare ( self , filepath : str ) :
2023-01-05 16:56:52 +01:00
content , _ = self . filesystem . get ( filepath . strip ( ' ./ ' ) . strip ( ' / ' ) )
2023-01-04 15:40:22 +01:00
fh , tempname = tempfile . mkstemp ( )
2023-01-02 11:12:01 +01:00
try :
with os . fdopen ( fh , ' wb ' ) as tmp :
tmp . write ( content )
2023-01-05 16:56:52 +01:00
subprocess . Popen ( f ' code --diff " { tempname } " " { os . path . join ( os . getcwd ( ) , filepath ) } " ' , stdout = subprocess . PIPE , shell = True ) . communicate ( )
2023-01-02 11:12:01 +01:00
finally :
2023-01-04 00:54:46 +01:00
input ( ' Press Enter to delete temp file. ' )
2023-01-04 15:40:22 +01:00
os . remove ( tempname )
2023-01-02 11:12:01 +01:00
2023-01-04 15:40:22 +01:00
def status ( self , return_output : bool = False ) :
2023-01-04 00:54:46 +01:00
changes = [ ]
try :
2023-01-04 15:40:22 +01:00
output = subprocess . check_output ( [ ' git ' , ' status ' , ' -s ' ] , stderr = subprocess . STDOUT )
2023-01-04 00:54:46 +01:00
for filename in [ f . strip ( ) for f in output . decode ( ' utf-8 ' ) . split ( ' \n ' ) ] :
if not filename :
continue
status , filename = filename . split ( ' ' )
2023-01-05 16:56:52 +01:00
if status in [ ' A ' , ' M ' , ' ?? ' ] :
changes . append ( ( 1 , filename ) )
elif status == ' D ' :
changes . append ( ( - 1 , filename ) )
2023-01-04 15:40:22 +01:00
except Exception as e :
2023-01-05 16:56:52 +01:00
try :
print ( e . output . decode ( ' utf-8 ' ) . strip ( ) )
except :
print ( e . decode ( ' utf-8 ' ) . strip ( ) )
2023-01-04 15:40:22 +01:00
finally :
if return_output :
return changes
2023-01-04 00:54:46 +01:00
2023-01-04 15:40:22 +01:00
tab = Tab ( 4 , 40 )
tab . labels ( ' [ ] ' , ' Filename ' )
2023-01-02 11:12:01 +01:00
2023-01-04 15:40:22 +01:00
for status , filename in changes :
tab . print ( ' [+] ' if status == 1 else ' [-] ' , filename )
def push ( self ) :
2023-01-04 18:28:31 +01:00
tab = Tab ( 4 , 30 , 15 , 100 )
2023-01-05 16:56:52 +01:00
tab . labels ( ' [ ] ' , ' Filename ' , ' Size (kb) ' , ' Checksum ' , ' Exception ' )
2023-01-04 15:40:22 +01:00
changes = self . status ( return_output = True )
for filepath in [ filename for status , filename in changes if status == - 1 ] :
filepath = filepath . strip ( ' / ' )
status , output , exception = self . filesystem . rm ( filepath )
if status :
for filename , checked , exception in output :
if checked :
2023-01-05 16:56:52 +01:00
tab . print ( ' [-] ' , filename , ' ' , ' DELETED ' )
2023-01-04 15:40:22 +01:00
else :
2023-01-05 16:56:52 +01:00
tab . print ( ' [?] ' , filename , ' ' , ' ' , exception )
2023-01-04 15:40:22 +01:00
else :
tab . print ( ' [?] ' , filepath , ' ' , exception )
queue = [ ]
2022-12-31 01:56:48 +01:00
2023-01-04 15:40:22 +01:00
for filepath in [ filename for status , filename in changes if status == 1 ] :
2023-01-05 16:56:52 +01:00
queue . extend ( self . internal_ls ( filepath ) )
2022-12-31 04:10:28 +01:00
2023-01-05 16:56:52 +01:00
for source , size in queue :
destination = source . replace ( os . getcwd ( ) . replace ( os . sep , ' / ' ) , ' ' ) . strip ( ' / ' )
2022-12-31 04:10:28 +01:00
2023-01-04 15:40:22 +01:00
try :
2023-01-05 16:56:52 +01:00
tab . print ( ' [↑] ' , destination , f ' { round ( size / 1024 , 2 ) } kb ' , self . filesystem . upload ( source , destination ) )
2023-01-04 15:40:22 +01:00
except Exception as e :
2023-01-05 16:56:52 +01:00
tab . print ( ' [?] ' , destination , ' ' , ' ' , str ( e ) )
2023-01-04 15:40:22 +01:00
print ( ' Pico board up to date. ' )
2023-01-05 16:56:52 +01:00
def compile ( self , filename : str ) :
_ , error = mpy_cross . run ( filename , stderr = subprocess . PIPE , stdout = subprocess . PIPE , shell = True ) . communicate ( )
if error :
print ( error . decode ( ' utf-8 ' ) )
else :
print ( f ' MicroPython File from " { filename } " created! ' )
2023-01-04 15:40:22 +01:00
def test ( self , filename : str ) :
2023-01-05 16:56:52 +01:00
with open ( os . path . join ( os . getcwd ( ) , filename ) , ' r ' ) as fh :
2023-01-04 15:40:22 +01:00
self . filesystem . terminal ( fh . read ( ) , stream_output = True )
2023-01-05 16:56:52 +01:00
def launch ( self , filename : str ) :
self . filesystem . launch ( filename )
2022-12-31 04:10:28 +01:00
2022-12-31 15:08:59 +01:00
print ( ' Welcome to Picowatch Terminal ' )
2023-01-05 16:56:52 +01:00
print ( f ' Listening to project: { os . getcwd ( ) . replace ( os . sep , " / " ) } / ' )
2023-01-04 15:40:22 +01:00
picowatch = False
2023-01-04 18:28:31 +01:00
try :
env = dotenv_values ( ' .picowatch ' )
2023-01-05 16:56:52 +01:00
picowatch = Picowatch ( Pyboard ( env [ " DEVICE " ] , env [ " BAUDRATE " ] ) )
2023-01-04 18:36:15 +01:00
print ( f ' Connected automatically to device: { env [ " DEVICE " ] } at a baudrate of: { env [ " BAUDRATE " ] } ' )
2023-01-04 18:28:31 +01:00
except :
while not picowatch :
print ( ' - ' * 50 )
device = input ( ' Port: ' ) . strip ( )
baudrate = input ( ' Baudrate (115200): ' ) . strip ( ) or 115200
try :
2023-01-05 16:56:52 +01:00
picowatch = Picowatch ( Pyboard ( device , baudrate ) )
2023-01-04 18:28:31 +01:00
print ( ' - ' * 50 )
2023-01-05 16:56:52 +01:00
print ( f ' Connected to device: { device } and baudrate: { baudrate } ' )
with open ( os . path . join ( os . getcwd ( ) , ' .picowatch ' ) , ' w+ ' ) as fh :
fh . write ( f ' DEVICE = " { device } " \n ' )
fh . write ( f ' BAUDRATE = { baudrate } \n ' )
2023-01-04 18:28:31 +01:00
except Exception as e :
print ( str ( e ) )
2023-01-04 15:40:22 +01:00
2023-01-04 18:36:15 +01:00
print ( ' - ' * 50 )
2022-12-31 04:10:28 +01:00
picowatch . interupt ( )
while True :
try :
2022-12-31 16:17:29 +01:00
unmessage = input ( ' >>> ' ) . strip ( )
for message in unmessage . split ( ' & ' ) :
match message . strip ( ) . split ( ' ' ) :
2023-01-02 11:12:01 +01:00
case [ ' exit ' ] :
2022-12-31 16:17:29 +01:00
sys . exit ( )
2023-01-04 18:28:31 +01:00
case [ ' help ' ] :
print ( ' TODO ' )
2023-01-02 11:12:01 +01:00
case [ ' reboot ' ] :
2022-12-31 16:17:29 +01:00
picowatch . terminal ( ' help() ' )
2023-01-04 15:40:22 +01:00
case [ ' ls ' | ' list ' , * source ] :
2022-12-31 16:17:29 +01:00
picowatch . listing ( source [ 0 ] if source else ' / ' )
2023-01-04 15:40:22 +01:00
case [ ' cat ' | ' code ' , source ] :
2022-12-31 16:17:29 +01:00
picowatch . contents ( source )
2023-01-04 15:40:22 +01:00
case [ ' rm ' | ' delete ' , source ] :
2022-12-31 16:17:29 +01:00
picowatch . delete ( source )
2023-01-04 15:40:22 +01:00
case [ ' put ' | ' upload ' , source ] :
2022-12-31 16:17:29 +01:00
picowatch . upload ( source )
2023-01-04 15:40:22 +01:00
case [ ' get ' | ' download ' , source ] :
2022-12-31 16:17:29 +01:00
picowatch . download ( source )
2023-01-04 15:40:22 +01:00
case [ ' diff ' | ' compare ' , filename ] :
2023-01-02 11:12:01 +01:00
picowatch . compare ( filename )
case [ ' status ' ] :
2023-01-04 15:40:22 +01:00
picowatch . status ( return_output = False )
2023-01-02 11:12:01 +01:00
case [ ' push ' ] :
2023-01-04 15:40:22 +01:00
picowatch . push ( )
2023-01-05 16:56:52 +01:00
case [ ' mpy ' | ' compile ' , filename ] :
picowatch . compile ( filename )
2023-01-04 15:40:22 +01:00
case [ ' install ' , package_name ] :
2023-01-02 11:12:01 +01:00
pass
case [ ' test ' , filename ] :
2023-01-04 15:40:22 +01:00
picowatch . test ( filename )
2022-12-31 16:17:29 +01:00
case [ ' ! ' ] :
2023-01-04 15:40:22 +01:00
picowatch . test ( ' main.py ' )
2022-12-31 16:17:29 +01:00
case [ ' !! ' ] :
picowatch . launch ( ' main.py ' )
case _ :
if message . startswith ( ' ./ ' ) :
picowatch . launch ( message [ 2 : ] )
elif message :
print ( f ' " { message } " is not recognized. ' )
2022-12-31 04:10:28 +01:00
except Exception as e :
print ( str ( e ) )