blob: f0e62708cd06cd42207fb377f4fe395ef8e0cce9 [file] [log] [blame]
Chris Liechtiece60cf2015-08-18 02:39:03 +02001#! python
2#
3# Python Serial Port Extension for Win32, Linux, BSD, Jython
4# see __init__.py
5#
6# This module implements a special URL handler that wraps an other port,
7# printint the traffic for debugging purposes
8#
9# (C) 2015 Chris Liechti <cliechti@gmx.net>
10#
11# SPDX-License-Identifier: BSD-3-Clause
12#
13# URL format: spy://port[?option[=value][&option[=value]]]
14# options:
15# - dev=X a file or device to write to
16# - color use escape code to colorize output
17# - hex hex encode the output
18#
19# example:
20# redirect output to an other terminal window on Posix (Linux):
21# python -m serial.tools.miniterm spy:///dev/ttyUSB0?dev=/dev/pts/12\&color
22
23import sys
24import serial
25
26try:
27 import urlparse
28except ImportError:
29 import urllib.parse as urlparse
30try:
31 basestring
32except NameError:
33 basestring = str # python 3
34
35class Serial(serial.Serial):
36 """Just inherit the native Serial port implementation and patch the port property."""
37
38 def __init__(self, *args, **kwargs):
39 super(Serial, self).__init__(*args, **kwargs)
40 self.output = sys.stderr
41 self.hexlify = False
42 self.color = False
43 self.rx_color = '\x1b[32m'
44 self.tx_color = '\x1b[31m'
45
46 @serial.Serial.port.setter
47 def port(self, value):
48 if value is not None:
49 serial.Serial.port.__set__(self, self.fromURL(value))
50
51 def fromURL(self, url):
52 """extract host and port from an URL string"""
53 print(url)
54 parts = urlparse.urlsplit(url)
55 if parts.scheme != "spy":
56 raise serial.SerialException('expected a string in the form "spy://port[?option[=value][&option[=value]]]": not starting with spy:// (%r)' % (parts.scheme,))
57 # process options now, directly altering self
58 for option, values in urlparse.parse_qs(parts.query, True).items():
59 if option == 'dev':
60 self.output = open(values[0], 'w')
61 elif option == 'color':
62 self.color = True
63 elif option == 'hex':
64 self.hexlify = True
65 return ''.join([parts.netloc, parts.path])
66
67 def write(self, tx):
68 if self.color:
69 self.output.write(self.tx_color)
70 if self.hexlify:
71 self.output.write(tx.encode('hex'))
72 else:
73 self.output.write(tx)
74 self.output.flush()
75 return super(Serial, self).write(tx)
76
77 def read(self, size=1):
78 rx = super(Serial, self).read(size)
79 if rx:
80 if self.color:
81 self.output.write(self.rx_color)
82 if self.hexlify:
83 self.output.write(rx.encode('hex'))
84 else:
85 self.output.write(rx)
86 self.output.flush()
87 return rx
88
89
90# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
91if __name__ == '__main__':
92 s = Serial(None)
93 s.port = 'spy:///dev/ttyS0'
94 print(s)
95