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.
		
		
		
		
		
			
		
			
				
					
					
						
							238 lines
						
					
					
						
							6.1 KiB
						
					
					
				
			
		
		
	
	
							238 lines
						
					
					
						
							6.1 KiB
						
					
					
				| #!/usr/bin/env python
 | |
| import os
 | |
| import struct
 | |
| import zmq
 | |
| import time
 | |
| 
 | |
| import selfdrive.messaging as messaging
 | |
| from common.realtime import Ratekeeper
 | |
| from selfdrive.services import service_list
 | |
| from selfdrive.swaglog import cloudlog
 | |
| 
 | |
| # USB is optional
 | |
| try:
 | |
|   import usb1
 | |
|   from usb1 import USBErrorIO, USBErrorOverflow
 | |
| except Exception:
 | |
|   pass
 | |
| 
 | |
| # TODO: rewrite in C to save CPU
 | |
| 
 | |
| SAFETY_NOOUTPUT = 0
 | |
| SAFETY_HONDA = 1
 | |
| SAFETY_TOYOTA = 2
 | |
| SAFETY_TOYOTA_NOLIMITS = 0x1336
 | |
| SAFETY_ALLOUTPUT = 0x1337
 | |
| 
 | |
| # *** serialization functions ***
 | |
| def can_list_to_can_capnp(can_msgs, msgtype='can'):
 | |
|   dat = messaging.new_message()
 | |
|   dat.init(msgtype, len(can_msgs))
 | |
|   for i, can_msg in enumerate(can_msgs):
 | |
|     if msgtype == 'sendcan':
 | |
|       cc = dat.sendcan[i]
 | |
|     else:
 | |
|       cc = dat.can[i]
 | |
|     cc.address = can_msg[0]
 | |
|     cc.busTime = can_msg[1]
 | |
|     cc.dat = str(can_msg[2])
 | |
|     cc.src = can_msg[3]
 | |
|   return dat
 | |
| 
 | |
| def can_capnp_to_can_list(can, src_filter=None):
 | |
|   ret = []
 | |
|   for msg in can:
 | |
|     if src_filter is None or msg.src in src_filter:
 | |
|       ret.append((msg.address, msg.busTime, msg.dat, msg.src))
 | |
|   return ret
 | |
| 
 | |
| # *** can driver ***
 | |
| def can_health():
 | |
|   while 1:
 | |
|     try:
 | |
|       dat = handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xd2, 0, 0, 0x10)
 | |
|       break
 | |
|     except (USBErrorIO, USBErrorOverflow):
 | |
|       cloudlog.exception("CAN: BAD HEALTH, RETRYING")
 | |
|   v, i, started = struct.unpack("IIB", dat[0:9])
 | |
|   # TODO: units
 | |
|   return {"voltage": v, "current": i, "started": bool(started)}
 | |
| 
 | |
| def __parse_can_buffer(dat):
 | |
|   ret = []
 | |
|   for j in range(0, len(dat), 0x10):
 | |
|     ddat = dat[j:j+0x10]
 | |
|     f1, f2 = struct.unpack("II", ddat[0:8])
 | |
|     ret.append((f1 >> 21, f2>>16, ddat[8:8+(f2&0xF)], (f2>>4)&0xF))
 | |
|   return ret
 | |
| 
 | |
| def can_send_many(arr):
 | |
|   snds = []
 | |
|   for addr, _, dat, alt in arr:
 | |
|     snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat
 | |
|     snd = snd.ljust(0x10, '\x00')
 | |
|     snds.append(snd)
 | |
|   while 1:
 | |
|     try:
 | |
|       handle.bulkWrite(3, ''.join(snds))
 | |
|       break
 | |
|     except (USBErrorIO, USBErrorOverflow):
 | |
|       cloudlog.exception("CAN: BAD SEND MANY, RETRYING")
 | |
| 
 | |
| def can_recv():
 | |
|   dat = ""
 | |
|   while 1:
 | |
|     try:
 | |
|       dat = handle.bulkRead(1, 0x10*256)
 | |
|       break
 | |
|     except (USBErrorIO, USBErrorOverflow):
 | |
|       cloudlog.exception("CAN: BAD RECV, RETRYING")
 | |
|   return __parse_can_buffer(dat)
 | |
| 
 | |
| def can_init():
 | |
|   global handle, context
 | |
|   handle = None
 | |
|   cloudlog.info("attempting can init")
 | |
| 
 | |
|   context = usb1.USBContext()
 | |
|   #context.setDebug(9)
 | |
| 
 | |
|   for device in context.getDeviceList(skip_on_error=True):
 | |
|     if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc:
 | |
|       handle = device.open()
 | |
|       handle.claimInterface(0)
 | |
|       handle.controlWrite(0x40, 0xdc, SAFETY_HONDA, 0, b'')
 | |
| 
 | |
|   if handle is None:
 | |
|     print "CAN NOT FOUND"
 | |
|     exit(-1)
 | |
| 
 | |
|   print "got handle"
 | |
|   cloudlog.info("can init done")
 | |
| 
 | |
| def boardd_mock_loop():
 | |
|   context = zmq.Context()
 | |
|   can_init()
 | |
|   handle.controlWrite(0x40, 0xdc, SAFETY_ALLOUTPUT, 0, b'')
 | |
| 
 | |
|   logcan = messaging.sub_sock(context, service_list['can'].port)
 | |
|   sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
 | |
| 
 | |
|   while 1:
 | |
|     tsc = messaging.drain_sock(logcan, wait_for_one=True)
 | |
|     snds = map(lambda x: can_capnp_to_can_list(x.can), tsc)
 | |
|     snd = []
 | |
|     for s in snds:
 | |
|       snd += s
 | |
|     snd = filter(lambda x: x[-1] <= 1, snd)
 | |
|     can_send_many(snd)
 | |
| 
 | |
|     # recv @ 100hz
 | |
|     can_msgs = can_recv()
 | |
|     print "sent %d got %d" % (len(snd), len(can_msgs))
 | |
|     m = can_list_to_can_capnp(can_msgs)
 | |
|     sendcan.send(m.to_bytes())
 | |
| 
 | |
| def boardd_test_loop():
 | |
|   can_init()
 | |
|   cnt = 0
 | |
|   while 1:
 | |
|     can_send_many([[0xbb,0,"\xaa\xaa\xaa\xaa",0], [0xaa,0,"\xaa\xaa\xaa\xaa"+struct.pack("!I", cnt),1]])
 | |
|     #can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",0]])
 | |
|     #can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",1]])
 | |
|     # recv @ 100hz
 | |
|     can_msgs = can_recv()
 | |
|     print "got %d" % (len(can_msgs))
 | |
|     time.sleep(0.01)
 | |
|     cnt += 1
 | |
| 
 | |
| # *** main loop ***
 | |
| def boardd_loop(rate=200):
 | |
|   rk = Ratekeeper(rate)
 | |
|   context = zmq.Context()
 | |
| 
 | |
|   can_init()
 | |
| 
 | |
|   # *** publishes can and health
 | |
|   logcan = messaging.pub_sock(context, service_list['can'].port)
 | |
|   health_sock = messaging.pub_sock(context, service_list['health'].port)
 | |
| 
 | |
|   # *** subscribes to can send
 | |
|   sendcan = messaging.sub_sock(context, service_list['sendcan'].port)
 | |
| 
 | |
|   while 1:
 | |
|     # health packet @ 1hz
 | |
|     if (rk.frame%rate) == 0:
 | |
|       health = can_health()
 | |
|       msg = messaging.new_message()
 | |
|       msg.init('health')
 | |
| 
 | |
|       # store the health to be logged
 | |
|       msg.health.voltage = health['voltage']
 | |
|       msg.health.current = health['current']
 | |
|       msg.health.started = health['started']
 | |
| 
 | |
|       health_sock.send(msg.to_bytes())
 | |
| 
 | |
|     # recv @ 100hz
 | |
|     can_msgs = can_recv()
 | |
| 
 | |
|     # publish to logger
 | |
|     # TODO: refactor for speed
 | |
|     if len(can_msgs) > 0:
 | |
|       dat = can_list_to_can_capnp(can_msgs)
 | |
|       logcan.send(dat.to_bytes())
 | |
| 
 | |
|     # send can if we have a packet
 | |
|     tsc = messaging.recv_sock(sendcan)
 | |
|     if tsc is not None:
 | |
|       can_send_many(can_capnp_to_can_list(tsc.sendcan))
 | |
| 
 | |
|     rk.keep_time()
 | |
| 
 | |
| # *** main loop ***
 | |
| def boardd_proxy_loop(rate=200, address="192.168.2.251"):
 | |
|   rk = Ratekeeper(rate)
 | |
|   context = zmq.Context()
 | |
| 
 | |
|   can_init()
 | |
| 
 | |
|   # *** subscribes can
 | |
|   logcan = messaging.sub_sock(context, service_list['can'].port, addr=address)
 | |
|   # *** publishes to can send
 | |
|   sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
 | |
| 
 | |
|   while 1:
 | |
|     # recv @ 100hz
 | |
|     can_msgs = can_recv()
 | |
|     #for m in can_msgs:
 | |
|     #  print "R:",hex(m[0]), str(m[2]).encode("hex")
 | |
| 
 | |
|     # publish to logger
 | |
|     # TODO: refactor for speed
 | |
|     if len(can_msgs) > 0:
 | |
|       dat = can_list_to_can_capnp(can_msgs, "sendcan")
 | |
|       sendcan.send(dat.to_bytes())
 | |
| 
 | |
|     # send can if we have a packet
 | |
|     tsc = messaging.recv_sock(logcan)
 | |
|     if tsc is not None:
 | |
|       cl = can_capnp_to_can_list(tsc.can)
 | |
|       #for m in cl:
 | |
|       #  print "S:",hex(m[0]), str(m[2]).encode("hex")
 | |
|       can_send_many(cl)
 | |
| 
 | |
|     rk.keep_time()
 | |
| 
 | |
| def main(gctx=None):
 | |
|   if os.getenv("MOCK") is not None:
 | |
|     boardd_mock_loop()
 | |
|   elif os.getenv("PROXY") is not None:
 | |
|     boardd_proxy_loop()
 | |
|   elif os.getenv("BOARDTEST") is not None:
 | |
|     boardd_test_loop()
 | |
|   else:
 | |
|     boardd_loop()
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|   main()
 | |
| 
 |