dragonpilot beta3

date: 2023-12-23T21:19:29
commit: 38612b14f1a8aa49d1c6ef61bd67f5a095abb3f9
This commit is contained in:
dragonpilot
2023-12-23 21:18:48 -07:00
parent dd4c663a58
commit 7ee7cb59b3
389 changed files with 36391 additions and 48841 deletions
-159
View File
@@ -1,159 +0,0 @@
import asyncio
import io
import numpy as np
import pyaudio
import wave
from aiortc.contrib.media import MediaBlackhole
from aiortc.mediastreams import AudioStreamTrack, MediaStreamError, MediaStreamTrack
from aiortc.mediastreams import VIDEO_CLOCK_RATE, VIDEO_TIME_BASE
from aiortc.rtcrtpsender import RTCRtpSender
from av import CodecContext, Packet
from pydub import AudioSegment
import cereal.messaging as messaging
AUDIO_RATE = 16000
SOUNDS = {
'engage': '../../selfdrive/assets/sounds/engage.wav',
'disengage': '../../selfdrive/assets/sounds/disengage.wav',
'error': '../../selfdrive/assets/sounds/warning_immediate.wav',
}
def force_codec(pc, sender, forced_codec='video/VP9', stream_type="video"):
codecs = RTCRtpSender.getCapabilities(stream_type).codecs
codec = [codec for codec in codecs if codec.mimeType == forced_codec]
transceiver = next(t for t in pc.getTransceivers() if t.sender == sender)
transceiver.setCodecPreferences(codec)
class EncodedBodyVideo(MediaStreamTrack):
kind = "video"
_start: float
_timestamp: int
def __init__(self):
super().__init__()
sock_name = 'livestreamDriverEncodeData'
messaging.context = messaging.Context()
self.sock = messaging.sub_sock(sock_name, None, conflate=True)
self.pts = 0
async def recv(self) -> Packet:
while True:
msg = messaging.recv_one_or_none(self.sock)
if msg is not None:
break
await asyncio.sleep(0.005)
evta = getattr(msg, msg.which())
self.last_idx = evta.idx.encodeId
packet = Packet(evta.header + evta.data)
packet.time_base = VIDEO_TIME_BASE
packet.pts = self.pts
self.pts += 0.05 * VIDEO_CLOCK_RATE
return packet
class WebClientSpeaker(MediaBlackhole):
def __init__(self):
super().__init__()
self.p = pyaudio.PyAudio()
self.buffer = io.BytesIO()
self.channels = 2
self.stream = self.p.open(format=pyaudio.paInt16, channels=self.channels, rate=48000, frames_per_buffer=9600,
output=True, stream_callback=self.pyaudio_callback)
def pyaudio_callback(self, in_data, frame_count, time_info, status):
if self.buffer.getbuffer().nbytes < frame_count * self.channels * 2:
buff = np.zeros((frame_count, 2), dtype=np.int16).tobytes()
elif self.buffer.getbuffer().nbytes > 115200: # 3x the usual read size
self.buffer.seek(0)
buff = self.buffer.read(frame_count * self.channels * 4)
buff = buff[:frame_count * self.channels * 2]
self.buffer.seek(2)
else:
self.buffer.seek(0)
buff = self.buffer.read(frame_count * self.channels * 2)
self.buffer.seek(2)
return (buff, pyaudio.paContinue)
async def consume(self, track):
while True:
try:
frame = await track.recv()
except MediaStreamError:
return
bio = bytes(frame.planes[0])
self.buffer.write(bio)
async def start(self):
for track, task in self._MediaBlackhole__tracks.items():
if task is None:
self._MediaBlackhole__tracks[track] = asyncio.ensure_future(self.consume(track))
async def stop(self):
for task in self._MediaBlackhole__tracks.values():
if task is not None:
task.cancel()
self._MediaBlackhole__tracks = {}
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
class BodyMic(AudioStreamTrack):
def __init__(self):
super().__init__()
self.sample_rate = AUDIO_RATE
self.AUDIO_PTIME = 0.020 # 20ms audio packetization
self.samples = int(self.AUDIO_PTIME * self.sample_rate)
self.FORMAT = pyaudio.paInt16
self.CHANNELS = 2
self.RATE = self.sample_rate
self.CHUNK = int(AUDIO_RATE * 0.020)
self.p = pyaudio.PyAudio()
self.mic_stream = self.p.open(format=self.FORMAT, channels=1, rate=self.RATE, input=True, frames_per_buffer=self.CHUNK)
self.codec = CodecContext.create('pcm_s16le', 'r')
self.codec.sample_rate = self.RATE
self.codec.channels = 2
self.audio_samples = 0
self.chunk_number = 0
async def recv(self):
mic_data = self.mic_stream.read(self.CHUNK)
mic_sound = AudioSegment(mic_data, sample_width=2, channels=1, frame_rate=self.RATE)
mic_sound = AudioSegment.from_mono_audiosegments(mic_sound, mic_sound)
mic_sound += 3 # increase volume by 3db
packet = Packet(mic_sound.raw_data)
frame = self.codec.decode(packet)[0]
frame.pts = self.audio_samples
self.audio_samples += frame.samples
self.chunk_number = self.chunk_number + 1
return frame
async def play_sound(sound):
chunk = 5120
with wave.open(SOUNDS[sound], 'rb') as wf:
def callback(in_data, frame_count, time_info, status):
data = wf.readframes(frame_count)
return data, pyaudio.paContinue
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True,
frames_per_buffer=chunk,
stream_callback=callback)
stream.start_stream()
while stream.is_active():
await asyncio.sleep(0)
stream.stop_stream()
stream.close()
p.terminate()
+7 -3
View File
@@ -1,5 +1,5 @@
import { handleKeyX, executePlan } from "./controls.js";
import { start, stop, last_ping } from "./webrtc.js";
import { start, stop, lastChannelMessageTime, playSoundRequest } from "./webrtc.js";
export var pc = null;
export var dc = null;
@@ -9,10 +9,14 @@ document.addEventListener('keyup', (e)=>(handleKeyX(e.key.toLowerCase(), 0)));
$(".keys").bind("mousedown touchstart", (e)=>handleKeyX($(e.target).attr('id').replace('key-', ''), 1));
$(".keys").bind("mouseup touchend", (e)=>handleKeyX($(e.target).attr('id').replace('key-', ''), 0));
$("#plan-button").click(executePlan);
$(".sound").click((e)=>{
const sound = $(e.target).attr('id').replace('sound-', '')
return playSoundRequest(sound);
});
setInterval( () => {
const dt = new Date().getTime();
if ((dt - last_ping) > 1000) {
if ((dt - lastChannelMessageTime) > 1000) {
$(".pre-blob").removeClass('blob');
$("#battery").text("-");
$("#ping-time").text('-');
@@ -20,4 +24,4 @@ setInterval( () => {
}
}, 5000);
start(pc, dc);
start(pc, dc);
+63 -81
View File
@@ -1,9 +1,34 @@
import { getXY } from "./controls.js";
import { pingPoints, batteryPoints, chartPing, chartBattery } from "./plots.js";
export let dcInterval = null;
export let batteryInterval = null;
export let last_ping = null;
export let controlCommandInterval = null;
export let latencyInterval = null;
export let lastChannelMessageTime = null;
export function offerRtcRequest(sdp, type) {
return fetch('/offer', {
body: JSON.stringify({sdp: sdp, type: type}),
headers: {'Content-Type': 'application/json'},
method: 'POST'
});
}
export function playSoundRequest(sound) {
return fetch('/sound', {
body: JSON.stringify({sound}),
headers: {'Content-Type': 'application/json'},
method: 'POST'
});
}
export function pingHeadRequest() {
return fetch('/', {
method: 'HEAD'
});
}
export function createPeerConnection(pc) {
@@ -45,16 +70,7 @@ export function negotiate(pc) {
});
}).then(function() {
var offer = pc.localDescription;
return fetch('/offer', {
body: JSON.stringify({
sdp: offer.sdp,
type: offer.type,
}),
headers: {
'Content-Type': 'application/json'
},
method: 'POST'
});
return offerRtcRequest(offer.sdp, offer.type);
}).then(function(response) {
console.log(response);
return response.json();
@@ -86,25 +102,6 @@ export const constraints = {
};
export function createDummyVideoTrack() {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const frameWidth = 5; // Set the width of the frame
const frameHeight = 5; // Set the height of the frame
canvas.width = frameWidth;
canvas.height = frameHeight;
context.fillStyle = 'black';
context.fillRect(0, 0, frameWidth, frameHeight);
const stream = canvas.captureStream();
const videoTrack = stream.getVideoTracks()[0];
return videoTrack;
}
export function start(pc, dc) {
pc = createPeerConnection(pc);
@@ -138,71 +135,56 @@ export function start(pc, dc) {
alert('Could not acquire media: ' + err);
});
// add a fake video?
// const dummyVideoTrack = createDummyVideoTrack();
// const dummyMediaStream = new MediaStream();
// dummyMediaStream.addTrack(dummyVideoTrack);
// pc.addTrack(dummyVideoTrack, dummyMediaStream);
// setInterval(() => {pc.getStats(null).then((stats) => {stats.forEach((report) => console.log(report))})}, 10000)
// var video = document.querySelector('video');
// var print = function (e, f){console.log(e, f); video.requestVideoFrameCallback(print);};
// video.requestVideoFrameCallback(print);
var parameters = {"ordered": true};
dc = pc.createDataChannel('data', parameters);
dc.onclose = function() {
console.log("data channel closed");
clearInterval(dcInterval);
clearInterval(batteryInterval);
clearInterval(controlCommandInterval);
clearInterval(latencyInterval);
};
function controlCommand() {
function sendJoystickOverDataChannel() {
const {x, y} = getXY();
const dt = new Date().getTime();
var message = JSON.stringify({type: 'control_command', x, y, dt});
var message = JSON.stringify({type: "testJoystick", data: {axes: [x, y], buttons: [false]}})
dc.send(message);
}
function batteryLevel() {
var message = JSON.stringify({type: 'battery_level'});
dc.send(message);
function checkLatency() {
const initialTime = new Date().getTime();
pingHeadRequest().then(function() {
const currentTime = new Date().getTime();
if (Math.abs(currentTime - lastChannelMessageTime) < 1000) {
const pingtime = currentTime - initialTime;
pingPoints.push({'x': currentTime, 'y': pingtime});
if (pingPoints.length > 1000) {
pingPoints.shift();
}
chartPing.update();
$("#ping-time").text((pingtime) + "ms");
}
})
}
dc.onopen = function() {
dcInterval = setInterval(controlCommand, 50);
batteryInterval = setInterval(batteryLevel, 10000);
controlCommand();
batteryLevel();
$(".sound").click((e)=>{
const sound = $(e.target).attr('id').replace('sound-', '')
dc.send(JSON.stringify({type: 'play_sound', sound}));
});
controlCommandInterval = setInterval(sendJoystickOverDataChannel, 50);
latencyInterval = setInterval(checkLatency, 1000);
sendJoystickOverDataChannel();
};
let val_print_idx = 0;
const textDecoder = new TextDecoder();
var carStaterIndex = 0;
dc.onmessage = function(evt) {
const data = JSON.parse(evt.data);
if(val_print_idx == 0 && data.type === 'ping_time') {
const dt = new Date().getTime();
const pingtime = dt - data.incoming_time;
pingPoints.push({'x': dt, 'y': pingtime});
if (pingPoints.length > 1000) {
pingPoints.shift();
}
chartPing.update();
$("#ping-time").text((pingtime) + "ms");
last_ping = dt;
$(".pre-blob").addClass('blob');
}
val_print_idx = (val_print_idx + 1 ) % 20;
if(data.type === 'battery_level') {
$("#battery").text(data.value + "%");
batteryPoints.push({'x': new Date().getTime(), 'y': data.value});
if (batteryPoints.length > 1000) {
const text = textDecoder.decode(evt.data);
const msg = JSON.parse(text);
if (carStaterIndex % 100 == 0 && msg.type === 'carState') {
const batteryLevel = Math.round(msg.data.fuelGauge * 100);
$("#battery").text(batteryLevel + "%");
batteryPoints.push({'x': new Date().getTime(), 'y': batteryLevel});
if (batteryPoints.length > 1000) {
batteryPoints.shift();
}
chartBattery.update();
}
carStaterIndex += 1;
lastChannelMessageTime = new Date().getTime();
$(".pre-blob").addClass('blob');
};
}
+90 -171
View File
@@ -1,205 +1,124 @@
import asyncio
import dataclasses
import json
import logging
import os
import ssl
import uuid
import time
import subprocess
# aiortc and its dependencies have lots of internal warnings :(
import warnings
warnings.resetwarnings()
warnings.simplefilter("always")
from aiohttp import web, ClientSession
import pyaudio
import wave
from aiohttp import web
from aiortc import RTCPeerConnection, RTCSessionDescription
import cereal.messaging as messaging
from openpilot.common.basedir import BASEDIR
from openpilot.tools.bodyteleop.bodyav import BodyMic, WebClientSpeaker, force_codec, play_sound, MediaBlackhole, EncodedBodyVideo
from openpilot.system.webrtc.webrtcd import StreamRequestBody
from openpilot.common.params import Params
logger = logging.getLogger("pc")
logger = logging.getLogger("bodyteleop")
logging.basicConfig(level=logging.INFO)
pcs = set()
pm, sm = None, None
TELEOPDIR = f"{BASEDIR}/tools/bodyteleop"
WEBRTCD_HOST, WEBRTCD_PORT = "localhost", 5001
## UTILS
async def play_sound(sound):
SOUNDS = {
"engage": "selfdrive/assets/sounds/engage.wav",
"disengage": "selfdrive/assets/sounds/disengage.wav",
"error": "selfdrive/assets/sounds/warning_immediate.wav",
}
assert sound in SOUNDS
chunk = 5120
with wave.open(os.path.join(BASEDIR, SOUNDS[sound]), "rb") as wf:
def callback(in_data, frame_count, time_info, status):
data = wf.readframes(frame_count)
return data, pyaudio.paContinue
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True,
frames_per_buffer=chunk,
stream_callback=callback)
stream.start_stream()
while stream.is_active():
await asyncio.sleep(0)
stream.stop_stream()
stream.close()
p.terminate()
## SSL
def create_ssl_cert(cert_path, key_path):
try:
proc = subprocess.run(f'openssl req -x509 -newkey rsa:4096 -nodes -out {cert_path} -keyout {key_path} \
-days 365 -subj "/C=US/ST=California/O=commaai/OU=comma body"',
stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
proc.check_returncode()
except subprocess.CalledProcessError as ex:
raise ValueError(f"Error creating SSL certificate:\n[stdout]\n{proc.stdout.decode()}\n[stderr]\n{proc.stderr.decode()}") from ex
def create_ssl_context():
cert_path = os.path.join(TELEOPDIR, "cert.pem")
key_path = os.path.join(TELEOPDIR, "key.pem")
if not os.path.exists(cert_path) or not os.path.exists(key_path):
logger.info("Creating certificate...")
create_ssl_cert(cert_path, key_path)
else:
logger.info("Certificate exists!")
ssl_context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(cert_path, key_path)
return ssl_context
## ENDPOINTS
async def index(request):
content = open(TELEOPDIR + "/static/index.html", "r").read()
now = time.monotonic()
request.app['mutable_vals']['last_send_time'] = now
request.app['mutable_vals']['last_override_time'] = now
request.app['mutable_vals']['prev_command'] = []
request.app['mutable_vals']['find_person'] = False
return web.Response(content_type="text/html", text=content)
with open(os.path.join(TELEOPDIR, "static", "index.html"), "r") as f:
content = f.read()
return web.Response(content_type="text/html", text=content)
async def control_body(data, app):
now = time.monotonic()
if (data['type'] == 'dummy_controls') and (now < (app['mutable_vals']['last_send_time'] + 0.2)):
return
if (data['type'] == 'control_command') and (app['mutable_vals']['prev_command'] == [data['x'], data['y']] and data['x'] == 0 and data['y'] == 0):
return
logger.info(str(data))
x = max(-1.0, min(1.0, data['x']))
y = max(-1.0, min(1.0, data['y']))
dat = messaging.new_message('testJoystick')
dat.testJoystick.axes = [x, y]
dat.testJoystick.buttons = [False]
pm.send('testJoystick', dat)
app['mutable_vals']['last_send_time'] = now
if (data['type'] == 'control_command'):
app['mutable_vals']['last_override_time'] = now
app['mutable_vals']['prev_command'] = [data['x'], data['y']]
async def ping(request):
return web.Response(text="pong")
async def dummy_controls_msg(app):
while True:
if 'last_send_time' in app['mutable_vals']:
this_time = time.monotonic()
if (app['mutable_vals']['last_send_time'] + 0.2) < this_time:
await control_body({'type': 'dummy_controls', 'x': 0, 'y': 0}, app)
await asyncio.sleep(0.2)
async def sound(request):
params = await request.json()
sound_to_play = params["sound"]
async def start_background_tasks(app):
app['bgtask_dummy_controls_msg'] = asyncio.create_task(dummy_controls_msg(app))
async def stop_background_tasks(app):
app['bgtask_dummy_controls_msg'].cancel()
await app['bgtask_dummy_controls_msg']
await play_sound(sound_to_play)
return web.json_response({"status": "ok"})
async def offer(request):
logger.info("\n\n\nnewoffer!\n\n")
params = await request.json()
offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])
speaker = WebClientSpeaker()
blackhole = MediaBlackhole()
body = StreamRequestBody(params["sdp"], ["driver"], ["testJoystick"], ["carState"])
body_json = json.dumps(dataclasses.asdict(body))
pc = RTCPeerConnection()
pc_id = "PeerConnection(%s)" % uuid.uuid4()
pcs.add(pc)
def log_info(msg, *args):
logger.info(pc_id + " " + msg, *args)
log_info("Created for %s", request.remote)
@pc.on("datachannel")
def on_datachannel(channel):
request.app['mutable_vals']['remote_channel'] = channel
@channel.on("message")
async def on_message(message):
data = json.loads(message)
if data['type'] == 'control_command':
await control_body(data, request.app)
times = {
'type': 'ping_time',
'incoming_time': data['dt'],
'outgoing_time': int(time.time() * 1000),
}
channel.send(json.dumps(times))
if data['type'] == 'battery_level':
sm.update(timeout=0)
if sm.updated['carState']:
channel.send(json.dumps({'type': 'battery_level', 'value': int(sm['carState'].fuelGauge * 100)}))
if data['type'] == 'play_sound':
logger.info(f"Playing sound: {data['sound']}")
await play_sound(data['sound'])
if data['type'] == 'find_person':
request.app['mutable_vals']['find_person'] = data['value']
@pc.on("connectionstatechange")
async def on_connectionstatechange():
log_info("Connection state is %s", pc.connectionState)
if pc.connectionState == "failed":
await pc.close()
pcs.discard(pc)
@pc.on('track')
def on_track(track):
logger.info(f"Track received: {track.kind}")
if track.kind == "audio":
speaker.addTrack(track)
elif track.kind == "video":
blackhole.addTrack(track)
@track.on("ended")
async def on_ended():
log_info("Remote %s track ended", track.kind)
if track.kind == "audio":
await speaker.stop()
elif track.kind == "video":
await blackhole.stop()
video_sender = pc.addTrack(EncodedBodyVideo())
force_codec(pc, video_sender, forced_codec='video/H264')
_ = pc.addTrack(BodyMic())
await pc.setRemoteDescription(offer)
await speaker.start()
await blackhole.start()
answer = await pc.createAnswer()
await pc.setLocalDescription(answer)
return web.Response(
content_type="application/json",
text=json.dumps(
{"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}
),
)
async def on_shutdown(app):
coros = [pc.close() for pc in pcs]
await asyncio.gather(*coros)
pcs.clear()
async def run(cmd):
proc = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await proc.communicate()
logger.info("Created key and cert!")
if stdout:
logger.info(f'[stdout]\n{stdout.decode()}')
if stderr:
logger.info(f'[stderr]\n{stderr.decode()}')
logger.info("Sending offer to webrtcd...")
webrtcd_url = f"http://{WEBRTCD_HOST}:{WEBRTCD_PORT}/stream"
async with ClientSession() as session, session.post(webrtcd_url, data=body_json) as resp:
assert resp.status == 200
answer = await resp.json()
return web.json_response(answer)
def main():
global pm, sm
pm = messaging.PubMaster(['testJoystick'])
sm = messaging.SubMaster(['carState', 'logMessage'])
# Enable joystick debug mode
Params().put_bool("JoystickDebugMode", True)
# App needs to be HTTPS for microphone and audio autoplay to work on the browser
cert_path = TELEOPDIR + '/cert.pem'
key_path = TELEOPDIR + '/key.pem'
if (not os.path.exists(cert_path)) or (not os.path.exists(key_path)):
asyncio.run(run(f'openssl req -x509 -newkey rsa:4096 -nodes -out {cert_path} -keyout {key_path} \
-days 365 -subj "/C=US/ST=California/O=commaai/OU=comma body"'))
else:
logger.info("Certificate exists!")
ssl_context = ssl.SSLContext()
ssl_context.load_cert_chain(cert_path, key_path)
ssl_context = create_ssl_context()
app = web.Application()
app['mutable_vals'] = {}
app.on_shutdown.append(on_shutdown)
app.router.add_post("/offer", offer)
app.router.add_get("/", index)
app.router.add_static('/static', TELEOPDIR + '/static')
app.on_startup.append(start_background_tasks)
app.on_cleanup.append(stop_background_tasks)
app.router.add_get("/ping", ping, allow_head=True)
app.router.add_post("/offer", offer)
app.router.add_post("/sound", sound)
app.router.add_static('/static', os.path.join(TELEOPDIR, 'static'))
web.run_app(app, access_log=None, host="0.0.0.0", port=5000, ssl_context=ssl_context)
-11
View File
@@ -45,17 +45,6 @@ In order to use a joystick over the network, we need to run joystickd locally fr
tools/joystick/joystickd.py
```
### Web joystick on your mobile device
A browser-based virtual joystick designed for touch screens. Starts automatically when installed on comma body (non-car robotics platform).
For cars, start the web joystick service manually via SSH before starting the car.
```shell
tools/joystick/web.py
```
After starting the car/body, open the web joystick app at this URL: `http://[comma three IP address]:5000`
---
Now start your car and openpilot should go into joystick mode with an alert on startup! The status of the axes will display on the alert, while button statuses print in the shell.
+1 -4
View File
@@ -82,9 +82,6 @@ def send_thread(joystick):
dat.testJoystick.buttons = [joystick.cancel]
joystick_sock.send(dat.to_bytes())
print('\n' + ', '.join(f'{name}: {round(v, 3)}' for name, v in joystick.axes_values.items()))
if "WEB" in os.environ:
import requests
requests.get("http://"+os.environ["WEB"]+":5000/control/%f/%f" % tuple([joystick.axes_values[a] for a in joystick.axes_order][::-1]), timeout=None)
rk.keep_time()
def joystick_thread(joystick):
@@ -101,7 +98,7 @@ if __name__ == '__main__':
parser.add_argument('--gamepad', action='store_true', help='Use gamepad configuration instead of joystick')
args = parser.parse_args()
if not Params().get_bool("IsOffroad") and "ZMQ" not in os.environ and "WEB" not in os.environ:
if not Params().get_bool("IsOffroad") and "ZMQ" not in os.environ:
print("The car must be off before running joystickd.")
exit()
+5 -11
View File
@@ -1,21 +1,15 @@
import json
import os
from openpilot.common.file_helpers import mkdirs_exists_ok
from openpilot.system.hardware import PC
from openpilot.system.hardware.hw import Paths
class MissingAuthConfigError(Exception):
pass
if PC:
CONFIG_DIR = os.path.expanduser('~/.comma')
else:
CONFIG_DIR = "/tmp/.comma"
def get_token():
try:
with open(os.path.join(CONFIG_DIR, 'auth.json')) as f:
with open(os.path.join(Paths.config_root(), 'auth.json')) as f:
auth = json.load(f)
return auth['access_token']
except Exception:
@@ -23,13 +17,13 @@ def get_token():
def set_token(token):
mkdirs_exists_ok(CONFIG_DIR)
with open(os.path.join(CONFIG_DIR, 'auth.json'), 'w') as f:
os.makedirs(Paths.config_root(), exist_ok=True)
with open(os.path.join(Paths.config_root(), 'auth.json'), 'w') as f:
json.dump({'access_token': token}, f)
def clear_token():
try:
os.unlink(os.path.join(CONFIG_DIR, 'auth.json'))
os.unlink(os.path.join(Paths.config_root(), 'auth.json'))
except FileNotFoundError:
pass
+1 -2
View File
@@ -1,12 +1,11 @@
import os
import urllib.parse
from openpilot.common.file_helpers import mkdirs_exists_ok
DEFAULT_CACHE_DIR = os.getenv("CACHE_ROOT", os.path.expanduser("~/.commacache"))
def cache_path_for_file_path(fn, cache_dir=DEFAULT_CACHE_DIR):
dir_ = os.path.join(cache_dir, "local")
mkdirs_exists_ok(dir_)
os.makedirs(dir_, exist_ok=True)
fn_parsed = urllib.parse.urlparse(fn)
if fn_parsed.scheme == '':
cache_fn = os.path.abspath(fn).replace("/", "_")
+2 -2
View File
@@ -5,7 +5,7 @@ import pycurl
from hashlib import sha256
from io import BytesIO
from tenacity import retry, wait_random_exponential, stop_after_attempt
from openpilot.common.file_helpers import mkdirs_exists_ok, atomic_write_in_dir
from openpilot.common.file_helpers import atomic_write_in_dir
from openpilot.system.hardware.hw import Paths
# Cache chunk size
K = 1000
@@ -40,7 +40,7 @@ class URLFile:
except AttributeError:
self._curl = self._tlocal.curl = pycurl.Curl()
if not self._force_download:
mkdirs_exists_ok(Paths.download_cache_root())
os.makedirs(Paths.download_cache_root(), exist_ok=True)
def __enter__(self):
return self
+1
View File
@@ -16,6 +16,7 @@ class ConsoleUI : public QObject {
public:
ConsoleUI(Replay *replay, QObject *parent = 0);
~ConsoleUI();
inline static const std::array speed_array = {0.2f, 0.5f, 1.0f, 2.0f, 3.0f};
private:
void initWindows();
+2 -4
View File
@@ -6,13 +6,11 @@
#endif
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "cereal/gen/cpp/log.capnp.h"
#include "system/camerad/cameras/camera_common.h"
#include "tools/replay/filereader.h"
const CameraType ALL_CAMERAS[] = {RoadCam, DriverCam, WideRoadCam};
const int MAX_CAMERAS = std::size(ALL_CAMERAS);
@@ -55,13 +53,13 @@ class LogReader {
public:
LogReader(size_t memory_pool_block_size = DEFAULT_EVENT_MEMORY_POOL_BLOCK_SIZE);
~LogReader();
bool load(const std::string &url, std::atomic<bool> *abort = nullptr, const std::set<cereal::Event::Which> &allow = {},
bool load(const std::string &url, std::atomic<bool> *abort = nullptr,
bool local_cache = false, int chunk_size = -1, int retries = 0);
bool load(const std::byte *data, size_t size, std::atomic<bool> *abort = nullptr);
std::vector<Event*> events;
private:
bool parse(const std::set<cereal::Event::Which> &allow, std::atomic<bool> *abort);
bool parse(std::atomic<bool> *abort);
std::string raw_;
#ifdef HAS_MEMORY_RESOURCE
std::unique_ptr<std::pmr::monotonic_buffer_resource> mbr_;
+3 -8
View File
@@ -4,7 +4,6 @@
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <tuple>
#include <vector>
@@ -28,7 +27,6 @@ enum REPLAY_FLAGS {
REPLAY_FLAG_NO_FILE_CACHE = 0x0020,
REPLAY_FLAG_QCAMERA = 0x0040,
REPLAY_FLAG_NO_HW_DECODER = 0x0100,
REPLAY_FLAG_FULL_SPEED = 0x0200,
REPLAY_FLAG_NO_VIPC = 0x0400,
REPLAY_FLAG_ALL_SERVICES = 0x0800,
};
@@ -50,8 +48,8 @@ class Replay : public QObject {
Q_OBJECT
public:
Replay(QString route, QStringList allow, QStringList block, QStringList base_blacklist, SubMaster *sm = nullptr,
uint32_t flags = REPLAY_FLAG_NONE, QString data_dir = "", QObject *parent = 0);
Replay(QString route, QStringList allow, QStringList block, SubMaster *sm = nullptr,
uint32_t flags = REPLAY_FLAG_NONE, QString data_dir = "", QObject *parent = 0);
~Replay();
bool load();
void start(int seconds = 0);
@@ -114,8 +112,6 @@ protected:
}
QThread *stream_thread_ = nullptr;
// logs
std::mutex stream_lock_;
std::condition_variable stream_cv_;
std::atomic<bool> updating_events_ = false;
@@ -142,9 +138,8 @@ protected:
std::mutex timeline_lock;
QFuture<void> timeline_future;
std::vector<std::tuple<double, double, TimelineType>> timeline;
std::set<cereal::Event::Which> allow_list;
std::string car_fingerprint_;
float speed_ = 1.0;
std::atomic<float> speed_ = 1.0;
replayEventFilter event_filter = nullptr;
void *filter_opaque = nullptr;
int segment_cache_limit = MIN_SEGMENTS_CACHE;
+1 -3
View File
@@ -2,7 +2,6 @@
#include <map>
#include <memory>
#include <set>
#include <string>
#include <QDateTime>
@@ -55,7 +54,7 @@ class Segment : public QObject {
Q_OBJECT
public:
Segment(int n, const SegmentFile &files, uint32_t flags, const std::set<cereal::Event::Which> &allow = {});
Segment(int n, const SegmentFile &files, uint32_t flags);
~Segment();
inline bool isLoaded() const { return !loading_ && !abort_; }
@@ -73,5 +72,4 @@ protected:
std::atomic<int> loading_ = 0;
QFutureSynchronizer<void> synchronizer_;
uint32_t flags;
std::set<cereal::Event::Which> allow;
};