parent
581906f7da
commit
81ebf6b142
70 changed files with 1841 additions and 483 deletions
@ -1,6 +1,9 @@ |
||||
.PHONY: all |
||||
|
||||
# TODO: Add a global build system to openpilot
|
||||
code_dir := $(shell pwd)
|
||||
|
||||
# TODO: Add a global build system
|
||||
|
||||
.PHONY: all |
||||
all: |
||||
cd /data/openpilot/selfdrive && PYTHONPATH=/data/openpilot PREPAREONLY=1 /data/openpilot/selfdrive/manager.py
|
||||
cd selfdrive && PYTHONPATH=$(code_dir) PREPAREONLY=1 ./manager.py
|
||||
|
||||
|
@ -1 +1 @@ |
||||
Subproject commit b8c0034c5d6ded3d85a4f0414b3509baae35a34b |
||||
Subproject commit 835a9739d6721e351e1814439b55b6c4212f7b85 |
@ -1 +1 @@ |
||||
Subproject commit 5ea4df111b735ce9efb27ab2e0f87837210f6fc3 |
||||
Subproject commit 849f68879d1ceacbf1f9d4174e16e1cd14527383 |
@ -0,0 +1,34 @@ |
||||
# Car button codes |
||||
class CruiseButtons: |
||||
RES_ACCEL = 4 |
||||
DECEL_SET = 3 |
||||
CANCEL = 2 |
||||
MAIN = 1 |
||||
|
||||
|
||||
#car chimes: enumeration from dbc file. Chimes are for alerts and warnings |
||||
class CM: |
||||
MUTE = 0 |
||||
SINGLE = 3 |
||||
DOUBLE = 4 |
||||
REPEATED = 1 |
||||
CONTINUOUS = 2 |
||||
|
||||
|
||||
#car beepss: enumeration from dbc file. Beeps are for activ and deactiv |
||||
class BP: |
||||
MUTE = 0 |
||||
SINGLE = 3 |
||||
TRIPLE = 2 |
||||
REPEATED = 1 |
||||
|
||||
class AH: |
||||
#[alert_idx, value] |
||||
# See dbc files for info on values" |
||||
NONE = [0, 0] |
||||
FCW = [1, 0x8] |
||||
STEER = [2, 1] |
||||
BRAKE_PRESSED = [3, 10] |
||||
GEAR_NOT_D = [4, 6] |
||||
SEATBELT = [5, 5] |
||||
SPEED_TOO_HIGH = [6, 8] |
@ -0,0 +1,236 @@ |
||||
from common.numpy_fast import clip, interp |
||||
from common.realtime import sec_since_boot |
||||
from selfdrive.boardd.boardd import can_list_to_can_capnp |
||||
from selfdrive.controls.lib.drive_helpers import rate_limit |
||||
from selfdrive.car.toyota.toyotacan import make_can_msg, create_video_target,\ |
||||
create_steer_command, create_ui_command, \ |
||||
create_ipas_steer_command, create_accel_command |
||||
|
||||
|
||||
ACCEL_HYST_GAP = 0.02 # don't change accel command for small oscilalitons within this value |
||||
ACCEL_MAX = 1500 # 1.5 m/s2 |
||||
ACCEL_MIN = -3000 # 3 m/s2 |
||||
ACCEL_SCALE = max(ACCEL_MAX, -ACCEL_MIN) |
||||
|
||||
STEER_MAX = 1500 |
||||
STEER_DELTA_UP = 10 # 1.5s time to peak torque |
||||
STEER_DELTA_DOWN = 45 # lower than 50 otherwise the Rav4 faults (Prius seems ok though) |
||||
STEER_ERROR_MAX = 500 # max delta between torque cmd and torque motor |
||||
|
||||
STEER_IPAS_MAX = 340 |
||||
STEER_IPAS_DELTA_MAX = 3 |
||||
|
||||
class CAR: |
||||
PRIUS = "TOYOTA PRIUS 2017" |
||||
RAV4 = "TOYOTA RAV4 2017" |
||||
|
||||
class ECU: |
||||
CAM = 0 # camera |
||||
DSU = 1 # driving support unit |
||||
APGS = 2 # advanced parking guidance system |
||||
|
||||
|
||||
TARGET_IDS = [0x340, 0x341, 0x342, 0x343, 0x344, 0x345, |
||||
0x363, 0x364, 0x365, 0x370, 0x371, 0x372, |
||||
0x373, 0x374, 0x375, 0x380, 0x381, 0x382, |
||||
0x383] |
||||
|
||||
# addr, [ecu, bus, 1/freq*100, vl] |
||||
STATIC_MSGS = {0x141: (ECU.DSU, (CAR.PRIUS, CAR.RAV4), 1, 2, '\x00\x00\x00\x46'), |
||||
0x128: (ECU.DSU, (CAR.PRIUS, CAR.RAV4), 1, 3, '\xf4\x01\x90\x83\x00\x37'), |
||||
|
||||
0x292: (ECU.APGS, (CAR.PRIUS, CAR.RAV4), 0, 3, '\x00\x00\x00\x00\x00\x00\x00\x9e'), |
||||
0x283: (ECU.DSU, (CAR.PRIUS, CAR.RAV4), 0, 3, '\x00\x00\x00\x00\x00\x00\x8c'), |
||||
0x2E6: (ECU.DSU, (CAR.PRIUS,), 0, 3, '\xff\xf8\x00\x08\x7f\xe0\x00\x4e'), |
||||
0x2E7: (ECU.DSU, (CAR.PRIUS,), 0, 3, '\xa8\x9c\x31\x9c\x00\x00\x00\x02'), |
||||
|
||||
0x240: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 1, 5, '\x00\x10\x01\x00\x10\x01\x00'), |
||||
0x241: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 1, 5, '\x00\x10\x01\x00\x10\x01\x00'), |
||||
0x244: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 1, 5, '\x00\x10\x01\x00\x10\x01\x00'), |
||||
0x245: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 1, 5, '\x00\x10\x01\x00\x10\x01\x00'), |
||||
0x248: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 1, 5, '\x00\x00\x00\x00\x00\x00\x01'), |
||||
0x344: (ECU.DSU, (CAR.PRIUS, CAR.RAV4), 0, 5, '\x00\x00\x01\x00\x00\x00\x00\x50'), |
||||
|
||||
0x160: (ECU.DSU, (CAR.PRIUS, CAR.RAV4), 1, 7, '\x00\x00\x08\x12\x01\x31\x9c\x51'), |
||||
0x161: (ECU.DSU, (CAR.PRIUS, CAR.RAV4), 1, 7, '\x00\x1e\x00\x00\x00\x80\x07'), |
||||
|
||||
0x32E: (ECU.APGS, (CAR.PRIUS, CAR.RAV4), 0, 20, '\x00\x00\x00\x00\x00\x00\x00\x00'), |
||||
0x33E: (ECU.DSU, (CAR.PRIUS,), 0, 20, '\x0f\xff\x26\x40\x00\x1f\x00'), |
||||
0x365: (ECU.DSU, (CAR.PRIUS,), 0, 20, '\x00\x00\x00\x80\x03\x00\x08'), |
||||
0x365: (ECU.DSU, (CAR.RAV4,), 0, 20, '\x00\x00\x00\x80\xfc\x00\x08'), |
||||
0x366: (ECU.DSU, (CAR.PRIUS,), 0, 20, '\x00\x00\x4d\x82\x40\x02\x00'), |
||||
0x366: (ECU.DSU, (CAR.RAV4,), 0, 20, '\x00\x72\x07\xff\x09\xfe\x00'), |
||||
|
||||
0x367: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 0, 40, '\x06\x00'), |
||||
|
||||
0x414: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x00\x00\x00\x00\x00\x00\x17\x00'), |
||||
0x489: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x00\x00\x00\x00\x00\x00\x00'), |
||||
0x48a: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x00\x00\x00\x00\x00\x00\x00'), |
||||
0x48b: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x66\x06\x08\x0a\x02\x00\x00\x00'), |
||||
0x4d3: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x1C\x00\x00\x01\x00\x00\x00\x00'), |
||||
0x130: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 1, 100, '\x00\x00\x00\x00\x00\x00\x38'), |
||||
0x466: (ECU.CAM, (CAR.PRIUS, CAR.RAV4), 1, 100, '\x20\x20\xAD'), |
||||
0x396: (ECU.APGS, (CAR.PRIUS, CAR.RAV4), 0, 100, '\xBD\x00\x00\x00\x60\x0F\x02\x00'), |
||||
0x43A: (ECU.APGS, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x84\x00\x00\x00\x00\x00\x00\x00'), |
||||
0x43B: (ECU.APGS, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x00\x00\x00\x00\x00\x00\x00\x00'), |
||||
0x497: (ECU.APGS, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x00\x00\x00\x00\x00\x00\x00\x00'), |
||||
0x4CC: (ECU.APGS, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x0D\x00\x00\x00\x00\x00\x00\x00'), |
||||
0x411: (ECU.DSU, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x00\x20\x00\x00\x10\x00\x80\x00'), |
||||
0x4CB: (ECU.DSU, (CAR.PRIUS, CAR.RAV4), 0, 100, '\x0c\x00\x00\x00\x00\x00\x00\x00'), |
||||
0x470: (ECU.DSU, (CAR.PRIUS,), 1, 100, '\x00\x00\x02\x7a'), |
||||
} |
||||
|
||||
|
||||
def check_ecu_msgs(fingerprint, candidate, ecu): |
||||
# return True if fingerprint contains messages normally sent by a given ecu |
||||
ecu_msgs = [x for x in STATIC_MSGS if (ecu == STATIC_MSGS[x][0] and |
||||
candidate in STATIC_MSGS[x][1] and |
||||
STATIC_MSGS[x][2] == 0)] |
||||
|
||||
return any(msg for msg in fingerprint if msg in ecu_msgs) |
||||
|
||||
|
||||
def accel_hysteresis(accel, accel_steady, enabled): |
||||
|
||||
# for small accel oscillations within ACCEL_HYST_GAP, don't change the accel command |
||||
if not enabled: |
||||
# send 0 when disabled, otherwise acc faults |
||||
accel_steady = 0. |
||||
elif accel > accel_steady + ACCEL_HYST_GAP: |
||||
accel_steady = accel - ACCEL_HYST_GAP |
||||
elif accel < accel_steady - ACCEL_HYST_GAP: |
||||
accel_steady = accel + ACCEL_HYST_GAP |
||||
accel = accel_steady |
||||
|
||||
return accel, accel_steady |
||||
|
||||
|
||||
def process_hud_alert(hud_alert, audible_alert): |
||||
# initialize to no alert |
||||
hud1 = 0 |
||||
hud2 = 0 |
||||
if hud_alert in ['steerRequired', 'fcw']: |
||||
if audible_alert == 'chimeRepeated': |
||||
hud1 = 3 |
||||
else: |
||||
hud1 = 1 |
||||
if audible_alert in ['beepSingle', 'chimeSingle', 'chimeDouble']: |
||||
# TODO: find a way to send single chimes |
||||
hud2 = 1 |
||||
|
||||
return hud1, hud2 |
||||
|
||||
|
||||
class CarController(object): |
||||
def __init__(self, car_fingerprint, enable_camera, enable_dsu, enable_apg): |
||||
self.braking = False |
||||
# redundant safety check with the board |
||||
self.controls_allowed = True |
||||
self.last_steer = 0. |
||||
self.accel_steady = 0. |
||||
self.car_fingerprint = car_fingerprint |
||||
self.alert_active = False |
||||
|
||||
self.fake_ecus = set() |
||||
if enable_camera: self.fake_ecus.add(ECU.CAM) |
||||
if enable_dsu: self.fake_ecus.add(ECU.DSU) |
||||
if enable_apg: self.fake_ecus.add(ECU.APGS) |
||||
|
||||
def update(self, sendcan, enabled, CS, frame, actuators, |
||||
pcm_cancel_cmd, hud_alert, audible_alert): |
||||
|
||||
# *** compute control surfaces *** |
||||
tt = sec_since_boot() |
||||
|
||||
# steer torque is converted back to CAN reference (positive when steering right) |
||||
apply_accel = actuators.gas - actuators.brake |
||||
apply_accel, self.accel_steady = accel_hysteresis(apply_accel, self.accel_steady, enabled) |
||||
apply_accel = int(round(clip(apply_accel * ACCEL_SCALE, ACCEL_MIN, ACCEL_MAX))) |
||||
|
||||
# steer torque is converted back to CAN reference (positive when steering right) |
||||
apply_steer = int(round(actuators.steer * STEER_MAX)) |
||||
|
||||
max_lim = min(max(CS.steer_torque_motor + STEER_ERROR_MAX, STEER_ERROR_MAX), STEER_MAX) |
||||
min_lim = max(min(CS.steer_torque_motor - STEER_ERROR_MAX, -STEER_ERROR_MAX), -STEER_MAX) |
||||
|
||||
apply_steer = clip(apply_steer, min_lim, max_lim) |
||||
|
||||
# slow rate if steer torque increases in magnitude |
||||
if self.last_steer > 0: |
||||
apply_steer = clip(apply_steer, max(self.last_steer - STEER_DELTA_DOWN, - STEER_DELTA_UP), self.last_steer + STEER_DELTA_UP) |
||||
else: |
||||
apply_steer = clip(apply_steer, self.last_steer - STEER_DELTA_UP, min(self.last_steer + STEER_DELTA_DOWN, STEER_DELTA_UP)) |
||||
|
||||
if not enabled and CS.pcm_acc_status: |
||||
# send pcm acc cancel cmd if drive is disabled but pcm is still on, or if the system can't be activated |
||||
pcm_cancel_cmd = 1 |
||||
|
||||
# dropping torque immediately might cause eps to temp fault. On the other hand, safety_toyota |
||||
# cuts steer torque immediately anyway TODO: monitor if this is a real issue |
||||
if not enabled or CS.steer_error: |
||||
apply_steer = 0 |
||||
|
||||
self.last_steer = apply_steer |
||||
self.last_accel = apply_accel |
||||
|
||||
can_sends = [] |
||||
|
||||
#*** control msgs *** |
||||
#print "steer", apply_steer, min_lim, max_lim, CS.steer_torque_motor |
||||
|
||||
# toyota can trace shows this message at 42Hz, with counter adding alternatively 1 and 2; |
||||
# sending it at 100Hz seem to allow a higher rate limit, as the rate limit seems imposed |
||||
# on consecutive messages |
||||
if ECU.CAM in self.fake_ecus: |
||||
can_sends.append(create_steer_command(apply_steer, frame)) |
||||
|
||||
if ECU.APGS in self.fake_ecus: |
||||
can_sends.append(create_ipas_steer_command(apply_steer)) |
||||
|
||||
# accel cmd comes from DSU, but we can spam can to cancel the system even if we are using lat only control |
||||
if (frame % 3 == 0 and ECU.DSU in self.fake_ecus) or (pcm_cancel_cmd and ECU.CAM in self.fake_ecus): |
||||
if ECU.DSU in self.fake_ecus: |
||||
can_sends.append(create_accel_command(apply_accel, pcm_cancel_cmd)) |
||||
else: |
||||
can_sends.append(create_accel_command(0, pcm_cancel_cmd)) |
||||
|
||||
if frame % 10 == 0 and ECU.CAM in self.fake_ecus: |
||||
for addr in TARGET_IDS: |
||||
can_sends.append(create_video_target(frame/10, addr)) |
||||
|
||||
# ui mesg is at 100Hz but we send asap if: |
||||
# - there is something to display |
||||
# - there is something to stop displaying |
||||
hud1, hud2 = process_hud_alert(hud_alert, audible_alert) |
||||
if ((hud1 or hud2) and not self.alert_active) or \ |
||||
(not (hud1 or hud2) and self.alert_active): |
||||
send_ui = True |
||||
self.alert_active = not self.alert_active |
||||
else: |
||||
send_ui = False |
||||
|
||||
if (frame % 100 == 0 or send_ui) and ECU.CAM in self.fake_ecus: |
||||
can_sends.append(create_ui_command(hud1, hud2)) |
||||
|
||||
#*** static msgs *** |
||||
|
||||
for addr, (ecu, cars, bus, fr_step, vl) in STATIC_MSGS.iteritems(): |
||||
if frame % fr_step == 0 and ecu in self.fake_ecus and self.car_fingerprint in cars: |
||||
# send msg! |
||||
# special cases |
||||
|
||||
if fr_step == 5 and ecu == ECU.CAM and bus == 1: |
||||
cnt = (((frame / 5) % 7) + 1) << 5 |
||||
vl = chr(cnt) + vl |
||||
elif addr in (0x489, 0x48a) and bus == 0: |
||||
# add counter for those 2 messages (last 4 bits) |
||||
cnt = ((frame/100)%0xf) + 1 |
||||
if addr == 0x48a: |
||||
# 0x48a has a 8 preceding the counter |
||||
cnt += 1 << 7 |
||||
vl += chr(cnt) |
||||
|
||||
can_sends.append(make_can_msg(addr, vl, bus, False)) |
||||
|
||||
|
||||
sendcan.send(can_list_to_can_capnp(can_sends, msgtype='sendcan').to_bytes()) |
@ -0,0 +1,187 @@ |
||||
import os |
||||
import time |
||||
from common.realtime import sec_since_boot |
||||
import selfdrive.messaging as messaging |
||||
from selfdrive.car.toyota.carcontroller import CAR |
||||
from selfdrive.can.parser import CANParser |
||||
from selfdrive.config import Conversions as CV |
||||
import numpy as np |
||||
|
||||
def parse_gear_shifter(can_gear, car_fingerprint): |
||||
|
||||
if car_fingerprint == CAR.PRIUS: |
||||
if can_gear == 0x0: |
||||
return "park" |
||||
elif can_gear == 0x1: |
||||
return "reverse" |
||||
elif can_gear == 0x2: |
||||
return "neutral" |
||||
elif can_gear == 0x3: |
||||
return "drive" |
||||
elif can_gear == 0x4: |
||||
return "brake" |
||||
elif car_fingerprint == CAR.RAV4: |
||||
if can_gear == 0x20: |
||||
return "park" |
||||
elif can_gear == 0x10: |
||||
return "reverse" |
||||
elif can_gear == 0x8: |
||||
return "neutral" |
||||
elif can_gear == 0x0: |
||||
return "drive" |
||||
elif can_gear == 0x1: |
||||
return "sport" |
||||
|
||||
return "unknown" |
||||
|
||||
|
||||
def get_can_parser(CP): |
||||
# this function generates lists for signal, messages and initial values |
||||
if CP.carFingerprint == CAR.PRIUS: |
||||
dbc_f = 'toyota_prius_2017_pt.dbc' |
||||
signals = [ |
||||
("GEAR", 295, 0), |
||||
("BRAKE_PRESSED", 550, 0), |
||||
("GAS_PEDAL", 581, 0), |
||||
] |
||||
checks = [ |
||||
(550, 40), |
||||
(581, 33) |
||||
] |
||||
elif CP.carFingerprint == CAR.RAV4: |
||||
dbc_f = 'toyota_rav4_2017_pt.dbc' |
||||
signals = [ |
||||
("GEAR", 956, 0x20), |
||||
("BRAKE_PRESSED", 548, 0), |
||||
("GAS_PEDAL", 705, 0), |
||||
] |
||||
checks = [ |
||||
(548, 40), |
||||
(705, 33) |
||||
] |
||||
|
||||
# TODO: DOORS, GAS_PEDAL, BRAKE_PRESSED for RAV4 |
||||
signals += [ |
||||
# sig_name, sig_address, default |
||||
("WHEEL_SPEED_FL", 170, 0), |
||||
("WHEEL_SPEED_FR", 170, 0), |
||||
("WHEEL_SPEED_RL", 170, 0), |
||||
("WHEEL_SPEED_RR", 170, 0), |
||||
("DOOR_OPEN_FL", 1568, 1), |
||||
("DOOR_OPEN_FR", 1568, 1), |
||||
("DOOR_OPEN_RL", 1568, 1), |
||||
("DOOR_OPEN_RR", 1568, 1), |
||||
("SEATBELT_DRIVER_UNLATCHED", 1568, 1), |
||||
("TC_DISABLED", 951, 1), |
||||
("STEER_ANGLE", 37, 0), |
||||
("STEER_FRACTION", 37, 0), |
||||
("STEER_RATE", 37, 0), |
||||
("GAS_RELEASED", 466, 0), |
||||
("CRUISE_STATE", 466, 0), |
||||
("MAIN_ON", 467, 0), |
||||
("SET_SPEED", 467, 0), |
||||
("LOW_SPEED_LOCKOUT", 467, 0), |
||||
("STEER_TORQUE_DRIVER", 608, 0), |
||||
("STEER_TORQUE_EPS", 608, 0), |
||||
("TURN_SIGNALS", 1556, 3), # 3 is no blinkers |
||||
("LKA_STATE", 610, 0), |
||||
] |
||||
|
||||
checks += [ |
||||
(170, 80), |
||||
(37, 80), |
||||
(466, 33), |
||||
(467, 33), |
||||
(608, 50), |
||||
(610, 25), |
||||
] |
||||
|
||||
return CANParser(os.path.splitext(dbc_f)[0], signals, checks, 0) |
||||
|
||||
|
||||
class CarState(object): |
||||
def __init__(self, CP, logcan): |
||||
|
||||
self.CP = CP |
||||
self.left_blinker_on = 0 |
||||
self.right_blinker_on = 0 |
||||
|
||||
# initialize can parser |
||||
self.cp = get_can_parser(CP) |
||||
self.car_fingerprint = CP.carFingerprint |
||||
|
||||
# vEgo kalman filter |
||||
dt = 0.01 |
||||
self.v_ego_x = np.matrix([[0.0], [0.0]]) |
||||
self.v_ego_A = np.matrix([[1.0, dt], [0.0, 1.0]]) |
||||
self.v_ego_C = np.matrix([1.0, 0.0]) |
||||
self.v_ego_Q = np.matrix([[10.0, 0.0], [0.0, 100.0]]) |
||||
self.v_ego_R = 1e3 |
||||
# import control |
||||
# (x, l, K) = control.dare(np.transpose(A), np.transpose(C), Q, R) |
||||
# self.v_ego_K = np.transpose(K) |
||||
self.v_ego_K = np.matrix([[0.12287673], [0.29666309]]) |
||||
|
||||
def update(self): |
||||
cp = self.cp |
||||
cp.update(int(sec_since_boot() * 1e9), False) |
||||
|
||||
# copy can_valid |
||||
self.can_valid = cp.can_valid |
||||
|
||||
if self.car_fingerprint == CAR.PRIUS: |
||||
can_gear = cp.vl[295]['GEAR'] |
||||
self.brake_pressed = cp.vl[550]['BRAKE_PRESSED'] |
||||
self.pedal_gas = cp.vl[581]['GAS_PEDAL'] |
||||
elif self.car_fingerprint == CAR.RAV4: |
||||
can_gear = cp.vl[956]['GEAR'] |
||||
self.brake_pressed = cp.vl[548]['BRAKE_PRESSED'] |
||||
self.pedal_gas = cp.vl[705]['GAS_PEDAL'] |
||||
|
||||
# update prevs, update must run once per loop |
||||
self.prev_left_blinker_on = self.left_blinker_on |
||||
self.prev_right_blinker_on = self.right_blinker_on |
||||
|
||||
# ******************* parse out can ******************* |
||||
self.door_all_closed = not any([cp.vl[1568]['DOOR_OPEN_FL'], cp.vl[1568]['DOOR_OPEN_FR'], |
||||
cp.vl[1568]['DOOR_OPEN_RL'], cp.vl[1568]['DOOR_OPEN_RR']]) |
||||
self.seatbelt = not cp.vl[1568]['SEATBELT_DRIVER_UNLATCHED'] |
||||
# whitelist instead of blacklist, safer at the expense of disengages |
||||
self.steer_error = False |
||||
self.brake_error = 0 |
||||
self.esp_disabled = cp.vl[951]['TC_DISABLED'] |
||||
# calc best v_ego estimate, by averaging two opposite corners |
||||
self.v_wheel_fl = cp.vl[170]['WHEEL_SPEED_FL'] |
||||
self.v_wheel_fr = cp.vl[170]['WHEEL_SPEED_FR'] |
||||
self.v_wheel_rl = cp.vl[170]['WHEEL_SPEED_RL'] |
||||
self.v_wheel_rr = cp.vl[170]['WHEEL_SPEED_RR'] |
||||
self.v_wheel = ( |
||||
cp.vl[170]['WHEEL_SPEED_FL'] + cp.vl[170]['WHEEL_SPEED_FR'] + |
||||
cp.vl[170]['WHEEL_SPEED_RL'] + cp.vl[170]['WHEEL_SPEED_RR']) / 4. * CV.KPH_TO_MS |
||||
|
||||
# Kalman filter |
||||
self.v_ego_x = np.dot((self.v_ego_A - np.dot(self.v_ego_K, self.v_ego_C)), self.v_ego_x) + np.dot(self.v_ego_K, self.v_wheel) |
||||
self.v_ego_raw = self.v_wheel |
||||
self.v_ego = float(self.v_ego_x[0]) |
||||
self.a_ego = float(self.v_ego_x[1]) |
||||
self.standstill = not self.v_wheel > 0.001 |
||||
|
||||
self.angle_steers = cp.vl[37]['STEER_ANGLE'] + cp.vl[37]['STEER_FRACTION'] |
||||
self.angle_steers_rate = cp.vl[37]['STEER_RATE'] |
||||
self.gear_shifter = parse_gear_shifter(can_gear, self.car_fingerprint) |
||||
self.main_on = cp.vl[467]['MAIN_ON'] |
||||
self.left_blinker_on = cp.vl[1556]['TURN_SIGNALS'] == 1 |
||||
self.right_blinker_on = cp.vl[1556]['TURN_SIGNALS'] == 2 |
||||
|
||||
# we could use the override bit from dbc, but it's triggered at too high torque values |
||||
self.steer_override = abs(cp.vl[608]['STEER_TORQUE_DRIVER']) > 100 |
||||
self.steer_error = cp.vl[610]['LKA_STATE'] == 50 |
||||
self.steer_torque_driver = cp.vl[608]['STEER_TORQUE_DRIVER'] |
||||
self.steer_torque_motor = cp.vl[608]['STEER_TORQUE_EPS'] |
||||
|
||||
self.user_brake = 0 |
||||
self.v_cruise_pcm = cp.vl[467]['SET_SPEED'] |
||||
self.pcm_acc_status = cp.vl[466]['CRUISE_STATE'] |
||||
self.car_gas = self.pedal_gas |
||||
self.gas_pressed = not cp.vl[466]['GAS_RELEASED'] |
||||
self.low_speed_lockout = cp.vl[467]['LOW_SPEED_LOCKOUT'] == 2 |
@ -0,0 +1,246 @@ |
||||
#!/usr/bin/env python |
||||
import os |
||||
import time |
||||
import common.numpy_fast as np |
||||
from selfdrive.config import Conversions as CV |
||||
from selfdrive.car.toyota.carstate import CarState, CAR |
||||
from selfdrive.car.toyota.carcontroller import CarController, ECU, check_ecu_msgs |
||||
from cereal import car |
||||
from selfdrive.services import service_list |
||||
import selfdrive.messaging as messaging |
||||
from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event |
||||
|
||||
|
||||
class CarInterface(object): |
||||
def __init__(self, CP, logcan, sendcan=None): |
||||
self.logcan = logcan |
||||
self.CP = CP |
||||
|
||||
self.frame = 0 |
||||
self.can_invalid_count = 0 |
||||
self.gas_pressed_prev = False |
||||
self.brake_pressed_prev = False |
||||
self.cruise_enabled_prev = False |
||||
|
||||
# *** init the major players *** |
||||
self.CS = CarState(CP, self.logcan) |
||||
|
||||
# sending if read only is False |
||||
if sendcan is not None: |
||||
self.sendcan = sendcan |
||||
self.CC = CarController(CP.carFingerprint, CP.enableCamera, CP.enableDsu, CP.enableApgs) |
||||
|
||||
@staticmethod |
||||
def get_params(candidate, fingerprint): |
||||
|
||||
# kg of standard extra cargo to count for drive, gas, etc... |
||||
std_cargo = 136 |
||||
|
||||
ret = car.CarParams.new_message() |
||||
|
||||
ret.carName = "toyota" |
||||
ret.radarName = "toyota" |
||||
ret.carFingerprint = candidate |
||||
|
||||
ret.safetyModel = car.CarParams.SafetyModels.toyota |
||||
|
||||
ret.enableSteer = True |
||||
ret.enableBrake = True |
||||
|
||||
# pedal |
||||
ret.enableCruise = True |
||||
|
||||
# FIXME: hardcoding honda civic 2016 touring params so they can be used to |
||||
# scale unknown params for other cars |
||||
m_civic = 2923./2.205 + std_cargo |
||||
l_civic = 2.70 |
||||
aF_civic = l_civic * 0.4 |
||||
aR_civic = l_civic - aF_civic |
||||
j_civic = 2500 |
||||
cF_civic = 85400 |
||||
cR_civic = 90000 |
||||
|
||||
stop_and_go = True |
||||
ret.m = 3045./2.205 + std_cargo |
||||
ret.l = 2.70 |
||||
ret.aF = ret.l * 0.44 |
||||
ret.sR = 14.5 #Rav4 2017, TODO: find exact value for Prius |
||||
if candidate == CAR.PRIUS: |
||||
ret.steerKp, ret.steerKi = 0.2, 0.01 |
||||
elif candidate == CAR.RAV4: # rav4 control seem to be ok with integrators |
||||
ret.steerKp, ret.steerKi = 0.2, 0.05 |
||||
ret.steerKf = 0.00007818594 # full torque for 10 deg at 80mph |
||||
|
||||
# min speed to enable ACC. if car can do stop and go, then set enabling speed |
||||
# to a negative value, so it won't matter. |
||||
if candidate == CAR.PRIUS: |
||||
ret.minEnableSpeed = -1. |
||||
elif candidate == CAR.RAV4: # TODO: hack Rav4 to do stop and go |
||||
ret.minEnableSpeed = 19. * CV.MPH_TO_MS |
||||
|
||||
ret.aR = ret.l - ret.aF |
||||
# TODO: get actual value, for now starting with reasonable value for |
||||
# civic and scaling by mass and wheelbase |
||||
ret.j = j_civic * ret.m * ret.l**2 / (m_civic * l_civic**2) |
||||
|
||||
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by |
||||
# mass and CG position, so all cars will have approximately similar dyn behaviors |
||||
ret.cF = cF_civic * ret.m / m_civic * (ret.aR / ret.l) / (aR_civic / l_civic) |
||||
ret.cR = cR_civic * ret.m / m_civic * (ret.aF / ret.l) / (aF_civic / l_civic) |
||||
|
||||
# no rear steering, at least on the listed cars above |
||||
ret.chi = 0. |
||||
|
||||
# steer, gas, brake limitations VS speed |
||||
ret.steerMaxBP = [16. * CV.KPH_TO_MS, 45. * CV.KPH_TO_MS] # breakpoints at 1 and 40 kph |
||||
ret.steerMaxV = [1., 1.] # 2/3rd torque allowed above 45 kph |
||||
ret.gasMaxBP = [0.] |
||||
ret.gasMaxV = [0.5] |
||||
ret.brakeMaxBP = [5., 20.] |
||||
ret.brakeMaxV = [1., 0.8] |
||||
|
||||
ret.enableCamera = not check_ecu_msgs(fingerprint, candidate, ECU.CAM) |
||||
ret.enableDsu = not check_ecu_msgs(fingerprint, candidate, ECU.DSU) |
||||
ret.enableApgs = False # not check_ecu_msgs(fingerprint, candidate, ECU.APGS) |
||||
print "ECU Camera Simulated: ", ret.enableCamera |
||||
print "ECU DSU Simulated: ", ret.enableDsu |
||||
print "ECU APGS Simulated: ", ret.enableApgs |
||||
ret.enableGas = True |
||||
|
||||
ret.steerLimitAlert = False |
||||
|
||||
return ret |
||||
|
||||
@staticmethod |
||||
def compute_gb(accel, speed): |
||||
# toyota interface is already in accelration cmd, so conversion to gas-brake it's a pass-through. |
||||
return accel |
||||
|
||||
# returns a car.CarState |
||||
def update(self, c): |
||||
# ******************* do can recv ******************* |
||||
can_pub_main = [] |
||||
canMonoTimes = [] |
||||
|
||||
self.CS.update() |
||||
|
||||
# create message |
||||
ret = car.CarState.new_message() |
||||
|
||||
# speeds |
||||
ret.vEgo = self.CS.v_ego |
||||
ret.vEgoRaw = self.CS.v_ego_raw |
||||
ret.aEgo = self.CS.a_ego |
||||
ret.standstill = self.CS.standstill |
||||
ret.wheelSpeeds.fl = self.CS.v_wheel_fl |
||||
ret.wheelSpeeds.fr = self.CS.v_wheel_fr |
||||
ret.wheelSpeeds.rl = self.CS.v_wheel_rl |
||||
ret.wheelSpeeds.rr = self.CS.v_wheel_rr |
||||
|
||||
# gear shifter |
||||
ret.gearShifter = self.CS.gear_shifter |
||||
|
||||
# gas pedal |
||||
ret.gas = self.CS.car_gas / 256.0 |
||||
ret.gasPressed = self.CS.pedal_gas > 0 |
||||
|
||||
# brake pedal |
||||
ret.brake = self.CS.user_brake |
||||
ret.brakePressed = self.CS.brake_pressed != 0 |
||||
|
||||
# steering wheel |
||||
ret.steeringAngle = self.CS.angle_steers |
||||
ret.steeringRate = self.CS.angle_steers_rate |
||||
|
||||
ret.steeringTorque = 0 |
||||
ret.steeringPressed = self.CS.steer_override |
||||
|
||||
# cruise state |
||||
ret.cruiseState.enabled = self.CS.pcm_acc_status != 0 |
||||
ret.cruiseState.speed = self.CS.v_cruise_pcm * CV.KPH_TO_MS |
||||
ret.cruiseState.available = bool(self.CS.main_on) |
||||
ret.cruiseState.speedOffset = 0. |
||||
|
||||
# TODO: button presses |
||||
buttonEvents = [] |
||||
|
||||
if self.CS.left_blinker_on != self.CS.prev_left_blinker_on: |
||||
be = car.CarState.ButtonEvent.new_message() |
||||
be.type = 'leftBlinker' |
||||
be.pressed = self.CS.left_blinker_on != 0 |
||||
buttonEvents.append(be) |
||||
|
||||
if self.CS.right_blinker_on != self.CS.prev_right_blinker_on: |
||||
be = car.CarState.ButtonEvent.new_message() |
||||
be.type = 'rightBlinker' |
||||
be.pressed = self.CS.right_blinker_on != 0 |
||||
buttonEvents.append(be) |
||||
|
||||
ret.buttonEvents = buttonEvents |
||||
|
||||
# events |
||||
events = [] |
||||
if not self.CS.can_valid: |
||||
self.can_invalid_count += 1 |
||||
if self.can_invalid_count >= 5: |
||||
events.append(create_event('commIssue', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE])) |
||||
else: |
||||
self.can_invalid_count = 0 |
||||
if not ret.gearShifter == 'drive' and self.CP.enableDsu: |
||||
events.append(create_event('wrongGear', [ET.NO_ENTRY, ET.SOFT_DISABLE])) |
||||
if not self.CS.door_all_closed: |
||||
events.append(create_event('doorOpen', [ET.NO_ENTRY, ET.SOFT_DISABLE])) |
||||
if not self.CS.seatbelt: |
||||
events.append(create_event('seatbeltNotLatched', [ET.NO_ENTRY, ET.SOFT_DISABLE])) |
||||
if self.CS.esp_disabled and self.CP.enableDsu: |
||||
events.append(create_event('espDisabled', [ET.NO_ENTRY, ET.SOFT_DISABLE])) |
||||
if not self.CS.main_on and self.CP.enableDsu: |
||||
events.append(create_event('wrongCarMode', [ET.NO_ENTRY, ET.USER_DISABLE])) |
||||
if ret.gearShifter == 'reverse' and self.CP.enableDsu: |
||||
events.append(create_event('reverseGear', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE])) |
||||
if self.CS.steer_error: |
||||
events.append(create_event('steerTempUnavailable', [ET.NO_ENTRY, ET.WARNING])) |
||||
if self.CS.low_speed_lockout: |
||||
events.append(create_event('lowSpeedLockout', [ET.NO_ENTRY])) |
||||
if ret.vEgo < self.CP.minEnableSpeed and self.CP.enableDsu: |
||||
events.append(create_event('speedTooLow', [ET.NO_ENTRY])) |
||||
if c.actuators.gas > 0.1: |
||||
# some margin on the actuator to not false trigger cancellation while stopping |
||||
events.append(create_event('speedTooLow', [ET.IMMEDIATE_DISABLE])) |
||||
if ret.vEgo < 0.001: |
||||
# while in standstill, send a user alert |
||||
events.append(create_event('manualRestart', [ET.WARNING])) |
||||
|
||||
# enable request in prius is simple, as we activate when Toyota is active (rising edge) |
||||
if ret.cruiseState.enabled and not self.cruise_enabled_prev: |
||||
events.append(create_event('pcmEnable', [ET.ENABLE])) |
||||
elif not ret.cruiseState.enabled: |
||||
events.append(create_event('pcmDisable', [ET.USER_DISABLE])) |
||||
|
||||
# disable on pedals rising edge or when brake is pressed and speed isn't zero |
||||
if (ret.gasPressed and not self.gas_pressed_prev) or \ |
||||
(ret.brakePressed and (not self.brake_pressed_prev or ret.vEgo > 0.001)): |
||||
events.append(create_event('pedalPressed', [ET.NO_ENTRY, ET.USER_DISABLE])) |
||||
|
||||
if ret.gasPressed: |
||||
events.append(create_event('pedalPressed', [ET.PRE_ENABLE])) |
||||
|
||||
ret.events = events |
||||
ret.canMonoTimes = canMonoTimes |
||||
|
||||
self.gas_pressed_prev = ret.gasPressed |
||||
self.brake_pressed_prev = ret.brakePressed |
||||
self.cruise_enabled_prev = ret.cruiseState.enabled |
||||
|
||||
return ret.as_reader() |
||||
|
||||
# pass in a car.CarControl |
||||
# to be called @ 100hz |
||||
def apply(self, c): |
||||
|
||||
self.CC.update(self.sendcan, c.enabled, self.CS, self.frame, |
||||
c.actuators, c.cruiseControl.cancel, c.hudControl.visualAlert, |
||||
c.hudControl.audibleAlert) |
||||
|
||||
self.frame += 1 |
||||
return False |
@ -0,0 +1,83 @@ |
||||
import struct |
||||
import common.numpy_fast as np |
||||
from selfdrive.config import Conversions as CV |
||||
|
||||
|
||||
# *** Toyota specific *** |
||||
|
||||
def fix(msg, addr): |
||||
checksum = 0 |
||||
idh = (addr & 0xff00) >> 8 |
||||
idl = (addr & 0xff) |
||||
|
||||
checksum = idh + idl + len(msg) + 1 |
||||
for d_byte in msg: |
||||
checksum += ord(d_byte) |
||||
|
||||
#return msg + chr(checksum & 0xFF) |
||||
return msg + struct.pack("B", checksum & 0xFF) |
||||
|
||||
|
||||
def make_can_msg(addr, dat, alt, cks=False): |
||||
if cks: |
||||
dat = fix(dat, addr) |
||||
return [addr, 0, dat, alt] |
||||
|
||||
|
||||
def create_video_target(frame, addr): |
||||
counter = frame & 0xff |
||||
msg = struct.pack("!BBBBBBB", counter, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00) |
||||
return make_can_msg(addr, msg, 1, True) |
||||
|
||||
|
||||
def create_ipas_steer_command(steer): |
||||
|
||||
"""Creates a CAN message for the Toyota Steer Command.""" |
||||
if steer < 0: |
||||
move = 0x60 |
||||
steer = 0xfff + steer + 1 |
||||
elif steer > 0: |
||||
move = 0x20 |
||||
else: |
||||
move = 0x40 |
||||
|
||||
mode = 0x30 if steer else 0x10 |
||||
|
||||
steer_h = (steer & 0xF00) >> 8 |
||||
steer_l = steer & 0xff |
||||
|
||||
msg = struct.pack("!BBBBBBB", mode | steer_h, steer_l, 0x10, 0x00, move, 0x40, 0x00) |
||||
|
||||
return make_can_msg(0x266, msg, 0, True) |
||||
|
||||
def create_steer_command(steer, raw_cnt): |
||||
"""Creates a CAN message for the Toyota Steer Command.""" |
||||
# from 0x80 to 0xff |
||||
counter = ((raw_cnt & 0x3f) << 1) | 0x80 |
||||
if steer != 0: |
||||
counter |= 1 |
||||
|
||||
# hud |
||||
# 00 => Regular |
||||
# 40 => Actively Steering (with beep) |
||||
# 80 => Actively Steering (without beep) |
||||
hud = 0x00 |
||||
|
||||
msg = struct.pack("!BhB", counter, steer, hud) |
||||
|
||||
return make_can_msg(0x2e4, msg, 0, True) |
||||
|
||||
|
||||
def create_accel_command(accel, pcm_cancel): |
||||
# TODO: find the exact canceling bit |
||||
state = 0xc0 + pcm_cancel # this allows automatic restart from hold without driver cmd |
||||
|
||||
msg = struct.pack("!hBBBBB", accel, 0x63, state, 0x00, 0x00, 0x00) |
||||
|
||||
return make_can_msg(0x343, msg, 0, True) |
||||
|
||||
|
||||
def create_ui_command(hud1, hud2): |
||||
msg = struct.pack('!BBBBBBBB', 0x54, 0x04 + hud1 + (hud2 << 4), 0x0C, |
||||
0x00, 0x00, 0x2C, 0x38, 0x02) |
||||
return make_can_msg(0x412, msg, 0, False) |
@ -1 +1 @@ |
||||
#define OPENPILOT_VERSION "0.3.7" |
||||
#define COMMA_VERSION "0.3.8.2-openpilot" |
||||
|
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c78a95b2550fdfdc0182add44508c59ec7af9d1c58d776dcd32ee85ea52a771e |
||||
oid sha256:170140586eb60aa9e772acd0988eb8f5df3549da1a2de1539aff768d940346e2 |
||||
size 8680 |
||||
|
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:963720039f3655eb3926e5bb3bbde21d92c824245055588ebaee223223accc02 |
||||
oid sha256:e02be34d0d234957aa4a69673c4e9bb6584572f2e62b969d9c3281bdf7daabb4 |
||||
size 18490 |
||||
|
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:55ef4b230fd392bb43a49b2bd4fbf6e478b6f7b6144f65df97fe3a952d6b0b49 |
||||
oid sha256:75a219c9aaacbbb4470b1be437005736667e9a190a6467e6d254201c2fe26b09 |
||||
size 1822 |
||||
|
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dd891792d5bc3c780941a0e3d8c37829d6f4ceef554a4704017f6024e29fba20 |
||||
size 194688 |
||||
oid sha256:f6f985c451e36d05b11e35ba61e229e28d49bcfac8874146bb500f6b0736e409 |
||||
size 194874 |
||||
|
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:2aaef8016fb65911037340c5a7a4c340b7cc49ea67723d01c1129246f8221bbe |
||||
size 1364512 |
||||
oid sha256:a1adb2870715bfac1640fc3afa1b7a274a24ff3aa98b3a8bbbab622039a7868a |
||||
size 1412368 |
||||
|
@ -0,0 +1,13 @@ |
||||
#!/usr/bin/env python |
||||
# simple boardd wrapper that updates the panda first |
||||
import os |
||||
from panda import ensure_st_up_to_date |
||||
|
||||
def main(gctx=None): |
||||
ensure_st_up_to_date() |
||||
|
||||
os.chdir("boardd") |
||||
os.execvp("./boardd", ["./boardd"]) |
||||
|
||||
if __name__ == "__main__": |
||||
main() |
@ -0,0 +1,81 @@ |
||||
#!/usr/bin/env python |
||||
import os |
||||
import numpy as np |
||||
from selfdrive.can.parser import CANParser |
||||
from cereal import car |
||||
from common.realtime import sec_since_boot |
||||
import zmq |
||||
from selfdrive.services import service_list |
||||
import selfdrive.messaging as messaging |
||||
|
||||
def _create_radard_can_parser(): |
||||
dbc_f = 'toyota_prius_2017_adas.dbc' |
||||
radar_messages = range(0x210, 0x220) |
||||
msg_n = len(radar_messages) |
||||
msg_last = radar_messages[-1] |
||||
signals = zip(['LONG_DIST'] * msg_n + ['NEW_TRACK'] * msg_n + ['LAT_DIST'] * msg_n + |
||||
['REL_SPEED'] * msg_n + ['VALID'] * msg_n, |
||||
radar_messages * 5, |
||||
[255] * msg_n + [1] * msg_n + [0] * msg_n + [0] * msg_n + [0] * msg_n) |
||||
checks = zip(radar_messages, [20]*msg_n) |
||||
|
||||
return CANParser(os.path.splitext(dbc_f)[0], signals, checks, 1) |
||||
|
||||
class RadarInterface(object): |
||||
def __init__(self): |
||||
# radar |
||||
self.pts = {} |
||||
self.track_id = 0 |
||||
|
||||
self.delay = 0.0 # Delay of radar |
||||
|
||||
# Nidec |
||||
self.rcp = _create_radard_can_parser() |
||||
|
||||
context = zmq.Context() |
||||
self.logcan = messaging.sub_sock(context, service_list['can'].port) |
||||
|
||||
def update(self): |
||||
canMonoTimes = [] |
||||
|
||||
updated_messages = set() |
||||
while 1: |
||||
tm = int(sec_since_boot() * 1e9) |
||||
updated_messages.update(self.rcp.update(tm, True)) |
||||
# TODO: use msg_last |
||||
if 0x21f in updated_messages: |
||||
break |
||||
|
||||
ret = car.RadarState.new_message() |
||||
errors = [] |
||||
if not self.rcp.can_valid: |
||||
errors.append("commIssue") |
||||
ret.errors = errors |
||||
ret.canMonoTimes = canMonoTimes |
||||
#print "NEW TRACKS" |
||||
for ii in updated_messages: |
||||
cpt = self.rcp.vl[ii] |
||||
if cpt['LONG_DIST'] < 255 and cpt['VALID']: |
||||
#print "%5s %5s %5s" % (round(cpt['LONG_DIST'], 1), round(cpt['LAT_DIST'], 1), round(cpt['REL_SPEED'], 1)) |
||||
if ii not in self.pts or cpt['NEW_TRACK']: |
||||
self.pts[ii] = car.RadarState.RadarPoint.new_message() |
||||
self.pts[ii].trackId = self.track_id |
||||
self.track_id += 1 |
||||
self.pts[ii].dRel = cpt['LONG_DIST'] # from front of car |
||||
self.pts[ii].yRel = -cpt['LAT_DIST'] # in car frame's y axis, left is positive |
||||
self.pts[ii].vRel = cpt['REL_SPEED'] |
||||
self.pts[ii].aRel = float('nan') |
||||
self.pts[ii].yvRel = float('nan') |
||||
else: |
||||
if ii in self.pts: |
||||
del self.pts[ii] |
||||
|
||||
ret.points = self.pts.values() |
||||
return ret |
||||
|
||||
if __name__ == "__main__": |
||||
RI = RadarInterface() |
||||
while 1: |
||||
ret = RI.update() |
||||
print(chr(27) + "[2J") |
||||
print ret |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:c1db908ac63c3036eb0e4758c2b3af19524df2c34f011de055cdbef16b655c0d |
||||
size 981608 |
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:ba62e6a2641999da0ae564166ebbcf01e1922f69dbcf9d48dc94c3bb0886e558 |
||||
size 939496 |
||||
oid sha256:056e3f9de2d89dfee1a7092615c1958959f7d01a0e3c660798b19b084dce8819 |
||||
size 972296 |
||||
|
@ -0,0 +1,29 @@ |
||||
#!/usr/bin/env python |
||||
|
||||
# simple service that waits for network access and tries to update every 3 hours |
||||
|
||||
import os |
||||
import time |
||||
import subprocess |
||||
|
||||
def main(gctx=None): |
||||
if not os.getenv("CLEAN"): |
||||
return |
||||
|
||||
while True: |
||||
# try network |
||||
r = subprocess.call(["ping", "-W", "4", "-c", "1", "8.8.8.8"]) |
||||
if r: |
||||
time.sleep(60) |
||||
continue |
||||
|
||||
# try fetch |
||||
r = subprocess.call(["nice", "-n", "19", "git", "fetch", "--depth=1"]) |
||||
if r: |
||||
time.sleep(60) |
||||
continue |
||||
|
||||
time.sleep(60*60*3) |
||||
|
||||
if __name__ == "__main__": |
||||
main() |
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:98b5f05a3820850ec41add581d6cd7d23afc68af354a7fd29556f97448e4be92 |
||||
size 13285152 |
||||
oid sha256:9818be4dff22fef5dbcf5b6fce468c15c34594f7e3ae45b8be0cb6164694919a |
||||
size 13330512 |
||||
|
Loading…
Reference in new issue