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