serialhdl: Support prepending a warn_prefix to error and log messages

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2021-06-09 14:46:56 -04:00
parent f00281d1e6
commit 31fcd491fd
3 changed files with 55 additions and 41 deletions

View File

@ -415,7 +415,8 @@ class MCU:
if self._name.startswith('mcu '): if self._name.startswith('mcu '):
self._name = self._name[4:] self._name = self._name[4:]
# Serial port # Serial port
self._serial = serialhdl.SerialReader(self._reactor) wp = "mcu '%s': " % (self._name)
self._serial = serialhdl.SerialReader(self._reactor, warn_prefix=wp)
self._baud = 0 self._baud = 0
self._canbus_iface = None self._canbus_iface = None
canbus_uuid = config.get('canbus_uuid', None) canbus_uuid = config.get('canbus_uuid', None)

View File

@ -105,8 +105,8 @@ class Enumeration:
def encode(self, out, v): def encode(self, out, v):
tv = self.enums.get(v) tv = self.enums.get(v)
if tv is None: if tv is None:
raise error("Unknown value '%s' in enumeration '%s'" % ( raise error("Unknown value '%s' in enumeration '%s'"
v, self.enum_name)) % (v, self.enum_name))
self.pt.encode(out, tv) self.pt.encode(out, tv)
def parse(self, s, pos): def parse(self, s, pos):
v, pos = self.pt.parse(s, pos) v, pos = self.pt.parse(s, pos)
@ -221,7 +221,8 @@ class UnknownFormat:
class MessageParser: class MessageParser:
error = error error = error
def __init__(self): def __init__(self, warn_prefix=""):
self.warn_prefix = warn_prefix
self.unknown = UnknownFormat() self.unknown = UnknownFormat()
self.enumerations = {} self.enumerations = {}
self.messages = [] self.messages = []
@ -231,6 +232,8 @@ class MessageParser:
self.version = self.build_versions = "" self.version = self.build_versions = ""
self.raw_identify_data = "" self.raw_identify_data = ""
self._init_messages(DefaultMessages) self._init_messages(DefaultMessages)
def _error(self, msg, *params):
raise error(self.warn_prefix + (msg % params))
def check_packet(self, s): def check_packet(self, s):
if len(s) < MESSAGE_MIN: if len(s) < MESSAGE_MIN:
return 0 return 0
@ -277,7 +280,7 @@ class MessageParser:
mid = self.messages_by_id.get(msgid, self.unknown) mid = self.messages_by_id.get(msgid, self.unknown)
params, pos = mid.parse(s, MESSAGE_HEADER_SIZE) params, pos = mid.parse(s, MESSAGE_HEADER_SIZE)
if pos != len(s)-MESSAGE_TRAILER_SIZE: if pos != len(s)-MESSAGE_TRAILER_SIZE:
raise error("Extra data at end of message") self._error("Extra data at end of message")
params['#name'] = mid.name params['#name'] = mid.name
return params return params
def encode(self, seq, cmd): def encode(self, seq, cmd):
@ -302,10 +305,10 @@ class MessageParser:
msgname = parts[0] msgname = parts[0]
mp = self.messages_by_name.get(msgname) mp = self.messages_by_name.get(msgname)
if mp is None: if mp is None:
raise error("Unknown command: %s" % (msgname,)) self._error("Unknown command: %s", msgname)
if msgformat != mp.msgformat: if msgformat != mp.msgformat:
raise error("Command format mismatch: %s vs %s" % ( self._error("Command format mismatch: %s vs %s",
msgformat, mp.msgformat)) msgformat, mp.msgformat)
return mp return mp
def create_command(self, msg): def create_command(self, msg):
parts = msg.strip().split() parts = msg.strip().split()
@ -314,7 +317,7 @@ class MessageParser:
msgname = parts[0] msgname = parts[0]
mp = self.messages_by_name.get(msgname) mp = self.messages_by_name.get(msgname)
if mp is None: if mp is None:
raise error("Unknown command: %s" % (msgname,)) self._error("Unknown command: %s", msgname)
try: try:
argparts = dict(arg.split('=', 1) for arg in parts[1:]) argparts = dict(arg.split('=', 1) for arg in parts[1:])
for name, value in argparts.items(): for name, value in argparts.items():
@ -330,14 +333,14 @@ class MessageParser:
raise raise
except: except:
#logging.exception("Unable to extract params") #logging.exception("Unable to extract params")
raise error("Unable to extract params from: %s" % (msgname,)) self._error("Unable to extract params from: %s", msgname)
try: try:
cmd = mp.encode_by_name(**argparts) cmd = mp.encode_by_name(**argparts)
except error as e: except error as e:
raise raise
except: except:
#logging.exception("Unable to encode") #logging.exception("Unable to encode")
raise error("Unable to encode: %s" % (msgname,)) self._error("Unable to encode: %s", msgname)
return cmd return cmd
def fill_enumerations(self, enumerations): def fill_enumerations(self, enumerations):
for add_name, add_enums in enumerations.items(): for add_name, add_enums in enumerations.items():
@ -366,7 +369,7 @@ class MessageParser:
msgtype = 'output' msgtype = 'output'
self.messages.append((msgtag, msgtype, msgformat)) self.messages.append((msgtag, msgtype, msgformat))
if msgtag < -32 or msgtag > 95: if msgtag < -32 or msgtag > 95:
raise error("Multi-byte msgtag not supported") self._error("Multi-byte msgtag not supported")
msgid = msgtag & 0x7f msgid = msgtag & 0x7f
if msgtype == 'output': if msgtype == 'output':
self.messages_by_id[msgid] = OutputFormat(msgid, msgformat) self.messages_by_id[msgid] = OutputFormat(msgid, msgformat)
@ -396,7 +399,7 @@ class MessageParser:
raise raise
except Exception as e: except Exception as e:
logging.exception("process_identify error") logging.exception("process_identify error")
raise error("Error during identify: %s" % (str(e),)) self._error("Error during identify: %s", str(e))
def get_version_info(self): def get_version_info(self):
return self.version, self.build_versions return self.version, self.build_versions
def get_messages(self): def get_messages(self):
@ -410,12 +413,12 @@ class MessageParser:
if name not in self.config: if name not in self.config:
if default is not self.sentinel: if default is not self.sentinel:
return default return default
raise error("Firmware constant '%s' not found" % (name,)) self._error("Firmware constant '%s' not found", name)
try: try:
value = parser(self.config[name]) value = parser(self.config[name])
except: except:
raise error("Unable to parse firmware constant %s: %s" % ( self._error("Unable to parse firmware constant %s: %s",
name, self.config[name])) name, self.config[name])
return value return value
def get_constant_float(self, name, default=sentinel): def get_constant_float(self, name, default=sentinel):
return self.get_constant(name, default, parser=float) return self.get_constant(name, default, parser=float)

View File

@ -13,11 +13,12 @@ class error(Exception):
class SerialReader: class SerialReader:
BITS_PER_BYTE = 10. BITS_PER_BYTE = 10.
def __init__(self, reactor): def __init__(self, reactor, warn_prefix=""):
self.reactor = reactor self.reactor = reactor
self.warn_prefix = warn_prefix
# Serial port # Serial port
self.serial_dev = None self.serial_dev = None
self.msgparser = msgproto.MessageParser() self.msgparser = msgproto.MessageParser(warn_prefix=warn_prefix)
# C interface # C interface
self.ffi_main, self.ffi_lib = chelper.get_ffi() self.ffi_main, self.ffi_lib = chelper.get_ffi()
self.serialqueue = None self.serialqueue = None
@ -55,7 +56,10 @@ class SerialReader:
hdl = self.handlers.get(hdl, self.handle_default) hdl = self.handlers.get(hdl, self.handle_default)
hdl(params) hdl(params)
except: except:
logging.exception("Exception in serial callback") logging.exception("%sException in serial callback",
self.warn_prefix)
def _error(self, msg, *params):
raise error(self.warn_prefix + (msg % params))
def _get_identify_data(self, eventtime): def _get_identify_data(self, eventtime):
# Query the "data dictionary" from the micro-controller # Query the "data dictionary" from the micro-controller
identify_data = "" identify_data = ""
@ -64,7 +68,8 @@ class SerialReader:
try: try:
params = self.send_with_response(msg, 'identify_response') params = self.send_with_response(msg, 'identify_response')
except error as e: except error as e:
logging.exception("Wait for identify_response") logging.exception("%sWait for identify_response",
self.warn_prefix)
return None return None
if params['offset'] == len(identify_data): if params['offset'] == len(identify_data):
msgdata = params['data'] msgdata = params['data']
@ -84,10 +89,10 @@ class SerialReader:
completion = self.reactor.register_callback(self._get_identify_data) completion = self.reactor.register_callback(self._get_identify_data)
identify_data = completion.wait(self.reactor.monotonic() + 5.) identify_data = completion.wait(self.reactor.monotonic() + 5.)
if identify_data is None: if identify_data is None:
logging.info("Timeout on connect") logging.info("%sTimeout on connect", self.warn_prefix)
self.disconnect() self.disconnect()
return False return False
msgparser = msgproto.MessageParser() msgparser = msgproto.MessageParser(warn_prefix=self.warn_prefix)
msgparser.process_identify(identify_data) msgparser.process_identify(identify_data)
self.msgparser = msgparser self.msgparser = msgparser
self.register_response(self.handle_unknown, '#unknown') self.register_response(self.handle_unknown, '#unknown')
@ -112,7 +117,7 @@ class SerialReader:
except ValueError: except ValueError:
uuid = -1 uuid = -1
if uuid < 0 or uuid > 0xffffffffffff: if uuid < 0 or uuid > 0xffffffffffff:
raise error("Invalid CAN uuid") self._error("Invalid CAN uuid")
uuid = [(uuid >> (40 - i*8)) & 0xff for i in range(6)] uuid = [(uuid >> (40 - i*8)) & 0xff for i in range(6)]
CANBUS_ID_ADMIN = 0x3f0 CANBUS_ID_ADMIN = 0x3f0
CMD_SET_NODEID = 0x01 CMD_SET_NODEID = 0x01
@ -120,18 +125,19 @@ class SerialReader:
set_id_msg = can.Message(arbitration_id=CANBUS_ID_ADMIN, set_id_msg = can.Message(arbitration_id=CANBUS_ID_ADMIN,
data=set_id_cmd, is_extended_id=False) data=set_id_cmd, is_extended_id=False)
# Start connection attempt # Start connection attempt
logging.info("Starting CAN connect") logging.info("%sStarting CAN connect", self.warn_prefix)
start_time = self.reactor.monotonic() start_time = self.reactor.monotonic()
while 1: while 1:
if self.reactor.monotonic() > start_time + 90.: if self.reactor.monotonic() > start_time + 90.:
raise error("Unable to connect") self._error("Unable to connect")
try: try:
bus = can.interface.Bus(channel=canbus_iface, bus = can.interface.Bus(channel=canbus_iface,
can_filters=filters, can_filters=filters,
bustype='socketcan') bustype='socketcan')
bus.send(set_id_msg) bus.send(set_id_msg)
except can.CanError as e: except can.CanError as e:
logging.warn("Unable to open CAN port: %s", e) logging.warn("%sUnable to open CAN port: %s",
self.warn_prefix, e)
self.reactor.pause(self.reactor.monotonic() + 5.) self.reactor.pause(self.reactor.monotonic() + 5.)
continue continue
bus.close = bus.shutdown # XXX bus.close = bus.shutdown # XXX
@ -145,19 +151,21 @@ class SerialReader:
if got_uuid == bytearray(uuid): if got_uuid == bytearray(uuid):
break break
except: except:
logging.exception("Error in canbus_uuid check") logging.exception("%sError in canbus_uuid check",
logging.info("Failed to match canbus_uuid - retrying..") self.warn_prefix)
logging.info("%sFailed to match canbus_uuid - retrying..",
self.warn_prefix)
self.disconnect() self.disconnect()
def connect_pipe(self, filename): def connect_pipe(self, filename):
logging.info("Starting connect") logging.info("%sStarting connect", self.warn_prefix)
start_time = self.reactor.monotonic() start_time = self.reactor.monotonic()
while 1: while 1:
if self.reactor.monotonic() > start_time + 90.: if self.reactor.monotonic() > start_time + 90.:
raise error("Unable to connect") self._error("Unable to connect")
try: try:
fd = os.open(filename, os.O_RDWR | os.O_NOCTTY) fd = os.open(filename, os.O_RDWR | os.O_NOCTTY)
except OSError as e: except OSError as e:
logging.warn("Unable to open port: %s", e) logging.warn("%sUnable to open port: %s", self.warn_prefix, e)
self.reactor.pause(self.reactor.monotonic() + 5.) self.reactor.pause(self.reactor.monotonic() + 5.)
continue continue
serial_dev = os.fdopen(fd, 'rb+', 0) serial_dev = os.fdopen(fd, 'rb+', 0)
@ -166,11 +174,11 @@ class SerialReader:
break break
def connect_uart(self, serialport, baud, rts=True): def connect_uart(self, serialport, baud, rts=True):
# Initial connection # Initial connection
logging.info("Starting serial connect") logging.info("%sStarting serial connect", self.warn_prefix)
start_time = self.reactor.monotonic() start_time = self.reactor.monotonic()
while 1: while 1:
if self.reactor.monotonic() > start_time + 90.: if self.reactor.monotonic() > start_time + 90.:
raise error("Unable to connect") self._error("Unable to connect")
try: try:
serial_dev = serial.Serial(baudrate=baud, timeout=0, serial_dev = serial.Serial(baudrate=baud, timeout=0,
exclusive=True) exclusive=True)
@ -178,7 +186,8 @@ class SerialReader:
serial_dev.rts = rts serial_dev.rts = rts
serial_dev.open() serial_dev.open()
except (OSError, IOError, serial.SerialException) as e: except (OSError, IOError, serial.SerialException) as e:
logging.warn("Unable to open serial port: %s", e) logging.warn("%sUnable to open serial port: %s",
self.warn_prefix, e)
self.reactor.pause(self.reactor.monotonic() + 5.) self.reactor.pause(self.reactor.monotonic() + 5.)
continue continue
stk500v2_leave(serial_dev, self.reactor) stk500v2_leave(serial_dev, self.reactor)
@ -238,7 +247,7 @@ class SerialReader:
cmd, len(cmd), minclock, reqclock, nid) cmd, len(cmd), minclock, reqclock, nid)
params = completion.wait() params = completion.wait()
if params is None: if params is None:
raise error("Serial connection closed") self._error("Serial connection closed")
return params return params
def send(self, msg, minclock=0, reqclock=0): def send(self, msg, minclock=0, reqclock=0):
cmd = self.msgparser.create_command(msg) cmd = self.msgparser.create_command(msg)
@ -276,15 +285,16 @@ class SerialReader:
return '\n'.join(out) return '\n'.join(out)
# Default message handlers # Default message handlers
def _handle_unknown_init(self, params): def _handle_unknown_init(self, params):
logging.debug("Unknown message %d (len %d) while identifying", logging.debug("%sUnknown message %d (len %d) while identifying",
params['#msgid'], len(params['#msg'])) self.warn_prefix, params['#msgid'], len(params['#msg']))
def handle_unknown(self, params): def handle_unknown(self, params):
logging.warn("Unknown message type %d: %s", logging.warn("%sUnknown message type %d: %s",
params['#msgid'], repr(params['#msg'])) self.warn_prefix, params['#msgid'], repr(params['#msg']))
def handle_output(self, params): def handle_output(self, params):
logging.info("%s: %s", params['#name'], params['#msg']) logging.info("%s%s: %s", self.warn_prefix,
params['#name'], params['#msg'])
def handle_default(self, params): def handle_default(self, params):
logging.warn("got %s", params) logging.warn("%sgot %s", self.warn_prefix, params)
# Class to send a query command and return the received response # Class to send a query command and return the received response
class SerialRetryCommand: class SerialRetryCommand: