diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 84f031371..796e69eef 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -122,7 +122,6 @@ procs = [ # debug procs NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar), PythonProcess("webrtcd", "system.webrtc.webrtcd", or_(and_(livestream, not_(iscar)), notcar)), - PythonProcess("webjoystick", "tools.bodyteleop.web", notcar), PythonProcess("joystick", "tools.joystick.joystick_control", and_(joystick, iscar)), ] diff --git a/tools/bodyteleop/static/index.html b/tools/bodyteleop/static/index.html deleted file mode 100644 index 48672dbbf..000000000 --- a/tools/bodyteleop/static/index.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - commabody - - - - - - - - - - -
-

comma body

- -
-
-
-
-
W
-
-
-
A
-
S
-
D
-
-
-
-
-
-
-
-
-
- - -
-

ping time

-
-
-
- - -
-

battery

-
-
-
-
-
-
-
-
-
-
-
- - - diff --git a/tools/bodyteleop/static/js/controls.js b/tools/bodyteleop/static/js/controls.js deleted file mode 100644 index 3a11f78b9..000000000 --- a/tools/bodyteleop/static/js/controls.js +++ /dev/null @@ -1,20 +0,0 @@ -const keyVals = {w: 0, a: 0, s: 0, d: 0} - -export function getXY() { - let x = -keyVals.w + keyVals.s - let y = -keyVals.d + keyVals.a - return {x, y} -} - -export const handleKeyX = (key, setValue) => { - if (['w', 'a', 's', 'd'].includes(key)){ - keyVals[key] = setValue; - let color = "#333"; - if (setValue === 1){ - color = "#e74c3c"; - } - $("#key-"+key).css('background', color); - const {x, y} = getXY(); - $("#pos-vals").text(x+","+y); - } -}; diff --git a/tools/bodyteleop/static/js/jsmain.js b/tools/bodyteleop/static/js/jsmain.js deleted file mode 100644 index 0db1dcd9b..000000000 --- a/tools/bodyteleop/static/js/jsmain.js +++ /dev/null @@ -1,21 +0,0 @@ -import { handleKeyX } from "./controls.js"; -import { start, stop, lastChannelMessageTime } from "./webrtc.js"; - -export var pc = null; -export var dc = null; - -document.addEventListener('keydown', (e)=>(handleKeyX(e.key.toLowerCase(), 1))); -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)); -setInterval( () => { - const dt = new Date().getTime(); - if ((dt - lastChannelMessageTime) > 1000) { - $(".pre-blob").removeClass('blob'); - $("#battery").text("-"); - $("#ping-time").text('-'); - $("video")[0].load(); - } -}, 5000); - -start(pc, dc); diff --git a/tools/bodyteleop/static/js/plots.js b/tools/bodyteleop/static/js/plots.js deleted file mode 100644 index 5327bf71b..000000000 --- a/tools/bodyteleop/static/js/plots.js +++ /dev/null @@ -1,53 +0,0 @@ -export const pingPoints = []; -export const batteryPoints = []; - -function getChartConfig(pts, color, title, ymax=100) { - return { - type: 'line', - data: { - datasets: [{ - label: title, - data: pts, - borderWidth: 1, - borderColor: color, - backgroundColor: color, - fill: 'origin' - }] - }, - options: { - scales: { - x: { - type: 'time', - time: { - unit: 'minute', - displayFormats: { - second: 'h:mm a' - } - }, - grid: { - color: '#222', // Grid lines color - }, - ticks: { - source: 'data', - fontColor: 'rgba(255, 255, 255, 1.0)', // Y-axis label color - } - }, - y: { - beginAtZero: true, - max: ymax, - grid: { - color: 'rgba(255, 255, 255, 0.1)', // Grid lines color - }, - ticks: { - fontColor: 'rgba(255, 255, 255, 0.7)', // Y-axis label color - } - } - } - } - } -} - -const ctxPing = document.getElementById('chart-ping'); -const ctxBattery = document.getElementById('chart-battery'); -export const chartPing = new Chart(ctxPing, getChartConfig(pingPoints, 'rgba(192, 57, 43, 0.7)', 'Controls Ping Time (ms)', 250)); -export const chartBattery = new Chart(ctxBattery, getChartConfig(batteryPoints, 'rgba(41, 128, 185, 0.7)', 'Battery %', 100)); diff --git a/tools/bodyteleop/static/js/webrtc.js b/tools/bodyteleop/static/js/webrtc.js deleted file mode 100644 index 28bea238e..000000000 --- a/tools/bodyteleop/static/js/webrtc.js +++ /dev/null @@ -1,175 +0,0 @@ -import { getXY } from "./controls.js"; -import { pingPoints, batteryPoints, chartPing, chartBattery } from "./plots.js"; - -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 pingHeadRequest() { - return fetch('/', { - method: 'HEAD' - }); -} - - -export function createPeerConnection(pc) { - var config = { - sdpSemantics: 'unified-plan' - }; - - pc = new RTCPeerConnection(config); - - // connect video - pc.addEventListener('track', function(evt) { - console.log("Adding Tracks!") - if (evt.track.kind == 'video') - document.getElementById('video').srcObject = evt.streams[0]; - }); - return pc; -} - - -export function negotiate(pc) { - return pc.createOffer({offerToReceiveVideo:true}).then(function(offer) { - return pc.setLocalDescription(offer); - }).then(function() { - return new Promise(function(resolve) { - if (pc.iceGatheringState === 'complete') { - resolve(); - } - else { - function checkState() { - if (pc.iceGatheringState === 'complete') { - pc.removeEventListener('icegatheringstatechange', checkState); - resolve(); - } - } - pc.addEventListener('icegatheringstatechange', checkState); - } - }); - }).then(function() { - var offer = pc.localDescription; - return offerRtcRequest(offer.sdp, offer.type); - }).then(function(response) { - console.log(response); - return response.json(); - }).then(function(answer) { - return pc.setRemoteDescription(answer); - }).catch(function(e) { - alert(e); - }); -} - - -function isMobile() { - let check = false; - (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera); - return check; -}; - - -export const constraints = { - video: isMobile() -}; - - -export function start(pc, dc) { - pc = createPeerConnection(pc); - - // add a local video track on mobile - (constraints.video ? navigator.mediaDevices.getUserMedia(constraints) : Promise.resolve(null)) - .then(function(stream) { - if (stream) { - stream.getTracks().forEach(function(track) { - pc.addTrack(track, stream); - }); - } - - return negotiate(pc); - }) - .catch(function(err) { - alert('Could not acquire media: ' + err); - }); - - var parameters = {"ordered": true}; - dc = pc.createDataChannel('data', parameters); - dc.onclose = function() { - clearInterval(controlCommandInterval); - clearInterval(latencyInterval); - }; - - function sendJoystickOverDataChannel() { - const {x, y} = getXY(); - var message = JSON.stringify({type: "testJoystick", data: {axes: [x, y], buttons: [false]}}) - 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() { - controlCommandInterval = setInterval(sendJoystickOverDataChannel, 50); - latencyInterval = setInterval(checkLatency, 1000); - sendJoystickOverDataChannel(); - }; - - const textDecoder = new TextDecoder(); - var carStaterIndex = 0; - dc.onmessage = function(evt) { - 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'); - }; -} - - -export function stop(pc, dc) { - if (dc) { - dc.close(); - } - if (pc.getTransceivers) { - pc.getTransceivers().forEach(function(transceiver) { - if (transceiver.stop) { - transceiver.stop(); - } - }); - } - pc.getSenders().forEach(function(sender) { - sender.track.stop(); - }); - setTimeout(function() { - pc.close(); - }, 500); -} diff --git a/tools/bodyteleop/static/main.css b/tools/bodyteleop/static/main.css deleted file mode 100644 index 79fe8052f..000000000 --- a/tools/bodyteleop/static/main.css +++ /dev/null @@ -1,178 +0,0 @@ -body { - background: #333 !important; - color: #fff !important; - display: flex; - justify-content: center; - align-items: start; -} - -p { - margin: 0px !important; -} - -i { - font-style: normal; -} - -.small { - font-size: 1em !important -} - -.jumbo { - font-size: 8rem; -} - - -@media (max-width: 600px) { - .small { - font-size: 0.5em !important - } - .jumbo { - display: none; - } - -} - -#main { - display: flex; - flex-direction: column; - align-content: center; - justify-content: center; - align-items: center; - font-size: 30px; - width: 100%; - max-width: 1200px; -} - -video { - width: 95%; -} - -.pre-blob { - display: flex; - background: #333; - border-radius: 50%; - margin: 10px; - height: 45px; - width: 45px; - justify-content: center; - align-items: center; - font-size: 1rem; -} - -.blob { - background: rgba(231, 76, 60,1.0); - box-shadow: 0 0 0 0 rgba(231, 76, 60,1.0); - animation: pulse 2s infinite; -} - -@keyframes pulse { - 0% { - box-shadow: 0 0 0 0px rgba(192, 57, 43, 1); - } - 100% { - box-shadow: 0 0 0 20px rgba(192, 57, 43, 0); - } -} - - -.icon-sup-panel { - display: flex; - flex-direction: row; - justify-content: space-around; - align-items: center; - background: #222; - border-radius: 10px; - padding: 5px; - margin: 5px 0px 5px 0px; -} - -.icon-sub-panel { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; -} - -#icon-panel { - display: flex; - width: 100%; - justify-content: space-between; - margin-top: 5px; -} - -.icon-sub-sub-panel { - display: flex; - flex-direction: row; -} - -.keys, #key-val { - background: #333; - padding: 2rem; - margin: 5px; - color: #fff; - display: flex; - justify-content: center; - align-items: center; - border-radius: 10px; - cursor: pointer; -} - -#key-val { - pointer-events: none; - background: #fff; - color: #333; - line-height: 1; - font-size: 20px; - flex-direction: column; -} - -.wasd-row { - display: flex; - flex-direction: row; - justify-content: center; - align-items: stretch; -} - -#wasd { - margin: 5px 0px 5px 0px; - background: #222; - border-radius: 10px; - width: 100%; - padding: 20px; - display: flex; - flex-direction: row; - justify-content: space-around; - align-items: stretch; - - user-select: none; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - touch-action: manipulation; -} - -.panel { - display: flex; - justify-content: center; - margin: 5px 0px 5px 0px !important; - background: #222; - border-radius: 10px; - width: 100%; - padding: 10px; -} - -#ping-time, #battery { - font-size: 25px; -} - -#stop { - display: none; -} - -.details { - display: flex; - padding: 0px 10px 0px 10px; -} diff --git a/tools/bodyteleop/static/poster.png b/tools/bodyteleop/static/poster.png deleted file mode 100644 index 2f2b02dd8..000000000 --- a/tools/bodyteleop/static/poster.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8740da2be7faac198b5e10780c646166056a76ebbe3d64499e0cdc49280c8a4f -size 8297 diff --git a/tools/bodyteleop/web.py b/tools/bodyteleop/web.py deleted file mode 100644 index d357561b2..000000000 --- a/tools/bodyteleop/web.py +++ /dev/null @@ -1,86 +0,0 @@ -import dataclasses -import json -import logging -import os -import ssl -import subprocess - -from aiohttp import web -from aiohttp import ClientSession - -from openpilot.common.basedir import BASEDIR -from openpilot.system.webrtc.webrtcd import StreamRequestBody -from openpilot.common.params import Params - -logger = logging.getLogger("bodyteleop") -logging.basicConfig(level=logging.INFO) - -TELEOPDIR = f"{BASEDIR}/tools/bodyteleop" -WEBRTCD_HOST, WEBRTCD_PORT = "localhost", 5001 - - -## SSL -def create_ssl_cert(cert_path: str, key_path: str): - 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"', - capture_output=True, 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: 'web.Request'): - with open(os.path.join(TELEOPDIR, "static", "index.html")) as f: - content = f.read() - return web.Response(content_type="text/html", text=content) - - -async def ping(request: 'web.Request'): - return web.Response(text="pong") - - -async def offer(request: 'web.Request'): - params = await request.json() - body = StreamRequestBody(params["sdp"], "driver", ["testJoystick"], ["carState"]) - body_json = json.dumps(dataclasses.asdict(body)) - - 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(): - # Enable joystick debug mode - Params().put_bool("JoystickDebugMode", True, block=True) - - # App needs to be HTTPS for WebRTC to work on the browser - ssl_context = create_ssl_context() - - app = web.Application() - app.router.add_get("/", index) - app.router.add_get("/ping", ping, allow_head=True) - app.router.add_post("/offer", offer) - 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) - - -if __name__ == "__main__": - main()