blob: 22805cc3e84215b898636f83484f4eb8bff71267 [file] [log] [blame]
cliechti6fa19112011-08-19 01:50:49 +00001#! python
2#
cliechti6fa19112011-08-19 01:50:49 +00003# This module implements a special URL handler that uses the port listing to
4# find ports by searching the string descriptions.
5#
Chris Liechti3e02f702015-12-16 23:06:04 +01006# This file is part of pySerial. https://github.com/pyserial/pyserial
Chris Liechti5fe3bdd2015-08-07 00:02:44 +02007# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
Chris Liechtifbdd8a02015-08-09 02:37:45 +02008#
9# SPDX-License-Identifier: BSD-3-Clause
cliechti6fa19112011-08-19 01:50:49 +000010#
Chris Liechti757d3ed2015-12-22 22:30:10 +010011# URL format: hwgrep://<regexp>&<option>
12#
13# where <regexp> is a Python regexp according to the re module
14#
Pierre Grimaud14259d62021-04-19 21:37:33 +020015# violating the normal definition for URLs, the character `&` is used to
Chris Liechti757d3ed2015-12-22 22:30:10 +010016# separate parameters from the arguments (instead of `?`, but the question mark
17# is heavily used in regexp'es)
18#
19# options:
20# n=<N> pick the N'th entry instead of the first one (numbering starts at 1)
Chris Liechti9df194c2015-12-23 20:12:17 +010021# skip_busy tries to open port to check if it is busy, fails on posix as ports are not locked!
cliechti6fa19112011-08-19 01:50:49 +000022
Kurt McKee057387c2018-02-07 22:10:38 -060023from __future__ import absolute_import
24
cliechti6fa19112011-08-19 01:50:49 +000025import serial
26import serial.tools.list_ports
27
Chris Liechti5fe3bdd2015-08-07 00:02:44 +020028try:
29 basestring
30except NameError:
Chris Liechti409e10b2016-02-10 22:40:34 +010031 basestring = str # python 3 pylint: disable=redefined-builtin
cliechti6fa19112011-08-19 01:50:49 +000032
Chris Liechti033f17c2015-08-30 21:28:04 +020033
Chris Liechti5fe3bdd2015-08-07 00:02:44 +020034class Serial(serial.Serial):
35 """Just inherit the native Serial port implementation and patch the port property."""
Chris Liechti409e10b2016-02-10 22:40:34 +010036 # pylint: disable=no-member
Chris Liechti5fe3bdd2015-08-07 00:02:44 +020037
38 @serial.Serial.port.setter
39 def port(self, value):
cliechti6fa19112011-08-19 01:50:49 +000040 """translate port name before storing it"""
cliechti9a3809e2011-08-19 23:43:10 +000041 if isinstance(value, basestring) and value.startswith('hwgrep://'):
Chris Liechti3ad62fb2015-08-29 21:53:32 +020042 serial.Serial.port.__set__(self, self.from_url(value))
cliechti6fa19112011-08-19 01:50:49 +000043 else:
Chris Liechti5fe3bdd2015-08-07 00:02:44 +020044 serial.Serial.port.__set__(self, value)
cliechti6fa19112011-08-19 01:50:49 +000045
Chris Liechti3ad62fb2015-08-29 21:53:32 +020046 def from_url(self, url):
cliechti6fa19112011-08-19 01:50:49 +000047 """extract host and port from an URL string"""
Chris Liechtifbdd8a02015-08-09 02:37:45 +020048 if url.lower().startswith("hwgrep://"):
49 url = url[9:]
Chris Liechti757d3ed2015-12-22 22:30:10 +010050 n = 0
Chris Liechti9df194c2015-12-23 20:12:17 +010051 test_open = False
Chris Liechti757d3ed2015-12-22 22:30:10 +010052 args = url.split('&')
53 regexp = args.pop(0)
54 for arg in args:
55 if '=' in arg:
56 option, value = arg.split('=', 1)
57 else:
58 option = arg
59 value = None
60 if option == 'n':
61 # pick n'th element
62 n = int(value) - 1
63 if n < 1:
Chris Liechtic8f3f822016-06-08 03:35:28 +020064 raise ValueError('option "n" expects a positive integer larger than 1: {!r}'.format(value))
Chris Liechti9df194c2015-12-23 20:12:17 +010065 elif option == 'skip_busy':
66 # open to test if port is available. not the nicest way..
67 test_open = True
Chris Liechti757d3ed2015-12-22 22:30:10 +010068 else:
Chris Liechtic8f3f822016-06-08 03:35:28 +020069 raise ValueError('unknown option: {!r}'.format(option))
cliechti6fa19112011-08-19 01:50:49 +000070 # use a for loop to get the 1st element from the generator
Chris Liechti757d3ed2015-12-22 22:30:10 +010071 for port, desc, hwid in sorted(serial.tools.list_ports.grep(regexp)):
Chris Liechti9df194c2015-12-23 20:12:17 +010072 if test_open:
73 try:
74 s = serial.Serial(port)
75 except serial.SerialException:
76 # it has some error, skip this one
77 continue
78 else:
79 s.close()
Chris Liechti757d3ed2015-12-22 22:30:10 +010080 if n:
81 n -= 1
82 continue
cliechti6fa19112011-08-19 01:50:49 +000083 return port
84 else:
Chris Liechtic8f3f822016-06-08 03:35:28 +020085 raise serial.SerialException('no ports found matching regexp {!r}'.format(url))
cliechti6fa19112011-08-19 01:50:49 +000086
cliechti6fa19112011-08-19 01:50:49 +000087# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
88if __name__ == '__main__':
cliechti6fa19112011-08-19 01:50:49 +000089 s = Serial(None)
90 s.port = 'hwgrep://ttyS0'
Chris Liechti68340d72015-08-03 14:15:48 +020091 print(s)