9
9
import argparse
10
10
import binascii
11
11
import re
12
+ import shutil
12
13
from threading import Thread
13
14
from datetime import datetime
14
15
try :
17
18
print ("\033 [1;31mPySerial must be installed, run \033 [1;34m`pip3 install pyserial`\033 [0m\r \n " )
18
19
sys .exit (1 )
19
20
21
+ KEY_NONE = 0x00
22
+ KEY_LEFT = 0x1f
23
+ KEY_RIGHT = 0x1e
24
+ KEY_HOME = 0x10
25
+ KEY_END = 0x03
26
+ KEY_QUIT = 0x11
27
+ KEY_ENTER = 0x0a
28
+ KEY_BACKSPACE = 0x08
29
+ KEY_DELETE = 0x7f
30
+ KEY_TAB = 0x09
31
+ KEY_DUP = 0x04
32
+
20
33
#============
21
34
class PyTerm :
22
35
36
+ KEYMAP = { ## Gets lengthy
37
+ "\x1b [D" : KEY_LEFT ,
38
+ "\x1b [C" : KEY_RIGHT ,
39
+ "\x1b [H" : KEY_HOME , ## in Linux Terminal
40
+ "\x1b OH" : KEY_HOME , ## Picocom, Minicom
41
+ "\x1b [1~" : KEY_HOME , ## Putty
42
+ "\x1b [F" : KEY_END , ## Linux Terminal
43
+ "\x1b OF" : KEY_END , ## Picocom, Minicom
44
+ "\x1b [4~" : KEY_END , ## Putty
45
+ "\x03 " : KEY_DUP , ## Ctrl-C
46
+ "\r " : KEY_ENTER ,
47
+ "\x7f " : KEY_BACKSPACE , ## Ctrl-? (127)
48
+ "\x1b [3~" : KEY_DELETE ,
49
+ "\x11 " : KEY_QUIT , ## Ctrl-Q
50
+ "\x1b q" : KEY_QUIT , ## Alt-Q
51
+ "\n " : KEY_ENTER ,
52
+ "\x04 " : KEY_DUP , ## Ctrl-D
53
+ "\x09 " : KEY_TAB ,
54
+ }
55
+
23
56
#-----------------------------------------------------------------
24
57
def __init__ (self , baudrate = 115200 , device = '/dev/ttyUSB0' , rst = 0 ):
25
58
self .DEVICE = device
26
59
self .BAUDRATE = baudrate
27
60
self .ESCAPECHAR = "\033 "
28
- self .VERSION = "5.1.2 "
61
+ self .VERSION = "5.1.3 "
29
62
self .ShutdownReceiver = False
30
63
self .ReceiverToStdout = True
31
64
self .DefaultTimeout = 0.1
65
+ self .width = 80
66
+ self .height = 80
67
+ self .width , self .height = shutil .get_terminal_size ()
32
68
33
69
print ("\n \033 [1;31m--[ \033 [1;34mMicroPython terminal \033 [1;31m ver. \033 [1;34m" + self .VERSION + "\033 [1;31m ]-- \033 [0m" )
34
70
print ("\033 [1;31m--[ \033 [1;34mPress ESC twice for command mode\033 [1;31m ]-- \033 [0m\n " )
@@ -49,6 +85,9 @@ def __init__(self, baudrate=115200, device='/dev/ttyUSB0', rst=0):
49
85
self .uart .dtr = False
50
86
time .sleep (0.1 )
51
87
self .uart .dtr = True
88
+ else :
89
+ self .uart .write (b'\r \n ' )
90
+
52
91
except Exception as e :
53
92
raise Exception ("\033 [1;31mAccessing \033 [1;37m" + self .DEVICE + " \033 [1;31mfailed\r \n \033 [1;37mPyTerm exit\033 [0m\r \n " )
54
93
@@ -77,6 +116,84 @@ def __init__(self, baudrate=115200, device='/dev/ttyUSB0', rst=0):
77
116
termios .tcsetattr (self .stdinfd , termios .TCSADRAIN , self .oldstdinsettings )
78
117
self .uart .close ()
79
118
119
+ #----------------------
120
+ def clear_to_eol (self ):
121
+ sys .stdout .write ("\x1b [0K" )
122
+ sys .stdout .flush ()
123
+
124
+ #-------------------
125
+ def get_input (self ): ## read from interface/keyboard one byte each and match against function keys
126
+ while True :
127
+ in_buffer = sys .stdin .read (1 )
128
+ if in_buffer == '\x1b ' : ## starting with ESC, must be fct
129
+ while True :
130
+ in_buffer += sys .stdin .read (1 )
131
+ c = in_buffer [- 1 ]
132
+ if c == '~' or (c .isalpha () and c != 'O' ):
133
+ break
134
+ if in_buffer in self .KEYMAP :
135
+ c = self .KEYMAP [in_buffer ]
136
+ return c , None
137
+ elif ord (in_buffer [0 ]) >= 32 :
138
+ return KEY_NONE , in_buffer
139
+
140
+ # Line editor
141
+ #------------------------------------------------
142
+ def line_edit (self , prompt , prompt_len , default ):
143
+ # Write a message and move cursor back
144
+ push_msg = lambda msg : sys .stdout .write (msg + "\b " * len (msg ))
145
+ sys .stdout .write (prompt )
146
+ sys .stdout .write (default )
147
+ sys .stdout .flush ()
148
+ self .clear_to_eol ()
149
+ res = default
150
+ pos = len (res )
151
+ while True :
152
+ key , char = self .get_input () ## Get Char of Fct.
153
+ if key == KEY_NONE : ## char to be inserted
154
+ if (prompt_len + len (res )) < (self .width - 2 ):
155
+ res = res [:pos ] + char + res [pos :]
156
+ sys .stdout .write (res [pos ])
157
+ sys .stdout .flush ()
158
+ pos += len (char )
159
+ push_msg (res [pos :]) ## update tail
160
+ sys .stdout .flush ()
161
+ elif key in (KEY_ENTER , KEY_TAB ): ## Finis
162
+ return res
163
+ elif key in (KEY_QUIT , KEY_DUP ): ## Abort
164
+ return None
165
+ elif key == KEY_LEFT :
166
+ if pos > 0 :
167
+ sys .stdout .write ("\b " )
168
+ sys .stdout .flush ()
169
+ pos -= 1
170
+ elif key == KEY_RIGHT :
171
+ if pos < len (res ):
172
+ sys .stdout .write (res [pos ])
173
+ sys .stdout .flush ()
174
+ pos += 1
175
+ elif key == KEY_HOME :
176
+ sys .stdout .write ("\b " * pos )
177
+ sys .stdout .flush ()
178
+ pos = 0
179
+ elif key == KEY_END :
180
+ sys .stdout .write (res [pos :])
181
+ sys .stdout .flush ()
182
+ pos = len (res )
183
+ elif key == KEY_DELETE : ## Delete
184
+ if pos < len (res ):
185
+ res = res [:pos ] + res [pos + 1 :]
186
+ push_msg (res [pos :] + ' ' ) ## update tail
187
+ sys .stdout .flush ()
188
+ elif key == KEY_BACKSPACE : ## Backspace
189
+ if pos > 0 :
190
+ res = res [:pos - 1 ] + res [pos :]
191
+ sys .stdout .write ("\b " )
192
+ sys .stdout .flush ()
193
+ pos -= 1
194
+ push_msg (res [pos :] + ' ' ) ## update tail
195
+ sys .stdout .flush ()
196
+
80
197
#-----------------------------------------
81
198
def ReceiveData (self , uart , binary = False ):
82
199
data = ""
@@ -406,26 +523,6 @@ def ReadDirFromRemote(self, remote_dir, short=True):
406
523
pass
407
524
return dirlist
408
525
409
- #---------------------
410
- def ReadCommand (self ):
411
- char = ""
412
- command = ""
413
-
414
- while True :
415
- char = sys .stdin .read (1 )
416
- if char == "\r " :
417
- break
418
- elif char == self .ESCAPECHAR :
419
- if len (command ) == 0 :
420
- command = self .ESCAPECHAR
421
- break
422
- else :
423
- sys .stdout .write (char )
424
- sys .stdout .flush ()
425
- command += char
426
-
427
- return command
428
-
429
526
#----------------------
430
527
def Get2ndEscape (self ):
431
528
char = sys .stdin .read (1 )
@@ -447,15 +544,17 @@ def HandleUnbufferedUserInput(self):
447
544
448
545
if char == self .ESCAPECHAR :
449
546
if self .Get2ndEscape ():
450
- print ("\r \n \033 [1;31m--[\033 [1;34mmpTerm command: \033 [0m" , end = "" )
451
- command = self .ReadCommand ()
547
+ prompt = "\033 [1;31m--[\033 [1;34mmpTerm command: \033 [0m"
548
+ print ("\r \n " )
549
+ #command = self.ReadCommand()
550
+ command = self .line_edit (prompt , 19 , '' )
452
551
453
- if command == self . ESCAPECHAR :
454
- sys .stdout .write ("\r \n " )
455
- sys .stdout .flush ()
456
- self . uart . write ( self . ESCAPECHAR . encode ( "utf-8" ) )
552
+ if command is None :
553
+ # sys.stdout.write("\r\n")
554
+ # sys.stdout.flush()
555
+ print ( " \r {} \033 [1;37maborted \033 [0m \033 [0K" . format ( prompt ), end = " \r \n " )
457
556
458
- if command == "exit" :
557
+ elif command == "exit" :
459
558
print ("\r \n \033 [1;34m Exit PyTerm \033 [1;31m]--\033 [0m\r \n " , end = "" )
460
559
break
461
560
@@ -588,6 +687,52 @@ def HandleUnbufferedUserInput(self):
588
687
except Exception as e :
589
688
print ("\r \n Error" , e , end = "\r \n " )
590
689
690
+ elif command [0 :8 ] == "lslocal " :
691
+ try :
692
+ cmd = re .sub (' +' , ' ' , command .strip ()).split (' ' )
693
+ if (len (cmd ) == 2 ) or (len (cmd ) == 3 ):
694
+ short_list = False
695
+ if (len (cmd ) == 3 ):
696
+ if cmd [2 ] == "short" :
697
+ short_list = True
698
+ rdir = cmd [1 ]
699
+ lpath = os .path .abspath (rdir )
700
+ dirlst = os .listdir (rdir )
701
+
702
+ if len (dirlst ) > 0 :
703
+ sys .stdout .write ("\r \n \r \n List of directory '{}':\r \n " .format (lpath ))
704
+ sys .stdout .write ("{}\r \n " .format ("" .rjust (21 + len (lpath ), '-' )))
705
+ if short_list is False :
706
+ dirlist = []
707
+ for f in dirlst :
708
+ file_path = os .path .abspath (lpath + "/" + f )
709
+ st = os .stat (file_path )
710
+ dirlist .append ((f , (st [0 ] & 0x8000 ) == 0 , st [6 ], st [8 ]))
711
+
712
+ dirlist .sort (key = lambda x : (not x [1 ], x [0 ].lower ()))
713
+
714
+ max_name_len = 0
715
+ max_size_len = 0
716
+ for f in dirlist :
717
+ if len (f [0 ]) > max_name_len :
718
+ max_name_len = len (f [0 ])
719
+ if len (str (f [2 ])) > max_size_len :
720
+ max_size_len = len (str (f [2 ]))
721
+ max_name_len += 1
722
+ max_size_len += 1
723
+ for f in dirlist :
724
+ print ("{} {} {} {}" .format (f [0 ].rjust (max_name_len ), " <dir>" if f [1 ] else "<file>" , str (f [2 ]).rjust (max_size_len ), datetime .utcfromtimestamp (f [3 ]).strftime ('%Y-%m-%d %H:%M:%S' )), end = "\r \n " )
725
+ else :
726
+ dirlst .sort (key = lambda name : name .lower ())
727
+ for f in dirlst :
728
+ print ("{}" .format (f ), end = "\r \n " )
729
+ else :
730
+ print ("\r \n No files to list\r \n {}\r \n " .format (dirlist ))
731
+ else :
732
+ print ("\r \n Wrong command arguments" , end = "\r \n " )
733
+ except Exception as e :
734
+ print ("\r \n Error" , e , end = "\r \n " )
735
+
591
736
elif command [0 :9 ] == "baudrate " :
592
737
try :
593
738
cmd = re .sub (' +' , ' ' , command .strip ()).split (' ' )
@@ -621,20 +766,24 @@ def HandleUnbufferedUserInput(self):
621
766
print ("Error" , end = "\r \n " )
622
767
623
768
else :
624
- print (""" \033 [1;37munknown command\033 [0m, use one of the following commands:\r
625
- \033 [1;34m exit \033 [0m - exit the terminal\r
626
- \033 [1;34m version \033 [0m - print version info\r
627
- \033 [1;34m synctime \033 [0m - synchronize device time to the PC time\r
628
- \033 [1;34m baudrate <bdr> \033 [0m - set terminal baudrate\r
629
- \033 [1;34mset_baudrate <bdr> \033 [0m - set device and terminal baudrate\r
630
- \033 [1;34m send <lfile> <rfile>\033 [0m - send file to device\r
631
- \033 [1;34m recv <rfile> <lfile>\033 [0m - receive file from device\r
632
- \033 [1;34m senddir <ldir> <rdir> \033 [0m - send all files from local directory to device's directory\r
633
- \033 [1;34m recvdir <rdir> <ldir> \033 [0m - receive all files from device's directory to local directory\r
634
- \033 [1;34m ls <rdir> [short]\033 [0m - list remote directory, if 'short' is given, only file names are printed\r
635
- """ )
769
+ print ("""\r {}\033 [1;31munknown command !\033 [0m\r \n \033 [1;37mAvailable commands:\033 [0m\033 [0K\r
770
+ \033 [1;34m exit \033 [0m - exit the terminal\r
771
+ \033 [1;34m version \033 [0m - print version info\r
772
+ \033 [1;34m synctime \033 [0m - synchronize device time to the PC time\r
773
+ \033 [1;34m baudrate \033 [0m\033 [1;37mbdr \033 [0m - set terminal baudrate\r
774
+ \033 [1;34mset_baudrate \033 [0m\033 [1;37mbdr \033 [0m - set device and terminal baudrate\r
775
+ \033 [1;34m send \033 [0m\033 [1;37mlfile rfile\033 [0m - send file to device\r
776
+ \033 [1;34m recv \033 [0m\033 [1;37mrfile lfile\033 [0m - receive file from device\r
777
+ \033 [1;34m senddir \033 [0m\033 [1;37mldir rdir \033 [0m - send all files from local directory to device's directory\r
778
+ \033 [1;34m recvdir \033 [0m\033 [1;37mrdir ldir \033 [0m - receive all files from device's directory to local directory\r
779
+ \033 [1;34m ls \033 [0m\033 [1;37mrdir [short]\033 [0m - list remote directory, if 'short' is given, only file names are printed\r
780
+ \033 [1;34m lslocal \033 [0m\033 [1;37mrdir [short]\033 [0m - list local directory, if 'short' is given, only file names are printed\r
781
+ \033 [1;33m Enter \033 [0m - accept and execute command\r
782
+ \033 [1;33m Ctrl-Q \033 [0m - aborts command mode\r
783
+ """ .format (prompt ))
636
784
637
785
print ("\033 [1;34mback to device \033 [1;31m]--\033 [0m\r \n " , end = "" )
786
+ self .uart .write (b'\r \n ' )
638
787
else :
639
788
data = char .encode ("utf-8" )
640
789
self .uart .write (data )
0 commit comments