diff --git a/dist/tools/pyterm/pyterm.py b/dist/tools/pyterm/pyterm.py index c3fbbb9abb77194a46e0ec276dd96edda8a5eeb8..4131df879de67b9aab55920c5c573fc3d0e09278 100755 --- a/dist/tools/pyterm/pyterm.py +++ b/dist/tools/pyterm/pyterm.py @@ -15,7 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA try: @@ -24,16 +25,18 @@ except ImportError: import ConfigParser as configparser -import cmd, serial, socket, sys, threading, readline, time, logging, os, \ - argparse, re, codecs, signal +import cmd, serial, socket, sys, threading, readline, time, logging, \ + os, argparse, re, codecs, signal ### import twisted if available, define dummy classes otherwise try: from twisted.internet import reactor - from twisted.internet.protocol import Protocol, ReconnectingClientFactory + from twisted.internet.protocol import Protocol, \ + ReconnectingClientFactory except ImportError: - logging.getLogger("").warn("Twisted not available, please install it if you" - "want to use pyterm's JSON capabilities") + logging.getLogger("").warn("Twisted not available, please install " + "it if you want to use pyterm's JSON " + "capabilities") class Protocol(): def __init__(self): @@ -67,11 +70,12 @@ default_fmt_str = '%(asctime)s - %(levelname)s # %(message)s' class SerCmd(cmd.Cmd): """Main class for pyterm based on Python's Cmd class. - Runs an interactive terminal that transfer between stdio and serial port. + Runs an interactive terminal that transfer between stdio and serial + port. """ - def __init__(self, port=None, baudrate=None, tcp_serial=None, confdir=None, - conffile=None, host=None, run_name=None): + def __init__(self, port=None, baudrate=None, tcp_serial=None, + confdir=None, conffile=None, host=None, run_name=None): """Constructor. Args: @@ -123,7 +127,8 @@ class SerCmd(cmd.Cmd): ### create Logging object my_millis = "{:.4f}".format(time.time()) - date_str = '{}.{}'.format(time.strftime('%Y%m%d-%H:%M:%S'), my_millis[-4:]) + date_str = '{}.{}'.format(time.strftime('%Y%m%d-%H:%M:%S'), + my_millis[-4:]) self.startup = date_str # create formatter formatter = logging.Formatter(self.fmt_str) @@ -131,8 +136,10 @@ class SerCmd(cmd.Cmd): directory = self.configdir + os.path.sep + self.host if not os.path.exists(directory): os.makedirs(directory) - logging.basicConfig(filename=directory + os.path.sep + self.run_name + '.log', \ - level=logging.DEBUG, format=self.fmt_str) + logging.basicConfig(filename = directory + os.path.sep + + self.run_name + '.log', + level=logging.DEBUG, + format=self.fmt_str) ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) @@ -152,13 +159,16 @@ class SerCmd(cmd.Cmd): # if no serial or TCP is specified use default serial port if not self.port and not self.tcp_serial: - sys.stderr.write("No port specified, using default (%s)!\n" % (defaultport)) + sys.stderr.write("No port specified, using default (%s)!\n" + % (defaultport)) self.port = defaultport # if a TCP port is specified try to connect if self.tcp_serial: - self.logger.info("Connect to localhost:%s" % self.tcp_serial) - for res in socket.getaddrinfo('localhost', self.tcp_serial, \ - socket.AF_UNSPEC, socket.SOCK_STREAM): + self.logger.info("Connect to localhost:%s" + % self.tcp_serial) + for res in socket.getaddrinfo('localhost', self.tcp_serial, + socket.AF_UNSPEC, + socket.SOCK_STREAM): af, socktype, proto, canonname, sa = res try: s = fdsocket(af, socktype, proto) @@ -175,15 +185,16 @@ class SerCmd(cmd.Cmd): if s: self.ser = s else: - self.logger.error("Something went wrong connecting to localhost:%s" - % self.tcp_serial) + self.logger.error("Something went wrong connecting to " + "localhost:%s" % self.tcp_serial) sys.exit(1) # otherwise go for the serial port elif self.port: self.logger.info("Connect to serial port %s" % self.port) self.serial_connect() - # wait until connection is established and fire startup commands to the node + # wait until connection is established and fire startup + # commands to the node time.sleep(1) for cmd in self.init_cmd: self.logger.debug("WRITE ----->>>>>> '" + cmd + "'\n") @@ -195,8 +206,8 @@ class SerCmd(cmd.Cmd): receiver_thread.start() def precmd(self, line): - """Check for command prefixes to distinguish between Pyterm interal - commands and commands that should be send to the node. + """Check for command prefixes to distinguish between Pyterm + interal commands and commands that should be send to the node. """ self.logger.debug("processing line #%s#" % line) if (line.startswith("/")): @@ -204,16 +215,18 @@ class SerCmd(cmd.Cmd): return line def default(self, line): - """In case of no Pyterm specific prefix is detected, split string by - colons and send it to the node. + """In case of no Pyterm specific prefix is detected, split + string by colons and send it to the node. """ - self.logger.debug("%s is no pyterm command, sending to default out" % line) + self.logger.debug("%s is no pyterm command, sending to default " + "out" % line) for tok in line.split(';'): tok = self.get_alias(tok) self.ser.write((tok.strip() + "\n").encode("utf-8")) def do_help(self, line): - """Do not use Cmd's internal help function, but redirect to the node. + """Do not use Cmd's internal help function, but redirect to the + node. """ self.ser.write("help\n".encode("utf-8")) @@ -257,12 +270,14 @@ class SerCmd(cmd.Cmd): if not self.config.has_section("triggers"): self.config.add_section("triggers") for trigger in self.triggers: - self.config.set("triggers", trigger.pattern, self.triggers[trigger]) + self.config.set("triggers", trigger.pattern, + self.triggers[trigger]) if len(self.json_regs): if not self.config.has_section("json_regs"): self.config.add_section("json_regs") for j in self.json_regs: - self.config.set("json_regs", j, self.json_regs[j].pattern) + self.config.set("json_regs", j, + self.json_regs[j].pattern) if len(self.filters): if not self.config.has_section("filters"): self.config.add_section("filters") @@ -285,7 +300,8 @@ class SerCmd(cmd.Cmd): self.config.set("init_cmd", "init_cmd%i" % i, ic) i += 1 - with open(self.configdir + os.path.sep + self.configfile, 'wb') as config_fd: + with open(self.configdir + os.path.sep + self.configfile, 'wb')\ + as config_fd: self.config.write(config_fd) self.logger.info("Config saved") @@ -296,18 +312,21 @@ class SerCmd(cmd.Cmd): print(str(key) + ": " + str(self.__dict__[key])) def do_PYTERM_alias(self, line): - """Pyterm command: Register an alias or show an list of all registered aliases. + """Pyterm command: Register an alias or show an list of all + registered aliases. """ if line.endswith("list"): for alias in self.aliases: - self.logger.info("{} = {}".format(alias, self.aliases[alias])) + self.logger.info("{} = {}".format(alias, + self.aliases[alias])) return if not line.count("="): sys.stderr.write("Usage: /alias <ALIAS> = <CMD>\n") return alias = line.split('=')[0].strip() command = line[line.index('=')+1:].strip() - self.logger.info("adding command %s for alias %s" % (command, alias)) + self.logger.info("adding command %s for alias %s" + % (command, alias)) self.aliases[alias] = command def do_PYTERM_rmalias(self, line): @@ -332,7 +351,8 @@ class SerCmd(cmd.Cmd): return trigger = line.split('=')[0].strip() action = line[line.index('=')+1:].strip() - self.logger.info("adding action %s for trigger %s" % (action, trigger)) + self.logger.info("adding action %s for trigger %s" % (action, + trigger)) self.triggers[re.compile(trigger)] = action def do_PYTERM_rmtrigger(self, line): @@ -372,9 +392,11 @@ class SerCmd(cmd.Cmd): sys.stderr.write("Filter for %s not found\n" % line.strip()) def do_PYTERM_json(self, line): - """Pyterm command: Transfer lines matching this Regex as JSON object. + """Pyterm command: Transfer lines matching this Regex as JSON + object. """ - self.json_regs[line.split(' ')[0].strip()] = re.compile(line.partition(' ')[2].strip()) + self.json_regs[line.split(' ')[0].strip()] = \ + re.compile(line.partition(' ')[2].strip()) def do_PYTERM_unjson(self, line): """Pyterm command: Remove a JSON filter. @@ -383,7 +405,8 @@ class SerCmd(cmd.Cmd): sys.stderr.write("JSON regex with ID %s not found" % line) def do_PYTERM_init(self, line): - """Pyterm command: Add an startup command. (Only useful in addition with /save). + """Pyterm command: Add an startup command. (Only useful in + addition with /save). """ self.init_cmd.append(line.strip()) @@ -391,25 +414,31 @@ class SerCmd(cmd.Cmd): """Internal function to laod configuration from file. """ self.config = configparser.SafeConfigParser() - self.config.read([self.configdir + os.path.sep + self.configfile]) + self.config.read([self.configdir + os.path.sep + \ + self.configfile]) for sec in self.config.sections(): if sec == "filters": for opt in self.config.options(sec): - self.filters.append(re.compile(self.config.get(sec, opt))) + self.filters.append( + re.compile(self.config.get(sec, opt))) if sec == "ignores": for opt in self.config.options(sec): - self.ignores.append(re.compile(self.config.get(sec, opt))) + self.ignores.append( + re.compile(self.config.get(sec, opt))) if sec == "json_regs": for opt in self.config.options(sec): - self.logger.info("add json regex for %s" % self.config.get(sec, opt)) - self.json_regs[opt] = re.compile(self.config.get(sec, opt)) + self.logger.info("add json regex for %s" + % self.config.get(sec, opt)) + self.json_regs[opt] = \ + re.compile(self.config.get(sec, opt)) if sec == "aliases": for opt in self.config.options(sec): self.aliases[opt] = self.config.get(sec, opt) if sec == "triggers": for opt in self.config.options(sec): - self.triggers[re.compile(opt)] = self.config.get(sec, opt) + self.triggers[re.compile(opt)] = \ + self.config.get(sec, opt) if sec == "init_cmd": for opt in self.config.options(sec): self.init_cmd.append(self.config.get(sec, opt)) @@ -419,7 +448,8 @@ class SerCmd(cmd.Cmd): self.__dict__[opt] = self.config.get(sec, opt) def process_line(self, line): - """Processes a valid line from node that should be printed and possibly forwarded. + """Processes a valid line from node that should be printed and + possibly forwarded. Args: line (str): input from node. @@ -427,13 +457,16 @@ class SerCmd(cmd.Cmd): self.logger.info(line) # check if line matches a trigger and fire the command(s) for trigger in self.triggers: - self.logger.debug("comparing input %s to trigger %s" % (line, trigger.pattern)) + self.logger.debug("comparing input %s to trigger %s" + % (line, trigger.pattern)) m = trigger.search(line) if m: self.onecmd(self.precmd(self.triggers[trigger])) - # ckecking if the line should be sent as JSON object to a tcp server - if (len(self.json_regs)) and self.factory and self.factory.myproto: + # ckecking if the line should be sent as JSON object to a tcp + # server + if (len(self.json_regs)) and self.factory and \ + self.factory.myproto: for j in self.json_regs: m = self.json_regs[j].search(line) if m: @@ -446,9 +479,11 @@ class SerCmd(cmd.Cmd): json_obj += '"date":%s, ' % int(time.time()*1000) for g in m.groupdict(): try: - json_obj += '"%s":%d, ' % (g, int(m.groupdict()[g])) + json_obj += '"%s":%d, ' \ + % (g, int(m.groupdict()[g])) except ValueError: - json_obj += '"%s":"%s", ' % (g, m.groupdict()[g]) + json_obj += '"%s":"%s", ' \ + % (g, m.groupdict()[g]) # eliminate the superfluous last ", " json_obj = json_obj[:-2] @@ -457,7 +492,8 @@ class SerCmd(cmd.Cmd): self.factory.myproto.sendMessage(json_obj) def handle_line(self, line): - """Handle line from node and check for further processing requirements. + """Handle line from node and check for further processing + requirements. Args: line (str): input line from node. @@ -494,15 +530,18 @@ class SerCmd(cmd.Cmd): while (1): # check if serial port can be accessed. try: - sr = codecs.getreader("UTF-8")(self.ser, errors='replace') + sr = codecs.getreader("UTF-8")(self.ser, + errors='replace') c = sr.read(1) # try to re-open it with a timeout of 1s otherwise except (serial.SerialException, ValueError) as se: - self.logger.warn("Serial port disconnected, waiting to get reconnected...") + self.logger.warn("Serial port disconnected, waiting to " + "get reconnected...") self.ser.close() time.sleep(1) if os.path.exists(self.port): - self.logger.warn("Try to reconnect to %s again..." % (self.port)) + self.logger.warn("Try to reconnect to %s again..." + % (self.port)) self.serial_connect() continue if c == '\n' or c == '\r': @@ -534,11 +573,13 @@ class PytermClientFactory(ReconnectingClientFactory): def clientConnectionLost(self, connector, reason): if reactor.running: print('Lost connection. Reason:', reason) - ReconnectingClientFactory.clientConnectionLost(self, connector, reason) + ReconnectingClientFactory.clientConnectionLost(self, connector, + reason) def clientConnectionFailed(self, connector, reason): print('Connection failed. Reason:', reason) - ReconnectingClientFactory.clientConnectionFailed(self, connector, + ReconnectingClientFactory.clientConnectionFailed(self, + connector, reason) def __stop_reactor(signum, stackframe): @@ -552,29 +593,36 @@ class fdsocket(socket.socket): try: return self.sendall(string) except socket.error as e: - logging.getLogger("").warn("Error in TCP connection (%s), closing down" % str(e)) + logging.getLogger("").warn("Error in TCP connection (%s), " + "closing down" % str(e)) self.close() sys.exit(0) if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Pyterm - The Python terminal program") + parser = argparse.ArgumentParser(description="Pyterm - The Python " + "terminal program") parser.add_argument("-p", "--port", - help="Specifies the serial port to use, default is %s" % defaultport, + help="Specifies the serial port to use, default is %s" + % defaultport, default=defaultport) parser.add_argument("-ts", "--tcp-serial", help="Connect to a TCP port instead of a serial port") parser.add_argument("-b", "--baudrate", - help="Specifies baudrate for the serial port, default is %s" % defaultbaud, + help="Specifies baudrate for the serial port, default is %s" + % defaultbaud, default=defaultbaud) parser.add_argument('-d', '--directory', - help="Specify the Pyterm directory, default is %s" % defaultdir, + help="Specify the Pyterm directory, default is %s" + % defaultdir, default=defaultdir) parser.add_argument("-c", "--config", - help="Specify the config filename, default is %s" % defaultfile, + help="Specify the config filename, default is %s" + % defaultfile, default=defaultfile) parser.add_argument("-s", "--server", - help="Connect via TCP to this server to send output as JSON") + help="Connect via TCP to this server to send output as " + "JSON") parser.add_argument("-P", "--tcp_port", type=int, help="Port at the JSON server") parser.add_argument("-H", "--host", @@ -583,15 +631,17 @@ if __name__ == "__main__": help="Run name, used for logfile") args = parser.parse_args() - myshell = SerCmd(args.port, args.baudrate, args.tcp_serial, args.directory, args.config, args.host, args.run_name) + myshell = SerCmd(args.port, args.baudrate, args.tcp_serial, + args.directory, args.config, args.host, args.run_name) myshell.prompt = '' if args.server and args.tcp_port: myfactory = PytermClientFactory() reactor.connectTCP(args.server, args.tcp_port, myfactory) myshell.factory = myfactory - reactor.callInThread(myshell.cmdloop, "Welcome to pyterm!\nType '/exit' to exit.") + reactor.callInThread(myshell.cmdloop, "Welcome to pyterm!\n" + "Type '/exit' to exit.") signal.signal(signal.SIGINT, __stop_reactor) reactor.run() else: - myshell.cmdloop("Welcome to pyterm!\nType 'exit' to exit.") + myshell.cmdloop("Welcome to pyterm!\nType '/exit' to exit.")