You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
4.6 KiB
130 lines
4.6 KiB
9 years ago
|
import os
|
||
8 years ago
|
import opendbc
|
||
9 years ago
|
from collections import defaultdict
|
||
|
|
||
8 years ago
|
from selfdrive.car.honda.hondacan import fix
|
||
9 years ago
|
from common.realtime import sec_since_boot
|
||
|
from common.dbc import dbc
|
||
|
|
||
|
class CANParser(object):
|
||
|
def __init__(self, dbc_f, signals, checks=[]):
|
||
|
### input:
|
||
|
# dbc_f : dbc file
|
||
|
# signals : List of tuples (name, address, ival) where
|
||
|
# - name is the signal name.
|
||
|
# - address is the corresponding message address.
|
||
|
# - ival is the initial value.
|
||
|
# checks : List of pairs (address, frequency) where
|
||
|
# - address is the message address of a message for which health should be
|
||
|
# monitored.
|
||
|
# - frequency is the frequency at which health should be monitored.
|
||
|
|
||
8 years ago
|
self.msgs_ck = set([check[0] for check in checks])
|
||
|
self.frqs = dict(checks)
|
||
9 years ago
|
self.can_valid = False # start with False CAN assumption
|
||
|
# list of received msg we want to monitor counter and checksum for
|
||
|
# read dbc file
|
||
8 years ago
|
self.can_dbc = dbc(os.path.join(opendbc.DBC_PATH, dbc_f))
|
||
9 years ago
|
# initialize variables to initial values
|
||
|
self.vl = {} # signal values
|
||
|
self.ts = {} # time stamp recorded in log
|
||
|
self.ct = {} # current time stamp
|
||
|
self.ok = {} # valid message?
|
||
|
self.cn = {} # message counter
|
||
|
self.cn_vl = {} # message counter mismatch value
|
||
|
self.ck = {} # message checksum status
|
||
|
|
||
|
for _, addr, _ in signals:
|
||
|
self.vl[addr] = {}
|
||
8 years ago
|
self.ts[addr] = {}
|
||
9 years ago
|
self.ct[addr] = sec_since_boot()
|
||
8 years ago
|
self.ok[addr] = True
|
||
9 years ago
|
self.cn[addr] = 0
|
||
|
self.cn_vl[addr] = 0
|
||
|
self.ck[addr] = False
|
||
|
|
||
|
for name, addr, ival in signals:
|
||
|
self.vl[addr][name] = ival
|
||
8 years ago
|
self.ts[addr][name] = 0
|
||
9 years ago
|
|
||
|
self._msgs = [s[1] for s in signals]
|
||
|
self._sgs = [s[0] for s in signals]
|
||
|
|
||
|
self._message_indices = defaultdict(list)
|
||
|
for i, x in enumerate(self._msgs):
|
||
|
self._message_indices[x].append(i)
|
||
|
|
||
|
def update_can(self, can_recv):
|
||
8 years ago
|
msgs_upd = []
|
||
9 years ago
|
cn_vl_max = 5 # no more than 5 wrong counter checks
|
||
|
|
||
8 years ago
|
self.sec_since_boot_cached = sec_since_boot()
|
||
|
|
||
9 years ago
|
# we are subscribing to PID_XXX, else data from USB
|
||
8 years ago
|
for msg, ts, cdat, _ in can_recv:
|
||
9 years ago
|
idxs = self._message_indices[msg]
|
||
|
if idxs:
|
||
8 years ago
|
msgs_upd.append(msg)
|
||
9 years ago
|
# read the entire message
|
||
8 years ago
|
out = self.can_dbc.decode((msg, 0, cdat))[1]
|
||
9 years ago
|
# checksum check
|
||
|
self.ck[msg] = True
|
||
|
if "CHECKSUM" in out.keys() and msg in self.msgs_ck:
|
||
|
# remove checksum (half byte)
|
||
8 years ago
|
ck_portion = cdat[:-1] + chr(ord(cdat[-1]) & 0xF0)
|
||
9 years ago
|
# recalculate checksum
|
||
|
msg_vl = fix(ck_portion, msg)
|
||
|
# compare recalculated vs received checksum
|
||
8 years ago
|
if msg_vl != cdat:
|
||
8 years ago
|
print "CHECKSUM FAIL: " + hex(msg)
|
||
9 years ago
|
self.ck[msg] = False
|
||
|
self.ok[msg] = False
|
||
|
# counter check
|
||
|
cn = 0
|
||
|
if "COUNTER" in out.keys():
|
||
|
cn = out["COUNTER"]
|
||
|
# check counter validity if it's a relevant message
|
||
|
if cn != ((self.cn[msg] + 1) % 4) and msg in self.msgs_ck and "COUNTER" in out.keys():
|
||
|
#print hex(msg), "FAILED COUNTER!"
|
||
|
self.cn_vl[msg] += 1 # counter check failed
|
||
|
else:
|
||
|
self.cn_vl[msg] -= 1 # counter check passed
|
||
|
# message status is invalid if we received too many wrong counter values
|
||
|
if self.cn_vl[msg] >= cn_vl_max:
|
||
8 years ago
|
print "COUNTER WRONG: " + hex(msg)
|
||
9 years ago
|
self.ok[msg] = False
|
||
|
|
||
|
# update msg time stamps and counter value
|
||
8 years ago
|
self.ct[msg] = self.sec_since_boot_cached
|
||
9 years ago
|
self.cn[msg] = cn
|
||
|
self.cn_vl[msg] = min(max(self.cn_vl[msg], 0), cn_vl_max)
|
||
|
|
||
|
# set msg valid status if checksum is good and wrong counter counter is zero
|
||
|
if self.ck[msg] and self.cn_vl[msg] == 0:
|
||
|
self.ok[msg] = True
|
||
|
|
||
|
# update value of signals in the
|
||
|
for ii in idxs:
|
||
|
sg = self._sgs[ii]
|
||
|
self.vl[msg][sg] = out[sg]
|
||
8 years ago
|
self.ts[msg][sg] = ts
|
||
9 years ago
|
|
||
8 years ago
|
# for each message, check if it's too long since last time we received it
|
||
9 years ago
|
self._check_dead_msgs()
|
||
|
|
||
|
# assess overall can validity: if there is one relevant message invalid, then set can validity flag to False
|
||
|
self.can_valid = True
|
||
8 years ago
|
|
||
9 years ago
|
if False in self.ok.values():
|
||
8 years ago
|
#print "CAN INVALID!"
|
||
9 years ago
|
self.can_valid = False
|
||
|
|
||
8 years ago
|
return msgs_upd
|
||
|
|
||
9 years ago
|
def _check_dead_msgs(self):
|
||
|
### input:
|
||
|
## simple stuff for now: msg is not valid if a message isn't received for 10 consecutive steps
|
||
|
for msg in set(self._msgs):
|
||
8 years ago
|
if msg in self.msgs_ck and self.sec_since_boot_cached - self.ct[msg] > 10./self.frqs[msg]:
|
||
9 years ago
|
self.ok[msg] = False
|