mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-09 19:39:00 +08:00
4359ba84ed
01942b89 add TODO
b74a456a don't hardcode the lists
ed5a4bf5 add face stds
396a2bb5 add can error counter to controlsState
c6b5c73b Switch default to msgq (#21)
a457ffa0 Fix indentation in readme.md
a1fc8c75 explicitly mention Python for syntax colouring (#20)
19e23931 Fix expected for cameraOdometry and liveCalibration
e7d2f978 Add radar comm issue error
db64cd43 Reserve safety #21 for VAG PQ35/PQ46/NMS (#19)
79d638d5 separate honda safety models between Bosch Giraffe and Bosch Nidec
2614a650 better name
b6b84cda add longitudinal
78f5934a Add canRxErrs to health
67588993 qlog liveCalibration
df80b870 add more stuff to fw log in CarParams
a87805ad fix doxs
4746b208 got doxed
21cf3f55 build on mac
31ac47c2 Add carUnrecognized event
git-subtree-dir: cereal
git-subtree-split: 01942b890d7acf19aecc09432fe5048ba21c0fc9
old-commit-hash: 9504037aa7
222 lines
5.7 KiB
Python
222 lines
5.7 KiB
Python
# must be build with scons
|
|
from .messaging_pyx import Context, Poller, SubSocket, PubSocket # pylint: disable=no-name-in-module, import-error
|
|
from .messaging_pyx import MultiplePublishersError, MessagingError # pylint: disable=no-name-in-module, import-error
|
|
import capnp
|
|
|
|
assert MultiplePublishersError
|
|
assert MessagingError
|
|
|
|
from cereal import log
|
|
from cereal.services import service_list
|
|
|
|
# sec_since_boot is faster, but allow to run standalone too
|
|
try:
|
|
from common.realtime import sec_since_boot
|
|
except ImportError:
|
|
import time
|
|
sec_since_boot = time.time
|
|
print("Warning, using python time.time() instead of faster sec_since_boot")
|
|
|
|
context = Context()
|
|
|
|
def new_message():
|
|
dat = log.Event.new_message()
|
|
dat.logMonoTime = int(sec_since_boot() * 1e9)
|
|
dat.valid = True
|
|
return dat
|
|
|
|
def pub_sock(endpoint):
|
|
sock = PubSocket()
|
|
sock.connect(context, endpoint)
|
|
return sock
|
|
|
|
def sub_sock(endpoint, poller=None, addr="127.0.0.1", conflate=False, timeout=None):
|
|
sock = SubSocket()
|
|
addr = addr.encode('utf8')
|
|
sock.connect(context, endpoint, addr, conflate)
|
|
|
|
if timeout is not None:
|
|
sock.setTimeout(timeout)
|
|
|
|
if poller is not None:
|
|
poller.registerSocket(sock)
|
|
return sock
|
|
|
|
|
|
def drain_sock_raw(sock, wait_for_one=False):
|
|
"""Receive all message currently available on the queue"""
|
|
ret = []
|
|
while 1:
|
|
if wait_for_one and len(ret) == 0:
|
|
dat = sock.receive()
|
|
else:
|
|
dat = sock.receive(non_blocking=True)
|
|
|
|
if dat is None:
|
|
break
|
|
|
|
ret.append(dat)
|
|
|
|
return ret
|
|
|
|
def drain_sock(sock, wait_for_one=False):
|
|
"""Receive all message currently available on the queue"""
|
|
ret = []
|
|
while 1:
|
|
if wait_for_one and len(ret) == 0:
|
|
dat = sock.receive()
|
|
else:
|
|
dat = sock.receive(non_blocking=True)
|
|
|
|
if dat is None: # Timeout hit
|
|
break
|
|
|
|
dat = log.Event.from_bytes(dat)
|
|
ret.append(dat)
|
|
|
|
return ret
|
|
|
|
|
|
# TODO: print when we drop packets?
|
|
def recv_sock(sock, wait=False):
|
|
"""Same as drain sock, but only returns latest message. Consider using conflate instead."""
|
|
dat = None
|
|
|
|
while 1:
|
|
if wait and dat is None:
|
|
rcv = sock.receive()
|
|
else:
|
|
rcv = sock.receive(non_blocking=True)
|
|
|
|
if rcv is None: # Timeout hit
|
|
break
|
|
|
|
dat = rcv
|
|
|
|
if dat is not None:
|
|
dat = log.Event.from_bytes(dat)
|
|
|
|
return dat
|
|
|
|
def recv_one(sock):
|
|
dat = sock.receive()
|
|
if dat is not None:
|
|
dat = log.Event.from_bytes(dat)
|
|
return dat
|
|
|
|
def recv_one_or_none(sock):
|
|
dat = sock.receive(non_blocking=True)
|
|
if dat is not None:
|
|
dat = log.Event.from_bytes(dat)
|
|
return dat
|
|
|
|
def recv_one_retry(sock):
|
|
"""Keep receiving until we get a message"""
|
|
while True:
|
|
dat = sock.receive()
|
|
if dat is not None:
|
|
return log.Event.from_bytes(dat)
|
|
|
|
# TODO: This does not belong in messaging
|
|
def get_one_can(logcan):
|
|
while True:
|
|
can = recv_one_retry(logcan)
|
|
if len(can.can) > 0:
|
|
return can
|
|
|
|
class SubMaster():
|
|
def __init__(self, services, ignore_alive=None, addr="127.0.0.1"):
|
|
self.poller = Poller()
|
|
self.frame = -1
|
|
self.updated = {s : False for s in services}
|
|
self.rcv_time = {s : 0. for s in services}
|
|
self.rcv_frame = {s : 0 for s in services}
|
|
self.alive = {s : False for s in services}
|
|
self.sock = {}
|
|
self.freq = {}
|
|
self.data = {}
|
|
self.logMonoTime = {}
|
|
self.valid = {}
|
|
|
|
if ignore_alive is not None:
|
|
self.ignore_alive = ignore_alive
|
|
else:
|
|
self.ignore_alive = []
|
|
|
|
for s in services:
|
|
if addr is not None:
|
|
self.sock[s] = sub_sock(s, poller=self.poller, addr=addr, conflate=True)
|
|
self.freq[s] = service_list[s].frequency
|
|
|
|
data = new_message()
|
|
try:
|
|
data.init(s)
|
|
except capnp.lib.capnp.KjException:
|
|
# lists
|
|
data.init(s, 0)
|
|
|
|
self.data[s] = getattr(data, s)
|
|
self.logMonoTime[s] = 0
|
|
self.valid[s] = data.valid
|
|
|
|
def __getitem__(self, s):
|
|
return self.data[s]
|
|
|
|
def update(self, timeout=1000):
|
|
msgs = []
|
|
for sock in self.poller.poll(timeout):
|
|
msgs.append(recv_one_or_none(sock))
|
|
self.update_msgs(sec_since_boot(), msgs)
|
|
|
|
def update_msgs(self, cur_time, msgs):
|
|
# TODO: add optional input that specify the service to wait for
|
|
self.frame += 1
|
|
self.updated = dict.fromkeys(self.updated, False)
|
|
for msg in msgs:
|
|
if msg is None:
|
|
continue
|
|
|
|
s = msg.which()
|
|
self.updated[s] = True
|
|
self.rcv_time[s] = cur_time
|
|
self.rcv_frame[s] = self.frame
|
|
self.data[s] = getattr(msg, s)
|
|
self.logMonoTime[s] = msg.logMonoTime
|
|
self.valid[s] = msg.valid
|
|
|
|
for s in self.data:
|
|
# arbitrary small number to avoid float comparison. If freq is 0, we can skip the check
|
|
if self.freq[s] > 1e-5:
|
|
# alive if delay is within 10x the expected frequency
|
|
self.alive[s] = (cur_time - self.rcv_time[s]) < (10. / self.freq[s])
|
|
else:
|
|
self.alive[s] = True
|
|
|
|
def all_alive(self, service_list=None):
|
|
if service_list is None: # check all
|
|
service_list = self.alive.keys()
|
|
return all(self.alive[s] for s in service_list if s not in self.ignore_alive)
|
|
|
|
def all_valid(self, service_list=None):
|
|
if service_list is None: # check all
|
|
service_list = self.valid.keys()
|
|
return all(self.valid[s] for s in service_list)
|
|
|
|
def all_alive_and_valid(self, service_list=None):
|
|
if service_list is None: # check all
|
|
service_list = self.alive.keys()
|
|
return self.all_alive(service_list=service_list) and self.all_valid(service_list=service_list)
|
|
|
|
|
|
class PubMaster():
|
|
def __init__(self, services):
|
|
self.sock = {}
|
|
for s in services:
|
|
self.sock[s] = pub_sock(s)
|
|
|
|
def send(self, s, dat):
|
|
# accept either bytes or capnp builder
|
|
if not isinstance(dat, bytes):
|
|
dat = dat.to_bytes()
|
|
self.sock[s].send(dat)
|