ESI and schema docs update
This commit is contained in:
Joel Collins 2019-07-16 11:15:38 +01:00
commit 5424883a38
11 changed files with 708 additions and 176 deletions

View file

@ -54,7 +54,7 @@ class ExtensibleSerialInstrument(object):
ignore_echo = False
port_settings = {}
def __init__(self, port, **kwargs):
def __init__(self, port=None, **kwargs):
"""
Set up the serial port and so on.
"""
@ -64,7 +64,7 @@ class ExtensibleSerialInstrument(object):
self.open(port, False) # Eventually this shouldn't rely on init...
logging.info("Opened ESI connection to port {}".format(port))
def open(self, port, quiet=True):
def open(self, port=None, quiet=True):
"""Open communications with the serial port.
If no port is specified, it will attempt to autodetect. If quiet=True
@ -74,6 +74,8 @@ class ExtensibleSerialInstrument(object):
if hasattr(self,'_ser') and self._ser.isOpen():
if not quiet: logging.warning("Attempted to open an already-open port!")
return
if port is None:
port=self.find_port()
assert port is not None, "We don't have a serial port to open, meaning you didn't specify a valid port. Are you sure the instrument is connected?"
self._ser = serial.Serial(port,**self.port_settings)
#the block above wraps the serial IO layer with a text IO layer
@ -199,15 +201,15 @@ class ExtensibleSerialInstrument(object):
response_regex = response_string
noop = lambda x: x #placeholder null parse function
placeholders = [ #tuples of (regex matching placeholder, regex to replace it with, parse function)
(r"%c",r".", noop),
(r"%(\d+)c",r".{\1}", noop), #TODO support %cn where n is a number of chars
(r"%d",r"[-+]?\d+", int),
(r"%[eEfg]",r"[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?", float),
(r"%i",r"[-+]?(?:0[xX][\dA-Fa-f]+|0[0-7]*|\d+)", lambda x: int(x, 0)), #0=autodetect base
(r"%o",r"[-+]?[0-7]+", lambda x: int(x, 8)), #8 means octal
(r"%s",r"\S+",noop),
(r"%u",r"\d+",int),
(r"%[xX]",r"[-+]?(?:0[xX])?[\dA-Fa-f]+",lambda x: int(x, 16)), #16 forces hexadecimal
(r"%c", r".", noop),
(r"%(\d+)c", r".{\1}", noop), #TODO support %cn where n is a number of chars
(r"%d", r"[-+]?\\d+", int),
(r"%[eEfg]", r"[-+]?(?:\\d+(?:\.\\d*)?|\.\\d+)(?:[eE][-+]?\\d+)?", float),
(r"%i", r"[-+]?(?:0[xX][\\dA-Fa-f]+|0[0-7]*|\\d+)", lambda x: int(x, 0)), #0=autodetect base
(r"%o", r"[-+]?[0-7]+", lambda x: int(x, 8)), #8 means octal
(r"%s", r"\\s+", noop),
(r"%u", r"\\d+", int),
(r"%[xX]", r"[-+]?(?:0[xX])?[\\dA-Fa-f]+", lambda x: int(x, 16)), #16 forces hexadecimal
]
matched_placeholders = []
for placeholder, regex, parse_fun in placeholders:
@ -261,7 +263,32 @@ class ExtensibleSerialInstrument(object):
Usually this function sends a command and checks for a known reply."""
with self.communications_lock:
return True
def find_port(self):
"""Iterate through the available serial ports and query them to see
if our instrument is there."""
with self.communications_lock:
success = False
for port_name, _, _ in serial.tools.list_ports.comports(): #loop through serial ports, apparently 256 is the limit?!
try:
logging.info("Trying port {}".format(port_name))
self.open(port_name)
success = True
logging.info("Success!")
except:
pass
finally:
try:
self.close()
except:
pass #we don't care if there's an error closing the port...
if success:
break #again, make sure this happens *after* closing the port
if success:
return port_name
else:
return None
class OptionalModule(object):
"""This allows a `ExtensibleSerialInstrument` to have optional features.

View file

@ -81,13 +81,8 @@ class Sangaboard(ExtensibleSerialInstrument):
it doesn't need to be named.
"""
# If no port is specified
if not port:
# Scan all available ports, and check for valid firmware
scanned_port = self.scan_ports()
# Initialise basic serial instrument with specified
ExtensibleSerialInstrument.__init__(self, scanned_port, **kwargs)
ExtensibleSerialInstrument.__init__(self, port, **kwargs)
try:
# Make absolutely sure that whatever port we're on is valid
@ -119,44 +114,18 @@ class Sangaboard(ExtensibleSerialInstrument):
logging.error("You may need to update the firmware running on the Sangaboard.")
raise e
def scan_ports(self):
"""Iterate through the available serial ports and query them to see
if our instrument is there."""
logging.debug("Running Sangaboard port scanner")
with self.communications_lock:
success = False
for port_name, _, _ in serial.tools.list_ports.comports(): #loop through serial ports, apparently 256 is the limit?!
try:
logging.info("Trying port {}".format(port_name))
self.open(port_name)
success = True
logging.info("Checking firmware")
fw = self.check_valid_firmware()
if not fw:
success = False
except Exception as e:
logging.warning("Error on port {}".format(port_name))
logging.warning(e)
pass
finally:
try:
self.close()
except:
pass #we don't care if there's an error closing the port...
if success:
break #again, make sure this happens *after* closing the port
if success:
return port_name
else:
return None
def test_communications(self):
"""
Overrides superclass, used in self.open(), and port scanning
"""
return self.check_valid_firmware()
def check_valid_firmware(self):
logging.debug("Running firmware checks")
# Request firmware version from the board
self.firmware = self.query("version",timeout=2).rstrip()
logging.info("Firmware response: {}".format(self.firmware))
# Check for valid firmware string
if self.firmware:
match = re.match(r"Sangaboard Firmware v(([\d]+)(?:\.([\d]+))+)", self.firmware)