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