00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 """
00016 ==================
00017 Interactive PyMite
00018 ==================
00019
00020 An interactive command line that runs on a host computer that is connected
00021 to a target device that is running PyMite. The host computer compiles the
00022 interactive statement and converts it to a form that PyMite can handle,
00023 sends that over the connection where the target device loads and interprets it.
00024 The target device then packages any result, sends it to the host computer
00025 and the host computer prints the result.
00026 """
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042 import cmd, code, dis, getopt, os, subprocess, sys
00043 import pmImgCreator
00044
00045
00046 __usage__ = """USAGE:
00047 ipm.py -f pmfeaturesfilename -[d|s /dev/tty] --[desktop | serial=/dev/tty [baud=19200]]
00048
00049 -h Prints this usage message.
00050 --help
00051
00052 -f <fn> Specify the file containing the PM_FEATURES dict to use
00053 -d Specifies a desktop connection; uses pipes to send/receive bytes
00054 --desktop to/from the target, which is the vm also running on the desktop.
00055 ipm spawns the vm and runs ipm-desktop as a subprocess.
00056
00057 -s <port> [<baud>] Specifies the port (device) for a serial connection.
00058 <port> resembles `com5` on Win32 or `/dev/cu.usbmodem1912`.
00059 Optional argument, <baud>, defaults to 19200.
00060
00061 --serial=<port> Specifies the port (device) for a serial connection.
00062
00063 --baud=<baud> Specifies the baud rate for a serial connection.
00064
00065 REQUIREMENTS:
00066
00067 - pySerial package from http://pyserial.sourceforge.net/
00068 """
00069
00070 NEED_PYSERIAL = "Install the pySerial module from http://pyserial.sourceforge.net/"
00071 if not sys.platform.lower().startswith("win"):
00072 PMVM_EXE = "../platform/desktop/main.out"
00073 else:
00074 PMVM_EXE = "../platform/windows/main.exe"
00075 IPM_PROMPT = "ipm> "
00076 IPM_PROMPT2 = ".... "
00077 COMPILE_FN = "<ipm>"
00078 COMPILE_MODE = "single"
00079 INIT_MESSAGE = """Python-on-a-Chip is Copyright 2003, 2006, 2007, 2009, 2010 Dean Hall and others.
00080 Python-on-a-Chip is licensed under the GNU LESSER GENERAL PUBLIC LICENSE V 2.1
00081 PyMite is Copyright 2003, 2006, 2007, 2009, 2010 Dean Hall.
00082 PyMite is licensed under the GNU GENERAL PUBLIC LICENSE V 2.
00083 This software is offered with NO WARRANTY. See LICENSE for details.
00084 """
00085 HELP_MESSAGE = """Type the Python code that you want to run on the target device.
00086 If you see no prompt, type two consecutive returns to exit multiline mode.
00087 Type Ctrl+C to interrupt and Ctrl+D to quit (or Ctrl+Z <enter> on Win32).
00088 """
00089
00090 REPLY_TERMINATOR = '\x04'
00091
00092 if sys.platform.lower().startswith("win"):
00093 EOF_KEY = 'Z'
00094 else:
00095 EOF_KEY = 'D'
00096
00097
00098 class Connection(object):
00099 def open(self,): raise NotImplementedError
00100 def read(self,): raise NotImplementedError
00101 def write(self, msg): raise NotImplementedError
00102 def close(self,): raise NotImplementedError
00103
00104
00105 class PipeConnection(Connection):
00106 """Provides ipm-host to target connection over stdio pipes on the desktop.
00107 This connection should work on any POSIX-compliant OS.
00108 The ipm-device must be spawned as a subprocess
00109 (the executable created when PyMite was built with PLATFORM=desktop).
00110 """
00111 def __init__(self, target=PMVM_EXE):
00112 self.open(target)
00113
00114
00115 def open(self, target):
00116 self.child = subprocess.Popen(target,
00117 bufsize=-1,
00118 stdin=subprocess.PIPE,
00119 stdout=subprocess.PIPE,
00120 stderr=subprocess.PIPE,
00121 )
00122
00123
00124 def read(self,):
00125
00126
00127
00128
00129
00130 chars = []
00131 c = ''
00132 while c != REPLY_TERMINATOR:
00133 c = self.child.stdout.read(1)
00134 if c == '':
00135
00136
00137 break
00138 chars.append(c)
00139 msg = "".join(chars)
00140 return msg
00141
00142
00143 def write(self, msg):
00144 self.child.stdin.write(msg)
00145 self.child.stdin.flush()
00146
00147
00148 def close(self,):
00149 self.write("\0")
00150
00151
00152 class SerialConnection(Connection):
00153 """Provides ipm-host to target connection over a serial device.
00154 This connection should work on any platform that PySerial supports.
00155 The ipm-device must be running at the same baud rate (19200 default).
00156 """
00157
00158 def __init__(self, serdev="/dev/cu.SLAB_USBtoUART", baud=19200):
00159 try:
00160 import serial
00161 except Exception, e:
00162 print NEED_PYSERIAL
00163 raise e
00164
00165 self.s = serial.Serial(serdev, baud)
00166 self.s.setTimeout(4)
00167
00168
00169 def read(self,):
00170
00171
00172
00173 b = bytearray()
00174 c = None
00175 while c != REPLY_TERMINATOR:
00176 c = self.s.read(1)
00177 if len(c) == 0:
00178 break
00179 b.append(c)
00180 return str(b)
00181
00182
00183 def write(self, msg):
00184 self.s.write(msg)
00185 self.s.flush()
00186
00187
00188 def close(self,):
00189 self.s.close()
00190
00191
00192 class Interactive(cmd.Cmd):
00193 """The interactive command line parser accepts typed input line-by-line.
00194 If a statement requires multiple lines to complete, the input
00195 is collected until two sequential end-of-line characters are received.
00196 """
00197 ipmcommands = ("?", "help", "load",)
00198
00199
00200 def __init__(self, conn, pmfn):
00201 cmd.Cmd.__init__(self,)
00202 self.prompt = IPM_PROMPT
00203 self.conn = conn
00204 self.pic = pmImgCreator.PmImgCreator(pmfn)
00205
00206
00207 def do_help(self, *args):
00208 """Prints the help message.
00209 """
00210 self.stdout.write(HELP_MESSAGE)
00211
00212
00213 def do_load(self, *args):
00214 """Loads a module from the host to the target device.
00215 """
00216
00217
00218 fn = args[0]
00219 if not os.path.exists(fn):
00220 self.stdout.write('File "%s" does not exist in %s.\n'
00221 % (fn, os.getcwd()))
00222 return
00223 if not fn.endswith(".py"):
00224 self.stdout.write('Error using "load <module>": '
00225 'module must be a ".py" source file.\n')
00226 return
00227
00228 src = open(fn).read()
00229 code = compile(src, fn, "exec")
00230
00231 img = self.pic.co_to_str(code)
00232
00233 self.conn.write(img)
00234 self.stdout.write(self.conn.read())
00235
00236
00237 def onecmd(self, line):
00238 """Gathers one interactive line of input (gets more lines as needed).
00239 """
00240
00241 if not line:
00242 return
00243
00244
00245 if line == "EOF":
00246 self.conn.close()
00247
00248
00249 self.stdout.write("\n")
00250
00251
00252 self.stop = True
00253 return True
00254
00255
00256 if line.split()[0] in Interactive.ipmcommands:
00257 cmd.Cmd.onecmd(self, line)
00258 return
00259
00260
00261 try:
00262 codeobj = code.compile_command(line, COMPILE_FN, COMPILE_MODE)
00263
00264
00265 if not codeobj:
00266
00267
00268 line += "\n"
00269
00270 while not line.endswith("\n\n") or not codeobj:
00271 self.stdout.write(IPM_PROMPT2)
00272 line += self.stdin.readline()
00273 codeobj = code.compile_command(line,
00274 COMPILE_FN,
00275 COMPILE_MODE)
00276
00277 except Exception, e:
00278 self.stdout.write("%s:%s\n" % (e.__class__.__name__, e))
00279 return
00280
00281
00282
00283
00284
00285 try:
00286 codeimg = self.pic.co_to_str(codeobj)
00287
00288
00289 except Exception, e:
00290 self.stdout.write("%s:%s\n" % (e.__class__.__name__, e))
00291
00292
00293 else:
00294
00295
00296
00297
00298
00299
00300 try:
00301 self.conn.write(codeimg)
00302 except Exception, e:
00303 self.stdout.write(
00304 "Connection write error, type Ctrl+%s to quit.\n" % EOF_KEY)
00305
00306 rv = self.conn.read()
00307 if rv == '':
00308 self.stdout.write(
00309 "Connection read error, type Ctrl+%s to quit.\n" % EOF_KEY)
00310 else:
00311 if rv.endswith(REPLY_TERMINATOR):
00312 self.stdout.write(rv[:-1])
00313 else:
00314 self.stdout.write(rv)
00315
00316
00317 def run(self,):
00318 """Runs the command loop and handles keyboard interrupts (ctrl+C).
00319 The command loop is what calls self.onecmd().
00320 """
00321
00322 print INIT_MESSAGE,
00323 print HELP_MESSAGE,
00324
00325 self.stop = False
00326 while not self.stop:
00327 try:
00328 self.cmdloop()
00329 except KeyboardInterrupt, ki:
00330 print "\n", ki.__class__.__name__
00331
00332
00333
00334 def parse_cmdline():
00335 """Parses the command line for options.
00336 """
00337 baud = 19200
00338 Conn = PipeConnection
00339 serdev = None
00340
00341 try:
00342 opts, args = getopt.getopt(sys.argv[1:], "dhsf:",
00343 ["desktop", "help", "serial=", "baud="])
00344 except Exception, e:
00345 print __usage__
00346 sys.exit()
00347
00348 if not opts:
00349 print __usage__
00350 sys.exit()
00351
00352 for opt in opts:
00353 if opt[0] == "-d" or opt[0] == "--desktop":
00354 Conn = PipeConnection
00355 elif opt[0] == "-s":
00356 Conn = SerialConnection
00357 serdev = args[0]
00358 if len(args) > 1:
00359 baud = int(args[1])
00360 elif opt[0] == "--serial":
00361 Conn = SerialConnection
00362 serdev = opt[1]
00363 elif opt[0] == "--baud":
00364 assert serdev, "--serial must be specified before --baud."
00365 baud = int(opt[1])
00366 elif opt[0] == "-f":
00367 pmfeatures_filename = opt[1]
00368 else:
00369 print __usage__
00370 sys.exit(0)
00371
00372 if Conn == SerialConnection:
00373 c = Conn(serdev, baud)
00374 else:
00375 c = Conn()
00376
00377 return (c, pmfeatures_filename)
00378
00379
00380 def main():
00381 conn, pmfeatures_filename = parse_cmdline()
00382 i = Interactive(conn, pmfeatures_filename)
00383 i.run()
00384
00385
00386 def ser_test():
00387 """Test ipm over serial connection directly.
00388 """
00389 try:
00390 import serial
00391 except Exception, e:
00392 print NEED_PYSERIAL
00393 raise e
00394
00395 pic = pmImgCreator.PmImgCreator("../platform/desktop/pmfeatures.py")
00396 serconn = serial.Serial("/dev/cu.SLAB_USBtoUART", 19200)
00397 serconn.setTimeout(2)
00398
00399 testcode = (
00400 'print "Hello"\n',
00401 'import sys\n',
00402 'print sys.heap()\n',
00403 )
00404
00405 for line in testcode:
00406 print "compiling ``%s``" % line
00407 codeobj = compile(line, COMPILE_FN, COMPILE_MODE)
00408 codeimg = pic.co_to_str(codeobj)
00409 print "codeimg is %d bytes" % len(codeimg)
00410 print "sending codeimg..."
00411 serconn.write(codeimg)
00412 reply = serconn.readline(eol=REPLY_TERMINATOR)
00413 print "reply is %d bytes" % len(reply)
00414 print "reply is:\n%s" % reply
00415
00416
00417 if __name__ == "__main__":
00418 main()