diff --git a/.gitignore b/.gitignore index 8ed8c2c0c..6ed87a3f8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,11 +3,14 @@ venv/ .tags .ipynb_checkpoints .idea +.overlay_init +.overlay_consistent .sconsign.dblite .vscode model2.png a.out +*.dylib *.DSYM *.d *.pyc @@ -27,6 +30,7 @@ a.out config.json clcache +persist board/obj/ selfdrive/boardd/boardd selfdrive/logcatd/logcatd @@ -51,4 +55,5 @@ panda_jungle .coverage* htmlcov +pandaextra diff --git a/Dockerfile.openpilot b/Dockerfile.openpilot index b5a140c5a..9faa5c282 100644 --- a/Dockerfile.openpilot +++ b/Dockerfile.openpilot @@ -17,12 +17,14 @@ RUN apt-get update && apt-get install -y \ libffi-dev \ libglew-dev \ libgles2-mesa-dev \ + libglfw3-dev \ libglib2.0-0 \ liblzma-dev \ libmysqlclient-dev \ libomp-dev \ libopencv-dev \ libssl-dev \ + libsqlite3-dev \ libtool \ libusb-1.0-0-dev \ libzmq5-dev \ diff --git a/Pipfile b/Pipfile index 4f8bc525e..4d3e046de 100644 --- a/Pipfile +++ b/Pipfile @@ -8,71 +8,54 @@ opencv-python= "==3.4.2.17" PyQt5 = "*" ipython = "*" networkx = "==2.3" -azure-common = "==1.1.23" +azure-core = "==1.1.1" +azure-common = "==1.1.24" azure-nspkg = "==3.0.2" azure-storage-blob = "==2.1.0" azure-storage-common = "==2.1.0" azure-storage-nspkg = "==3.1.0" -bincopy = "*" -bleach = "*" boto = "*" "boto3" = "*" -celery = "*" control = "*" datadog = "*" -decorator = "*" dlib = "*" -dominate = "*" elasticsearch = "*" -fasteners = "*" future = "*" futures = "*" -gevent = "*" pycocotools = {git = "https://github.com/cocodataset/cocoapi.git",subdirectory = "PythonAPI"} gunicorn = "*" "h5py" = "*" hexdump = "*" "html5lib" = "*" imageio = "*" -intervaltree = "*" ipykernel = "*" joblib = "*" json-logging-py = "*" jupyter = "*" libarchive = "*" lru-dict = "*" -lxml = "*" "mpld3" = "*" msgpack-python = "*" nbstripout = "*" -nose-parameterized = "*" numpy = "*" osmium = "*" pbr = "*" percache = "*" pprofile = "*" -psutil = "*" pycurl = "*" git-pylint-commit-hook = "*" pymongo = "*" "pynmea2" = "*" pypolyline = "*" -pysendfile = "*" python-logstash = "*" -pyvcd = "*" redis = "*" -redlock = "*" "s2sphere" = "*" scikit-image = "*" "subprocess32" = "*" -supervisor = "*" tenacity = "*" tensorflow-gpu = "" -utm = "*" -"v4l2" = "*" PyJWT = "==1.4.1" PyMySQL = "==0.9.2" -Theano = "*" Werkzeug = "*" "backports.lzma" = "*" Flask-Cors = "*" @@ -84,7 +67,6 @@ PyNaCl = "*" reverse_geocoder = "*" Shapely = "*" SQLAlchemy = "*" -uWSGI = "*" scipy = "*" fastcluster = "*" backports-abc = "*" @@ -92,15 +74,13 @@ pygame = "*" simplejson = "*" python-logstash-async = "*" seaborn = "*" -tensorflow-estimator = "*" pyproj = "*" mock = "*" -blinker = "*" -gast = "==0.2.2" matplotlib = "*" dictdiffer = "*" aenum = "*" coverage = "*" +azure-cli-core = "*" [packages] overpy = {git = "https://github.com/commaai/python-overpy.git",ref = "f86529af402d4642e1faeb146671c40284007323"} @@ -144,6 +124,5 @@ pillow = "*" scons = "*" cysignals = "*" - [requires] python_version = "3.7.3" diff --git a/Pipfile.lock b/Pipfile.lock index b4e69cd05..045857cd1 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2b47bb704ca3062d9bc7c03a02ffb17fb82bf1cd9d3263c7d3d66fb160bf6dc2" + "sha256": "bc5d2f0b8b59443cbdf4373de05f7522ff24afa7f6e55d1aae08c3961e970beb" }, "pipfile-spec": 6, "requires": { @@ -34,10 +34,10 @@ }, "certifi": { "hashes": [ - "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", - "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" + "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", + "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" ], - "version": "==2019.9.11" + "version": "==2019.11.28" }, "cffi": { "hashes": [ @@ -202,11 +202,11 @@ }, "gunicorn": { "hashes": [ - "sha256:0806b5e8a2eb8ba9ac1be65d7b743ec896fc25f5d6cb16c5e051540157b315bb", - "sha256:ef69dea4814df95e64e3f40b47b7ffedc6911c5009233be9d01cfd0d14aa3f50" + "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626", + "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c" ], "index": "pypi", - "version": "==20.0.0" + "version": "==20.0.4" }, "hexdump": { "hashes": [ @@ -249,11 +249,11 @@ }, "json-rpc": { "hashes": [ - "sha256:35d22e2179c4c8b20d66b044ef45da3138a87b4730f25f6126444d7b4feca69e", - "sha256:8a72c3b33c851cd39899cd77b4da98cf036be72b609943f59ca1b73ffe70ff28" + "sha256:84b45058e5ba95f49c7b6afcf7e03ab86bee89bf2c01f3ad8dd41fe114fc1f84", + "sha256:def0dbcf5b7084fc31d677f2f5990d988d06497f2f47f13024274cfb2d5d7589" ], "index": "pypi", - "version": "==1.12.2" + "version": "==1.13.0" }, "lazy-object-proxy": { "hashes": [ @@ -352,30 +352,30 @@ }, "numpy": { "hashes": [ - "sha256:0a7a1dd123aecc9f0076934288ceed7fd9a81ba3919f11a855a7887cbe82a02f", - "sha256:0c0763787133dfeec19904c22c7e358b231c87ba3206b211652f8cbe1241deb6", - "sha256:3d52298d0be333583739f1aec9026f3b09fdfe3ddf7c7028cb16d9d2af1cca7e", - "sha256:43bb4b70585f1c2d153e45323a886839f98af8bfa810f7014b20be714c37c447", - "sha256:475963c5b9e116c38ad7347e154e5651d05a2286d86455671f5b1eebba5feb76", - "sha256:64874913367f18eb3013b16123c9fed113962e75d809fca5b78ebfbb73ed93ba", - "sha256:683828e50c339fc9e68720396f2de14253992c495fdddef77a1e17de55f1decc", - "sha256:6ca4000c4a6f95a78c33c7dadbb9495c10880be9c89316aa536eac359ab820ae", - "sha256:75fd817b7061f6378e4659dd792c84c0b60533e867f83e0d1e52d5d8e53df88c", - "sha256:7d81d784bdbed30137aca242ab307f3e65c8d93f4c7b7d8f322110b2e90177f9", - "sha256:8d0af8d3664f142414fd5b15cabfd3b6cc3ef242a3c7a7493257025be5a6955f", - "sha256:9679831005fb16c6df3dd35d17aa31dc0d4d7573d84f0b44cc481490a65c7725", - "sha256:a8f67ebfae9f575d85fa859b54d3bdecaeece74e3274b0b5c5f804d7ca789fe1", - "sha256:acbf5c52db4adb366c064d0b7c7899e3e778d89db585feadd23b06b587d64761", - "sha256:ada4805ed51f5bcaa3a06d3dd94939351869c095e30a2b54264f5a5004b52170", - "sha256:c7354e8f0eca5c110b7e978034cd86ed98a7a5ffcf69ca97535445a595e07b8e", - "sha256:e2e9d8c87120ba2c591f60e32736b82b67f72c37ba88a4c23c81b5b8fa49c018", - "sha256:e467c57121fe1b78a8f68dd9255fbb3bb3f4f7547c6b9e109f31d14569f490c3", - "sha256:ede47b98de79565fcd7f2decb475e2dcc85ee4097743e551fe26cfc7eb3ff143", - "sha256:f58913e9227400f1395c7b800503ebfdb0772f1c33ff8cb4d6451c06cabdf316", - "sha256:fe39f5fd4103ec4ca3cb8600b19216cd1ff316b4990f4c0b6057ad982c0a34d5" + "sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6", + "sha256:17aa7a81fe7599a10f2b7d95856dc5cf84a4eefa45bc96123cbbc3ebc568994e", + "sha256:20b26aaa5b3da029942cdcce719b363dbe58696ad182aff0e5dcb1687ec946dc", + "sha256:2d75908ab3ced4223ccba595b48e538afa5ecc37405923d1fea6906d7c3a50bc", + "sha256:39d2c685af15d3ce682c99ce5925cc66efc824652e10990d2462dfe9b8918c6a", + "sha256:56bc8ded6fcd9adea90f65377438f9fea8c05fcf7c5ba766bef258d0da1554aa", + "sha256:590355aeade1a2eaba17617c19edccb7db8d78760175256e3cf94590a1a964f3", + "sha256:70a840a26f4e61defa7bdf811d7498a284ced303dfbc35acb7be12a39b2aa121", + "sha256:77c3bfe65d8560487052ad55c6998a04b654c2fbc36d546aef2b2e511e760971", + "sha256:9537eecf179f566fd1c160a2e912ca0b8e02d773af0a7a1120ad4f7507cd0d26", + "sha256:9acdf933c1fd263c513a2df3dceecea6f3ff4419d80bf238510976bf9bcb26cd", + "sha256:ae0975f42ab1f28364dcda3dde3cf6c1ddab3e1d4b2909da0cb0191fa9ca0480", + "sha256:b3af02ecc999c8003e538e60c89a2b37646b39b688d4e44d7373e11c2debabec", + "sha256:b6ff59cee96b454516e47e7721098e6ceebef435e3e21ac2d6c3b8b02628eb77", + "sha256:b765ed3930b92812aa698a455847141869ef755a87e099fddd4ccf9d81fffb57", + "sha256:c98c5ffd7d41611407a1103ae11c8b634ad6a43606eca3e2a5a269e5d6e8eb07", + "sha256:cf7eb6b1025d3e169989416b1adcd676624c2dbed9e3bcb7137f51bfc8cc2572", + "sha256:d92350c22b150c1cae7ebb0ee8b5670cc84848f6359cf6b5d8f86617098a9b73", + "sha256:e422c3152921cece8b6a2fb6b0b4d73b6579bd20ae075e7d15143e711f3ca2ca", + "sha256:e840f552a509e3380b0f0ec977e8124d0dc34dc0e68289ca28f4d7c1d0d79474", + "sha256:f3d0a94ad151870978fb93538e95411c83899c9dc63e6fb65542f769568ecfa5" ], "index": "pypi", - "version": "==1.17.4" + "version": "==1.18.1" }, "overpy": { "git": "https://github.com/commaai/python-overpy.git", @@ -384,56 +384,48 @@ }, "pillow": { "hashes": [ - "sha256:047d9473cf68af50ac85f8ee5d5f21a60f849bc17d348da7fc85711287a75031", - "sha256:0f66dc6c8a3cc319561a633b6aa82c44107f12594643efa37210d8c924fc1c71", - "sha256:12c9169c4e8fe0a7329e8658c7e488001f6b4c8e88740e76292c2b857af2e94c", - "sha256:248cffc168896982f125f5c13e9317c059f74fffdb4152893339f3be62a01340", - "sha256:27faf0552bf8c260a5cee21a76e031acaea68babb64daf7e8f2e2540745082aa", - "sha256:285edafad9bc60d96978ed24d77cdc0b91dace88e5da8c548ba5937c425bca8b", - "sha256:384b12c9aa8ef95558abdcb50aada56d74bc7cc131dd62d28c2d0e4d3aadd573", - "sha256:38950b3a707f6cef09cd3cbb142474357ad1a985ceb44d921bdf7b4647b3e13e", - "sha256:4aad1b88933fd6dc2846552b89ad0c74ddbba2f0884e2c162aa368374bf5abab", - "sha256:4ac6148008c169603070c092e81f88738f1a0c511e07bd2bb0f9ef542d375da9", - "sha256:4deb1d2a45861ae6f0b12ea0a786a03d19d29edcc7e05775b85ec2877cb54c5e", - "sha256:59aa2c124df72cc75ed72c8d6005c442d4685691a30c55321e00ed915ad1a291", - "sha256:5a47d2123a9ec86660fe0e8d0ebf0aa6bc6a17edc63f338b73ea20ba11713f12", - "sha256:5cc901c2ab9409b4b7ac7b5bcc3e86ac14548627062463da0af3b6b7c555a871", - "sha256:6c1db03e8dff7b9f955a0fb9907eb9ca5da75b5ce056c0c93d33100a35050281", - "sha256:7ce80c0a65a6ea90ef9c1f63c8593fcd2929448613fc8da0adf3e6bfad669d08", - "sha256:809c19241c14433c5d6135e1b6c72da4e3b56d5c865ad5736ab99af8896b8f41", - "sha256:83792cb4e0b5af480588601467c0764242b9a483caea71ef12d22a0d0d6bdce2", - "sha256:846fa202bd7ee0f6215c897a1d33238ef071b50766339186687bd9b7a6d26ac5", - "sha256:9f5529fc02009f96ba95bea48870173426879dc19eec49ca8e08cd63ecd82ddb", - "sha256:a423c2ea001c6265ed28700df056f75e26215fd28c001e93ef4380b0f05f9547", - "sha256:ac4428094b42907aba5879c7c000d01c8278d451a3b7cccd2103e21f6397ea75", - "sha256:b1ae48d87f10d1384e5beecd169c77502fcc04a2c00a4c02b85f0a94b419e5f9", - "sha256:bf4e972a88f8841d8fdc6db1a75e0f8d763e66e3754b03006cbc3854d89f1cb1", - "sha256:c6414f6aad598364aaf81068cabb077894eb88fed99c6a65e6e8217bab62ae7a", - "sha256:c710fcb7ee32f67baf25aa9ffede4795fd5d93b163ce95fdc724383e38c9df96", - "sha256:c7be4b8a09852291c3c48d3c25d1b876d2494a0a674980089ac9d5e0d78bd132", - "sha256:c9e5ffb910b14f090ac9c38599063e354887a5f6d7e6d26795e916b4514f2c1a", - "sha256:e0697b826da6c2472bb6488db4c0a7fa8af0d52fa08833ceb3681358914b14e5", - "sha256:e9a3edd5f714229d41057d56ac0f39ad9bdba6767e8c888c951869f0bdd129b0" + "sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be", + "sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946", + "sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837", + "sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f", + "sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00", + "sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d", + "sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533", + "sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a", + "sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358", + "sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda", + "sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435", + "sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2", + "sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313", + "sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff", + "sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317", + "sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2", + "sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614", + "sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0", + "sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386", + "sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9", + "sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636", + "sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865" ], "index": "pypi", - "version": "==6.2.1" + "version": "==7.0.0" }, "psutil": { "hashes": [ - "sha256:021d361439586a0fd8e64f8392eb7da27135db980f249329f1a347b9de99c695", - "sha256:145e0f3ab9138165f9e156c307100905fd5d9b7227504b8a9d3417351052dc3d", - "sha256:348ad4179938c965a27d29cbda4a81a1b2c778ecd330a221aadc7bd33681afbd", - "sha256:3feea46fbd634a93437b718518d15b5dd49599dfb59a30c739e201cc79bb759d", - "sha256:474e10a92eeb4100c276d4cc67687adeb9d280bbca01031a3e41fb35dfc1d131", - "sha256:47aeb4280e80f27878caae4b572b29f0ec7967554b701ba33cd3720b17ba1b07", - "sha256:73a7e002781bc42fd014dfebb3fc0e45f8d92a4fb9da18baea6fb279fbc1d966", - "sha256:d051532ac944f1be0179e0506f6889833cf96e466262523e57a871de65a15147", - "sha256:dfb8c5c78579c226841908b539c2374da54da648ee5a837a731aa6a105a54c00", - "sha256:e3f5f9278867e95970854e92d0f5fe53af742a7fc4f2eba986943345bcaed05d", - "sha256:e9649bb8fc5cea1f7723af53e4212056a6f984ee31784c10632607f472dec5ee" + "sha256:094f899ac3ef72422b7e00411b4ed174e3c5a2e04c267db6643937ddba67a05b", + "sha256:10b7f75cc8bd676cfc6fa40cd7d5c25b3f45a0e06d43becd7c2d2871cbb5e806", + "sha256:1b1575240ca9a90b437e5a40db662acd87bbf181f6aa02f0204978737b913c6b", + "sha256:21231ef1c1a89728e29b98a885b8e0a8e00d09018f6da5cdc1f43f988471a995", + "sha256:28f771129bfee9fc6b63d83a15d857663bbdcae3828e1cb926e91320a9b5b5cd", + "sha256:70387772f84fa5c3bb6a106915a2445e20ac8f9821c5914d7cbde148f4d7ff73", + "sha256:b560f5cd86cf8df7bcd258a851ca1ad98f0d5b8b98748e877a0aec4e9032b465", + "sha256:b74b43fecce384a57094a83d2778cdfc2e2d9a6afaadd1ebecb2e75e0d34e10d", + "sha256:e85f727ffb21539849e6012f47b12f6dd4c44965e56591d8dec6e8bc9ab96f4a", + "sha256:fd2e09bb593ad9bdd7429e779699d2d47c1268cbde4dda95fcd1bd17544a0217", + "sha256:ffad8eb2ac614518bbe3c0b8eb9dffdb3a8d2e3a7d5da51c5b974fb723a5c5aa" ], "index": "pypi", - "version": "==5.6.5" + "version": "==5.6.7" }, "pycapnp": { "hashes": [ @@ -536,22 +528,20 @@ }, "pyyaml": { "hashes": [ - "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", - "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", - "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", - "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", - "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", - "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", - "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", - "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", - "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", - "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", - "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", - "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", - "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8" + "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6", + "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf", + "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5", + "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e", + "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811", + "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e", + "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d", + "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20", + "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689", + "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994", + "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615" ], "index": "pypi", - "version": "==5.1.2" + "version": "==5.3" }, "pyzmq": { "hashes": [ @@ -605,11 +595,11 @@ }, "scons": { "hashes": [ - "sha256:822b99f82295dfa1270f613d63a9cd43cd007c7e98b48cee28067d9c3c9fd593", - "sha256:fd44f8f2a4562e7e5bc8c63c82b01e469e8115805a3e9c2923ee54cdcd6678b3" + "sha256:0f860678cd96fc943ff2294389b0f33cbe51080801591497bc652e72237f0176", + "sha256:8aaa483c303efeb678e6f7c776c8444a482f8ddc3ad891f8b6cdd35264da9a1f" ], "index": "pypi", - "version": "==3.1.1" + "version": "==3.1.2" }, "setproctitle": { "hashes": [ @@ -636,19 +626,19 @@ }, "sympy": { "hashes": [ - "sha256:71a11e5686ae7ab6cb8feb5bd2651ef4482f8fd43a7c27e645a165e4353b23e1", - "sha256:f9b00ec76151c98470e84f1da2d7d03633180b71fb318428ddccce1c867d3eaa" + "sha256:4880d3a351558063bd89febda302f220dc4b88de393bba81fa6539a3966f03fa", + "sha256:d77901d748287d15281f5ffe5b0fef62dd38f357c2b827c44ff07f35695f4e7e" ], "index": "pypi", - "version": "==1.4" + "version": "==1.5.1" }, "tqdm": { "hashes": [ - "sha256:9de4722323451eb7818deb0161d9d5523465353a6707a9f500d97ee42919b902", - "sha256:c1d677f3a85fa291b34bdf8f770f877119b9754b32673699653556f85e2c2f13" + "sha256:4789ccbb6fc122b5a6a85d512e4e41fc5acad77216533a6f2b8ce51e0f265c23", + "sha256:efab950cf7cc1e4d8ee50b2bb9c8e4a89f8307b49e0b2c9cfef3ec4ca26655eb" ], "index": "pypi", - "version": "==4.38.0" + "version": "==4.41.1" }, "typed-ast": { "hashes": [ @@ -693,11 +683,11 @@ }, "websocket-client": { "hashes": [ - "sha256:1151d5fb3a62dc129164292e1227655e4bbc5dd5340a5165dfae61128ec50aa9", - "sha256:1fd5520878b68b84b5748bb30e592b10d0a91529d5383f74f4964e72b297fd3a" + "sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549", + "sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010" ], "index": "pypi", - "version": "==0.56.0" + "version": "==0.57.0" }, "werkzeug": { "hashes": [ @@ -717,9 +707,16 @@ "develop": { "absl-py": { "hashes": [ - "sha256:d9129186431e150d7fe455f1cb1ecbb92bb5dba9da9bc3ef7b012d98c4db2526" + "sha256:75e737d6ce7723d9ff9b7aa1ba3233c34be62ef18d5859e706b8fdc828989830" ], - "version": "==0.8.1" + "version": "==0.9.0" + }, + "adal": { + "hashes": [ + "sha256:5a7f1e037c6290c6d7609cab33a9e5e988c2fbec5c51d1c4c649ee3faff37eaf", + "sha256:fd17e5661f60634ddf96a569b95d34ccb8a98de60593d729c28bdcfe360eaad1" + ], + "version": "==1.2.2" }, "aenum": { "hashes": [ @@ -730,21 +727,34 @@ "index": "pypi", "version": "==2.2.3" }, - "amqp": { + "antlr4-python3-runtime": { "hashes": [ - "sha256:6e649ca13a7df3faacdc8bbb280aa9a6602d22fd9d545336077e573a1f4ff3b8", - "sha256:77f1aef9410698d20eaeac5b73a87817365f457a507d82edf292e12cbb83b08d" + "sha256:168cdcec8fb9152e84a87ca6fd261b3d54c8f6358f42ab3b813b14a7193bb50b" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.5.2" + "markers": "python_version >= '3.0'", + "version": "==4.7.2" + }, + "applicationinsights": { + "hashes": [ + "sha256:30a11aafacea34f8b160fbdc35254c9029c7e325267874e3c68f6bdbcd6ed2c3", + "sha256:b88bc5a41385d8e516489128d5e63f8c52efe597a3579b1718d1ab2f7cf150a2" + ], + "version": "==0.11.9" + }, + "argcomplete": { + "hashes": [ + "sha256:52a08b426bd0b03b6881182dd84149b2493540d1c3109ccf9f09f78e4459e387", + "sha256:783d6a12c6c84a33653dc5bac4d6c0640ba64d1037c2662acd9dbe410c26056f" + ], + "version": "==1.11.0" }, "astor": { "hashes": [ - "sha256:0e41295809baf43ae8303350e031aff81ae52189b6f881f36d623fa8b2f1960e", - "sha256:37a6eed8b371f1228db08234ed7f6cfdc7817a3ed3824797e20cbb11dc2a7862" + "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5", + "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.8.0" + "version": "==0.8.1" }, "astroid": { "hashes": [ @@ -762,13 +772,50 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==19.3.0" }, - "azure-common": { + "azure-cli-core": { "hashes": [ - "sha256:53b1195b8f20943ccc0e71a17849258f7781bc6db1c72edc7d6c055f79bd54e3", - "sha256:99ef36e74b6395329aada288764ce80504da16ecc8206cb9a72f55fb02e8b484" + "sha256:979954688c56cb76be7043e4c4ecc8433ce20dfc4d95ea42d6584b061cbafdb3", + "sha256:bcd371210ccc83a29884a9add0fa84702538092926eefa4dfdb5075d71ccb6c2" ], "index": "pypi", - "version": "==1.1.23" + "version": "==2.0.79" + }, + "azure-cli-nspkg": { + "hashes": [ + "sha256:1bde56090f548c6435bd3093995cf88e4c445fb040604df8b5b5f70780d79181", + "sha256:9a1e4f3197183470e4afecfdd45c92320f6753555b06a70651f89972332ffaf6" + ], + "version": "==3.0.4" + }, + "azure-cli-telemetry": { + "hashes": [ + "sha256:1f239d544d309c29e827982cc20113eb57037dba16db6cdd2e0283e437e0e577", + "sha256:7b18d7520e35e134136a0f7de38403a7dbce7b1e835065bd9e965579815ddf2f" + ], + "version": "==1.0.4" + }, + "azure-common": { + "hashes": [ + "sha256:184ad6a05a3089dfdc1ce07c1cbfa489bbc45b5f6f56e848cac0851e6443da21", + "sha256:3d64e9ab995300f42abd5bc0ef02f02bab661321e394d4dbacb4382ea1fb2f72" + ], + "index": "pypi", + "version": "==1.1.24" + }, + "azure-core": { + "hashes": [ + "sha256:4d047fd4e46a958c9b63f9d5cb52e6bf7dfc5c2a1c2a81b968499335a94bb5cb", + "sha256:b44fe5b46d2bb0260cafb737ab5ee89a16d478fc1885dabe21c426c4df205502" + ], + "index": "pypi", + "version": "==1.1.1" + }, + "azure-mgmt-resource": { + "hashes": [ + "sha256:a557a87fad2a2a5190d03e12cd7cf6307a194604e808773972c34847503b482b", + "sha256:e04e867af9289a237cfe285995025555fcceb90de5deb420c540dca3a4c9c622" + ], + "version": "==6.0.0" }, "azure-nspkg": { "hashes": [ @@ -825,36 +872,38 @@ "index": "pypi", "version": "==0.0.14" }, - "billiard": { + "bcrypt": { "hashes": [ - "sha256:01afcb4e7c4fd6480940cfbd4d9edc19d7a7509d6ada533984d0d0f49901ec82", - "sha256:b8809c74f648dfe69b973c8e660bcec00603758c9db8ba89d7719f88d5f01f26" + "sha256:0258f143f3de96b7c14f762c770f5fc56ccd72f8a1857a451c1cd9a655d9ac89", + "sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42", + "sha256:19a4b72a6ae5bb467fea018b825f0a7d917789bcfe893e53f15c92805d187294", + "sha256:5432dd7b34107ae8ed6c10a71b4397f1c853bd39a4d6ffa7e35f40584cffd161", + "sha256:6305557019906466fc42dbc53b46da004e72fd7a551c044a827e572c82191752", + "sha256:69361315039878c0680be456640f8705d76cb4a3a3fe1e057e0f261b74be4b31", + "sha256:6fe49a60b25b584e2f4ef175b29d3a83ba63b3a4df1b4c0605b826668d1b6be5", + "sha256:74a015102e877d0ccd02cdeaa18b32aa7273746914a6c5d0456dd442cb65b99c", + "sha256:763669a367869786bb4c8fcf731f4175775a5b43f070f50f46f0b59da45375d0", + "sha256:8b10acde4e1919d6015e1df86d4c217d3b5b01bb7744c36113ea43d529e1c3de", + "sha256:9fe92406c857409b70a38729dbdf6578caf9228de0aef5bc44f859ffe971a39e", + "sha256:a190f2a5dbbdbff4b74e3103cef44344bc30e61255beb27310e2aec407766052", + "sha256:a595c12c618119255c90deb4b046e1ca3bcfad64667c43d1166f2b04bc72db09", + "sha256:c9457fa5c121e94a58d6505cadca8bed1c64444b83b3204928a866ca2e599105", + "sha256:cb93f6b2ab0f6853550b74e051d297c27a638719753eb9ff66d1e4072be67133", + "sha256:ce4e4f0deb51d38b1611a27f330426154f2980e66582dc5f438aad38b5f24fc1", + "sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7", + "sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc" ], - "version": "==3.6.1.0" - }, - "bincopy": { - "hashes": [ - "sha256:1b4c7219e01042cd8217fb6c12b66f6b0ff01d1986d27b0cb18fcdcf1ea3dcb7", - "sha256:3ba5fe82fc07cb1be40e5ff668a9869aabb5b264a9223b3ff4ded77273260c8c" - ], - "index": "pypi", - "version": "==16.1.3" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==3.1.7" }, "bleach": { "hashes": [ "sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", "sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa" ], - "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==3.1.0" }, - "blinker": { - "hashes": [ - "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6" - ], - "index": "pypi", - "version": "==1.4" - }, "boto": { "hashes": [ "sha256:147758d41ae7240dc989f0039f27da8ca0d53734be0eb869ef16e3adcfa462e8", @@ -865,40 +914,33 @@ }, "boto3": { "hashes": [ - "sha256:165e967db773b14e7a8d430e0dca6da0306fe12db8aff23e9a36e408704b1b45", - "sha256:f60eb8ff4e782c78f09d5ee9a0398185ff45ea799902e3485c18a3de44c55df8" + "sha256:b3b134d8df25ba2465eb4c39b642aaa7b5342917c7810dc24c0aeb866bc6d816", + "sha256:ff3539243b9d8fde9a1e86f8e79a5ae385ccb583ec2a1083f3a63aed078aa42d" ], "index": "pypi", - "version": "==1.10.23" + "version": "==1.10.47" }, "botocore": { "hashes": [ - "sha256:4389d2e97a453f769b613d66128148ae0179d56c12651d27bcb6aec9e2bf5fea", - "sha256:6c7cf235dcb4ff27eb240fa62fc1cb9e1438578116cd8e9179111dbb4f9b325e" + "sha256:a5187cc5ec9558890ce5522a3c7e73c812cbae31c6d905d13a868e861a771272", + "sha256:b7d1001208a0c514ced7b126606dae360ca5e0141cc9496d37f83a2c89ebd915" ], - "version": "==1.13.23" + "version": "==1.13.47" }, "cachetools": { "hashes": [ - "sha256:428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae", - "sha256:8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a" + "sha256:9a52dd97a85f257f4e4127f15818e71a0c7899f121b34591fcc1173ea79a0198", + "sha256:b304586d357c43221856be51d73387f93e2a961598a9b6b6670664746f3b6c6c" ], - "version": "==3.1.1" - }, - "celery": { - "hashes": [ - "sha256:4c4532aa683f170f40bd76f928b70bc06ff171a959e06e71bf35f2f9d6031ef9", - "sha256:528e56767ae7e43a16cfef24ee1062491f5754368d38fcfffa861cdb9ef219be" - ], - "index": "pypi", - "version": "==4.3.0" + "markers": "python_version ~= '3.5'", + "version": "==4.0.0" }, "certifi": { "hashes": [ - "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", - "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" + "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", + "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" ], - "version": "==2019.9.11" + "version": "==2019.11.28" }, "cffi": { "hashes": [ @@ -955,50 +997,57 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==7.0" }, + "colorama": { + "hashes": [ + "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", + "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.4.3" + }, "control": { "hashes": [ - "sha256:726e8c36a253a54c8886df31f860d740d70de4f8b041421d5df078c3bff3aadb" + "sha256:1fcfdcf39f96523cb1f2cf7bf7b8ae68ec3ef8350e5c55e17e02afdb0872edbb" ], "index": "pypi", - "version": "==0.8.2" + "version": "==0.8.3" }, "coverage": { "hashes": [ - "sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6", - "sha256:0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650", - "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5", - "sha256:19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d", - "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351", - "sha256:245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755", - "sha256:331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef", - "sha256:386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca", - "sha256:3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca", - "sha256:60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9", - "sha256:63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc", - "sha256:6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5", - "sha256:6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f", - "sha256:7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe", - "sha256:826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888", - "sha256:93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5", - "sha256:9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce", - "sha256:af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5", - "sha256:bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e", - "sha256:bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e", - "sha256:c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9", - "sha256:dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437", - "sha256:df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1", - "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c", - "sha256:e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24", - "sha256:e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47", - "sha256:eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2", - "sha256:eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28", - "sha256:ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c", - "sha256:efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7", - "sha256:fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0", - "sha256:ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025" + "sha256:189aac76d6e0d7af15572c51892e7326ee451c076c5a50a9d266406cd6c49708", + "sha256:1bf7ba2af1d373a1750888724f84cffdfc697738f29a353c98195f98fc011509", + "sha256:1f4ee8e2e4243971618bc16fcc4478317405205f135e95226c2496e2a3b8dbbf", + "sha256:225e79a5d485bc1642cb7ba02281419c633c216cdc6b26c26494ba959f09e69f", + "sha256:23688ff75adfa8bfa2a67254d889f9bdf9302c27241d746e17547c42c732d3f4", + "sha256:28f7f73b34a05e23758e860a89a7f649b85c6749e252eff60ebb05532d180e86", + "sha256:2d0cb9b1fe6ad0d915d45ad3d87f03a38e979093a98597e755930db1f897afae", + "sha256:47874b4711c5aeb295c31b228a758ce3d096be83dc37bd56da48ed99efb8813b", + "sha256:511ec0c00840e12fb4e852e4db58fa6a01ca4da72f36a9766fae344c3d502033", + "sha256:53e7438fef0c97bc248f88ba1edd10268cd94d5609970aaf87abbe493691af87", + "sha256:569f9ee3025682afda6e9b0f5bb14897c0db03f1a1dc088b083dd36e743f92bb", + "sha256:593853aa1ac6dcc6405324d877544c596c9d948ef20d2e9512a0f5d2d3202356", + "sha256:5b0a07158360d22492f9abd02a0f2ee7981b33f0646bf796598b7673f6bbab14", + "sha256:7ca3db38a61f3655a2613ee2c190d63639215a7a736d3c64cc7bbdb002ce6310", + "sha256:7d1cc7acc9ce55179616cf72154f9e648136ea55987edf84addbcd9886ffeba2", + "sha256:88b51153657612aea68fa684a5b88037597925260392b7bb4509d4f9b0bdd889", + "sha256:955ec084f549128fa2702f0b2dc696392001d986b71acd8fd47424f28289a9c3", + "sha256:b251c7092cbb6d789d62dc9c9e7c4fb448c9138b51285c36aeb72462cad3600e", + "sha256:bd82b684bb498c60ef47bb1541a50e6d006dde8579934dcbdbc61d67d1ea70d9", + "sha256:bfe102659e2ec13b86c7f3b1db6c9a4e7beea4255058d006351339e6b342d5d2", + "sha256:c1e4e39e43057396a5e9d069bfbb6ffeee892e40c5d2effbd8cd71f34ee66c4d", + "sha256:cb2b74c123f65e8166f7e1265829a6c8ed755c3cd16d7f50e75a83456a5f3fd7", + "sha256:cca38ded59105f7705ef6ffe1e960b8db6c7d8279c1e71654a4775ab4454ca15", + "sha256:cf908840896f7aa62d0ec693beb53264b154f972eb8226fb864ac38975590c4f", + "sha256:d095a7b473f8a95f7efe821f92058c8a2ecfb18f8db6677ae3819e15dc11aaae", + "sha256:d22b4297e7e4225ccf01f1aa55e7a96412ea0796b532dd614c3fcbafa341128e", + "sha256:d4a2b578a7a70e0c71f662705262f87a456f1e6c1e40ada7ea699abaf070a76d", + "sha256:ddeb42a3d5419434742bf4cc71c9eaa22df3b76808e23a82bd0b0bd360f1a9f1", + "sha256:e65a5aa1670db6263f19fdc03daee1d7dbbadb5cb67fd0a1f16033659db13c1d", + "sha256:eaad65bd20955131bcdb3967a4dea66b4e4d4ca488efed7c00d91ee0173387e8", + "sha256:f45fba420b94165c17896861bb0e8b27fb7abdcedfeb154895d8553df90b7b00" ], "index": "pypi", - "version": "==4.5.4" + "version": "==5.0.2" }, "cryptography": { "hashes": [ @@ -1036,18 +1085,17 @@ }, "datadog": { "hashes": [ - "sha256:2746dd41055805e7b41610de887444ccbe5ee38ed7cf118bc6ba34e23c5a2e73", - "sha256:d97d85a8d2b90fe01f00b637a943c8b4a2d31928d7d4248610c8aab2292e8407" + "sha256:22d3c935e83de02b64efed635ac5fb45db26e152ac759105ec6d5a7ed8b9f6a9", + "sha256:bce73f33a4496b004402baa502251150e3b48a48f610ff89d4cd110b366ee0ab" ], "index": "pypi", - "version": "==0.32.0" + "version": "==0.33.0" }, "decorator": { "hashes": [ "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce", "sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d" ], - "index": "pypi", "version": "==4.4.1" }, "defusedxml": { @@ -1060,18 +1108,18 @@ }, "dictdiffer": { "hashes": [ - "sha256:97cf4ef98ebc1acf737074aed41e379cf48ab5ff528c92109dfb8e2e619e6809", - "sha256:b3ad476fc9cca60302b52c50e1839342d2092aeaba586d69cbf9249f87f52463" + "sha256:1adec0d67cdf6166bda96ae2934ddb5e54433998ceab63c984574d187cc563d2", + "sha256:d79d9a39e459fe33497c858470ca0d2e93cb96621751de06d631856adfd9c390" ], "index": "pypi", - "version": "==0.8.0" + "version": "==0.8.1" }, "dlib": { "hashes": [ - "sha256:8ca127253a0ca82a3d847148515f82ff2c504ed77a6385ec4f38c7f8e5360860" + "sha256:d0eeaca07bc4c75973ad0f739a541d8fa4003af778f0dc1c2c595d470823819a" ], "index": "pypi", - "version": "==19.18.0" + "version": "==19.19.0" }, "docutils": { "hashes": [ @@ -1082,14 +1130,6 @@ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.15.2" }, - "dominate": { - "hashes": [ - "sha256:6e833aea505f0236a9fc692326bac575f8bd38ae0f3a1bdc73d20ca606ac75d5", - "sha256:a92474b4312bd8b4c1789792f3ec8c571cd8afa8e7502a2b1c64dd48cd67e59c" - ], - "index": "pypi", - "version": "==2.4.0" - }, "elasticsearch": { "hashes": [ "sha256:0140787216646e1eb7eb001f8146aff7071d7ca438854249787b6cc221ddd266", @@ -1108,41 +1148,32 @@ }, "fastcluster": { "hashes": [ - "sha256:0024b1304d4618a32900473b809265694bc6d0a10bfbc272407443318644d405", - "sha256:0b33b076ea98939742e0734e944c903c8c67af3eecd5a9f8b4349407c1affdd3", - "sha256:244d400a81d23b48f2b825e50e99e1ff3c7d2be2acc40942e3359b4deef68483", - "sha256:2bb3bf8e1c7af42e47ef24bc2386a1d3e6ec8dc5e6be4123a491a36b89227b49", - "sha256:539d84d43fbe541a38d94c84bf0469c430cb7cda488364941ce57f680a07b091", - "sha256:5e7d81385bbc2148c554c4aaa32e26a00fbadc1190d5bad9c8719f6432629307", - "sha256:642e4d92220c1deede1e89409c5ad366e4a3f3ed55fff0a4e325d4eb2574d65a", - "sha256:66c868fdfc161e5c45cdf399f1274216602ea82c9fc46c632c9602dbaa149840", - "sha256:6952c9d25b3ea60d6a53ee92205e7f16cc6baf7caa4776fb1e55dc6e8e2965f2", - "sha256:8606da6f6f51f81c3b4e000edeb8668711a1769a6ebf3c99ab98f9c6d2fb7534", - "sha256:8d552d29ed054ce20f5bb0445b51bd7b6b8088fdae334625b4a9950189dcc2bf", - "sha256:922735fde2848a6f0478c6968048a16a5d3d6ba6e1d1a9596d578b59dce719d7", - "sha256:9f3cd227a9ebeec090e4186a63e46cd87d121968bd1d45ea8a7c2d483bc082fb", - "sha256:a382cb411610ca06a69e7b131239f0bb02961fec762d5b6804e653a32cd5ab52", - "sha256:a986b3afe823bcaea3dad9de1485b176c6dd5b0e2f154ede71aaf6635e82e159", - "sha256:b1902ab52418c6f4c2f3f7439aea711c6b5aa9bbb4c97b6d4b0c6f5e5c894342", - "sha256:bb56f6c7343bbb68beb1ffd13abdb87ce7a84ab391b3a4e63ed5c02b2d1ce46f", - "sha256:c2e5d11f5f96e6861fd632246f18bc6592ac439f236caffd4d2a75697a6f8969", - "sha256:c61973bb16117ee16b7642ff9da8e0899fb4d5eb5d0cc9c56d3269551ccd6307", - "sha256:d2deec11a6625c5578325f4dd10980e2981d1f1d1e658c7c79f2497377f92bdf", - "sha256:e0f2feb03f67b12f25538aa05b1150c7a2c1cd1c21ad7d0bde94e8169ef46627", - "sha256:e32e7287c47291eca2c83cd5cc6a5007eff2da00c12ac21fd721a16d0c428059", - "sha256:e889ea36c4e469ff3fb2a6a868c8e557fc988a81cd1cf3d09876107e59ee9cbd", - "sha256:e96f3b65e3748f2be06aa0977a00872ddeca4f8959703126f2b103978243bbdd" + "sha256:18988adb9bc07fcb5970a17ced626092d28806866f7914aa8079f1fe57d1fcdf", + "sha256:3fe5102b58736ff8ed2a7944be98fbd4837b6ba4f3acbf6c06ef3b8ccdda00f1", + "sha256:4c0d310b74bfac6121eec4b704ffe52bdca995d022b7898ce02d3609a141d7a3", + "sha256:5026d6a69819c07a9df578529c7ed6615737e70650c42c55267c6a7c57c1b2be", + "sha256:5045697238bc71e37177f6b6ae9d902c02e8947ee43657476227c92d84885a68", + "sha256:67fe1890ba5f281e145cc17444056670b60e0232650d189345001f658046f46c", + "sha256:69055a8cd51e6acafd4d0973cdeaa03620e93f548527310a2448a6d19731ef29", + "sha256:6e421e7eda88f3140c8786da80656d85c7c73b53665439970acfa7e1f54fc699", + "sha256:71b2f1c455aae6bb13aa1c9c1db1b0ccc0cdc336646005008d47fe5d584a07a9", + "sha256:7cb427be975cdadf969fe62eccd5256dccb623342e80ea0fdda8870e40ac756f", + "sha256:8dd5ef24ea7014ea2d2478627fec509a4366053bcce5b8559b72fc4f62d3990e", + "sha256:94699ce6a452e34e1ac530823dbfb5a8b97942f00abd07231cb17b3c9f4d11fb", + "sha256:9849b86b44d2307f52c85a92ecb8dd2c8365bc41d835fb34c859a3622fa52417", + "sha256:a202f44a3b06f5cf9cdba3c67d6c523288922d6e6a1cdf737292f93759aa82f7", + "sha256:aaaef36300cbf20fabea182369186e6899b122b2667e1408c981e5177a8ebc67", + "sha256:accf6df86ce86905a9a87f10cb241d8d772d11ce77a2e8721ac731a7ad11fe8c", + "sha256:b1d905eb21a40a8b80e678030b985f42bbff4d6eef4c3158cbd0c00c36966de9", + "sha256:b86f22c46e8217dcd70a58a35b6134de7fa45d5986d703a8cd0812087782d900", + "sha256:ba26b92bd991714f6edf787e2ec42f294e30d1d6b79cf0c390199cb2b5dcf952", + "sha256:c17e9d650cefdab2f4632faf0239fe5b77d8f92cde56860a3cbdce33d2963bb9", + "sha256:c29af2b6c2c8c39292fba4db1f988ae2328298d16b9dc2e8aa4b969020332f5b", + "sha256:ca988e233e6aee007616af0fc7fbf5b236a3940f334a0fd176cd165151e8aea2", + "sha256:fd1ada69e745032274632ba0ef5532c8ca631a8b1d2db819689385a5253f3129" ], "index": "pypi", - "version": "==1.1.25" - }, - "fasteners": { - "hashes": [ - "sha256:007e4d2b2d4a10093f67e932e5166722d2eab83b77724156e92ad013c6226574", - "sha256:3a176da6b70df9bb88498e1a18a9e4a8579ed5b9141207762368a1017bf8f5ef" - ], - "index": "pypi", - "version": "==0.15" + "version": "==1.1.26" }, "flask": { "hashes": [ @@ -1188,7 +1219,6 @@ "hashes": [ "sha256:fe939df4583692f0512161ec1c880e0a10e71e6a232da045ab8edd3756fbadf0" ], - "index": "pypi", "version": "==0.2.2" }, "geoalchemy2": { @@ -1199,35 +1229,6 @@ "index": "pypi", "version": "==0.6.3" }, - "gevent": { - "hashes": [ - "sha256:0774babec518a24d9a7231d4e689931f31b332c4517a771e532002614e270a64", - "sha256:0e1e5b73a445fe82d40907322e1e0eec6a6745ca3cea19291c6f9f50117bb7ea", - "sha256:0ff2b70e8e338cf13bedf146b8c29d475e2a544b5d1fe14045aee827c073842c", - "sha256:107f4232db2172f7e8429ed7779c10f2ed16616d75ffbe77e0e0c3fcdeb51a51", - "sha256:14b4d06d19d39a440e72253f77067d27209c67e7611e352f79fe69e0f618f76e", - "sha256:1b7d3a285978b27b469c0ff5fb5a72bcd69f4306dbbf22d7997d83209a8ba917", - "sha256:1eb7fa3b9bd9174dfe9c3b59b7a09b768ecd496debfc4976a9530a3e15c990d1", - "sha256:2711e69788ddb34c059a30186e05c55a6b611cb9e34ac343e69cf3264d42fe1c", - "sha256:28a0c5417b464562ab9842dd1fb0cc1524e60494641d973206ec24d6ec5f6909", - "sha256:3249011d13d0c63bea72d91cec23a9cf18c25f91d1f115121e5c9113d753fa12", - "sha256:44089ed06a962a3a70e96353c981d628b2d4a2f2a75ea5d90f916a62d22af2e8", - "sha256:4bfa291e3c931ff3c99a349d8857605dca029de61d74c6bb82bd46373959c942", - "sha256:50024a1ee2cf04645535c5ebaeaa0a60c5ef32e262da981f4be0546b26791950", - "sha256:53b72385857e04e7faca13c613c07cab411480822ac658d97fd8a4ddbaf715c8", - "sha256:74b7528f901f39c39cdbb50cdf08f1a2351725d9aebaef212a29abfbb06895ee", - "sha256:7d0809e2991c9784eceeadef01c27ee6a33ca09ebba6154317a257353e3af922", - "sha256:896b2b80931d6b13b5d9feba3d4eebc67d5e6ec54f0cf3339d08487d55d93b0e", - "sha256:8d9ec51cc06580f8c21b41fd3f2b3465197ba5b23c00eb7d422b7ae0380510b0", - "sha256:9f7a1e96fec45f70ad364e46de32ccacab4d80de238bd3c2edd036867ccd48ad", - "sha256:ab4dc33ef0e26dc627559786a4fba0c2227f125db85d970abbf85b77506b3f51", - "sha256:d1e6d1f156e999edab069d79d890859806b555ce4e4da5b6418616322f0a3df1", - "sha256:d752bcf1b98174780e2317ada12013d612f05116456133a6acf3e17d43b71f05", - "sha256:e5bcc4270671936349249d26140c267397b7b4b1381f5ec8b13c53c5b53ab6e1" - ], - "index": "pypi", - "version": "==1.4.0" - }, "git-pylint-commit-hook": { "hashes": [ "sha256:e1d39e7856b3ef0a0269121ca210dc3f5a97da158b322411e8e1185918a91b3c" @@ -1237,11 +1238,11 @@ }, "google-auth": { "hashes": [ - "sha256:84105be98837fb8436e9d0bcb7a279fd85fa1d97bb35a077e70ba2fb95bcc983", - "sha256:baf1b3f8b29a5f96f66753ad848473699322b63f4d68964e510554b12d002443" + "sha256:7bb2034a3a290190cf4e3eb8ebf29e5025c90f0b06a00ba4d1fb94bf0c6448f7", + "sha256:c57074e594d2c6e3e316162734e8af3e15d40252022e69414cba67de66594417" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.7.1" + "version": "==1.10.0" }, "google-auth-oauthlib": { "hashes": [ @@ -1258,91 +1259,61 @@ ], "version": "==0.1.8" }, - "greenlet": { - "hashes": [ - "sha256:000546ad01e6389e98626c1367be58efa613fa82a1be98b0c6fc24b563acc6d0", - "sha256:0d48200bc50cbf498716712129eef819b1729339e34c3ae71656964dac907c28", - "sha256:23d12eacffa9d0f290c0fe0c4e81ba6d5f3a5b7ac3c30a5eaf0126bf4deda5c8", - "sha256:37c9ba82bd82eb6a23c2e5acc03055c0e45697253b2393c9a50cef76a3985304", - "sha256:51503524dd6f152ab4ad1fbd168fc6c30b5795e8c70be4410a64940b3abb55c0", - "sha256:8041e2de00e745c0e05a502d6e6db310db7faa7c979b3a5877123548a4c0b214", - "sha256:81fcd96a275209ef117e9ec91f75c731fa18dcfd9ffaa1c0adbdaa3616a86043", - "sha256:853da4f9563d982e4121fed8c92eea1a4594a2299037b3034c3c898cb8e933d6", - "sha256:8b4572c334593d449113f9dc8d19b93b7b271bdbe90ba7509eb178923327b625", - "sha256:9416443e219356e3c31f1f918a91badf2e37acf297e2fa13d24d1cc2380f8fbc", - "sha256:9854f612e1b59ec66804931df5add3b2d5ef0067748ea29dc60f0efdcda9a638", - "sha256:99a26afdb82ea83a265137a398f570402aa1f2b5dfb4ac3300c026931817b163", - "sha256:a19bf883b3384957e4a4a13e6bd1ae3d85ae87f4beb5957e35b0be287f12f4e4", - "sha256:a9f145660588187ff835c55a7d2ddf6abfc570c2651c276d3d4be8a2766db490", - "sha256:ac57fcdcfb0b73bb3203b58a14501abb7e5ff9ea5e2edfa06bb03035f0cff248", - "sha256:bcb530089ff24f6458a81ac3fa699e8c00194208a724b644ecc68422e1111939", - "sha256:beeabe25c3b704f7d56b573f7d2ff88fc99f0138e43480cecdfcaa3b87fe4f87", - "sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720", - "sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656" - ], - "markers": "platform_python_implementation == 'CPython'", - "version": "==0.4.15" - }, "grpcio": { "hashes": [ - "sha256:0419ae5a45f49c7c40d9ae77ae4de9442431b7822851dfbbe56ee0eacb5e5654", - "sha256:1e8631eeee0fb0b4230aeb135e4890035f6ef9159c2a3555fa184468e325691a", - "sha256:24db2fa5438f3815a4edb7a189035051760ca6aa2b0b70a6a948b28bfc63c76b", - "sha256:2adb1cdb7d33e91069517b41249622710a94a1faece1fed31cd36904e4201cde", - "sha256:2cd51f35692b551aeb1fdeb7a256c7c558f6d78fcddff00640942d42f7aeba5f", - "sha256:3247834d24964589f8c2b121b40cd61319b3c2e8d744a6a82008643ef8a378b1", - "sha256:3433cb848b4209717722b62392e575a77a52a34d67c6730138102abc0a441685", - "sha256:39671b7ff77a962bd745746d9d2292c8ed227c5748f16598d16d8631d17dd7e5", - "sha256:40a0b8b2e6f6dd630f8b267eede2f40a848963d0f3c40b1b1f453a4a870f679e", - "sha256:40f9a74c7aa210b3e76eb1c9d56aa8d08722b73426a77626967019df9bbac287", - "sha256:423f76aa504c84cb94594fb88b8a24027c887f1c488cf58f2173f22f4fbd046c", - "sha256:43bd04cec72281a96eb361e1b0232f0f542b46da50bcfe72ef7e5a1b41d00cb3", - "sha256:43e38762635c09e24885d15e3a8e374b72d105d4178ee2cc9491855a8da9c380", - "sha256:4413b11c2385180d7de03add6c8845dd66692b148d36e27ec8c9ef537b2553a1", - "sha256:4450352a87094fd58daf468b04c65a9fa19ad11a0ac8ac7b7ff17d46f873cbc1", - "sha256:49ffda04a6e44de028b3b786278ac9a70043e7905c3eea29eed88b6524d53a29", - "sha256:4a38c4dde4c9120deef43aaabaa44f19186c98659ce554c29788c4071ab2f0a4", - "sha256:50b1febdfd21e2144b56a9aa226829e93a79c354ef22a4e5b013d9965e1ec0ed", - "sha256:559b1a3a8be7395ded2943ea6c2135d096f8cc7039d6d12127110b6496f251fe", - "sha256:5de86c182667ec68cf84019aa0d8ceccf01d352cdca19bf9e373725204bdbf50", - "sha256:5fc069bb481fe3fad0ba24d3baaf69e22dfa6cc1b63290e6dfeaf4ac1e996fb7", - "sha256:6a19d654da49516296515d6f65de4bbcbd734bc57913b21a610cfc45e6df3ff1", - "sha256:7535b3e52f498270e7877dde1c8944d6b7720e93e2e66b89c82a11447b5818f5", - "sha256:7c4e495bcabc308198b8962e60ca12f53b27eb8f03a21ac1d2d711d6dd9ecfca", - "sha256:8a8fc4a0220367cb8370cedac02272d574079ccc32bffbb34d53aaf9e38b5060", - "sha256:8b008515e067232838daca020d1af628bf6520c8cc338bf383284efe6d8bd083", - "sha256:8d1684258e1385e459418f3429e107eec5fb3d75e1f5a8c52e5946b3f329d6ea", - "sha256:8eb5d54b87fb561dc2e00a5c5226c33ffe8dbc13f2e4033a412bafb7b37b194d", - "sha256:94cdef0c61bd014bb7af495e21a1c3a369dd0399c3cd1965b1502043f5c88d94", - "sha256:9d9f3be69c7a5e84c3549a8c4403fa9ac7672da456863d21e390b2bbf45ccad1", - "sha256:9fb6fb5975a448169756da2d124a1beb38c0924ff6c0306d883b6848a9980f38", - "sha256:a5eaae8700b87144d7dfb475aa4675e500ff707292caba3deff41609ddc5b845", - "sha256:aaeac2d552772b76d24eaff67a5d2325bc5205c74c0d4f9fbe71685d4a971db2", - "sha256:bb611e447559b3b5665e12a7da5160c0de6876097f62bf1d23ba66911564868e", - "sha256:bc0d41f4eb07da8b8d3ea85e50b62f6491ab313834db86ae2345be07536a4e5a", - "sha256:bf51051c129b847d1bb63a9b0826346b5f52fb821b15fe5e0d5ef86f268510f5", - "sha256:c948c034d8997526011960db54f512756fb0b4be1b81140a15b4ef094c6594a4", - "sha256:d435a01334157c3b126b4ee5141401d44bdc8440993b18b05e2f267a6647f92d", - "sha256:d46c1f95672b73288e08cdca181e14e84c6229b5879561b7b8cfd48374e09287", - "sha256:d5d58309b42064228b16b0311ff715d6c6e20230e81b35e8d0c8cfa1bbdecad8", - "sha256:dc6e2e91365a1dd6314d615d80291159c7981928b88a4c65654e3fefac83a836", - "sha256:e0dfb5f7a39029a6cbec23affa923b22a2c02207960fd66f109e01d6f632c1eb", - "sha256:eb4bf58d381b1373bd21d50837a53953d625d1693f1b58fed12743c75d3dd321", - "sha256:ebb211a85248dbc396b29320273c1ffde484b898852432613e8df0164c091006", - "sha256:ec759ece4786ae993a5b7dc3b3dead6e9375d89a6c65dfd6860076d2eb2abe7b", - "sha256:f55108397a8fa164268238c3e69cc134e945d1f693572a2f05a028b8d0d2b837", - "sha256:f6c706866d424ff285b85a02de7bbe5ed0ace227766b2c42cbe12f3d9ea5a8aa", - "sha256:f8370ad332b36fbad117440faf0dd4b910e80b9c49db5648afd337abdde9a1b6" + "sha256:066630f6b62bffa291dacbee56994279a6a3682b8a11967e9ccaf3cc770fc11e", + "sha256:07e95762ca6b18afbeb3aa2793e827c841152d5e507089b1db0b18304edda105", + "sha256:0a0fb2f8e3a13537106bc77e4c63005bc60124a6203034304d9101921afa4e90", + "sha256:0c61b74dcfb302613926e785cb3542a0905b9a3a86e9410d8cf5d25e25e10104", + "sha256:13383bd70618da03684a8aafbdd9e3d9a6720bf8c07b85d0bc697afed599d8f0", + "sha256:1c6e0f6b9d091e3717e9a58d631c8bb4898be3b261c2a01fe46371fdc271052f", + "sha256:1cf710c04689daa5cc1e598efba00b028215700dcc1bf66fcb7b4f64f2ea5d5f", + "sha256:2da5cee9faf17bb8daf500cd0d28a17ae881ab5500f070a6aace457f4c08cac4", + "sha256:2f78ebf340eaf28fa09aba0f836a8b869af1716078dfe8f3b3f6ff785d8f2b0f", + "sha256:33a07a1a8e817d733588dbd18e567caad1a6fe0d440c165619866cd490c7911a", + "sha256:3d090c66af9c065b7228b07c3416f93173e9839b1d40bb0ce3dd2aa783645026", + "sha256:42b903a3596a10e2a3727bae2a76f8aefd324d498424b843cfa9606847faea7b", + "sha256:4fffbb58134c4f23e5a8312ac3412db6f5e39e961dc0eb5e3115ce5aa16bf927", + "sha256:57be5a6c509a406fe0ffa6f8b86904314c77b5e2791be8123368ad2ebccec874", + "sha256:5b0fa09efb33e2af4e8822b4eb8b2cbc201d562e3e185c439be7eaeee2e8b8aa", + "sha256:5ef42dfc18f9a63a06aca938770b69470bb322e4c137cf08cf21703d1ef4ae5c", + "sha256:6a43d2f2ff8250f200fdf7aa31fa191a997922aa9ea1182453acd705ad83ab72", + "sha256:6d8ab28559be98b02f8b3a154b53239df1aa5b0d28ff865ae5be4f30e7ed4d3f", + "sha256:6e47866b7dc14ca3a12d40c1d6082e7bea964670f1c5315ea0fb8b0550244d64", + "sha256:6edda1b96541187f73aab11800d25f18ee87e53d5f96bb74473873072bf28a0e", + "sha256:7109c8738a8a3c98cfb5dda1c45642a8d6d35dc00d257ab7a175099b2b4daecd", + "sha256:8d866aafb08657c456a18c4a31c8526ea62de42427c242b58210b9eae6c64559", + "sha256:9939727d9ae01690b24a2b159ac9dbca7b7e8e6edd5af6a6eb709243cae7b52b", + "sha256:99fd873699df17cb11c542553270ae2b32c169986e475df0d68a8629b8ef4df7", + "sha256:b6fda5674f990e15e1bcaacf026428cf50bce36e708ddcbd1de9673b14aab760", + "sha256:bdb2f3dcb664f0c39ef1312cd6acf6bc6375252e4420cf8f36fff4cb4fa55c71", + "sha256:bfd7d3130683a1a0a50c456273c21ec8a604f2d043b241a55235a78a0090ee06", + "sha256:c6c2db348ac73d73afe14e0833b18abbbe920969bf2c5c03c0922719f8020d06", + "sha256:cb7a4b41b5e2611f85c3402ac364f1d689f5d7ecbc24a55ef010eedcd6cf460f", + "sha256:cd3d3e328f20f7c807a862620c6ee748e8d57ba2a8fc960d48337ed71c6d9d32", + "sha256:d1a481777952e4f99b8a6956581f3ee866d7614100d70ae6d7e07327570b85ce", + "sha256:d1d49720ed636920bb3d74cedf549382caa9ad55aea89d1de99d817068d896b2", + "sha256:d42433f0086cccd192114343473d7dbd4aae9141794f939e2b7b83efc57543db", + "sha256:d44c34463a7c481e076f691d8fa25d080c3486978c2c41dca09a8dd75296c2d7", + "sha256:d7e5b7af1350e9c8c17a7baf99d575fbd2de69f7f0b0e6ebd47b57506de6493a", + "sha256:d9542366a0917b9b48bab1fee481ac01f56bdffc52437b598c09e7840148a6a9", + "sha256:df7cdfb40179acc9790a462c049e0b8e109481164dd7ad1a388dd67ff1528759", + "sha256:e1a9d9d2e7224d981aea8da79260c7f6932bf31ce1f99b7ccfa5eceeb30dc5d0", + "sha256:ed10e5fad105ecb0b12822f924e62d0deb07f46683a0b64416b17fd143daba1d", + "sha256:f0ec5371ce2363b03531ed522bfbe691ec940f51f0e111f0500fc0f44518c69d", + "sha256:f6580a8a4f5e701289b45fd62a8f6cb5ec41e4d77082424f8b676806dcd22564", + "sha256:f7b83e4b2842d44fce3cdc0d54db7a7e0d169a598751bf393601efaa401c83e0", + "sha256:ffec45b0db18a555fdfe0c6fa2d0a3fceb751b22b31e8fcd14ceed7bde05481e" ], - "version": "==1.25.0" + "version": "==1.26.0" }, "gunicorn": { "hashes": [ - "sha256:0806b5e8a2eb8ba9ac1be65d7b743ec896fc25f5d6cb16c5e051540157b315bb", - "sha256:ef69dea4814df95e64e3f40b47b7ffedc6911c5009233be9d01cfd0d14aa3f50" + "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626", + "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c" ], "index": "pypi", - "version": "==20.0.0" + "version": "==20.0.4" }, "h5py": { "hashes": [ @@ -1353,6 +1324,7 @@ "sha256:51ae56894c6c93159086ffa2c94b5b3388c0400548ab26555c143e7cfa05b8e5", "sha256:54817b696e87eb9e403e42643305f142cd8b940fe9b3b490bbf98c3b8a894cf4", "sha256:549ad124df27c056b2e255ea1c44d30fb7a17d17676d03096ad5cd85edb32dc1", + "sha256:64f74da4a1dd0d2042e7d04cf8294e04ddad686f8eba9bb79e517ae582f6668d", "sha256:6998be619c695910cb0effe5eb15d3a511d3d1a5d217d4bd0bebad1151ec2262", "sha256:6ef7ab1089e3ef53ca099038f3c0a94d03e3560e6aff0e9d6c64c55fb13fc681", "sha256:769e141512b54dee14ec76ed354fcacfc7d97fea5a7646b709f7400cf1838630", @@ -1367,6 +1339,7 @@ "sha256:c0d4b04bbf96c47b6d360cd06939e72def512b20a18a8547fa4af810258355d5", "sha256:c54a2c0dd4957776ace7f95879d81582298c5daf89e77fb8bee7378f132951de", "sha256:cbf28ae4b5af0f05aa6e7551cee304f1d317dbed1eb7ac1d827cee2f1ef97a99", + "sha256:d35f7a3a6cefec82bfdad2785e78359a0e6a5fbb3f605dd5623ce88082ccd681", "sha256:d3c59549f90a891691991c17f8e58c8544060fdf3ccdea267100fa5f561ff62f", "sha256:d7ae7a0576b06cb8e8a1c265a8bc4b73d05fdee6429bffc9a26a6eb531e79d72", "sha256:ecf4d0b56ee394a0984de15bceeb97cbe1fe485f1ac205121293fc44dcf3f31f", @@ -1417,18 +1390,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", - "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af" + "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", + "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" ], - "markers": "python_version < '3.8'", - "version": "==0.23" - }, - "intervaltree": { - "hashes": [ - "sha256:cb4f61c81dcb4fea6c09903f3599015a83c9bdad1f0bbd232495e6681e19e273" - ], - "index": "pypi", - "version": "==3.0.2" + "markers": "python_version == '3.7'", + "version": "==1.3.0" }, "ipykernel": { "hashes": [ @@ -1440,11 +1406,11 @@ }, "ipython": { "hashes": [ - "sha256:dfd303b270b7b5232b3d08bd30ec6fd685d8a58cabd54055e3d69d8f029f7280", - "sha256:ed7ebe1cba899c1c3ccad6f7f1c2d2369464cc77dba8eebc65e2043e19cda995" + "sha256:0f4bcf18293fb666df8511feec0403bdb7e061a5842ea6e88a3177b0ceb34ead", + "sha256:387686dd7fc9caf29d2fddcf3116c4b07a11d9025701d220c589a430b0171d8a" ], "index": "pypi", - "version": "==7.9.0" + "version": "==7.11.1" }, "ipython-genutils": { "hashes": [ @@ -1460,6 +1426,13 @@ ], "version": "==7.5.1" }, + "isodate": { + "hashes": [ + "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8", + "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81" + ], + "version": "==0.6.0" + }, "isort": { "hashes": [ "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", @@ -1478,11 +1451,11 @@ }, "jedi": { "hashes": [ - "sha256:786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27", - "sha256:ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e" + "sha256:1349c1e8c107095a55386628bb3b2a79422f3a2cab8381e34ce19909e0cf5064", + "sha256:e909527104a903606dd63bea6e8e888833f0ef087057829b89a18364a856f807" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.15.1" + "version": "==0.15.2" }, "jinja2": { "hashes": [ @@ -1501,11 +1474,11 @@ }, "joblib": { "hashes": [ - "sha256:006108c7576b3eb6c5b27761ddbf188eb6e6347696325ab2027ea1ee9a4b922d", - "sha256:6fcc57aacb4e89451fd449e9412687c51817c3f48662c3d8f38ba3f8a0a193ff" + "sha256:0630eea4f5664c463f23fbf5dcfc54a2bc6168902719fa8e19daf033022786c8", + "sha256:bdb4fd9b72915ffb49fde2229ce482dd7ae79d842ed8c2b4c932441495af1403" ], "index": "pypi", - "version": "==0.14.0" + "version": "==0.14.1" }, "json-logging-py": { "hashes": [ @@ -1611,13 +1584,12 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.1.0" }, - "kombu": { + "knack": { "hashes": [ - "sha256:1760b54b1d15a547c9a26d3598a1c8cdaf2436386ac1f5561934bc8a3cbbbd86", - "sha256:e7465aa85a1db889116819f08c5de29520d2fa103324dcdca5e90af345f01771" + "sha256:b1ac92669641b902e1aef97138666a21b8852f65d83cbde03eb9ddebf82ce121", + "sha256:bd240163d4e2ce9fc8535f77519358da0afd6c0ca19f001c639c3160b57630a9" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==4.6.6" + "version": "==0.6.3" }, "lazy-object-proxy": { "hashes": [ @@ -1655,10 +1627,10 @@ }, "limits": { "hashes": [ - "sha256:9df578f4161017d79f5188609f1d65f6b639f8aad2914c3960c9252e56a0ff95", - "sha256:a017b8d9e9da6761f4574642149c337f8f540d4edfe573fb91ad2c4001a2bc76" + "sha256:98accbccf66e6e2edc0bb7b6e295e6bb8596be3588a7c385de16c8e8463644a4", + "sha256:c071295307c447f85aaa3c3ab3ce058e29d67010f4fabf278a8e163916e4deab" ], - "version": "==1.3" + "version": "==1.4.1" }, "lru-dict": { "hashes": [ @@ -1667,38 +1639,6 @@ "index": "pypi", "version": "==1.1.6" }, - "lxml": { - "hashes": [ - "sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4", - "sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc", - "sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1", - "sha256:1409b14bf83a7d729f92e2a7fbfe7ec929d4883ca071b06e95c539ceedb6497c", - "sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046", - "sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36", - "sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5", - "sha256:2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d", - "sha256:4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916", - "sha256:6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0", - "sha256:726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27", - "sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc", - "sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7", - "sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38", - "sha256:9277562f175d2334744ad297568677056861070399cec56ff06abbe2564d1232", - "sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5", - "sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832", - "sha256:ae88588d687bd476be588010cbbe551e9c2872b816f2da8f01f6f1fda74e1ef0", - "sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a", - "sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f", - "sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9", - "sha256:c7fccd08b14aa437fe096c71c645c0f9be0655a9b1a4b7cffc77bcb23b3d61d2", - "sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692", - "sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84", - "sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79", - "sha256:fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681" - ], - "index": "pypi", - "version": "==4.4.1" - }, "markdown": { "hashes": [ "sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a", @@ -1743,18 +1683,22 @@ }, "matplotlib": { "hashes": [ - "sha256:1febd22afe1489b13c6749ea059d392c03261b2950d1d45c17e3aed812080c93", - "sha256:31a30d03f39528c79f3a592857be62a08595dec4ac034978ecd0f814fa0eec2d", - "sha256:4442ce720907f67a79d45de9ada47be81ce17e6c2f448b3c64765af93f6829c9", - "sha256:796edbd1182cbffa7e1e7a97f1e141f875a8501ba8dd834269ae3cd45a8c976f", - "sha256:934e6243df7165aad097572abf5b6003c77c9b6c480c3c4de6f2ef1b5fdd4ec0", - "sha256:bab9d848dbf1517bc58d1f486772e99919b19efef5dd8596d4b26f9f5ee08b6b", - "sha256:c1fe1e6cdaa53f11f088b7470c2056c0df7d80ee4858dadf6cbe433fcba4323b", - "sha256:e5b8aeca9276a3a988caebe9f08366ed519fff98f77c6df5b64d7603d0e42e36", - "sha256:ec6bd0a6a58df3628ff269978f4a4b924a0d371ad8ce1f8e2b635b99e482877a" + "sha256:08ccc8922eb4792b91c652d3e6d46b1c99073f1284d1b6705155643e8046463a", + "sha256:161dcd807c0c3232f4dcd4a12a382d52004a498174cbfafd40646106c5bcdcc8", + "sha256:1f9e885bfa1b148d16f82a6672d043ecf11197f6c71ae222d0546db706e52eb2", + "sha256:2d6ab54015a7c0d727c33e36f85f5c5e4172059efdd067f7527f6e5d16ad01aa", + "sha256:5d2e408a2813abf664bd79431107543ecb449136912eb55bb312317edecf597e", + "sha256:61c8b740a008218eb604de518eb411c4953db0cb725dd0b32adf8a81771cab9e", + "sha256:80f10af8378fccc136da40ea6aa4a920767476cdfb3241acb93ef4f0465dbf57", + "sha256:819d4860315468b482f38f1afe45a5437f60f03eaede495d5ff89f2eeac89500", + "sha256:8cc0e44905c2c8fda5637cad6f311eb9517017515a034247ab93d0cf99f8bb7a", + "sha256:8e8e2c2fe3d873108735c6ee9884e6f36f467df4a143136209cff303b183bada", + "sha256:98c2ffeab8b79a4e3a0af5dd9939f92980eb6e3fec10f7f313df5f35a84dacab", + "sha256:d59bb0e82002ac49f4152963f8a1079e66794a4f454457fd2f0dcc7bf0797d30", + "sha256:ee59b7bb9eb75932fe3787e54e61c99b628155b0cedc907864f24723ba55b309" ], "index": "pypi", - "version": "==3.1.1" + "version": "==3.1.2" }, "mccabe": { "hashes": [ @@ -1778,20 +1722,13 @@ "index": "pypi", "version": "==3.0.5" }, - "monotonic": { - "hashes": [ - "sha256:23953d55076df038541e648a53676fb24980f7a1be290cdda21300b3bc21dfb0", - "sha256:552a91f381532e33cbd07c6a2655a21908088962bb8fa7239ecbcc6ad1140cc7" - ], - "version": "==1.5" - }, "more-itertools": { "hashes": [ - "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", - "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4" + "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", + "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" ], - "markers": "python_version >= '3.4'", - "version": "==7.2.0" + "markers": "python_version >= '3.5'", + "version": "==8.0.2" }, "mpld3": { "hashes": [ @@ -1807,6 +1744,20 @@ "index": "pypi", "version": "==0.5.6" }, + "msrest": { + "hashes": [ + "sha256:56b8b5b4556fb2a92cac640df267d560889bdc9e2921187772d4691d97bc4e8d", + "sha256:f5153bfe60ee757725816aedaa0772cbfe0bddb52cd2d6db4cb8b4c3c6c6f928" + ], + "version": "==0.6.10" + }, + "msrestazure": { + "hashes": [ + "sha256:63db9f646fffc9244b332090e679d1e5f283ac491ee0cc321f5116f9450deb4a", + "sha256:fecb6a72a3eb5483e4deff38210d26ae42d3f6d488a7a275bd2423a1a014b22c" + ], + "version": "==0.6.2" + }, "nbconvert": { "hashes": [ "sha256:21fb48e700b43e82ba0e3142421a659d7739b65568cc832a13976a77be16b523", @@ -1817,18 +1768,18 @@ }, "nbformat": { "hashes": [ - "sha256:b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", - "sha256:f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402" + "sha256:cca9a1acfd4e049dcd6c3628d3c84db8e48a770182fb7b87d6a62f9ceacfae39", + "sha256:d1407544cf0c53ee88f504b6c732aef6e0f407a0858b405fcf133e0a25bb787b" ], - "version": "==4.4.0" + "version": "==5.0.3" }, "nbstripout": { "hashes": [ - "sha256:1960caf7d1c1e281126c6c5cb98053db89eca8aaa616b58eed381e3e1508c0f4", - "sha256:d35c553f724d3fb7ec9e9602c6e55a75101064a6bbec4f8c28e8c84d6e3dd060" + "sha256:62f1b1fe9c7c298061089fd9bd5d297eb6209f7fbef0758631dbe58d38fc828f", + "sha256:cf745ae8c49fccdb3068b73fc3b783898d5d62ee929429e9af37a6dfefba34b7" ], "index": "pypi", - "version": "==0.3.6" + "version": "==0.3.7" }, "networkx": { "hashes": [ @@ -1846,14 +1797,6 @@ "index": "pypi", "version": "==1.3.7" }, - "nose-parameterized": { - "hashes": [ - "sha256:8f519b9739ac67e3d95f69c15cc80416eea4d63559530d01a37b9565eb629277", - "sha256:d35e677aba2f15135b6b7ea7feb88f792b899492ba5365ec0e269015df5214ce" - ], - "index": "pypi", - "version": "==0.6.0" - }, "notebook": { "hashes": [ "sha256:399a4411e171170173344761e7fd4491a3625659881f76ce47c50231ed714d9b", @@ -1864,30 +1807,30 @@ }, "numpy": { "hashes": [ - "sha256:0a7a1dd123aecc9f0076934288ceed7fd9a81ba3919f11a855a7887cbe82a02f", - "sha256:0c0763787133dfeec19904c22c7e358b231c87ba3206b211652f8cbe1241deb6", - "sha256:3d52298d0be333583739f1aec9026f3b09fdfe3ddf7c7028cb16d9d2af1cca7e", - "sha256:43bb4b70585f1c2d153e45323a886839f98af8bfa810f7014b20be714c37c447", - "sha256:475963c5b9e116c38ad7347e154e5651d05a2286d86455671f5b1eebba5feb76", - "sha256:64874913367f18eb3013b16123c9fed113962e75d809fca5b78ebfbb73ed93ba", - "sha256:683828e50c339fc9e68720396f2de14253992c495fdddef77a1e17de55f1decc", - "sha256:6ca4000c4a6f95a78c33c7dadbb9495c10880be9c89316aa536eac359ab820ae", - "sha256:75fd817b7061f6378e4659dd792c84c0b60533e867f83e0d1e52d5d8e53df88c", - "sha256:7d81d784bdbed30137aca242ab307f3e65c8d93f4c7b7d8f322110b2e90177f9", - "sha256:8d0af8d3664f142414fd5b15cabfd3b6cc3ef242a3c7a7493257025be5a6955f", - "sha256:9679831005fb16c6df3dd35d17aa31dc0d4d7573d84f0b44cc481490a65c7725", - "sha256:a8f67ebfae9f575d85fa859b54d3bdecaeece74e3274b0b5c5f804d7ca789fe1", - "sha256:acbf5c52db4adb366c064d0b7c7899e3e778d89db585feadd23b06b587d64761", - "sha256:ada4805ed51f5bcaa3a06d3dd94939351869c095e30a2b54264f5a5004b52170", - "sha256:c7354e8f0eca5c110b7e978034cd86ed98a7a5ffcf69ca97535445a595e07b8e", - "sha256:e2e9d8c87120ba2c591f60e32736b82b67f72c37ba88a4c23c81b5b8fa49c018", - "sha256:e467c57121fe1b78a8f68dd9255fbb3bb3f4f7547c6b9e109f31d14569f490c3", - "sha256:ede47b98de79565fcd7f2decb475e2dcc85ee4097743e551fe26cfc7eb3ff143", - "sha256:f58913e9227400f1395c7b800503ebfdb0772f1c33ff8cb4d6451c06cabdf316", - "sha256:fe39f5fd4103ec4ca3cb8600b19216cd1ff316b4990f4c0b6057ad982c0a34d5" + "sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6", + "sha256:17aa7a81fe7599a10f2b7d95856dc5cf84a4eefa45bc96123cbbc3ebc568994e", + "sha256:20b26aaa5b3da029942cdcce719b363dbe58696ad182aff0e5dcb1687ec946dc", + "sha256:2d75908ab3ced4223ccba595b48e538afa5ecc37405923d1fea6906d7c3a50bc", + "sha256:39d2c685af15d3ce682c99ce5925cc66efc824652e10990d2462dfe9b8918c6a", + "sha256:56bc8ded6fcd9adea90f65377438f9fea8c05fcf7c5ba766bef258d0da1554aa", + "sha256:590355aeade1a2eaba17617c19edccb7db8d78760175256e3cf94590a1a964f3", + "sha256:70a840a26f4e61defa7bdf811d7498a284ced303dfbc35acb7be12a39b2aa121", + "sha256:77c3bfe65d8560487052ad55c6998a04b654c2fbc36d546aef2b2e511e760971", + "sha256:9537eecf179f566fd1c160a2e912ca0b8e02d773af0a7a1120ad4f7507cd0d26", + "sha256:9acdf933c1fd263c513a2df3dceecea6f3ff4419d80bf238510976bf9bcb26cd", + "sha256:ae0975f42ab1f28364dcda3dde3cf6c1ddab3e1d4b2909da0cb0191fa9ca0480", + "sha256:b3af02ecc999c8003e538e60c89a2b37646b39b688d4e44d7373e11c2debabec", + "sha256:b6ff59cee96b454516e47e7721098e6ceebef435e3e21ac2d6c3b8b02628eb77", + "sha256:b765ed3930b92812aa698a455847141869ef755a87e099fddd4ccf9d81fffb57", + "sha256:c98c5ffd7d41611407a1103ae11c8b634ad6a43606eca3e2a5a269e5d6e8eb07", + "sha256:cf7eb6b1025d3e169989416b1adcd676624c2dbed9e3bcb7137f51bfc8cc2572", + "sha256:d92350c22b150c1cae7ebb0ee8b5670cc84848f6359cf6b5d8f86617098a9b73", + "sha256:e422c3152921cece8b6a2fb6b0b4d73b6579bd20ae075e7d15143e711f3ca2ca", + "sha256:e840f552a509e3380b0f0ec977e8124d0dc34dc0e68289ca28f4d7c1d0d79474", + "sha256:f3d0a94ad151870978fb93538e95411c83899c9dc63e6fb65542f769568ecfa5" ], "index": "pypi", - "version": "==1.17.4" + "version": "==1.18.1" }, "oauthlib": { "hashes": [ @@ -1996,20 +1939,27 @@ ], "version": "==1.4.2" }, + "paramiko": { + "hashes": [ + "sha256:920492895db8013f6cc0179293147f830b8c7b21fdfc839b6bad760c27459d9f", + "sha256:9c980875fa4d2cb751604664e9a2d0f69096643f5be4db1b99599fe114a97b2f" + ], + "version": "==2.7.1" + }, "parso": { "hashes": [ - "sha256:63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", - "sha256:666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c" + "sha256:55cf25df1a35fd88b878715874d2c4dc1ad3f0eebd1e0266a67e1f55efccfbe1", + "sha256:5c1f7791de6bd5dbbeac8db0ef5594b36799de198b3f7f7014643b0c5536b9d3" ], - "version": "==0.5.1" + "version": "==0.5.2" }, "pbr": { "hashes": [ - "sha256:2c8e420cd4ed4cec4e7999ee47409e876af575d4c35a45840d59e8b5f3155ab8", - "sha256:b32c8ccaac7b1a20c0ce00ce317642e6cf231cf038f9875e0280e28af5bf7ac9" + "sha256:139d2625547dbfa5fb0b81daebb39601c478c21956dc57e2e07b74450a8c506b", + "sha256:61aa52a0f18b71c5cc58232d2cf8f8d09cd67fcad60b742a60124cb8d6951488" ], "index": "pypi", - "version": "==5.4.3" + "version": "==5.4.4" }, "percache": { "hashes": [ @@ -2035,39 +1985,38 @@ }, "pillow": { "hashes": [ - "sha256:047d9473cf68af50ac85f8ee5d5f21a60f849bc17d348da7fc85711287a75031", - "sha256:0f66dc6c8a3cc319561a633b6aa82c44107f12594643efa37210d8c924fc1c71", - "sha256:12c9169c4e8fe0a7329e8658c7e488001f6b4c8e88740e76292c2b857af2e94c", - "sha256:248cffc168896982f125f5c13e9317c059f74fffdb4152893339f3be62a01340", - "sha256:27faf0552bf8c260a5cee21a76e031acaea68babb64daf7e8f2e2540745082aa", - "sha256:285edafad9bc60d96978ed24d77cdc0b91dace88e5da8c548ba5937c425bca8b", - "sha256:384b12c9aa8ef95558abdcb50aada56d74bc7cc131dd62d28c2d0e4d3aadd573", - "sha256:38950b3a707f6cef09cd3cbb142474357ad1a985ceb44d921bdf7b4647b3e13e", - "sha256:4aad1b88933fd6dc2846552b89ad0c74ddbba2f0884e2c162aa368374bf5abab", - "sha256:4ac6148008c169603070c092e81f88738f1a0c511e07bd2bb0f9ef542d375da9", - "sha256:4deb1d2a45861ae6f0b12ea0a786a03d19d29edcc7e05775b85ec2877cb54c5e", - "sha256:59aa2c124df72cc75ed72c8d6005c442d4685691a30c55321e00ed915ad1a291", - "sha256:5a47d2123a9ec86660fe0e8d0ebf0aa6bc6a17edc63f338b73ea20ba11713f12", - "sha256:5cc901c2ab9409b4b7ac7b5bcc3e86ac14548627062463da0af3b6b7c555a871", - "sha256:6c1db03e8dff7b9f955a0fb9907eb9ca5da75b5ce056c0c93d33100a35050281", - "sha256:7ce80c0a65a6ea90ef9c1f63c8593fcd2929448613fc8da0adf3e6bfad669d08", - "sha256:809c19241c14433c5d6135e1b6c72da4e3b56d5c865ad5736ab99af8896b8f41", - "sha256:83792cb4e0b5af480588601467c0764242b9a483caea71ef12d22a0d0d6bdce2", - "sha256:846fa202bd7ee0f6215c897a1d33238ef071b50766339186687bd9b7a6d26ac5", - "sha256:9f5529fc02009f96ba95bea48870173426879dc19eec49ca8e08cd63ecd82ddb", - "sha256:a423c2ea001c6265ed28700df056f75e26215fd28c001e93ef4380b0f05f9547", - "sha256:ac4428094b42907aba5879c7c000d01c8278d451a3b7cccd2103e21f6397ea75", - "sha256:b1ae48d87f10d1384e5beecd169c77502fcc04a2c00a4c02b85f0a94b419e5f9", - "sha256:bf4e972a88f8841d8fdc6db1a75e0f8d763e66e3754b03006cbc3854d89f1cb1", - "sha256:c6414f6aad598364aaf81068cabb077894eb88fed99c6a65e6e8217bab62ae7a", - "sha256:c710fcb7ee32f67baf25aa9ffede4795fd5d93b163ce95fdc724383e38c9df96", - "sha256:c7be4b8a09852291c3c48d3c25d1b876d2494a0a674980089ac9d5e0d78bd132", - "sha256:c9e5ffb910b14f090ac9c38599063e354887a5f6d7e6d26795e916b4514f2c1a", - "sha256:e0697b826da6c2472bb6488db4c0a7fa8af0d52fa08833ceb3681358914b14e5", - "sha256:e9a3edd5f714229d41057d56ac0f39ad9bdba6767e8c888c951869f0bdd129b0" + "sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be", + "sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946", + "sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837", + "sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f", + "sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00", + "sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d", + "sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533", + "sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a", + "sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358", + "sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda", + "sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435", + "sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2", + "sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313", + "sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff", + "sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317", + "sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2", + "sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614", + "sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0", + "sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386", + "sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9", + "sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636", + "sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865" ], "index": "pypi", - "version": "==6.2.1" + "version": "==7.0.0" + }, + "portalocker": { + "hashes": [ + "sha256:6f57aabb25ba176462dc7c63b86c42ad6a9b5bd3d679a9d776d0536bfb803d54", + "sha256:dac62e53e5670cb40d2ee4cdc785e6b829665932c3ee75307ad677cf5f7d2e9f" + ], + "version": "==1.5.2" }, "pprofile": { "hashes": [ @@ -2092,42 +2041,27 @@ }, "protobuf": { "hashes": [ - "sha256:125713564d8cfed7610e52444c9769b8dcb0b55e25cc7841f2290ee7bc86636f", - "sha256:1accdb7a47e51503be64d9a57543964ba674edac103215576399d2d0e34eac77", - "sha256:27003d12d4f68e3cbea9eb67427cab3bfddd47ff90670cb367fcd7a3a89b9657", - "sha256:3264f3c431a631b0b31e9db2ae8c927b79fc1a7b1b06b31e8e5bcf2af91fe896", - "sha256:3c5ab0f5c71ca5af27143e60613729e3488bb45f6d3f143dc918a20af8bab0bf", - "sha256:45dcf8758873e3f69feab075e5f3177270739f146255225474ee0b90429adef6", - "sha256:56a77d61a91186cc5676d8e11b36a5feb513873e4ae88d2ee5cf530d52bbcd3b", - "sha256:5984e4947bbcef5bd849d6244aec507d31786f2dd3344139adc1489fb403b300", - "sha256:6b0441da73796dd00821763bb4119674eaf252776beb50ae3883bed179a60b2a", - "sha256:6f6677c5ade94d4fe75a912926d6796d5c71a2a90c2aeefe0d6f211d75c74789", - "sha256:84a825a9418d7196e2acc48f8746cf1ee75877ed2f30433ab92a133f3eaf8fbe", - "sha256:b842c34fe043ccf78b4a6cf1019d7b80113707d68c88842d061fa2b8fb6ddedc", - "sha256:ca33d2f09dae149a1dcf942d2d825ebb06343b77b437198c9e2ef115cf5d5bc1", - "sha256:cc9af00df3fc9302f537a8335668c20be27916b2277e9a5eaed510266e2bb33b", - "sha256:db83b5c12c0cd30150bb568e6feb2435c49ce4e68fe2d7b903113f0e221e58fe", - "sha256:f50f3b1c5c1c1334ca7ce9cad5992f098f460ffd6388a3cabad10b66c2006b09", - "sha256:f99f127909731cafb841c52f9216e447d3e4afb99b17bebfad327a75aee206de" + "sha256:0329e86a397db2a83f9dcbe21d9be55a47f963cdabc893c3a24f4d3a8f117c37", + "sha256:0a7219254afec0d488211f3d482d8ed57e80ae735394e584a98d8f30a8c88a36", + "sha256:14d6ac53df9cb5bb87c4f91b677c1bc5cec9c0fd44327f367a3c9562de2877c4", + "sha256:180fc364b42907a1d2afa183ccbeffafe659378c236b1ec3daca524950bb918d", + "sha256:3d7a7d8d20b4e7a8f63f62de2d192cfd8b7a53c56caba7ece95367ca2b80c574", + "sha256:3f509f7e50d806a434fe4a5fbf602516002a0f092889209fff7db82060efffc0", + "sha256:4571da974019849201fc1ec6626b9cea54bd11b6bed140f8f737c0a33ea37de5", + "sha256:557686c43fbd04f5f7c533f00feee9a37dcca7b5896e3ae3664a33864e6dd546", + "sha256:56bd1d84fbf4505c7b73f04de987eef5682e5752c811141b0186a3809bfb396f", + "sha256:680c668d00b5eff08b86aef9e5ba9a705e621ea05d39071cfea8e28cb2400946", + "sha256:6b5b947dc8b3f2aec0eaad65b0b5113fcd642c358c31357c647da6281ee31104", + "sha256:6e96dffaf4d0a9a329e528b353ba62fd9ef13599688723d96bc9c165d0b6871e", + "sha256:919f0d6f6addc836d08658eba3b52be2e92fd3e76da3ce00c325d8e9826d17c7", + "sha256:9c7b19c30cf0644afd0e4218b13f637ce54382fdcb1c8f75bf3e84e49a5f6d0a", + "sha256:a2e6f57114933882ec701807f217df2fb4588d47f71f227c0a163446b930d507", + "sha256:a6b970a2eccfcbabe1acf230fbf112face1c4700036c95e195f3554d7bcb04c1", + "sha256:bc45641cbcdea068b67438244c926f9fd3e5cbdd824448a4a64370610df7c593", + "sha256:d61b14a9090da77fe87e38ba4c6c43d3533dcbeb5d84f5474e7ac63c532dcc9c", + "sha256:d6faf5dbefb593e127463f58076b62fcfe0784187be8fe1aa9167388f24a22a1" ], - "version": "==3.10.0" - }, - "psutil": { - "hashes": [ - "sha256:021d361439586a0fd8e64f8392eb7da27135db980f249329f1a347b9de99c695", - "sha256:145e0f3ab9138165f9e156c307100905fd5d9b7227504b8a9d3417351052dc3d", - "sha256:348ad4179938c965a27d29cbda4a81a1b2c778ecd330a221aadc7bd33681afbd", - "sha256:3feea46fbd634a93437b718518d15b5dd49599dfb59a30c739e201cc79bb759d", - "sha256:474e10a92eeb4100c276d4cc67687adeb9d280bbca01031a3e41fb35dfc1d131", - "sha256:47aeb4280e80f27878caae4b572b29f0ec7967554b701ba33cd3720b17ba1b07", - "sha256:73a7e002781bc42fd014dfebb3fc0e45f8d92a4fb9da18baea6fb279fbc1d966", - "sha256:d051532ac944f1be0179e0506f6889833cf96e466262523e57a871de65a15147", - "sha256:dfb8c5c78579c226841908b539c2374da54da648ee5a837a731aa6a105a54c00", - "sha256:e3f5f9278867e95970854e92d0f5fe53af742a7fc4f2eba986943345bcaed05d", - "sha256:e9649bb8fc5cea1f7723af53e4212056a6f984ee31784c10632607f472dec5ee" - ], - "index": "pypi", - "version": "==5.6.5" + "version": "==3.11.2" }, "ptyprocess": { "hashes": [ @@ -2139,8 +2073,19 @@ }, "pyasn1": { "hashes": [ + "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359", + "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576", + "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf", + "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7", "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", - "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba" + "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00", + "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8", + "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86", + "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12", + "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776", + "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba", + "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2", + "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3" ], "version": "==0.4.8" }, @@ -2164,9 +2109,9 @@ }, "pycocotools": { "git": "https://github.com/cocodataset/cocoapi.git", - "ref": "636becdc73d54283b3aac6d4ec363cffbb6f9b20", + "ref": "e140a084d678eacd18e85a9d8cfa45d1d5911db9", "subdirectory": "PythonAPI", - "version": "==2.0" + "version": "==0.0.0" }, "pycparser": { "hashes": [ @@ -2215,11 +2160,11 @@ }, "pygments": { "hashes": [ - "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", - "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" + "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b", + "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe" ], "index": "pypi", - "version": "==2.4.2" + "version": "==2.5.2" }, "pyjwt": { "hashes": [ @@ -2246,40 +2191,65 @@ }, "pymongo": { "hashes": [ - "sha256:09f8196e1cb081713aa3face08d1806dc0a5dd64cb9f67fefc568519253a7ff2", - "sha256:1be549c0ce2ba8242c149156ae2064b12a5d4704448d49f630b4910606efd474", - "sha256:1f9fe869e289210250cba4ea20fbd169905b1793e1cd2737f423e107061afa98", - "sha256:3653cea82d1e35edd0a2355150daf8a27ebf12cf55182d5ad1046bfa288f5140", - "sha256:4249c6ba45587b959292a727532826c5032d59171f923f7f823788f413c2a5a3", - "sha256:4ff8f5e7c0a78983c1ee07894fff1b21c0e0ad3a122d9786cc3745fd60e4a2ce", - "sha256:56b29c638ab924716b48a3e94e3d7ac00b04acec1daa8190c36d61fc714c3629", - "sha256:56ec9358bbfe5ae3b25e785f8a14619d6799c855a44734c9098bb457174019bf", - "sha256:5b59bbde4eb417f3f9379f7b1a9de3669894f2bae9de933a836e2bffea2bbfa1", - "sha256:5dca250cbf1183c3e7b7b18c882c2b2199bfb20c74c4c68dbf11596808a296da", - "sha256:61101d1cc92881fac1f9ac7e99b033062f4c210178dc33193c8f5567feecb069", - "sha256:7b4aea184e4868ebd4f9f786ffee14a1121bda5436ad04f6bcbacfa2147f8386", - "sha256:86624c0205a403fb4fbfedef79c5b4ab27e21fd018fdb6a27cf03b3c32a9e2b9", - "sha256:88ac09e1b197c3b4531e43054d49c022a3ea1281431b2f4980abafa35d2a5ce2", - "sha256:8b0339809b12ea292d468524dd1777f1a9637d9bdc0353a9261b88f82537d606", - "sha256:93dbf7388f6bf9af48dbb32f265b75b3dbc743a7a2ce98e44c88c049c58d85d3", - "sha256:9b705daec636c560dd2d63935f428a6b3cddfe903fffc0f349e0e91007c893d6", - "sha256:a090a819fe6fefadc2901d3911c07c76c0935ec5c790a50e9f3c3c47bacd5978", - "sha256:a102b346f1921237eaa9a31ee89eda57ad3c3973d79be3a456d92524e7df8fec", - "sha256:a13363869f2f36291d6367069c65d51d7b8d1b2fb410266b0b6b1f3c90d6deb0", - "sha256:a409a43c76da50881b70cc9ee70a1744f882848e8e93a68fb434254379777fa3", - "sha256:a76475834a978058425b0163f1bad35a5f70e45929a543075633c3fc1df564c5", - "sha256:ad474e93525baa6c58d75d63a73143af24c9f93c8e26e8d382f32c4da637901a", - "sha256:b268c7fa03ac77a8662fab3b2ab0be4beecb82f60f4c24b584e69565691a107f", - "sha256:b67ec339b180acdbebcd03807ae4b1764a43e7069340fe860a60ac310b9d38be", - "sha256:cca4e1ab5ba0cd7877d3938167ee8ae9c2986cc0e10d3dcc3243d664d3a83fec", - "sha256:cef61de3f0f4441ec40266ff2ab42e5c16eaba1dc1fc6e1036f274621c52adc1", - "sha256:e28153b5d5ca33d4ba0c3bbc0e1ff161b9016e5e5f3f8ca10d6fa49106eb9e04", - "sha256:f30d7b37804daf0bab1143abc71666c630d7e270f5c14c5a7c300a6699c21108", - "sha256:f70f0133301cccf9bfd68fd20f67184ef991be578b646e78441106f9e27cc44d", - "sha256:fa75c21c1d82f20cce62f6fc4a68c2b0f33572ab406df1b17cd77a947d0b2993" + "sha256:0369136c6e79c5edc16aa5de2b48a1b1c1fe5e6f7fc5915a2deaa98bd6e9dad5", + "sha256:08364e1bea1507c516b18b826ec790cb90433aec2f235033ec5eecfd1011633b", + "sha256:0af1d2bc8cc9503bf92ec3669a77ec3a6d7938193b583fb867b7e9696eed52e8", + "sha256:0cfd1aeeb8c0a634646ab3ebeb4ce6828b94b2e33553a69ff7e6c07c250bf201", + "sha256:15bbd2b5397f7d22498e2f2769fd698a8a247b9cc1a630ee8dabf647fb333480", + "sha256:1b4a13dff15641e58620524db15d7a323d60572b2b187261c5cb58c36d74778d", + "sha256:22fbdb908257f9aaaa372a7684f3e094a05ca52eb84f8f381c8b1827c49556fd", + "sha256:264272fd1c95fc48002ad85d5e41270831777b4180f2500943e45e12b2a3ab43", + "sha256:3372e98eebbfd05ebf020388003f8a4438bed41e0fef1ef696d2c13633c416c8", + "sha256:339d24ecdc42745d2dc09b26fda8151988e806ca81134a7bd10513c4031d91e1", + "sha256:38281855fc3961ba5510fbb503b8d16cc1fcb326e9f7ba0dd096ed4eb72a7084", + "sha256:4acdd2e16392472bfd49ca49038845c95e5254b5af862b55f7f2cc79aa258886", + "sha256:4e0c006bc6e98e861b678432e05bf64ba3eb889b6ab7e7bf1ebaecf9f1ba0e58", + "sha256:4e4284bcbe4b7be1b37f9641509085b715c478e7fbf8f820358362b5dd359379", + "sha256:4e5e94a5f9823f0bd0c56012a57650bc6772636c29d83d253260c26b908fcfd9", + "sha256:4e61f30800a40f1770b2ec56bbf5dc0f0e3f7e9250eb05fa4feb9ccb7bbe39ca", + "sha256:53577cf57ba9d93b58ab41d45250277828ff83c5286dde14f855e4b17ec19976", + "sha256:681cb31e8631882804a6cc3c8cc8f54a74ff3a82261a78e50f20c5eec05ac855", + "sha256:6dfc2710f43dd1d66991a0f160d196356732ccc8aa9dbc6875aeba78388fa142", + "sha256:72218201b13d8169be5736417987e9a0a3b10d4349e40e4db7a6a5ac670c7ef2", + "sha256:7247fbcdbf7ab574eb70743461b3cfc14d9cfae3f27a9afb6ce14d87f67dd0b5", + "sha256:72651f4b4adf50201891580506c8cca465d94d38f26ed92abfc56440662c723c", + "sha256:87b3aaf12ad6a9b5570b12d2a4b8802757cb3588a903aafd3c25f07f9caf07e3", + "sha256:87c28b7b37617c5a01eb396487f7d3b61a453e1fa0475a175ab87712d6f5d52f", + "sha256:88efe627b628f36ef53f09abb218d4630f83d8ebde7028689439559475c43dae", + "sha256:89bfbca22266f12df7fb80092b7c876734751d02b93789580b68957ad4a8bf56", + "sha256:908a3caf348a672b28b8a06fe7b4a27c2fdcf7f873df671e4027d48bcd7f971f", + "sha256:9128e7bea85f3a3041306fa14a7aa82a24b47881918500e1b8396dd1c933b5a6", + "sha256:9737d6d688a15b8d5c0bfa909638b79261e195be817b9f1be79c722bbb23cd76", + "sha256:98a8305da158f46e99e7e51db49a2f8b5fcdd7683ea7083988ccb9c4450507a6", + "sha256:99285cd44c756f0900cbdb5fe75f567c0a76a273b7e0467f23cb76f47e60aac0", + "sha256:9ed568f8026ffeb00ce31e5351e0d09d704cc19a29549ba4da0ac145d2a26fdf", + "sha256:a006162035032021dfd00a879643dc06863dac275f9210d843278566c719eebc", + "sha256:a03cb336bc8d25a11ff33b94967478a9775b0d2b23b39e952d9cc6cb93b75d69", + "sha256:a863ceb67be163060d1099b7e89b6dd83d6dd50077c7ceae31ac844c4c2baff9", + "sha256:b82628eaf0a16c1f50e1c205fd1dd406d7874037dd84643da89e91b5043b5e82", + "sha256:bc6446a41fb7eeaf2c808bab961b9bac81db0f5de69eab74eebe1b8b072399f7", + "sha256:c42d290ed54096355838421cf9d2a56e150cb533304d2439ef1adf612a986eaf", + "sha256:c43879fe427ea6aa6e84dae9fbdc5aa14428a4cfe613fe0fee2cc004bf3f307c", + "sha256:c566cbdd1863ba3ccf838656a1403c3c81fdb57cbe3fdd3515be7c9616763d33", + "sha256:c5b7a0d7e6ca986de32b269b6dbbd5162c1a776ece72936f55decb4d1b197ee9", + "sha256:ca109fe9f74da4930590bb589eb8fdf80e5d19f5cd9f337815cac9309bbd0a76", + "sha256:d0260ba68f9bafd8775b2988b5aeace6e69a37593ec256e23e150c808160c05c", + "sha256:d12d86e771fc3072a0e6bdbf4e417c63fec85ee47cb052ba7ad239403bf5e154", + "sha256:d2ce33501149b373118fcfec88a292a87ef0b333fb30c7c6aac72fe64700bdf6", + "sha256:d582ea8496e2a0e124e927a67dca55c8833f0dbfbc2c84aaf0e5949a2dd30c51", + "sha256:d68b9ab0a900582a345fb279675b0ad4fac07d6a8c2678f12910d55083b7240d", + "sha256:dbf1fa571db6006907aeaf6473580aaa76041f4f3cd1ff8a0039fd0f40b83f6d", + "sha256:e032437a7d2b89dab880c79379d88059cee8019da0ff475d924c4ccab52db88f", + "sha256:e0f5798f3ad60695465a093e3d002f609c41fef3dcb97fcefae355d24d3274cf", + "sha256:e756355704a2cf91a7f4a649aa0bbf3bbd263018b9ed08f60198c262f4ee24b6", + "sha256:e824b4b87bd88cbeb25c8babeadbbaaaf06f02bbb95a93462b7c6193a064974e", + "sha256:ea1171470b52487152ed8bf27713cc2480dc8b0cd58e282a1bff742541efbfb8", + "sha256:fa19aef44d5ed8f798a8136ff981aedfa508edac3b1bed481eca5dde5f14fd3d", + "sha256:faf83d20c041637cb277e5fdb59abc217c40ab3202dd87cc95d6fbd9ce5ffd9b", + "sha256:fceb6ae5a149a42766efb8344b0df6cfb21b55c55f360170abaddb11d43af0f1" ], "index": "pypi", - "version": "==3.9.0" + "version": "==3.10.0" }, "pymysql": { "hashes": [ @@ -2324,13 +2294,20 @@ "index": "pypi", "version": "==1.15.0" }, + "pyopenssl": { + "hashes": [ + "sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504", + "sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507" + ], + "version": "==19.1.0" + }, "pyparsing": { "hashes": [ - "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", - "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a" + "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", + "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.4.5" + "version": "==2.4.6" }, "pypolyline": { "hashes": [ @@ -2350,41 +2327,46 @@ }, "pyproj": { "hashes": [ - "sha256:0e59b9120ba4729d4bbbfb60bfb3206c9d119c55a92227d11b967892c3e82606", - "sha256:1ea692abc31199d3b0c614d0f8d0b8e8cf0ac9d104df6cbd1159ed4f6c12f318", - "sha256:346ceff4a5a07ef4ef19c51f617e30222554eb48418f22a370497918d389a6d4", - "sha256:3ad7c8c2e5cebd8c40e7101fb5f7c8dbe3dd73787b22611474c3db39a77affec", - "sha256:52d3c8ff42547c7f087d3adc08f7217a7d3c392923708e1ba9a3eb964caecd7c", - "sha256:53aa9b9febf247fc2f407682cd64a052d0a0275838ad4f9dd8bcb6960c8bd68b", - "sha256:5d11b2ff5721d4384388df18498513987979e327fb2dd675062a28024e462f0c", - "sha256:6343e499b4d430637616b1362197b6c4014516e7c2839fd9ff492b34b885f14f", - "sha256:817d4b762adee4479200471042e2393f75a8ec2384061979ccfe177b862b4f8b", - "sha256:8eb1eeaba895282a44e11e9c83696d7ac567925f00d847001f118e73335c7554", - "sha256:8f266a968085b3c1fbe9abc238127ecf3f058461e0cb2d1250dc86077e84e9ba", - "sha256:95cade1aa565e98b23585b1b1e900d949ffde0424f724290d8763f3df75fed4a", - "sha256:a1422da59673ca5bf56d89cf8e2a3cb2c9868bea27bbee6600ae603d322316a8", - "sha256:a25e50194af94e312d21952386522a7bbd97118056e913e47f2bb9cfca70a117", - "sha256:a2b08ac9c285e1adfec35756f561079638a333781e315777e497150e57d8dca9", - "sha256:b86c4c52d9bf0c7118541e9b001cddf0d027a8d49503641101faad60d6abb759", - "sha256:c173b6a33cb4c9eb40e78fc0d8a4c4e227f997f6a02ba665e27871cd308c346b", - "sha256:c32441a0526ff600cb69bad6f8f9748bca78b6be9e5ad8d0eade29aeb252fbce", - "sha256:cdb17bef8862a41c86cb51d8f3fdadee9fe1afd9a3362814c2eda59a5ce08133", - "sha256:e027eed39a6ba0f63ab108962c550995474be58f77b80470fa4a9bc0114161a5", - "sha256:efca2ef75eb75bf2a6451a2ea4760c3b371e08a66bfa8a6abafd997de1fcd74e", - "sha256:f93f42c50e40ee6358d6006c4b5be927d83ad402080438617c2182a62a7b3700" + "sha256:0608ac0aed84dcf57c859df87ac315b9acce18268f62bafc04071b7b1ff1c5a9", + "sha256:18265fb755e01df1d2248f1e837d81da4c9625e8f09481d64a9d6282c96f7467", + "sha256:190540946bb6fbfce285f46c08fcfd9d03e9331a0e952a3ef2047e6b8e8d8125", + "sha256:1da7f86d3b5e80ba3dabfd2c904a41bb6997ad9b55b47a934035492eaa0f331e", + "sha256:2ebbaee33e076664058effc3f6c943ed4c19a45df3989203ac081fca4a4722e3", + "sha256:32168c57450a1e6310b7ca331983d62d88393cc3e93b866fd6ea63dac30c7d3b", + "sha256:34b8ccf42032d89ebb8e0a839ae91e943ed222dab9bf3c1373f6fb972f8bcac4", + "sha256:432b4d28030635fac72713610aad2ed7424a7f07746fa1aa620c89761eb5e7a4", + "sha256:55103aa0adf25d207efd6f7f36d79dadee7706f22c1791955cc52033b40071e3", + "sha256:6bc74337edc1239f8c59d0d5b18a7996670b8fd523712d2dac599d5b792feae2", + "sha256:6d2838bec2d9ccd31dba68c76e8e7504bf819a4d4ace86adfca1e009d8f30f19", + "sha256:763ccac4398889cb798668824d34c4135f2e84a50681465a4199554aa1bd8611", + "sha256:8dbf1633ad2abdae6f73fe8989700c74a12dc82cb8597e66af28ff3d990d9c45", + "sha256:8ddffa4bcd9008c963840e8e79f2f3124f85f18d5987d4bbd9e7f38d9839a985", + "sha256:8f225c6186b0cd2cb07fe377786425a2ddc4183ae438fe63c60b4a879c91620f", + "sha256:97844a87cac739e389d1d0c69bc3b36c1d5c50c9f91443ef68bdef8fdf007f02", + "sha256:9d7a13def19a91836a2c84e5c7fcb6dd5e2c9bb205fb75ee102ffba24d80bf32", + "sha256:abd0784a017eedb3b03cd13f51b8852f4c68aa07affbee549bbd421f9b4268bb", + "sha256:acf150ca1506fcdaa52b0570f2903216413a2a4da78dfdf5ff7ee4eb92c2f8d5", + "sha256:b41522f8b77b64553280fb93823555bc8afb2469f77b8ce0e9aeed39abb50adc", + "sha256:c1058da6c02152d8637bb739dca940c6ab72683e59db6065fdcbe9102f66ca46", + "sha256:c70e713748c9c9d4a9d7bc42e1c71a17b1fc9b75b686b408a04eaf4909ead365", + "sha256:d47caa0a89dcb39ecd405e3899e07b69d8eaa6dbf267621087a4a5328da8492a", + "sha256:ed186edb4b610ed1e5589f3ba964d61da33d0bc54e89b8cbf8751da2e18555b3", + "sha256:f2dc8c2128f20ee9ed571783ce4730b181476083c403514714e15000b8b470cf", + "sha256:fba87f98344474da6df19bbfde4ca31c7d98a007069c8ef78cb27189f4bc7f04" ], "index": "pypi", - "version": "==2.4.1" + "version": "==2.4.2.post1" }, "pyqt5": { "hashes": [ - "sha256:14737bb4673868d15fa91dad79fe293d7a93d76c56d01b3757b350b8dcb32b2d", - "sha256:1936c321301f678d4e6703d52860e1955e5c4964e6fd00a1f86725ce5c29083c", - "sha256:3f79de6e9f29e858516cc36ffc2b992e262af841f3799246aec282b76a3eccdf", - "sha256:509daab1c5aca22e3cf9508128abf38e6e5ae311d7426b21f4189ffd66b196e9" + "sha256:2b79209aa6e4688f6ac46e6d2694236dcf91db5f3a87270150d0f82082e3d360", + "sha256:2f230f2dbd767099de7a0cb915abdf0cbc3256a0b5bb910eb09b99117db7a65b", + "sha256:3d6e315e6e2d6489a2e1e0148d00e784e277c6590c189227d6060f15b9be690a", + "sha256:812233bd155735377e2e9c7eea7a28815f357440334db51788d941e2a8b62f64", + "sha256:be10fa95e6bdc9cad616ebf368c51b3f5748138b2b3a600cf7c4f80b78cb9852" ], "index": "pypi", - "version": "==5.13.2" + "version": "==5.14.1" }, "pyqt5-sip": { "hashes": [ @@ -2411,9 +2393,9 @@ }, "pyrsistent": { "hashes": [ - "sha256:eb6545dbeb1aa69ab1fb4809bfbf5a8705e44d92ef8fc7c2361682a47c46c778" + "sha256:f3b280d030afb652f79d67c5586157c5c1355c9a58dfc7940566e28d28f3df1b" ], - "version": "==0.15.5" + "version": "==0.15.6" }, "pysdl2": { "hashes": [ @@ -2422,13 +2404,6 @@ "index": "pypi", "version": "==0.9.6" }, - "pysendfile": { - "hashes": [ - "sha256:510a414b270986fba3c79cb76d90a4c910c701bfb43ff983a5d4e92846050e17" - ], - "index": "pypi", - "version": "==2.0.1" - }, "python-dateutil": { "hashes": [ "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", @@ -2439,10 +2414,10 @@ }, "python-engineio": { "hashes": [ - "sha256:4a13fb87c819b855c55a731fdf82559adb8311c04cfdfebd6b9ecd1c2afbb575", - "sha256:9c9a6035b4b5e5a225f426f846afa14cf627f7571d1ae02167cb703fefd134b7" + "sha256:47ae4a9b3b4f2e8a68929f37a518338838e119f24c9a9121af92c49f8bea55c3", + "sha256:c3a3822deb51fdf9c7fe4d78abf807c73b83ea538036a50862d3024450746253" ], - "version": "==3.10.0" + "version": "==3.11.2" }, "python-logstash": { "hashes": [ @@ -2453,18 +2428,18 @@ }, "python-logstash-async": { "hashes": [ - "sha256:897152edc052fbf7f711eef057570660ac5f33f9dac5fd4ea261a4898d2061eb", - "sha256:abc5bf9a367b49e20990398752b3be4aa67d7b37500fff7b551a5264a1609f4e" + "sha256:16a85e7c76265b06e2e42f5a2babc39811e3920aaa41119e059722d4185f1912", + "sha256:6f2da753ee2307704b0c0d2e3dc6a52e3ad0605184690b4495356e418700abaa" ], "index": "pypi", - "version": "==1.6.0" + "version": "==1.6.2" }, "python-socketio": { "hashes": [ - "sha256:506b2cf7a520b40ea0b3f25e1272eff8de134dce6f471c1f6bc0de8c90fe8c57", - "sha256:d4e2c23241afa0aae2a5bcc107523b2fcc71f5020df89a093f3634eb48955967" + "sha256:48cba5b827ac665dbf923a4f5ec590812aed5299a831fc43576a9af346272534", + "sha256:af6c23c35497960f82106e36688123ecb52ad5a77d0ca27954ff3811c4d9d562" ], - "version": "==4.3.1" + "version": "==4.4.0" }, "pytz": { "hashes": [ @@ -2473,14 +2448,6 @@ ], "version": "==2019.3" }, - "pyvcd": { - "hashes": [ - "sha256:791fd7608fb8113c9658f699cb6292d66d7fb90bcab9ebc00b05adc40da7a5ce", - "sha256:bdcb848b79cea2196ebf317178eff2c9c7d6354b85f9eb4991e9175f98e26937" - ], - "index": "pypi", - "version": "==0.1.4" - }, "pywavelets": { "hashes": [ "sha256:076ca8907001fdfe4205484f719d12b4a0262dfe6652fa1cfc3c5c362d14dc84", @@ -2508,6 +2475,23 @@ "markers": "python_version >= '3.5'", "version": "==1.1.1" }, + "pyyaml": { + "hashes": [ + "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6", + "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf", + "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5", + "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e", + "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811", + "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e", + "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d", + "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20", + "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689", + "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994", + "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615" + ], + "index": "pypi", + "version": "==5.3" + }, "pyzmq": { "hashes": [ "sha256:01b588911714a6696283de3904f564c550c9e12e8b4995e173f1011755e01086", @@ -2557,14 +2541,6 @@ "index": "pypi", "version": "==3.3.11" }, - "redlock": { - "hashes": [ - "sha256:b718646239d300745475a76e81d350ec523e7146cf84d696b3c4a7dfdd5dd4d4", - "sha256:ce7e6ab404882b64a9c5017c7a78b1a3714f2c712635bcb22cbb74d20719bbd1" - ], - "index": "pypi", - "version": "==1.2.0" - }, "requests": { "hashes": [ "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", @@ -2613,15 +2589,19 @@ "scikit-image": { "hashes": [ "sha256:063d1c20fcd53762f82ee58c29783ae4e8f6fbed445b41b704fa33b6f355729d", + "sha256:0715b7940778ba5d73da3908d60ddf2eb93863f7c394493a522fe56d3859295c", "sha256:0808ab5f8218d91a1c008036993636535a37efd67a52ab0f2e6e3f4b7e75aeda", "sha256:2a54bea469eb1b611bee1ce36e60710f5f94f29205bc5bd67a51793909b1e62b", "sha256:2aa962aa82d815606d7dad7f045f5d7ca55c65b4320d47e15a98fc92612c2d6c", "sha256:2d346d49b6852cffb47cbde995e2696d5b07f688d8c057a0a4548abf3a98f920", "sha256:3ad2efa792ab8de5fcefe6f4f5bc1ab64c411cdb5c829ce1526ab3a5a7729627", "sha256:3af3d781ce085573ced37b2b5b9abfd32ce3d4723bd17f37e829025d189b0421", + "sha256:41e28db0136f29ecd305bef0408fdfc64be9d415e54f5099a95555c65f5c1865", "sha256:6786b127f33470fd843e644435522fbf43bce05c9f5527946c390ccb9e1cac27", "sha256:8b2b768b02c6b7476f2e16ddd91f827d3817aef73f82cf28bff7a8dcdfd8c55c", + "sha256:a48fb0d34a090b578b87ffebab0fe035295c1945dbc2b28e1a55ea2cf6031751", "sha256:dd7fbd32da74d4e9967dc15845f731f16e7966cee61f5dc0e12e2abb1305068c", + "sha256:e18d73cc8893e2268b172c29f9aab530faf8cd3b7c11ae0bee3e763d719d35c5", "sha256:e774377876cb258e8f4d63f7809863f961c98aa02263b3ff54a39483bc6f7d26" ], "index": "pypi", @@ -2629,30 +2609,30 @@ }, "scipy": { "hashes": [ - "sha256:0359576d8cc058bd615999cf985e2423dc6cc824666d60e8b8d4810569a04655", - "sha256:07673b5b96dbe28c88f3a53ca9af67f802aa853de7402e31f473b4dd6501c799", - "sha256:0f81e71149539ac09053a3f9165659367b060eceef3bbde11e6600e1c341f1f2", - "sha256:125aa82f7b3d4bd7f77fed6c3c6e31be47e33f129d829799569389ae59f913e7", - "sha256:2dc26e5b3eb86b7adad506b6b04020f6a87e1102c9acd039e937d28bdcee7fa6", - "sha256:2e4b5fdb635dd425bf46fbd6381612692d3c795f1eb6fe62410305a440691d46", - "sha256:33ac3213ee617bbc0eac84d02b130d69093ed7738afb281dfdeb12a9dbdf1530", - "sha256:34c48d922760782732d6f8f4532e320984d1280763c6787c6582021d34c8ad79", - "sha256:3f556f63e070e9596624e42e99d23b259d8f0fc63ec093bef97a9f1c579565b2", - "sha256:470d8fc76ccab6cfff60a9de4ce316a23ee7f63615d948c7446dc7c1bb45042d", - "sha256:4ad7a3ae9831d2085d6f50b81bfcd76368293eafdf31f4ac9f109c6061309c24", - "sha256:61812a7db0d9bc3f13653e52b8ddb1935cf444ec55f39160fc2778aeb2719057", - "sha256:7a0477929e6f9d5928fe81fe75d00b7da9545a49109e66028d85848b18aeef99", - "sha256:9c3221039da50f3b60da70b65d6b020ea26cefbb097116cfec696010432d1f6c", - "sha256:a03939b431994289f39373c57bbe452974a7da724ae7f9620a1beee575434da4", - "sha256:df4dbd3d40db3f667e0145dba5f50954bf28b2dd5b8b400c79d5e3fe8cb67ce2", - "sha256:e837c8068bd1929a533e9d51562faf6584ddb5303d9e218d8c11aa4719dcd617", - "sha256:ecfd45ca0ce1d6c13bef17794b4052cc9a9574f4be8d44c9bcfd7e34294bd2d7", - "sha256:ee5888c62cd83c9bf9927ffcee08434e7d5c81a8f31e5b85af5470e511022c08", - "sha256:f018892621b787b9abf76d51d1f0c21611c71752ebb1891ccf7992e0bf973708", - "sha256:f2d5db81d90d14a32d4aff920f52fca5639bcaaaf87b4f61bce83a1d238f49fc" + "sha256:00af72998a46c25bdb5824d2b729e7dabec0c765f9deb0b504f928591f5ff9d4", + "sha256:0902a620a381f101e184a958459b36d3ee50f5effd186db76e131cbefcbb96f7", + "sha256:1e3190466d669d658233e8a583b854f6386dd62d655539b77b3fa25bfb2abb70", + "sha256:2cce3f9847a1a51019e8c5b47620da93950e58ebc611f13e0d11f4980ca5fecb", + "sha256:3092857f36b690a321a662fe5496cb816a7f4eecd875e1d36793d92d3f884073", + "sha256:386086e2972ed2db17cebf88610aab7d7f6e2c0ca30042dc9a89cf18dcc363fa", + "sha256:71eb180f22c49066f25d6df16f8709f215723317cc951d99e54dc88020ea57be", + "sha256:770254a280d741dd3436919d47e35712fb081a6ff8bafc0f319382b954b77802", + "sha256:787cc50cab3020a865640aba3485e9fbd161d4d3b0d03a967df1a2881320512d", + "sha256:8a07760d5c7f3a92e440ad3aedcc98891e915ce857664282ae3c0220f3301eb6", + "sha256:8d3bc3993b8e4be7eade6dcc6fd59a412d96d3a33fa42b0fa45dc9e24495ede9", + "sha256:9508a7c628a165c2c835f2497837bf6ac80eb25291055f56c129df3c943cbaf8", + "sha256:a144811318853a23d32a07bc7fd5561ff0cac5da643d96ed94a4ffe967d89672", + "sha256:a1aae70d52d0b074d8121333bc807a485f9f1e6a69742010b33780df2e60cfe0", + "sha256:a2d6df9eb074af7f08866598e4ef068a2b310d98f87dc23bd1b90ec7bdcec802", + "sha256:bb517872058a1f087c4528e7429b4a44533a902644987e7b2fe35ecc223bc408", + "sha256:c5cac0c0387272ee0e789e94a570ac51deb01c796b37fb2aad1fb13f85e2f97d", + "sha256:cc971a82ea1170e677443108703a2ec9ff0f70752258d0e9f5433d00dda01f59", + "sha256:dba8306f6da99e37ea08c08fef6e274b5bf8567bb094d1dbe86a20e532aca088", + "sha256:dc60bb302f48acf6da8ca4444cfa17d52c63c5415302a9ee77b3b21618090521", + "sha256:dee1bbf3a6c8f73b6b218cb28eed8dd13347ea2f87d572ce19b289d6fd3fbc59" ], "index": "pypi", - "version": "==1.3.2" + "version": "==1.4.1" }, "seaborn": { "hashes": [ @@ -2729,19 +2709,12 @@ "index": "pypi", "version": "==1.13.0" }, - "sortedcontainers": { - "hashes": [ - "sha256:974e9a32f56b17c1bac2aebd9dcf197f3eb9cd30553c5852a3187ad162e1a03a", - "sha256:d9e96492dd51fae31e60837736b38fe42a187b5404c16606ff7ee7cd582d4c60" - ], - "version": "==2.1.0" - }, "sqlalchemy": { "hashes": [ - "sha256:afa5541e9dea8ad0014251bc9d56171ca3d8b130c9627c6cb3681cff30be3f8a" + "sha256:bfb8f464a5000b567ac1d350b9090cf081180ec1ab4aa87e7bca12dab25320ec" ], "index": "pypi", - "version": "==1.3.11" + "version": "==1.3.12" }, "subprocess32": { "hashes": [ @@ -2751,13 +2724,11 @@ "index": "pypi", "version": "==3.5.4" }, - "supervisor": { + "tabulate": { "hashes": [ - "sha256:2dc86fe0476e945e61483d614ceb2cf4f93b95282eb243bdf792621994360383", - "sha256:a76b2f77a560f2dc411c0254a4eb15f555e99faac48621b0f1fc9ab013944f47" + "sha256:5470cc6687a091c7042cee89b2946d9235fe9f6d49c193a4ae2ac7bf386737c8" ], - "index": "pypi", - "version": "==4.1.0" + "version": "==0.8.6" }, "tenacity": { "hashes": [ @@ -2769,17 +2740,16 @@ }, "tensorboard": { "hashes": [ - "sha256:203bd0c2fa33e18c009fa21253b67b67b78ef9624c4df3f70d3ef1b4f0ca3f9c", - "sha256:bf66fc182fcbfff6fc2e770754a100ef5c6bdc8601fece92375f31da60733fdc" + "sha256:32d9dec38d053d7d75796eb7c2e0d77285af35f69ee1a6796ab5ecc896679fb3", + "sha256:ccae56f01acc78a138474081b631af52017c2075ffe1c453d58c49d5046ef081" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.0.1" + "version": "==2.0.2" }, "tensorflow-estimator": { "hashes": [ "sha256:aa8deab25d09a9730dfbae8ec58f4eb00ec2a90b5ca3dcbd8fa0717103d3bbb3" ], - "index": "pypi", "version": "==2.0.1" }, "tensorflow-gpu": { @@ -2816,13 +2786,6 @@ ], "version": "==0.4.4" }, - "theano": { - "hashes": [ - "sha256:35c9bbef56b61ffa299265a42a4e8f8cb5a07b2997dabaef0f8830b397086913" - ], - "index": "pypi", - "version": "==1.0.4" - }, "tornado": { "hashes": [ "sha256:349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c", @@ -2877,41 +2840,12 @@ "index": "pypi", "version": "==1.25.7" }, - "utm": { - "hashes": [ - "sha256:07e55707ed660eec1ae983bd54a406c437962618a6261b38d70592fe30f5f508" - ], - "index": "pypi", - "version": "==0.5.0" - }, - "uwsgi": { - "hashes": [ - "sha256:4972ac538800fb2d421027f49b4a1869b66048839507ccf0aa2fda792d99f583" - ], - "index": "pypi", - "version": "==2.0.18" - }, - "v4l2": { - "hashes": [ - "sha256:0d8f31f9d554ded4d0b50a31a7be5590b861df9e1ba256ee757e1c09175dd4a2" - ], - "index": "pypi", - "version": "==0.2" - }, - "vine": { - "hashes": [ - "sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87", - "sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.3.0" - }, "wcwidth": { "hashes": [ - "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", - "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" + "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603", + "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8" ], - "version": "==0.1.7" + "version": "==0.1.8" }, "webencodings": { "hashes": [ @@ -2930,11 +2864,11 @@ }, "wheel": { "hashes": [ - "sha256:10c9da68765315ed98850f8e048347c3eb06dd81822dc2ab1d4fde9dc9702646", - "sha256:f4da1763d3becf2e2cd92a14a7c920f0f00eca30fdde9ea992c836685b9faf28" + "sha256:9515fe0a94e823fd90b08d22de45d7bde57c90edce705b22f5e1ecf7e1b653c8", + "sha256:e721e53864f084f956f40f96124a74da0631ac13fbbd1ba99e8e2b5e9cafdf64" ], "markers": "python_version >= '3'", - "version": "==0.33.6" + "version": "==0.30.0" }, "widgetsnbextension": { "hashes": [ diff --git a/README.md b/README.md index 00ae8f969..013397a77 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ openpilot should preserve all other vehicle's stock features, including, but are Supported Hardware ------ -At the moment, openpilot supports the [EON DevKit](https://comma.ai/shop/products/eon-dashcam-devkit). A [car harness](https://comma.ai/shop/products/car-harness) is recommended to connect the EON to the car. In the future, we'd like to support other platforms as well. +At the moment, openpilot supports the [EON DevKit](https://comma.ai/shop/products/eon-dashcam-devkit) and the [comma two](https://comma.ai/shop/products/comma-two-devkit). A [car harness](https://comma.ai/shop/products/car-harness) is recommended to connect the EON or comma two to the car. In the future, we'd like to support other platforms as well, like gaming PCs. Supported Cars ------ @@ -159,6 +159,8 @@ Limitations of openpilot ALC and LDW openpilot ALC and openpilot LDW do not automatically drive the vehicle or reduce the amount of attention that must be paid to operate your vehicle. The driver must always keep control of the steering wheel and be ready to correct the openpilot ALC action at all times. +While changing lanes, openpilot is not capable of looking next to you or checking your blind spot. Only nudge the wheel to initiate a lane change after you have confirmed it's safe to do so. + Many factors can impact the performance of openpilot ALC and openpilot LDW, causing them to be unable to function as intended. These include, but are not limited to: * Poor visibility (heavy rain, snow, fog, etc.) or weather conditions that may interfere with sensor operation. diff --git a/RELEASES.md b/RELEASES.md index 2e99a28bc..1b4caf963 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,12 @@ +Version 0.7.1 (2020-01-20) +======================== + * comma two support! + * Lane Change Assist above 45 mph! + * Replace zmq with custom messaging library, msgq! + * Supercombo model: calibration and driving models are combined for better lead estimate + * More robust updater thanks to jyoung8607! Requires NEOS update + * Improve low speed ACC tuning + Version 0.7 (2019-12-13) ======================== * Move to SCons build system! diff --git a/SConstruct b/SConstruct index 9de2ba53a..404cc4491 100644 --- a/SConstruct +++ b/SConstruct @@ -46,6 +46,7 @@ else: "#phonelibs/capnp-cpp/include", "#phonelibs/capnp-c/include", "#phonelibs/zmq/x64/include", + "#external/tensorflow/include", ] libpath = [ "#phonelibs/capnp-cpp/x64/lib", @@ -55,6 +56,7 @@ else: "#phonelibs/zmq/x64/lib", "#phonelibs/libyuv/x64/lib", "#external/zmq/lib", + "#external/tensorflow/lib", "#cereal", "#selfdrive/common", "/usr/lib", @@ -62,6 +64,7 @@ else: ] rpath = ["phonelibs/capnp-cpp/x64/lib", + "external/tensorflow/lib", "cereal", "selfdrive/common"] @@ -201,11 +204,13 @@ SConscript(['selfdrive/controls/lib/longitudinal_mpc/SConscript']) SConscript(['selfdrive/boardd/SConscript']) SConscript(['selfdrive/proclogd/SConscript']) +SConscript(['selfdrive/ui/SConscript']) +SConscript(['selfdrive/loggerd/SConscript']) + if arch == "aarch64": SConscript(['selfdrive/logcatd/SConscript']) - SConscript(['selfdrive/ui/SConscript']) SConscript(['selfdrive/sensord/SConscript']) - SConscript(['selfdrive/loggerd/SConscript']) + SConscript(['selfdrive/clocksd/SConscript']) SConscript(['selfdrive/locationd/SConscript']) diff --git a/apk/ai.comma.plus.frame.apk b/apk/ai.comma.plus.frame.apk index 58f22edb3..11ba39d19 100644 Binary files a/apk/ai.comma.plus.frame.apk and b/apk/ai.comma.plus.frame.apk differ diff --git a/apk/ai.comma.plus.offroad.apk b/apk/ai.comma.plus.offroad.apk index 7e77114ab..2a84019cb 100644 Binary files a/apk/ai.comma.plus.offroad.apk and b/apk/ai.comma.plus.offroad.apk differ diff --git a/common/android.py b/common/android.py index eda82385a..e58b0e56e 100644 --- a/common/android.py +++ b/common/android.py @@ -1,10 +1,15 @@ +import os import binascii import itertools import re import struct import subprocess +ANDROID = os.path.isfile('/EON') + def getprop(key): + if not ANDROID: + return "" return subprocess.check_output(["getprop", key], encoding='utf8').strip() def get_imei(): @@ -14,7 +19,10 @@ def get_imei(): return ret def get_serial(): - return getprop("ro.serialno") + ret = getprop("ro.serialno") + if ret == "": + ret = "cccccccc" + return ret def get_subscriber_info(): ret = parse_service_call_string(["iphonesubinfo", "7"]) @@ -60,6 +68,8 @@ def parse_service_call_string(call): return None def parse_service_call_bytes(call): + if not ANDROID: + return None ret = subprocess.check_output(["service", "call", *call], encoding='utf8').strip() if 'Parcel' not in ret: return None diff --git a/common/logging_extra.py b/common/logging_extra.py index 43ae48882..d57332780 100644 --- a/common/logging_extra.py +++ b/common/logging_extra.py @@ -78,28 +78,6 @@ class SwagLogger(logging.Logger): self.log_local = local() self.log_local.ctx = {} - def findCaller(self, stack_info=None): - """ - Find the stack frame of the caller so that we can note the source - file name, line number and function name. - """ - # f = currentframe() - f = sys._getframe(3) - #On some versions of IronPython, currentframe() returns None if - #IronPython isn't run with -X:Frames. - if f is not None: - f = f.f_back - rv = "(unknown file)", 0, "(unknown function)" - while hasattr(f, "f_code"): - co = f.f_code - filename = os.path.normcase(co.co_filename) - if filename in (logging._srcfile, _srcfile): - f = f.f_back - continue - rv = (co.co_filename, f.f_lineno, co.co_name) - break - return rv - def local_ctx(self): try: return self.log_local.ctx diff --git a/common/params.py b/common/params.py index 5067aa8d4..3a4e4a2a1 100755 --- a/common/params.py +++ b/common/params.py @@ -69,9 +69,10 @@ keys = { "IsLdwEnabled": [TxType.PERSISTENT], "IsGeofenceEnabled": [TxType.PERSISTENT], "IsMetric": [TxType.PERSISTENT], + "IsOffroad": [TxType.CLEAR_ON_MANAGER_START], "IsRHD": [TxType.PERSISTENT], "IsTakingSnapshot": [TxType.CLEAR_ON_MANAGER_START], - "IsUpdateAvailable": [TxType.PERSISTENT], + "IsUpdateAvailable": [TxType.CLEAR_ON_MANAGER_START], "IsUploadRawEnabled": [TxType.PERSISTENT], "LastUpdateTime": [TxType.PERSISTENT], "LimitSetSpeed": [TxType.PERSISTENT], @@ -80,6 +81,7 @@ keys = { "LongitudinalControl": [TxType.PERSISTENT], "OpenpilotEnabledToggle": [TxType.PERSISTENT], "PandaFirmware": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT], + "PandaFirmwareHex": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT], "PandaDongleId": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT], "Passive": [TxType.PERSISTENT], "RecordFront": [TxType.PERSISTENT], diff --git a/common/spinner.py b/common/spinner.py index 734015bcf..3582d3fee 100644 --- a/common/spinner.py +++ b/common/spinner.py @@ -5,21 +5,31 @@ from common.basedir import BASEDIR class Spinner(): def __init__(self): - self.spinner_proc = subprocess.Popen(["./spinner"], - stdin=subprocess.PIPE, - cwd=os.path.join(BASEDIR, "selfdrive", "ui", "spinner"), - close_fds=True) + try: + self.spinner_proc = subprocess.Popen(["./spinner"], + stdin=subprocess.PIPE, + cwd=os.path.join(BASEDIR, "selfdrive", "ui", "spinner"), + close_fds=True) + except OSError: + self.spinner_proc = None def __enter__(self): return self def update(self, spinner_text): - self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n") - self.spinner_proc.stdin.flush() + if self.spinner_proc is not None: + self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n") + try: + self.spinner_proc.stdin.flush() + except BrokenPipeError: + pass def close(self): if self.spinner_proc is not None: - self.spinner_proc.stdin.close() + try: + self.spinner_proc.stdin.close() + except BrokenPipeError: + pass self.spinner_proc.terminate() self.spinner_proc = None diff --git a/common/transformations/camera.py b/common/transformations/camera.py index 960c063f9..489874eb6 100644 --- a/common/transformations/camera.py +++ b/common/transformations/camera.py @@ -44,6 +44,7 @@ def get_calib_from_vp(vp): roll_calib = 0 return roll_calib, pitch_calib, yaw_calib + # aka 'extrinsic_matrix' # road : x->forward, y -> left, z->up def get_view_frame_from_road_frame(roll, pitch, yaw, height): @@ -61,6 +62,13 @@ def vp_from_ke(m): """ return (m[0, 0]/m[2,0], m[1,0]/m[2,0]) + +def vp_from_rpy(rpy): + e = get_view_frame_from_road_frame(rpy[0], rpy[1], rpy[2], 1.22) + ke = np.dot(eon_intrinsics, e) + return vp_from_ke(ke) + + def roll_from_ke(m): # note: different from calibration.h/RollAnglefromKE: i think that one's just wrong return np.arctan2(-(m[1, 0] - m[1, 1] * m[2, 0] / m[2, 1]), diff --git a/installer/updater/update.json b/installer/updater/update.json index fb009b4c1..6e2692bfa 100644 --- a/installer/updater/update.json +++ b/installer/updater/update.json @@ -1,7 +1,7 @@ { - "ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-07df505453684371b6c22583ffbb74ee414fcd389a46ff369ffd1b6bac75414e.zip", - "ota_hash": "07df505453684371b6c22583ffbb74ee414fcd389a46ff369ffd1b6bac75414e", - "recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-3a6f973295ded6e4ff5cfff3b12e19c80d3bf45e2e8dd8699da3fc25b23ed7c6.img", - "recovery_len": 15848748, - "recovery_hash": "3a6f973295ded6e4ff5cfff3b12e19c80d3bf45e2e8dd8699da3fc25b23ed7c6" + "ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-efdf7de63b1aef63d68301e6175930991bf9a5927d16ec6fcc69287e2ee7ca4a.zip", + "ota_hash": "efdf7de63b1aef63d68301e6175930991bf9a5927d16ec6fcc69287e2ee7ca4a", + "recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-97c27e6ed04ed6bb0608b845a2d4100912093f9380c3f2ba6b56bccd608e5f6e.img", + "recovery_len": 15861036, + "recovery_hash": "97c27e6ed04ed6bb0608b845a2d4100912093f9380c3f2ba6b56bccd608e5f6e" } diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index 5e2389fe3..a33deaafd 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -6,20 +6,53 @@ export NUMEXPR_NUM_THREADS=1 export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 +if [ -z "$BASEDIR" ]; then + BASEDIR="/data/openpilot" +fi + if [ -z "$PASSIVE" ]; then export PASSIVE="1" fi +STAGING_ROOT="/data/safe_staging" + function launch { # Wifi scan wpa_cli IFNAME=wlan0 SCAN - # apply update - if [ "$(git rev-parse HEAD)" != "$(git rev-parse @{u})" ]; then - git reset --hard @{u} && - git clean -xdf && + # Check to see if there's a valid overlay-based update available. Conditions + # are as follows: + # + # 1. The BASEDIR init file has to exist, with a newer modtime than anything in + # the BASEDIR Git repo. This checks for local development work or the user + # switching branches/forks, which should not be overwritten. + # 2. The FINALIZED consistent file has to exist, indicating there's an update + # that completed successfully and synced to disk. - exec "${BASH_SOURCE[0]}" + if [ -f "${BASEDIR}/.overlay_init" ]; then + find ${BASEDIR}/.git -newer ${BASEDIR}/.overlay_init | grep -q '.' 2> /dev/null + if [ $? -eq 0 ]; then + echo "${BASEDIR} has been modified, skipping overlay update installation" + else + if [ -f "${STAGING_ROOT}/finalized/.overlay_consistent" ]; then + if [ ! -d /data/safe_staging/old_openpilot ]; then + echo "Valid overlay update found, installing" + LAUNCHER_LOCATION="${BASH_SOURCE[0]}" + + mv $BASEDIR /data/safe_staging/old_openpilot + mv "${STAGING_ROOT}/finalized" $BASEDIR + + # The mv changed our working directory to /data/safe_staging/old_openpilot + cd "${BASEDIR}" + + echo "Restarting launch script ${LAUNCHER_LOCATION}" + exec "${LAUNCHER_LOCATION}" + else + echo "openpilot backup found, not updating" + # TODO: restore backup? This means the updater didn't start after swapping + fi + fi + fi fi # no cpu rationing for now @@ -32,17 +65,17 @@ function launch { DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" # Remove old NEOS update file + # TODO: move this code to the updater if [ -d /data/neoupdate ]; then rm -rf /data/neoupdate fi # Check for NEOS update - if [ $(< /VERSION) != "13" ]; then + if [ $(< /VERSION) != "14" ]; then if [ -f "$DIR/scripts/continue.sh" ]; then cp "$DIR/scripts/continue.sh" "/data/data/com.termux/files/continue.sh" fi - git clean -xdf "$DIR/installer/updater/updater" "file://$DIR/installer/updater/update.json" fi diff --git a/models/driving_model.dlc b/models/driving_model.dlc deleted file mode 100644 index 2ff6435ed..000000000 Binary files a/models/driving_model.dlc and /dev/null differ diff --git a/models/monitoring_model_q.dlc b/models/monitoring_model_q.dlc index 74a4337a9..59104e18e 100644 Binary files a/models/monitoring_model_q.dlc and b/models/monitoring_model_q.dlc differ diff --git a/models/posenet.dlc b/models/posenet.dlc deleted file mode 100644 index 58dee250f..000000000 Binary files a/models/posenet.dlc and /dev/null differ diff --git a/models/supercombo.dlc b/models/supercombo.dlc new file mode 100644 index 000000000..fb48eb2ec Binary files /dev/null and b/models/supercombo.dlc differ diff --git a/panda/board/obj/panda.bin.signed b/panda/board/obj/panda.bin.signed new file mode 100644 index 000000000..f48f6203c Binary files /dev/null and b/panda/board/obj/panda.bin.signed differ diff --git a/scripts/stop_updater.sh b/scripts/stop_updater.sh new file mode 100755 index 000000000..4243d30e9 --- /dev/null +++ b/scripts/stop_updater.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +# Stop updater +pkill -2 -f selfdrive.updated + +# Remove pending update +rm -f /data/safe_staging/finalized/.overlay_consistent diff --git a/scripts/update_now.sh b/scripts/update_now.sh new file mode 100755 index 000000000..3f0193f08 --- /dev/null +++ b/scripts/update_now.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env sh + +# Send SIGHUP to updater +pkill -1 -f selfdrive.updated diff --git a/selfdrive/athena/test.py b/selfdrive/athena/test.py index 99226176b..0bedfdeb7 100755 --- a/selfdrive/athena/test.py +++ b/selfdrive/athena/test.py @@ -32,10 +32,8 @@ class TestAthenadMethods(unittest.TestCase): assert dispatcher["echo"]("bob") == "bob" def test_getMessage(self): - try: + with self.assertRaises(TimeoutError) as _: dispatcher["getMessage"]("controlsState") - except TimeoutError: - pass def send_thermal(): messaging.context = messaging.Context() @@ -50,6 +48,7 @@ class TestAthenadMethods(unittest.TestCase): p = Process(target=send_thermal) p.start() + time.sleep(0.1) try: thermal = dispatcher["getMessage"]("thermal") assert thermal['thermal'] @@ -60,7 +59,7 @@ class TestAthenadMethods(unittest.TestCase): print(dispatcher["listDataDirectory"]()) @with_http_server - def test_do_upload(self): + def test_do_upload(self, host): fn = os.path.join(athenad.ROOT, 'qlog.bz2') Path(fn).touch() @@ -71,14 +70,14 @@ class TestAthenadMethods(unittest.TestCase): except requests.exceptions.ConnectionError: pass - item = athenad.UploadItem(path=fn, url="http://localhost:44444/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='') + item = athenad.UploadItem(path=fn, url=f"{host}/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='') resp = athenad._do_upload(item) self.assertEqual(resp.status_code, 201) finally: os.unlink(fn) @with_http_server - def test_uploadFileToUrl(self): + def test_uploadFileToUrl(self, host): not_exists_resp = dispatcher["uploadFileToUrl"]("does_not_exist.bz2", "http://localhost:1238", {}) self.assertEqual(not_exists_resp, 404) @@ -86,9 +85,9 @@ class TestAthenadMethods(unittest.TestCase): Path(fn).touch() try: - resp = dispatcher["uploadFileToUrl"]("qlog.bz2", "http://localhost:44444/qlog.bz2", {}) + resp = dispatcher["uploadFileToUrl"]("qlog.bz2", f"{host}/qlog.bz2", {}) self.assertEqual(resp['enqueued'], 1) - self.assertDictContainsSubset({"path": fn, "url": "http://localhost:44444/qlog.bz2", "headers": {}}, resp['item']) + self.assertDictContainsSubset({"path": fn, "url": f"{host}/qlog.bz2", "headers": {}}, resp['item']) self.assertIsNotNone(resp['item'].get('id')) self.assertEqual(athenad.upload_queue.qsize(), 1) finally: @@ -96,10 +95,10 @@ class TestAthenadMethods(unittest.TestCase): os.unlink(fn) @with_http_server - def test_upload_handler(self): + def test_upload_handler(self, host): fn = os.path.join(athenad.ROOT, 'qlog.bz2') Path(fn).touch() - item = athenad.UploadItem(path=fn, url="http://localhost:44444/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='') + item = athenad.UploadItem(path=fn, url=f"{host}/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='') end_event = threading.Event() thread = threading.Thread(target=athenad.upload_handler, args=(end_event,)) diff --git a/selfdrive/athena/test_helpers.py b/selfdrive/athena/test_helpers.py index a6b2d4897..2335ce89c 100644 --- a/selfdrive/athena/test_helpers.py +++ b/selfdrive/athena/test_helpers.py @@ -1,4 +1,7 @@ import http.server +import multiprocessing +import queue +import random import requests import socket import time @@ -70,27 +73,41 @@ class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler): self.send_response(201, "Created") self.end_headers() +def http_server(port_queue, **kwargs): + while 1: + try: + port = random.randrange(40000, 50000) + port_queue.put(port) + http.server.test(**kwargs, port=port) + except OSError as e: + if e.errno == 98: + continue + def with_http_server(func): @wraps(func) def inner(*args, **kwargs): - p = Process(target=http.server.test, + port_queue = multiprocessing.Queue() + host = '127.0.0.1' + p = Process(target=http_server, + args=(port_queue,), kwargs={ 'HandlerClass': HTTPRequestHandler, - 'port': 44444, - 'bind': '127.0.0.1'}) + 'bind': host}) p.start() now = time.time() + port = None while 1: if time.time() - now > 5: raise Exception('HTTP Server did not start') try: - requests.put('http://localhost:44444/qlog.bz2', data='') + port = port_queue.get(timeout=0.1) + requests.put(f'http://{host}:{port}/qlog.bz2', data='') break - except requests.exceptions.ConnectionError: + except (requests.exceptions.ConnectionError, queue.Empty): time.sleep(0.1) try: - return func(*args, **kwargs) + return func(*args, f'http://{host}:{port}', **kwargs) finally: p.terminate() diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index e8a313d90..37ee22e70 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -21,6 +21,7 @@ #include "cereal/gen/cpp/log.capnp.h" #include "cereal/gen/cpp/car.capnp.h" +#include "common/util.h" #include "common/messaging.h" #include "common/params.h" #include "common/swaglog.h" @@ -35,8 +36,10 @@ #define MAX_IR_POWER 0.5f #define MIN_IR_POWER 0.0f -#define CUTOFF_GAIN 0.015625f // iso400 -#define SATURATE_GAIN 0.0625f // iso1600 +#define CUTOFF_GAIN 0.015625f // iso400 +#define SATURATE_GAIN 0.0625f // iso1600 +#define NIBBLE_TO_HEX(n) ((n) < 10 ? (n) + '0' : ((n) - 10) + 'a') +#define VOLTAGE_K 0.091 // LPF gain for 5s tau (dt/tau / (dt/tau + 1)) namespace { @@ -62,14 +65,19 @@ bool loopback_can = false; cereal::HealthData::HwType hw_type = cereal::HealthData::HwType::UNKNOWN; bool is_pigeon = false; const uint32_t NO_IGNITION_CNT_MAX = 2 * 60 * 60 * 30; // turn off charge after 30 hrs -const uint32_t VBATT_START_CHARGING = 11500; -const uint32_t VBATT_PAUSE_CHARGING = 10500; +const float VBATT_START_CHARGING = 11.5; +const float VBATT_PAUSE_CHARGING = 11.0; +float voltage_f = 12.5; // filtered voltage uint32_t no_ignition_cnt = 0; bool connected_once = false; bool ignition_last = false; -pthread_t safety_setter_thread_handle = -1; -pthread_t pigeon_thread_handle = -1; +bool safety_setter_thread_initialized = false; +pthread_t safety_setter_thread_handle; + +bool pigeon_thread_initialized = false; +pthread_t pigeon_thread_handle; + bool pigeon_needs_init; void pigeon_init(); @@ -130,10 +138,7 @@ void *safety_setter_thread(void *s) { pthread_mutex_lock(&usb_lock); // set in the mutex to avoid race - safety_setter_thread_handle = -1; - - // set if long_control is allowed by openpilot. Hardcoded to True for now - libusb_control_transfer(dev_handle, 0x40, 0xdf, 1, 0, NULL, 0, TIMEOUT); + safety_setter_thread_initialized = false; libusb_control_transfer(dev_handle, 0x40, 0xdc, safety_model, safety_param, NULL, 0, TIMEOUT); @@ -144,13 +149,12 @@ void *safety_setter_thread(void *s) { // must be called before threads or with mutex bool usb_connect() { - int err; + int err, err2; unsigned char hw_query[1] = {0}; - unsigned char fw_ver_buf[64]; + unsigned char fw_sig_buf[128]; + unsigned char fw_sig_hex_buf[16]; unsigned char serial_buf[16]; - const char *fw_ver; const char *serial; - int fw_ver_sz = 0; int serial_sz = 0; ignition_last = false; @@ -169,12 +173,17 @@ bool usb_connect() { } // get panda fw - err = libusb_control_transfer(dev_handle, 0xc0, 0xd6, 0, 0, fw_ver_buf, 64, TIMEOUT); - if (err > 0) { - fw_ver = (const char *)fw_ver_buf; - fw_ver_sz = err; - write_db_value(NULL, "PandaFirmware", fw_ver, fw_ver_sz); - printf("panda fw: %.*s\n", fw_ver_sz, fw_ver); + err = libusb_control_transfer(dev_handle, 0xc0, 0xd3, 0, 0, fw_sig_buf, 64, TIMEOUT); + err2 = libusb_control_transfer(dev_handle, 0xc0, 0xd4, 0, 0, fw_sig_buf + 64, 64, TIMEOUT); + if ((err == 64) && (err2 == 64)) { + printf("FW signature read\n"); + write_db_value(NULL, "PandaFirmware", (const char *)fw_sig_buf, 128); + + for (size_t i = 0; i < 8; i++){ + fw_sig_hex_buf[2*i] = NIBBLE_TO_HEX(fw_sig_buf[i] >> 4); + fw_sig_hex_buf[2*i+1] = NIBBLE_TO_HEX(fw_sig_buf[i] & 0xF); + } + write_db_value(NULL, "PandaFirmwareHex", (const char *)fw_sig_hex_buf, 16); } else { goto fail; } @@ -206,9 +215,10 @@ bool usb_connect() { if (is_pigeon) { LOGW("panda with gps detected"); pigeon_needs_init = true; - if (pigeon_thread_handle == -1) { + if (!pigeon_thread_initialized) { err = pthread_create(&pigeon_thread_handle, NULL, pigeon_thread, NULL); assert(err == 0); + pigeon_thread_initialized = true; } } @@ -289,6 +299,8 @@ void can_recv(PubSocket *publisher) { // return if length is 0 if (recv <= 0) { return; + } else if (recv == RECV_SIZE) { + LOGW("Receive buffer full"); } // create message @@ -296,7 +308,6 @@ void can_recv(PubSocket *publisher) { cereal::Event::Builder event = msg.initRoot(); event.setLogMonoTime(start_time); size_t num_msg = recv / 0x10; - auto canData = event.initCan(num_msg); // populate message @@ -330,6 +341,7 @@ void can_health(PubSocket *publisher) { uint32_t uptime; uint32_t voltage; uint32_t current; + uint32_t can_rx_errs; uint32_t can_send_errs; uint32_t can_fwd_errs; uint32_t gmlan_send_errs; @@ -355,6 +367,12 @@ void can_health(PubSocket *publisher) { } while(cnt != sizeof(health)); pthread_mutex_unlock(&usb_lock); + if (spoofing_started) { + health.ignition_line = 1; + } + + voltage_f = VOLTAGE_K * (health.voltage / 1000.0) + (1.0 - VOLTAGE_K) * voltage_f; // LPF + // Make sure CAN buses are live: safety_setter_thread does not work if Panda CAN are silent and there is only one other CAN node if (health.safety_model == (uint8_t)(cereal::CarParams::SafetyModel::SILENT)) { pthread_mutex_lock(&usb_lock); @@ -373,13 +391,15 @@ void can_health(PubSocket *publisher) { #ifndef __x86_64__ bool cdp_mode = health.usb_power_mode == (uint8_t)(cereal::HealthData::UsbPowerMode::CDP); bool no_ignition_exp = no_ignition_cnt > NO_IGNITION_CNT_MAX; - if ((no_ignition_exp || (health.voltage < VBATT_PAUSE_CHARGING)) && cdp_mode && !ignition) { + if ((no_ignition_exp || (voltage_f < VBATT_PAUSE_CHARGING)) && cdp_mode && !ignition) { printf("TURN OFF CHARGING!\n"); pthread_mutex_lock(&usb_lock); libusb_control_transfer(dev_handle, 0xc0, 0xe6, (uint16_t)(cereal::HealthData::UsbPowerMode::CLIENT), 0, NULL, 0, TIMEOUT); pthread_mutex_unlock(&usb_lock); + printf("POWER DOWN DEVICE\n"); + system("service call power 17 i32 0 i32 1"); } - if (!no_ignition_exp && (health.voltage > VBATT_START_CHARGING) && !cdp_mode) { + if (!no_ignition_exp && (voltage_f > VBATT_START_CHARGING) && !cdp_mode) { printf("TURN ON CHARGING!\n"); pthread_mutex_lock(&usb_lock); libusb_control_transfer(dev_handle, 0xc0, 0xe6, (uint16_t)(cereal::HealthData::UsbPowerMode::CDP), 0, NULL, 0, TIMEOUT); @@ -406,15 +426,15 @@ void can_health(PubSocket *publisher) { // clear VIN, CarParams, and set new safety on car start if (ignition && !ignition_last) { - int result = delete_db_value(NULL, "CarVin"); assert((result == 0) || (result == ERR_NO_VALUE)); result = delete_db_value(NULL, "CarParams"); assert((result == 0) || (result == ERR_NO_VALUE)); - if (safety_setter_thread_handle == -1) { + if (!safety_setter_thread_initialized) { err = pthread_create(&safety_setter_thread_handle, NULL, safety_setter_thread, NULL); assert(err == 0); + safety_setter_thread_initialized = true; } } @@ -459,15 +479,12 @@ void can_health(PubSocket *publisher) { healthData.setUptime(health.uptime); healthData.setVoltage(health.voltage); healthData.setCurrent(health.current); - if (spoofing_started) { - healthData.setIgnitionLine(true); - } else { - healthData.setIgnitionLine(health.ignition_line); - } + healthData.setIgnitionLine(health.ignition_line); healthData.setIgnitionCan(health.ignition_can); healthData.setControlsAllowed(health.controls_allowed); healthData.setGasInterceptorDetected(health.gas_interceptor_detected); healthData.setHasGps(is_pigeon); + healthData.setCanRxErrs(health.can_rx_errs); healthData.setCanSendErrs(health.can_send_errs); healthData.setCanFwdErrs(health.can_fwd_errs); healthData.setGmlanSendErrs(health.gmlan_send_errs); @@ -842,14 +859,6 @@ void *pigeon_thread(void *crap) { return NULL; } -int set_realtime_priority(int level) { - // should match python using chrt - struct sched_param sa; - memset(&sa, 0, sizeof(sa)); - sa.sched_priority = level; - return sched_setscheduler(getpid(), SCHED_FIFO, &sa); -} - } int main() { diff --git a/selfdrive/camerad/main.cc b/selfdrive/camerad/main.cc index 7679a5248..896a4cac6 100644 --- a/selfdrive/camerad/main.cc +++ b/selfdrive/camerad/main.cc @@ -680,14 +680,6 @@ void* visionserver_client_thread(void* arg) { } else { assert(false); } - - if (stream_type == VISION_STREAM_RGB_BACK || - stream_type == VISION_STREAM_RGB_FRONT) { - /*stream_bufs->buf_info.ui_info = (VisionUIInfo){ - .transformed_width = s->model.in.transformed_width, - .transformed_height = s->model.in.transformed_height, - };*/ - } vipc_send(fd, &rep); streams[stream_type].subscribed = true; } else if (p.type == VIPC_STREAM_RELEASE) { diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index e3a96617b..446778861 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -3,6 +3,7 @@ from common.params import Params from common.basedir import BASEDIR from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_known_cars from selfdrive.car.vin import get_vin, VIN_UNKNOWN +from selfdrive.car.fw_versions import get_fw_versions from selfdrive.swaglog import cloudlog import cereal.messaging as messaging from selfdrive.car import gen_empty_fingerprint @@ -56,12 +57,14 @@ def only_toyota_left(candidate_cars): # BOUNTY: every added fingerprint in selfdrive/car/*/values.py is a $100 coupon code on shop.comma.ai # **** for use live only **** def fingerprint(logcan, sendcan, has_relay): - if has_relay: # Vin query only reliably works thorugh OBDII - vin = get_vin(logcan, sendcan, 1) + bus = 1 + addr, vin = get_vin(logcan, sendcan, bus) + _, car_fw = get_fw_versions(logcan, sendcan, bus) else: vin = VIN_UNKNOWN + _, car_fw = set(), [] cloudlog.warning("VIN %s", vin) Params().put("CarVin", vin) @@ -108,18 +111,19 @@ def fingerprint(logcan, sendcan, has_relay): frame += 1 cloudlog.warning("fingerprinted %s", car_fingerprint) - return car_fingerprint, finger, vin + return car_fingerprint, finger, vin, car_fw def get_car(logcan, sendcan, has_relay=False): - - candidate, fingerprints, vin = fingerprint(logcan, sendcan, has_relay) + candidate, fingerprints, vin, car_fw = fingerprint(logcan, sendcan, has_relay) if candidate is None: cloudlog.warning("car doesn't match any fingerprints: %r", fingerprints) candidate = "mock" CarInterface, CarController = interfaces[candidate] - car_params = CarInterface.get_params(candidate, fingerprints, vin, has_relay) + car_params = CarInterface.get_params(candidate, fingerprints, has_relay, car_fw) + car_params.carVin = vin + car_params.carFw = car_fw return CarInterface(car_params, CarController), car_params diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 2a6422ecb..f96d95ae9 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -35,13 +35,12 @@ class CarInterface(CarInterfaceBase): return float(accel) / 3.0 @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): ret = car.CarParams.new_message() ret.carName = "chrysler" ret.carFingerprint = candidate - ret.carVin = vin ret.isPandaBlack = has_relay ret.safetyModel = car.CarParams.SafetyModel.chrysler diff --git a/selfdrive/car/fingerprints.py b/selfdrive/car/fingerprints.py index 0e29e6c1e..4ca9fa093 100644 --- a/selfdrive/car/fingerprints.py +++ b/selfdrive/car/fingerprints.py @@ -1,27 +1,43 @@ import os from common.basedir import BASEDIR +def get_attr_from_cars(attr): + # read all the folders in selfdrive/car and return a dict where: + # - keys are all the car models + # - values are attr values from all car folders + result = {} + + for car_folder in [x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]: + try: + car_name = car_folder.split('/')[-1] + values = __import__('selfdrive.car.%s.values' % car_name, fromlist=[attr]) + if hasattr(values, attr): + attr_values = getattr(values, attr) + else: + continue + + for f, v in attr_values.items(): + result[f] = v + + except (ImportError, IOError): + pass + + return result + + +def get_fw_versions_list(): + return get_attr_from_cars('FW_VERSIONS') + + def get_fingerprint_list(): # read all the folders in selfdrive/car and return a dict where: # - keys are all the car models for which we have a fingerprint # - values are lists dicts of messages that constitute the unique # CAN fingerprint of each car model and all its variants - fingerprints = {} - for car_folder in [x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]: - try: - car_name = car_folder.split('/')[-1] - values = __import__('selfdrive.car.%s.values' % car_name, fromlist=['FINGERPRINTS']) - if hasattr(values, 'FINGERPRINTS'): - car_fingerprints = values.FINGERPRINTS - else: - continue - for f, v in car_fingerprints.items(): - fingerprints[f] = v - except (ImportError, IOError): - pass - return fingerprints + return get_attr_from_cars('FINGERPRINTS') +FW_VERSIONS = get_fw_versions_list() _FINGERPRINTS = get_fingerprint_list() _DEBUG_ADDRESS = {1880: 8} # reserved for debug purposes diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index 5b2deed55..70df1cf95 100755 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -34,13 +34,12 @@ class CarInterface(CarInterfaceBase): return float(accel) / 3.0 @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): ret = car.CarParams.new_message() ret.carName = "ford" ret.carFingerprint = candidate - ret.carVin = vin ret.isPandaBlack = has_relay ret.safetyModel = car.CarParams.SafetyModel.ford diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py new file mode 100755 index 000000000..1175c5978 --- /dev/null +++ b/selfdrive/car/fw_versions.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +import traceback +import struct +from tqdm import tqdm + +from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery +from selfdrive.swaglog import cloudlog +from selfdrive.car.fingerprints import FW_VERSIONS +import panda.python.uds as uds + +from cereal import car +Ecu = car.CarParams.Ecu + +def p16(val): + return struct.pack("!H", val) + +TESTER_PRESENT_REQUEST = bytes([uds.SERVICE_TYPE.TESTER_PRESENT, 0x0]) +TESTER_PRESENT_RESPONSE = bytes([uds.SERVICE_TYPE.TESTER_PRESENT + 0x40, 0x0]) + +SHORT_TESTER_PRESENT_REQUEST = bytes([uds.SERVICE_TYPE.TESTER_PRESENT]) +SHORT_TESTER_PRESENT_RESPONSE = bytes([uds.SERVICE_TYPE.TESTER_PRESENT + 0x40]) + +DEFAULT_DIAGNOSTIC_REQUEST = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, + uds.SESSION_TYPE.DEFAULT]) +DEFAULT_DIAGNOSTIC_RESPONSE = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40, + uds.SESSION_TYPE.DEFAULT, 0x0, 0x32, 0x1, 0xf4]) + +EXTENDED_DIAGNOSTIC_REQUEST = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, + uds.SESSION_TYPE.EXTENDED_DIAGNOSTIC]) +EXTENDED_DIAGNOSTIC_RESPONSE = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40, + uds.SESSION_TYPE.EXTENDED_DIAGNOSTIC, 0x0, 0x32, 0x1, 0xf4]) + +UDS_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ + p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) +UDS_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ + p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) + +TOYOTA_VERSION_REQUEST = b'\x1a\x88\x01' +TOYOTA_VERSION_RESPONSE = b'\x5a\x88\x01' + +OBD_VERSION_REQUEST = b'\x09\x04' +OBD_VERSION_RESPONSE = b'\x49\x04' + + +REQUESTS = [ + # Honda + ( + [UDS_VERSION_REQUEST], + [UDS_VERSION_RESPONSE] + ), + # Toyota + ( + [SHORT_TESTER_PRESENT_REQUEST, TOYOTA_VERSION_REQUEST], + [SHORT_TESTER_PRESENT_RESPONSE, TOYOTA_VERSION_RESPONSE] + ), + ( + [SHORT_TESTER_PRESENT_REQUEST, OBD_VERSION_REQUEST], + [SHORT_TESTER_PRESENT_RESPONSE, OBD_VERSION_RESPONSE] + ), + ( + [TESTER_PRESENT_REQUEST, DEFAULT_DIAGNOSTIC_REQUEST, EXTENDED_DIAGNOSTIC_REQUEST, UDS_VERSION_REQUEST], + [TESTER_PRESENT_RESPONSE, DEFAULT_DIAGNOSTIC_RESPONSE, EXTENDED_DIAGNOSTIC_RESPONSE, UDS_VERSION_RESPONSE] + ) +] + +def chunks(l, n=128): + for i in range(0, len(l), n): + yield l[i:i + n] + +def match_fw_to_car(fw_versions): + candidates = FW_VERSIONS + invalid = [] + + for candidate, fws in candidates.items(): + for ecu, expected_versions in fws.items(): + ecu_type = ecu[0] + addr = ecu[1:] + + found_version = fw_versions.get(addr, None) + + # Allow DSU not being present + if ecu_type in [Ecu.unknown, Ecu.dsu] and found_version is None: + continue + + if found_version not in expected_versions: + invalid.append(candidate) + break + + return set(candidates.keys()) - set(invalid) + + +def get_fw_versions(logcan, sendcan, bus, extra=None, timeout=0.1, debug=False, progress=False): + ecu_types = {} + + # Extract ECU adresses to query from fingerprints + # ECUs using a subadress need be queried one by one, the rest can be done in parallel + addrs = [] + parallel_addrs = [] + + versions = FW_VERSIONS + if extra is not None: + versions.update(extra) + + for c in versions.values(): + for ecu_type, addr, sub_addr in c.keys(): + a = (addr, sub_addr) + if a not in ecu_types: + ecu_types[a] = ecu_type + + if sub_addr is None: + parallel_addrs.append(a) + else: + addrs.append([a]) + addrs.insert(0, parallel_addrs) + + fw_versions = {} + for i, addr in enumerate(tqdm(addrs, disable=not progress)): + for addr_chunk in chunks(addr): + for request, response in REQUESTS: + try: + query = IsoTpParallelQuery(sendcan, logcan, bus, addr_chunk, request, response, debug=debug) + t = 2 * timeout if i == 0 else timeout + fw_versions.update(query.get_data(t)) + except Exception: + cloudlog.warning(f"FW query exception: {traceback.format_exc()}") + + # Build capnp list to put into CarParams + car_fw = [] + for addr, version in fw_versions.items(): + f = car.CarParams.CarFw.new_message() + + f.ecu = ecu_types[addr] + f.fwVersion = version + f.address = addr[0] + + if addr[1] is not None: + f.subAddress = addr[1] + + car_fw.append(f) + + candidates = match_fw_to_car(fw_versions) + return candidates, car_fw + + +if __name__ == "__main__": + import time + import argparse + import cereal.messaging as messaging + from selfdrive.car.vin import get_vin + + + parser = argparse.ArgumentParser(description='Get firmware version of ECUs') + parser.add_argument('--scan', action='store_true') + parser.add_argument('--debug', action='store_true') + args = parser.parse_args() + + logcan = messaging.sub_sock('can') + sendcan = messaging.pub_sock('sendcan') + + extra = None + if args.scan: + extra = {"DEBUG": {}} + # Honda + for i in range(256): + extra["DEBUG"][(Ecu.unknown, 0x18da00f1 + (i << 8), None)] = [] + extra["DEBUG"][(Ecu.unknown, 0x700 + i, None)] = [] + extra["DEBUG"][(Ecu.unknown, 0x750, i)] = [] + + time.sleep(1.) + + t = time.time() + print("Getting vin...") + addr, vin = get_vin(logcan, sendcan, 1, retry=10, debug=args.debug) + print(f"VIN: {vin}") + print("Getting VIN took %.3f s" % (time.time() - t)) + print() + + t = time.time() + candidates, fw_vers = get_fw_versions(logcan, sendcan, 1, extra=extra, debug=args.debug, progress=True) + + print() + print("Found FW versions") + print("{") + for version in fw_vers: + subaddr = None if version.subAddress == 0 else hex(version.subAddress) + print(f" (Ecu.{version.ecu}, {hex(version.address)}, {subaddr}): [{version.fwVersion}]") + print("}") + + + print() + print("Possible matches:", candidates) + print("Getting fw took %.3f s" % (time.time() - t)) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index eee03b11d..b58a1a997 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -43,12 +43,11 @@ class CarInterface(CarInterfaceBase): return float(accel) / 4.0 @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): ret = car.CarParams.new_message() ret.carName = "gm" ret.carFingerprint = candidate - ret.carVin = vin ret.isPandaBlack = has_relay ret.enableCruise = False diff --git a/selfdrive/car/honda/carcontroller.py b/selfdrive/car/honda/carcontroller.py index 2ba51ce4e..9b0e0706c 100644 --- a/selfdrive/car/honda/carcontroller.py +++ b/selfdrive/car/honda/carcontroller.py @@ -1,12 +1,14 @@ from collections import namedtuple +from cereal import car from common.realtime import DT_CTRL from selfdrive.controls.lib.drive_helpers import rate_limit from common.numpy_fast import clip from selfdrive.car import create_gas_command from selfdrive.car.honda import hondacan -from selfdrive.car.honda.values import AH, CruiseButtons, CAR +from selfdrive.car.honda.values import CruiseButtons, CAR, VISUAL_HUD from opendbc.can.packer import CANPacker +VisualAlert = car.CarControl.HUDControl.VisualAlert def actuator_hystereses(brake, braking, brake_steady, v_ego, car_fingerprint): # hyst params @@ -56,25 +58,25 @@ def process_hud_alert(hud_alert): fcw_display = 0 steer_required = 0 acc_alert = 0 - if hud_alert == AH.NONE: # no alert - pass - elif hud_alert == AH.FCW: # FCW - fcw_display = hud_alert[1] - elif hud_alert == AH.STEER: # STEER - steer_required = hud_alert[1] - else: # any other ACC alert - acc_alert = hud_alert[1] + + # priority is: FCW, steer required, all others + if hud_alert == VisualAlert.fcw: + fcw_display = VISUAL_HUD[hud_alert.raw] + elif hud_alert == VisualAlert.steerRequired: + steer_required = VISUAL_HUD[hud_alert.raw] + else: + acc_alert = VISUAL_HUD[hud_alert.raw] return fcw_display, steer_required, acc_alert HUDData = namedtuple("HUDData", - ["pcm_accel", "v_cruise", "mini_car", "car", "X4", - "lanes", "fcw", "acc_alert", "steer_required"]) + ["pcm_accel", "v_cruise", "car", + "lanes", "fcw", "acc_alert", "steer_required"]) class CarController(): - def __init__(self, dbc_name): + def __init__(self, dbc_name, CP): self.braking = False self.brake_steady = 0. self.brake_last = 0. @@ -82,6 +84,11 @@ class CarController(): self.last_pump_ts = 0. self.packer = CANPacker(dbc_name) self.new_radar_config = False + self.eps_modified = False + for fw in CP.carFw: + if fw.ecu == "eps" and b"," in fw.fwVersion: + print("EPS FW MODIFIED!") + self.eps_modified = True def update(self, enabled, CS, frame, actuators, \ pcm_speed, pcm_override, pcm_cancel_cmd, pcm_accel, \ @@ -96,7 +103,7 @@ class CarController(): pcm_cancel_cmd = True # *** rate limit after the enable check *** - self.brake_last = rate_limit(brake, self.brake_last, -2., 1./100) + self.brake_last = rate_limit(brake, self.brake_last, -2., DT_CTRL) # vehicle hud display, wait for one update from 10Hz 0x304 msg if hud_show_lanes: @@ -114,8 +121,8 @@ class CarController(): fcw_display, steer_required, acc_alert = process_hud_alert(hud_alert) - hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), 1, hud_car, - 0xc1, hud_lanes, fcw_display, acc_alert, steer_required) + hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_car, + hud_lanes, fcw_display, acc_alert, steer_required) # **** process the car messages **** @@ -124,9 +131,11 @@ class CarController(): if CS.CP.carFingerprint in (CAR.ACURA_ILX): STEER_MAX = 0xF00 elif CS.CP.carFingerprint in (CAR.CRV, CAR.ACURA_RDX): - STEER_MAX = 0x3e8 # CR-V only uses 12-bits and requires a lower value (max value from energee) + STEER_MAX = 0x3e8 # CR-V only uses 12-bits and requires a lower value elif CS.CP.carFingerprint in (CAR.ODYSSEY_CHN): STEER_MAX = 0x7FFF + elif CS.CP.carFingerprint in (CAR.CIVIC) and self.eps_modified: + STEER_MAX = 0x1400 else: STEER_MAX = 0x1000 @@ -135,6 +144,12 @@ class CarController(): apply_brake = int(clip(self.brake_last * BRAKE_MAX, 0, BRAKE_MAX - 1)) apply_steer = int(clip(-actuators.steer * STEER_MAX, -STEER_MAX, STEER_MAX)) + if CS.CP.carFingerprint in (CAR.CIVIC) and self.eps_modified: + if apply_steer > 0xA00: + apply_steer = (apply_steer - 0xA00) / 2 + 0xA00 + elif apply_steer < -0xA00: + apply_steer = (apply_steer + 0xA00) / 2 - 0xA00 + lkas_active = enabled and not CS.steer_not_allowed # Send CAN commands. diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index 55f86d25c..39b91c866 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -41,8 +41,8 @@ def get_can_signals(CP): ("WHEEL_SPEED_RR", "WHEEL_SPEEDS", 0), ("STEER_ANGLE", "STEERING_SENSORS", 0), ("STEER_ANGLE_RATE", "STEERING_SENSORS", 0), + ("MOTOR_TORQUE", "STEER_MOTOR_TORQUE", 0), ("STEER_TORQUE_SENSOR", "STEER_STATUS", 0), - ("STEER_TORQUE_MOTOR", "STEER_STATUS", 0), ("LEFT_BLINKER", "SCM_FEEDBACK", 0), ("RIGHT_BLINKER", "SCM_FEEDBACK", 0), ("GEAR", "GEARBOX", 0), @@ -325,7 +325,7 @@ class CarState(): self.car_gas = cp.vl["GAS_PEDAL_2"]['CAR_GAS'] self.steer_torque_driver = cp.vl["STEER_STATUS"]['STEER_TORQUE_SENSOR'] - self.steer_torque_motor = cp.vl["STEER_STATUS"]['STEER_TORQUE_MOTOR'] + self.steer_torque_motor = cp.vl["STEER_MOTOR_TORQUE"]['MOTOR_TORQUE'] self.steer_override = abs(self.steer_torque_driver) > STEER_THRESHOLD[self.CP.carFingerprint] self.brake_switch = cp.vl["POWERTRAIN_DATA"]['BRAKE_SWITCH'] diff --git a/selfdrive/car/honda/hondacan.py b/selfdrive/car/honda/hondacan.py index 411d0c881..38d099fdc 100644 --- a/selfdrive/car/honda/hondacan.py +++ b/selfdrive/car/honda/hondacan.py @@ -54,7 +54,7 @@ def create_ui_commands(packer, pcm_speed, hud, car_fingerprint, is_metric, idx, 'PCM_SPEED': pcm_speed * CV.MS_TO_KPH, 'PCM_GAS': hud.pcm_accel, 'CRUISE_SPEED': hud.v_cruise, - 'ENABLE_MINI_CAR': hud.mini_car, + 'ENABLE_MINI_CAR': 1, 'HUD_LEAD': hud.car, 'HUD_DISTANCE': 3, # max distance setting on display 'IMPERIAL_UNIT': int(not is_metric), diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 9ffc92e75..b1abeed3d 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -8,12 +8,12 @@ from selfdrive.config import Conversions as CV from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET, get_events from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.car.honda.carstate import CarState, get_can_parser, get_cam_can_parser -from selfdrive.car.honda.values import CruiseButtons, CAR, HONDA_BOSCH, VISUAL_HUD, ECU, ECU_FINGERPRINT, FINGERPRINTS +from selfdrive.car.honda.values import CruiseButtons, CAR, HONDA_BOSCH, ECU, ECU_FINGERPRINT, FINGERPRINTS from selfdrive.car import STD_CARGO_KG, CivicParams, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint -from selfdrive.controls.lib.planner import _A_CRUISE_MAX_V +from selfdrive.controls.lib.planner import _A_CRUISE_MAX_V_FOLLOWING from selfdrive.car.interfaces import CarInterfaceBase -A_ACC_MAX = max(_A_CRUISE_MAX_V) +A_ACC_MAX = max(_A_CRUISE_MAX_V_FOLLOWING) ButtonType = car.CarState.ButtonEvent.Type GearShifter = car.CarState.GearShifter @@ -91,7 +91,7 @@ class CarInterface(CarInterfaceBase): self.CC = None if CarController is not None: - self.CC = CarController(self.cp.dbc_name) + self.CC = CarController(self.cp.dbc_name, CP) if self.CS.CP.carFingerprint == CAR.ACURA_ILX: self.compute_gb = get_compute_gb_acura() @@ -131,22 +131,21 @@ class CarInterface(CarInterfaceBase): return float(max(max_accel, a_target / A_ACC_MAX)) * min(speedLimiter, accelLimiter) @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): ret = car.CarParams.new_message() ret.carName = "honda" ret.carFingerprint = candidate - ret.carVin = vin ret.isPandaBlack = has_relay if candidate in HONDA_BOSCH: - ret.safetyModel = car.CarParams.SafetyModel.hondaBosch + ret.safetyModel = car.CarParams.SafetyModel.hondaBoschHarness if has_relay else car.CarParams.SafetyModel.hondaBoschGiraffe rdr_bus = 0 if has_relay else 2 ret.enableCamera = is_ecu_disconnected(fingerprint[rdr_bus], FINGERPRINTS, ECU_FINGERPRINT, candidate, ECU.CAM) or has_relay ret.radarOffCan = True ret.openpilotLongitudinalControl = False else: - ret.safetyModel = car.CarParams.SafetyModel.honda + ret.safetyModel = car.CarParams.SafetyModel.hondaNidec ret.enableCamera = is_ecu_disconnected(fingerprint[0], FINGERPRINTS, ECU_FINGERPRINT, candidate, ECU.CAM) or has_relay ret.enableGasInterceptor = 0x201 in fingerprint[0] ret.openpilotLongitudinalControl = ret.enableCamera @@ -165,6 +164,11 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kf = 0.00006 # conservative feed-forward + eps_modified = False + for fw in car_fw: + if fw.ecu == "eps" and b"," in fw.fwVersion: + eps_modified = True + if candidate in [CAR.CIVIC, CAR.CIVIC_BOSCH]: stop_and_go = True ret.mass = CivicParams.MASS @@ -173,7 +177,8 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 15.38 # 10.93 is end-to-end spec tire_stiffness_factor = 1. - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.4], [0.12]] if eps_modified else [[0.8], [0.24]] + ret.lateralTuning.pid.kf = 0.00006 ret.longitudinalTuning.kpBP = [0., 5., 35.] ret.longitudinalTuning.kpV = [3.6, 2.4, 1.5] ret.longitudinalTuning.kiBP = [0., 35.] @@ -575,8 +580,6 @@ class CarInterface(CarInterfaceBase): else: hud_v_cruise = 255 - hud_alert = VISUAL_HUD[c.hudControl.visualAlert.raw] - pcm_accel = int(clip(c.cruiseControl.accelOverride, 0, 1) * 0xc6) can_sends = self.CC.update(c.enabled, self.CS, self.frame, @@ -588,7 +591,7 @@ class CarInterface(CarInterfaceBase): hud_v_cruise, c.hudControl.lanesVisible, hud_show_car=c.hudControl.leadVisible, - hud_alert=hud_alert) + hud_alert=c.hudControl.visualAlert) self.frame += 1 return can_sends diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 90dfd4eab..4301128bc 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -1,6 +1,7 @@ from cereal import car from selfdrive.car import dbc_dict +Ecu = car.CarParams.Ecu VisualAlert = car.CarControl.HUDControl.VisualAlert # Car button codes @@ -10,28 +11,18 @@ class CruiseButtons: CANCEL = 2 MAIN = 1 -class AH: - #[alert_idx, value] - # See dbc files for info on values" - NONE = [0, 0] - FCW = [1, 1] - STEER = [2, 1] - BRAKE_PRESSED = [3, 10] - GEAR_NOT_D = [4, 6] - SEATBELT = [5, 5] - SPEED_TOO_HIGH = [6, 8] - +# See dbc files for info on values" VISUAL_HUD = { - VisualAlert.none: AH.NONE, - VisualAlert.fcw: AH.FCW, - VisualAlert.steerRequired: AH.STEER, - VisualAlert.brakePressed: AH.BRAKE_PRESSED, - VisualAlert.wrongGear: AH.GEAR_NOT_D, - VisualAlert.seatbeltUnbuckled: AH.SEATBELT, - VisualAlert.speedTooHigh: AH.SPEED_TOO_HIGH} + VisualAlert.none: 0, + VisualAlert.fcw: 1, + VisualAlert.steerRequired: 1, + VisualAlert.brakePressed: 10, + VisualAlert.wrongGear: 6, + VisualAlert.seatbeltUnbuckled: 5, + VisualAlert.speedTooHigh: 8} class ECU: - CAM = 0 + CAM = Ecu.fwdCamera class CAR: ACCORD = "HONDA ACCORD 2018 SPORT 2T" @@ -81,9 +72,8 @@ FINGERPRINTS = { CAR.CRV: [{ 57: 3, 145: 8, 316: 8, 340: 8, 342: 6, 344: 8, 380: 8, 398: 3, 399: 6, 401: 8, 404: 4, 420: 8, 422: 8, 426: 8, 432: 7, 464: 8, 474: 5, 476: 4, 487: 4, 490: 8, 493: 3, 506: 8, 507: 1, 512: 6, 513: 6, 542: 7, 545: 4, 597: 8, 660: 8, 661: 4, 773: 7, 777: 8, 780: 8, 800: 8, 804: 8, 808: 8, 829: 5, 882: 2, 884: 7, 888: 8, 891: 8, 892: 8, 923: 2, 929: 8, 983: 8, 985: 3, 1024: 5, 1027: 5, 1029: 8, 1033: 5, 1036: 8, 1039: 8, 1057: 5, 1064: 7, 1108: 8, 1125: 8, 1296: 8, 1365: 5, 1424: 5, 1600: 5, 1601: 8, }], - # msg 1115 has seen with len 2 and 4, so ignore it CAR.CRV_5G: [{ - 57: 3, 148: 8, 199: 4, 228: 5, 231: 5, 232: 7, 304: 8, 330: 8, 340: 8, 344: 8, 380: 8, 399: 7, 401: 8, 420: 8, 423: 2, 427: 3, 428: 8, 432: 7, 441: 5, 446: 3, 450: 8, 464: 8, 467: 2, 469: 3, 470: 2, 474: 8, 476: 7, 477: 8, 479: 8, 490: 8, 493: 5, 495: 8, 507: 1, 545: 6, 597: 8, 661: 4, 662: 4, 773: 7, 777: 8, 780: 8, 795: 8, 800: 8, 804: 8, 806: 8, 808: 8, 814: 4, 815: 8, 817: 4, 825: 4, 829: 5, 862: 8, 881: 8, 882: 4, 884: 8, 888: 8, 891: 8, 927: 8, 918: 7, 929: 8, 983: 8, 985: 3, 1024: 5, 1027: 5, 1029: 8, 1036: 8, 1039: 8, 1064: 7, 1108: 8, 1092: 1, 1125: 8, 1127: 2, 1296: 8, 1302: 8, 1322: 5, 1361: 5, 1365: 5, 1424: 5, 1600: 5, 1601: 8, 1618: 5, 1633: 8, 1670: 5 + 57: 3, 148: 8, 199: 4, 228: 5, 231: 5, 232: 7, 304: 8, 330: 8, 340: 8, 344: 8, 380: 8, 399: 7, 401: 8, 420: 8, 423: 2, 427: 3, 428: 8, 432: 7, 441: 5, 446: 3, 450: 8, 464: 8, 467: 2, 469: 3, 470: 2, 474: 8, 476: 7, 477: 8, 479: 8, 490: 8, 493: 5, 495: 8, 507: 1, 545: 6, 597: 8, 661: 4, 662: 4, 773: 7, 777: 8, 780: 8, 795: 8, 800: 8, 804: 8, 806: 8, 808: 8, 814: 4, 815: 8, 817: 4, 825: 4, 829: 5, 862: 8, 881: 8, 882: 4, 884: 8, 888: 8, 891: 8, 927: 8, 918: 7, 929: 8, 983: 8, 985: 3, 1024: 5, 1027: 5, 1029: 8, 1036: 8, 1039: 8, 1064: 7, 1108: 8, 1092: 1, 1115: 2, 1125: 8, 1127: 2, 1296: 8, 1302: 8, 1322: 5, 1361: 5, 1365: 5, 1424: 5, 1600: 5, 1601: 8, 1618: 5, 1633: 8, 1670: 5 }], CAR.CRV_HYBRID: [{ 57: 3, 148: 8, 228: 5, 304: 8, 330: 8, 344: 8, 380: 8, 387: 8, 388: 8, 399: 7, 408: 6, 415: 6, 419: 8, 420: 8, 427: 3, 428: 8, 432: 7, 441: 5, 450: 8, 464: 8, 477: 8, 479: 8, 490: 8, 495: 8, 525: 8, 531: 8, 545: 6, 662: 4, 773: 7, 777: 8, 780: 8, 804: 8, 806: 8, 808: 8, 814: 4, 829: 5, 833: 6, 862: 8, 884: 8, 891: 8, 927: 8, 929: 8, 930: 8, 931: 8, 1302: 8, 1361: 5, 1365: 5, 1600: 5, 1601: 8, 1626: 5, 1627: 5 @@ -130,6 +120,36 @@ for c in FINGERPRINTS: for d in DIAG_MSGS: FINGERPRINTS[c][f][d] = DIAG_MSGS[d] +# TODO: Figure out what is relevant +FW_VERSIONS = { + CAR.CIVIC: { + (Ecu.unknown, 0x18da10f1, None): [b'37805-5AA-L660\x00\x00'], + (Ecu.unknown, 0x18da1ef1, None): [b'28101-5CG-A050\x00\x00'], + (Ecu.unknown, 0x18da28f1, None): [b'57114-TBA-A550\x00\x00'], + (Ecu.eps, 0x18da30f1, None): [b'39990-TBA-A030\x00\x00', b'39990-TBA,A030\x00\x00'], + (Ecu.unknown, 0x18da53f1, None): [b'77959-TBA-A030\x00\x00'], + (Ecu.unknown, 0x18da60f1, None): [b'78109-TBC-A310\x00\x00'], + (Ecu.unknown, 0x18dab0f1, None): [b'36161-TBC-A030\x00\x00'], + (Ecu.unknown, 0x18daeff1, None): [b'38897-TBA-A020\x00\x00'], + + }, + CAR.ACCORD: { + (Ecu.unknown, 0x18da10f1, None): [b'37805-6B2-A650\x00\x00'], + (Ecu.unknown, 0x18da0bf1, None): [b'54008-TVC-A910\x00\x00'], + (Ecu.unknown, 0x18da1ef1, None): [b'28102-6B8-A560\x00\x00'], + (Ecu.unknown, 0x18da2bf1, None): [b'46114-TVA-A060\x00\x00'], + (Ecu.unknown, 0x18da28f1, None): [b'57114-TVA-C050\x00\x00'], + (Ecu.eps, 0x18da30f1, None): [b'39990-TVA-A150\x00\x00'], + (Ecu.unknown, 0x18da3af1, None): [b'39390-TVA-A020\x00\x00'], + (Ecu.unknown, 0x18da53f1, None): [b'77959-TVA-A460\x00\x00'], + (Ecu.unknown, 0x18da60f1, None): [b'78109-TVC-A210\x00\x00'], + (Ecu.unknown, 0x18da61f1, None): [b'78209-TVA-A010\x00\x00'], + (Ecu.unknown, 0x18dab0f1, None): [b'36802-TVA-A160\x00\x00'], + (Ecu.unknown, 0x18dab5f1, None): [b'36161-TVA-A060\x00\x00'], + (Ecu.unknown, 0x18daeff1, None): [b'38897-TVA-A010\x00\x00'], + } +} + DBC = { CAR.ACCORD: dbc_dict('honda_accord_s2t_2018_can_generated', None), CAR.ACCORD_15: dbc_dict('honda_accord_lx15t_2018_can_generated', None), diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 39d04d3bc..50c31af3d 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -38,13 +38,12 @@ class CarInterface(CarInterfaceBase): return float(accel) / 3.0 @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): ret = car.CarParams.new_message() ret.carName = "hyundai" ret.carFingerprint = candidate - ret.carVin = vin ret.isPandaBlack = has_relay ret.radarOffCan = True ret.safetyModel = car.CarParams.SafetyModel.hyundai diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 7a3275278..ca1972a16 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -18,7 +18,7 @@ class CarInterfaceBase(): raise NotImplementedError @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): raise NotImplementedError # returns a car.CarState, pass in car.CarControl diff --git a/selfdrive/car/isotp_parallel_query.py b/selfdrive/car/isotp_parallel_query.py new file mode 100644 index 000000000..cda36d50f --- /dev/null +++ b/selfdrive/car/isotp_parallel_query.py @@ -0,0 +1,128 @@ +import time +from collections import defaultdict +from functools import partial + +import cereal.messaging as messaging +from selfdrive.swaglog import cloudlog +from selfdrive.boardd.boardd import can_list_to_can_capnp +from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_addr_for_tx_addr + + +class IsoTpParallelQuery(): + def __init__(self, sendcan, logcan, bus, addrs, request, response, functional_addr=False, debug=False): + self.sendcan = sendcan + self.logcan = logcan + self.bus = bus + self.request = request + self.response = response + self.debug = debug + self.functional_addr = functional_addr + + self.real_addrs = [] + for a in addrs: + if isinstance(a, tuple): + self.real_addrs.append(a) + else: + self.real_addrs.append((a, None)) + + self.msg_addrs = {tx_addr: get_rx_addr_for_tx_addr(tx_addr[0]) for tx_addr in self.real_addrs} + self.msg_buffer = defaultdict(list) + + def rx(self): + """Drain can socket and sort messages into buffers based on address""" + can_packets = messaging.drain_sock(self.logcan, wait_for_one=True) + + for packet in can_packets: + for msg in packet.can: + if msg.src == self.bus: + if self.functional_addr: + if (0x7E8 <= msg.address <= 0x7EF) or (0x18DAF100 <= msg.address <= 0x18DAF1FF): + fn_addr = next(a for a in FUNCTIONAL_ADDRS if msg.address - a <= 32) + self.msg_buffer[fn_addr].append((msg.address, msg.busTime, msg.dat, msg.src)) + elif msg.address in self.msg_addrs.values(): + self.msg_buffer[msg.address].append((msg.address, msg.busTime, msg.dat, msg.src)) + + def _can_tx(self, tx_addr, dat, bus): + """Helper function to send single message""" + msg = [tx_addr, 0, dat, bus] + self.sendcan.send(can_list_to_can_capnp([msg], msgtype='sendcan')) + + def _can_rx(self, addr, sub_addr=None): + """Helper function to retrieve message with specified address and subadress from buffer""" + keep_msgs = [] + + if sub_addr is None: + msgs = self.msg_buffer[addr] + else: + # Filter based on subadress + msgs = [] + for m in self.msg_buffer[addr]: + first_byte = m[2][0] + if first_byte == sub_addr: + msgs.append(m) + else: + keep_msgs.append(m) + + self.msg_buffer[addr] = keep_msgs + return msgs + + def _drain_rx(self): + messaging.drain_sock(self.logcan) + self.msg_buffer = defaultdict(list) + + def get_data(self, timeout): + self._drain_rx() + + # Create message objects + msgs = {} + request_counter = {} + request_done = {} + for tx_addr, rx_addr in self.msg_addrs.items(): + # rx_addr not set when using functional tx addr + id_addr = rx_addr or tx_addr[0] + sub_addr = tx_addr[1] + + can_client = CanClient(self._can_tx, partial(self._can_rx, id_addr, sub_addr=sub_addr), tx_addr[0], rx_addr, self.bus, sub_addr=sub_addr, debug=self.debug) + + max_len = 8 if sub_addr is None else 7 + + msg = IsoTpMessage(can_client, timeout=0, max_len=max_len, debug=self.debug) + msg.send(self.request[0]) + + msgs[tx_addr] = msg + request_counter[tx_addr] = 0 + request_done[tx_addr] = False + + results = {} + start_time = time.time() + while True: + self.rx() + + if all(request_done.values()): + break + + for tx_addr, msg in msgs.items(): + dat = msg.recv() + + if not dat: + continue + + counter = request_counter[tx_addr] + expected_response = self.response[counter] + response_valid = dat[:len(expected_response)] == expected_response + + if response_valid: + if counter + 1 < len(self.request): + msg.send(self.request[counter + 1]) + request_counter[tx_addr] += 1 + else: + results[tx_addr] = dat[len(expected_response):] + request_done[tx_addr] = True + else: + request_done[tx_addr] = True + cloudlog.warning(f"iso-tp query bad response: 0x{bytes.hex(dat)}") + + if time.time() - start_time > timeout: + break + + return results diff --git a/selfdrive/car/mock/interface.py b/selfdrive/car/mock/interface.py index 30df9f2fd..09c5b5c61 100755 --- a/selfdrive/car/mock/interface.py +++ b/selfdrive/car/mock/interface.py @@ -34,7 +34,7 @@ class CarInterface(CarInterfaceBase): return accel @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): ret = car.CarParams.new_message() diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index cd102766d..d4388727b 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -35,13 +35,12 @@ class CarInterface(CarInterfaceBase): return float(accel) / 4.0 @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): ret = car.CarParams.new_message() ret.carName = "subaru" ret.radarOffCan = True ret.carFingerprint = candidate - ret.carVin = vin ret.isPandaBlack = has_relay ret.safetyModel = car.CarParams.SafetyModel.subaru diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 3c6772228..83f499f45 100755 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -37,13 +37,12 @@ class CarInterface(CarInterfaceBase): return float(accel) / 3.0 @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): ret = car.CarParams.new_message() ret.carName = "toyota" ret.carFingerprint = candidate - ret.carVin = vin ret.isPandaBlack = has_relay ret.safetyModel = car.CarParams.SafetyModel.toyota diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 5c7c49a69..f8edbf48b 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -1,4 +1,6 @@ from selfdrive.car import dbc_dict +from cereal import car +Ecu = car.CarParams.Ecu # Steer torque limits class SteerLimitParams: @@ -31,9 +33,9 @@ class CAR: class ECU: - CAM = 0 # camera - DSU = 1 # driving support unit - APGS = 2 # advanced parking guidance system + CAM = Ecu.fwdCamera # camera + DSU = Ecu.dsu # driving support unit + APGS = Ecu.apgs # advanced parking guidance system # addr: (ecu, cars, bus, 1/freq*100, vl) @@ -215,6 +217,32 @@ FINGERPRINTS = { }] } +FW_VERSIONS = { + CAR.COROLLA_TSS2: { + (Ecu.engine, 0x700, None): [b'\x01896630ZG5000\x00\x00\x00\x00'], + (Ecu.eps, 0x7a1, None): [b'\x018965B12350\x00\x00\x00\x00\x00\x00'], + (Ecu.esp, 0x7b0, None): [b'\x01F152602280\x00\x00\x00\x00\x00\x00'], + (Ecu.fwdRadar, 0x750, 0xf): [b'\x018821F3301100\x00\x00\x00\x00'], + (Ecu.fwdCamera, 0x750, 0x6d): [b'\x028646F1201200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00'], + }, + CAR.PRIUS: { + (Ecu.engine, 0x700, None): [b'\x03896634759200\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4701003\x00\x00\x00\x00'], + (Ecu.eps, 0x7a1, None): [b'8965B47023\x00\x00\x00\x00\x00\x00'], + (Ecu.esp, 0x7b0, None): [b'F152647416\x00\x00\x00\x00\x00\x00'], + (Ecu.dsu, 0x791, None): [b'881514703100\x00\x00\x00\x00'], + (Ecu.fwdRadar, 0x750, 0xf): [b'8821F4702100\x00\x00\x00\x00'], + (Ecu.fwdCamera, 0x750, 0x6d): [b'8646F4702100\x00\x00\x00\x00'], + }, + CAR.RAV4: { + (Ecu.engine, 0x7e0, None): [b'\x02342Q2100\x00\x00\x00\x00\x00\x00\x00\x0054213000\x00\x00\x00\x00\x00\x00\x00\x00'], + (Ecu.eps, 0x7a1, None): [b'8965B42083\x00\x00\x00\x00\x00\x00'], + (Ecu.esp, 0x7b0, None): [b'F15260R103\x00\x00\x00\x00\x00\x00'], + (Ecu.dsu, 0x791, None): [b'881514201400\x00\x00\x00\x00'], + (Ecu.fwdRadar, 0x750, 0xf): [b'8821F4702100\x00\x00\x00\x00'], + (Ecu.fwdCamera, 0x750, 0x6d): [b'8646F4202100\x00\x00\x00\x00'], + } +} + STEER_THRESHOLD = 100 DBC = { diff --git a/selfdrive/car/vin.py b/selfdrive/car/vin.py index 605e22d50..648f41651 100755 --- a/selfdrive/car/vin.py +++ b/selfdrive/car/vin.py @@ -1,104 +1,33 @@ #!/usr/bin/env python3 -import cereal.messaging as messaging -from selfdrive.boardd.boardd import can_list_to_can_capnp +import traceback +import cereal.messaging as messaging +from panda.python.uds import FUNCTIONAL_ADDRS +from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery +from selfdrive.swaglog import cloudlog + +VIN_REQUEST = b'\x09\x02' +VIN_RESPONSE = b'\x49\x02\x01' VIN_UNKNOWN = "0" * 17 -# sanity checks on response messages from vin query -def is_vin_response_valid(can_dat, step, cnt): - if len(can_dat) != 8: - # ISO-TP meesages are all 8 bytes - return False - if step == 0: - # VIN does not fit in a single message and it's 20 bytes of data - if can_dat[0] != 0x10 or can_dat[1] != 0x14: - return False +def get_vin(logcan, sendcan, bus, timeout=0.1, retry=5, debug=False): + for i in range(retry): + try: + query = IsoTpParallelQuery(sendcan, logcan, bus, FUNCTIONAL_ADDRS, [VIN_REQUEST], [VIN_RESPONSE], functional_addr=True, debug=debug) + for addr, vin in query.get_data(timeout).items(): + return addr[0], vin.decode() + print(f"vin query retry ({i+1}) ...") + except Exception: + cloudlog.warning(f"VIN query exception: {traceback.format_exc()}") - if step == 1 and cnt == 0: - # first response after a CONTINUE query is sent - if can_dat[0] != 0x21: - return False - - if step == 1 and cnt == 1: - # second response after a CONTINUE query is sent - if can_dat[0] != 0x22: - return False - - return True - - -class VinQuery(): - def __init__(self, bus): - self.bus = bus - # works on standard 11-bit addresses for diagnostic. Tested on Toyota and Subaru; - # Honda uses the extended 29-bit addresses, and unfortunately only works from OBDII - self.query_ext_msgs = [[0x18DB33F1, 0, b'\x02\x09\x02'.ljust(8, b"\x00"), bus], - [0x18DA10f1, 0, b'\x30'.ljust(8, b"\x00"), bus]] - self.query_nor_msgs = [[0x7df, 0, b'\x02\x09\x02'.ljust(8, b"\x00"), bus], - [0x7e0, 0, b'\x30'.ljust(8, b"\x00"), bus]] - - self.cnts = [1, 2] # number of messages to wait for at each iteration - self.step = 0 - self.cnt = 0 - self.responded = False - self.never_responded = True - self.dat = b"" - self.got_vin = False - self.vin = VIN_UNKNOWN - - def check_response(self, msg): - # have we got a VIN query response? - if msg.src == self.bus and msg.address in [0x18daf110, 0x7e8]: - self.never_responded = False - # basic sanity checks on ISO-TP response - if is_vin_response_valid(msg.dat, self.step, self.cnt): - self.dat += bytes(msg.dat[2:]) if self.step == 0 else bytes(msg.dat[1:]) - self.cnt += 1 - if self.cnt == self.cnts[self.step]: - self.responded = True - self.step += 1 - if self.step == len(self.cnts): - self.got_vin = True - - def send_query(self, sendcan): - # keep sending VIN query if ECU isn't responsing. - # sendcan is probably not ready due to the zmq slow joiner syndrome - if self.never_responded or (self.responded and not self.got_vin): - sendcan.send(can_list_to_can_capnp([self.query_ext_msgs[self.step]], msgtype='sendcan')) - sendcan.send(can_list_to_can_capnp([self.query_nor_msgs[self.step]], msgtype='sendcan')) - self.responded = False - self.cnt = 0 - - def get_vin(self): - if self.got_vin: - try: - self.vin = self.dat[3:].decode('utf8') - except UnicodeDecodeError: - pass # have seen unexpected non-unicode characters - return self.vin - - -def get_vin(logcan, sendcan, bus, query_time=1.): - vin_query = VinQuery(bus) - frame = 0 - - # 1s max of VIN query time - while frame < query_time * 100 and not vin_query.got_vin: - a = messaging.get_one_can(logcan) - - for can in a.can: - vin_query.check_response(can) - if vin_query.got_vin: - break - - vin_query.send_query(sendcan) - frame += 1 - - return vin_query.get_vin() + return 0, VIN_UNKNOWN if __name__ == "__main__": - logcan = messaging.sub_sock('can') + import time sendcan = messaging.pub_sock('sendcan') - print(get_vin(logcan, sendcan, 0)) + logcan = messaging.sub_sock('can') + time.sleep(1) + addr, vin = get_vin(logcan, sendcan, 1, debug=False) + print(hex(addr), vin) diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index 14c58b367..0e8b5206d 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -42,12 +42,11 @@ class CarInterface(CarInterfaceBase): return float(accel) / 4.0 @staticmethod - def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False): + def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): ret = car.CarParams.new_message() ret.carFingerprint = candidate ret.isPandaBlack = has_relay - ret.carVin = vin if candidate == CAR.GOLF: # Set common MQB parameters that will apply globally diff --git a/selfdrive/clocksd/.gitignore b/selfdrive/clocksd/.gitignore new file mode 100644 index 000000000..a6d841d65 --- /dev/null +++ b/selfdrive/clocksd/.gitignore @@ -0,0 +1 @@ +clocksd diff --git a/selfdrive/clocksd/SConscript b/selfdrive/clocksd/SConscript new file mode 100644 index 000000000..63c508c4f --- /dev/null +++ b/selfdrive/clocksd/SConscript @@ -0,0 +1,2 @@ +Import('env', 'common', 'messaging') +env.Program('clocksd.cc', LIBS=['diag', 'time_genoff', common, messaging, 'capnp', 'zmq', 'kj']) \ No newline at end of file diff --git a/selfdrive/clocksd/clocksd.cc b/selfdrive/clocksd/clocksd.cc new file mode 100644 index 000000000..0dba6259e --- /dev/null +++ b/selfdrive/clocksd/clocksd.cc @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include +#include +#include "messaging.hpp" +#include "common/timing.h" +#include "cereal/gen/cpp/log.capnp.h" + +namespace { + int64_t arm_cntpct() { + int64_t v; + asm volatile("mrs %0, cntpct_el0" : "=r"(v)); + return v; + } +} + +int main() { + setpriority(PRIO_PROCESS, 0, -13); + + int err = 0; + Context *context = Context::create(); + + PubSocket* clock_publisher = PubSocket::create(context, "clocks"); + assert(clock_publisher != NULL); + + int timerfd = timerfd_create(CLOCK_BOOTTIME, 0); + assert(timerfd >= 0); + + struct itimerspec spec = {0}; + spec.it_interval.tv_sec = 1; + spec.it_interval.tv_nsec = 0; + spec.it_value.tv_sec = 1; + spec.it_value.tv_nsec = 0; + + err = timerfd_settime(timerfd, 0, &spec, 0); + assert(err == 0); + + uint64_t expirations = 0; + while ((err = read(timerfd, &expirations, sizeof(expirations)))) { + if (err < 0) break; + + uint64_t boottime = nanos_since_boot(); + uint64_t monotonic = nanos_monotonic(); + uint64_t monotonic_raw = nanos_monotonic_raw(); + uint64_t wall_time = nanos_since_epoch(); + + uint64_t modem_uptime_v = arm_cntpct() / 19200ULL; // 19.2 mhz clock + + capnp::MallocMessageBuilder msg; + cereal::Event::Builder event = msg.initRoot(); + event.setLogMonoTime(boottime); + auto clocks = event.initClocks(); + + clocks.setBootTimeNanos(boottime); + clocks.setMonotonicNanos(monotonic); + clocks.setMonotonicRawNanos(monotonic_raw); + clocks.setWallTimeNanos(wall_time); + clocks.setModemUptimeMillis(modem_uptime_v); + + auto words = capnp::messageToFlatArray(msg); + auto bytes = words.asBytes(); + clock_publisher->send((char*)bytes.begin(), bytes.size()); + } + + close(timerfd); + delete clock_publisher; + + return 0; +} \ No newline at end of file diff --git a/selfdrive/common/framebuffer.cc b/selfdrive/common/framebuffer.cc index 757c2a1ea..788b81299 100644 --- a/selfdrive/common/framebuffer.cc +++ b/selfdrive/common/framebuffer.cc @@ -1,4 +1,3 @@ - #include #include #include @@ -39,7 +38,6 @@ extern "C" void framebuffer_set_power(FramebufferState *s, int mode) { extern "C" FramebufferState* framebuffer_init( const char* name, int32_t layer, int alpha, - EGLDisplay *out_display, EGLSurface *out_surface, int *out_w, int *out_h) { status_t status; int success; @@ -131,11 +129,14 @@ extern "C" FramebufferState* framebuffer_init( const char brightness_level[] = BACKLIGHT_LEVEL; write(brightness_fd, brightness_level, strlen(brightness_level)); - - if (out_display) *out_display = s->display; - if (out_surface) *out_surface = s->surface; if (out_w) *out_w = w; if (out_h) *out_h = h; return s; } + +extern "C" void framebuffer_swap(FramebufferState *s) { + eglSwapBuffers(s->display, s->surface); + assert(glGetError() == GL_NO_ERROR); +} + diff --git a/selfdrive/common/framebuffer.h b/selfdrive/common/framebuffer.h index 6091eebce..52c60d8ec 100644 --- a/selfdrive/common/framebuffer.h +++ b/selfdrive/common/framebuffer.h @@ -11,10 +11,10 @@ typedef struct FramebufferState FramebufferState; FramebufferState* framebuffer_init( const char* name, int32_t layer, int alpha, - EGLDisplay *out_display, EGLSurface *out_surface, int *out_w, int *out_h); void framebuffer_set_power(FramebufferState *s, int mode); +void framebuffer_swap(FramebufferState *s); /* Display power modes */ enum { diff --git a/selfdrive/common/params.cc b/selfdrive/common/params.cc index 6e0e8b186..79bc5d911 100644 --- a/selfdrive/common/params.cc +++ b/selfdrive/common/params.cc @@ -243,10 +243,6 @@ int read_db_value(const char* params_path, const char* key, char** value, goto cleanup; } - // Remove one for null byte. - if (value_sz != NULL) { - *value_sz -= 1; - } result = 0; cleanup: diff --git a/selfdrive/common/util.c b/selfdrive/common/util.c index 01b8a0b6d..9bdb23f99 100644 --- a/selfdrive/common/util.c +++ b/selfdrive/common/util.c @@ -19,7 +19,7 @@ void* read_file(const char* path, size_t* out_len) { long f_len = ftell(f); rewind(f); - char* buf = calloc(f_len + 1, 1); + char* buf = (char*)calloc(f_len, 1); assert(buf); size_t num_read = fread(buf, f_len, 1, f); @@ -31,7 +31,7 @@ void* read_file(const char* path, size_t* out_len) { } if (out_len) { - *out_len = f_len + 1; + *out_len = f_len; } return buf; @@ -54,6 +54,8 @@ int set_realtime_priority(int level) { memset(&sa, 0, sizeof(sa)); sa.sched_priority = level; return sched_setscheduler(tid, SCHED_FIFO, &sa); +#else + return -1; #endif } diff --git a/selfdrive/common/version.h b/selfdrive/common/version.h index 6f1cf7ff0..d61e42c49 100644 --- a/selfdrive/common/version.h +++ b/selfdrive/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.7-release" +#define COMMA_VERSION "0.7.1-release" diff --git a/selfdrive/common/visionimg.h b/selfdrive/common/visionimg.h index 74b0f3137..1cc0cb0ac 100644 --- a/selfdrive/common/visionimg.h +++ b/selfdrive/common/visionimg.h @@ -1,13 +1,12 @@ #ifndef VISIONIMG_H #define VISIONIMG_H -#ifdef QCOM +#include "common/visionbuf.h" + #include #include #include -#endif - -#include "common/visionbuf.h" +#undef Status #ifdef __cplusplus extern "C" { @@ -26,11 +25,9 @@ typedef struct VisionImg { void visionimg_compute_aligned_width_and_height(int width, int height, int *aligned_w, int *aligned_h); VisionImg visionimg_alloc_rgb24(int width, int height, VisionBuf *out_buf); -#ifdef QCOM EGLClientBuffer visionimg_to_egl(const VisionImg *img, void **pph); GLuint visionimg_to_gl(const VisionImg *img, EGLImageKHR *pkhr, void **pph); void visionimg_destroy_gl(EGLImageKHR khr, void *ph); -#endif #ifdef __cplusplus } // extern "C" diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index ad39b19b8..3cc1de90e 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -67,7 +67,7 @@ def events_to_bytes(events): return ret -def data_sample(CI, CC, sm, can_sock, driver_status, state, mismatch_counter, params): +def data_sample(CI, CC, sm, can_sock, driver_status, state, mismatch_counter, can_error_counter, params): """Receive data from sockets and create events for battery, temperature and disk space""" # Update carstate from CAN and create events @@ -82,6 +82,7 @@ def data_sample(CI, CC, sm, can_sock, driver_status, state, mismatch_counter, pa # Check for CAN timeout if not can_strs: + can_error_counter += 1 events.append(create_event('canError', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE])) overtemp = sm['thermal'].thermalStatus >= ThermalStatus.red @@ -147,7 +148,7 @@ def data_sample(CI, CC, sm, can_sock, driver_status, state, mismatch_counter, pa if driver_status.terminal_alert_cnt >= MAX_TERMINAL_ALERTS or driver_status.terminal_time >= MAX_TERMINAL_DURATION: events.append(create_event("tooDistracted", [ET.NO_ENTRY])) - return CS, events, cal_perc, mismatch_counter + return CS, events, cal_perc, mismatch_counter, can_error_counter def state_transition(frame, CS, CP, state, events, soft_disable_timer, v_cruise_kph, AM): @@ -319,7 +320,7 @@ def state_control(frame, rcv_frame, plan, path_plan, CS, CP, state, events, v_cr def data_send(sm, pm, CS, CI, CP, VM, state, events, actuators, v_cruise_kph, rk, AM, driver_status, LaC, LoC, read_only, start_time, v_acc, a_acc, lac_log, events_prev, - last_blinker_frame, is_ldw_enabled): + last_blinker_frame, is_ldw_enabled, can_error_counter): """Send actuators and hud commands to the car, send controlsstate and MPC logging""" CC = car.CarControl.new_message() @@ -416,6 +417,7 @@ def data_send(sm, pm, CS, CI, CP, VM, state, events, actuators, v_cruise_kph, rk "startMonoTime": int(start_time * 1e9), "mapValid": sm['plan'].mapValid, "forceDecel": bool(force_decel), + "canErrorCounter": can_error_counter, } if CP.lateralTuning.which() == 'pid': @@ -534,6 +536,7 @@ def controlsd_thread(sm=None, pm=None, can_sock=None): v_cruise_kph = 255 v_cruise_kph_last = 0 mismatch_counter = 0 + can_error_counter = 0 last_blinker_frame = 0 events_prev = [] @@ -557,11 +560,13 @@ def controlsd_thread(sm=None, pm=None, can_sock=None): prof.checkpoint("Ratekeeper", ignore=True) # Sample data and compute car events - CS, events, cal_perc, mismatch_counter = data_sample(CI, CC, sm, can_sock, driver_status, state, mismatch_counter, params) + CS, events, cal_perc, mismatch_counter, can_error_counter = data_sample(CI, CC, sm, can_sock, driver_status, state, mismatch_counter, can_error_counter, params) prof.checkpoint("Sample") # Create alerts - if not sm.all_alive_and_valid(): + if not sm.alive['plan'] and sm.alive['pathPlan']: # only plan not being received: radar not communicating + events.append(create_event('radarCommIssue', [ET.NO_ENTRY, ET.SOFT_DISABLE])) + elif not sm.all_alive_and_valid(): events.append(create_event('commIssue', [ET.NO_ENTRY, ET.SOFT_DISABLE])) if not sm['pathPlan'].mpcSolutionValid: events.append(create_event('plannerError', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE])) @@ -583,6 +588,8 @@ def controlsd_thread(sm=None, pm=None, can_sock=None): events.append(create_event('internetConnectivityNeeded', [ET.NO_ENTRY, ET.PERMANENT])) if community_feature_disallowed: events.append(create_event('communityFeatureDisallowed', [ET.PERMANENT])) + if read_only and not passive: + events.append(create_event('carUnrecognized', [ET.PERMANENT])) # Only allow engagement with brake pressed when stopped behind another stopped car if CS.brakePressed and sm['plan'].vTargetFuture >= STARTING_TARGET_SPEED and not CP.radarOffCan and CS.vEgo < 0.3: @@ -603,7 +610,8 @@ def controlsd_thread(sm=None, pm=None, can_sock=None): # Publish data CC, events_prev = data_send(sm, pm, CS, CI, CP, VM, state, events, actuators, v_cruise_kph, rk, AM, driver_status, LaC, - LoC, read_only, start_time, v_acc, a_acc, lac_log, events_prev, last_blinker_frame, is_ldw_enabled) + LoC, read_only, start_time, v_acc, a_acc, lac_log, events_prev, last_blinker_frame, + is_ldw_enabled, can_error_counter) prof.checkpoint("Sent") rk.monitor_time() diff --git a/selfdrive/controls/lib/alerts.py b/selfdrive/controls/lib/alerts.py index e9295bfef..83d430f07 100644 --- a/selfdrive/controls/lib/alerts.py +++ b/selfdrive/controls/lib/alerts.py @@ -410,6 +410,13 @@ ALERTS = [ AlertStatus.critical, AlertSize.full, Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.), + Alert( + "radarCommIssue", + "TAKE CONTROL IMMEDIATELY", + "Radar Communication Issue", + AlertStatus.critical, AlertSize.full, + Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.), + Alert( "radarCanError", "TAKE CONTROL IMMEDIATELY", @@ -659,6 +666,13 @@ ALERTS = [ AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.), + Alert( + "radarCommIssueNoEntry", + "openpilot Unavailable", + "Radar Communication Issue", + AlertStatus.normal, AlertSize.mid, + Priority.LOW, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.), + Alert( "internetConnectivityNeededNoEntry", "openpilot Unavailable", @@ -739,11 +753,18 @@ ALERTS = [ Alert( "lowMemoryPermanent", - "RAM Memory Critically Low", + "RAM Critically Low", "Reboot your EON", AlertStatus.normal, AlertSize.mid, Priority.LOW_LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2), + Alert( + "carUnrecognizedPermanent", + "Dashcam Mode", + "Car Unrecognized", + AlertStatus.normal, AlertSize.mid, + Priority.LOW_LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2), + Alert( "vehicleModelInvalid", "Vehicle Parameter Identification Failed", diff --git a/selfdrive/controls/lib/driver_monitor.py b/selfdrive/controls/lib/driver_monitor.py index 13a8130b7..753955975 100644 --- a/selfdrive/controls/lib/driver_monitor.py +++ b/selfdrive/controls/lib/driver_monitor.py @@ -20,9 +20,9 @@ _PITCH_WEIGHT = 1.35 # 1.5 # pitch matters a lot more _METRIC_THRESHOLD = 0.4 _METRIC_THRESHOLD_SLACK = 0.55 _METRIC_THRESHOLD_STRICT = 0.4 -_PITCH_POS_ALLOWANCE = 0.04 # 0.08 # rad, to not be too sensitive on positive pitch -_PITCH_NATURAL_OFFSET = 0.12 # 0.1 # people don't seem to look straight when they drive relaxed, rather a bit up -_YAW_NATURAL_OFFSET = 0.08 # people don't seem to look straight when they drive relaxed, rather a bit to the right (center of car) +_PITCH_POS_ALLOWANCE = 0.12 # rad, to not be too sensitive on positive pitch +_PITCH_NATURAL_OFFSET = 0.02 # people don't seem to look straight when they drive relaxed, rather a bit up +_YAW_NATURAL_OFFSET = 0.08 # people don't seem to look straight when they drive relaxed, rather a bit to the right (center of car) _DISTRACTED_FILTER_TS = 0.25 # 0.6Hz @@ -138,13 +138,13 @@ class DriverStatus(): if not self.pose_calibrated: pitch_error = pose.pitch - _PITCH_NATURAL_OFFSET yaw_error = pose.yaw - _YAW_NATURAL_OFFSET - # add positive pitch allowance - if pitch_error > 0.: - pitch_error = max(pitch_error - _PITCH_POS_ALLOWANCE, 0.) else: pitch_error = pose.pitch - self.pose.pitch_offseter.filtered_stat.mean() yaw_error = pose.yaw - self.pose.yaw_offseter.filtered_stat.mean() + # positive pitch allowance + if pitch_error > 0.: + pitch_error = max(pitch_error - _PITCH_POS_ALLOWANCE, 0.) pitch_error *= _PITCH_WEIGHT pose_metric = np.sqrt(yaw_error**2 + pitch_error**2) diff --git a/selfdrive/controls/lib/pathplanner.py b/selfdrive/controls/lib/pathplanner.py index 87e3c520f..8a6e5286f 100644 --- a/selfdrive/controls/lib/pathplanner.py +++ b/selfdrive/controls/lib/pathplanner.py @@ -14,6 +14,9 @@ LaneChangeDirection = log.PathPlan.LaneChangeDirection LOG_MPC = os.environ.get('LOG_MPC', False) +LANE_CHANGE_SPEED_MIN = 45 * CV.MPH_TO_MS +LANE_CHANGE_TIME_MAX = 10. + DESIRES = { LaneChangeDirection.none: { LaneChangeState.off: log.PathPlan.Desire.none, @@ -88,8 +91,9 @@ class PathPlanner(): # Lane change logic lane_change_direction = LaneChangeDirection.none one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker + below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN - if not active or self.lane_change_timer > 10.0: + if not active or self.lane_change_timer > LANE_CHANGE_TIME_MAX: self.lane_change_state = LaneChangeState.off else: if sm['carState'].leftBlinker: @@ -97,23 +101,23 @@ class PathPlanner(): elif sm['carState'].rightBlinker: lane_change_direction = LaneChangeDirection.right - if lane_change_direction == LaneChangeDirection.left: - torque_applied = sm['carState'].steeringTorque > 0 and sm['carState'].steeringPressed - else: - torque_applied = sm['carState'].steeringTorque < 0 and sm['carState'].steeringPressed + torque_applied = sm['carState'].steeringPressed and \ + ((sm['carState'].steeringTorque > 0 and lane_change_direction == LaneChangeDirection.left) or \ + (sm['carState'].steeringTorque < 0 and lane_change_direction == LaneChangeDirection.right)) lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob # State transitions # off - if False: # self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker: + if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed: self.lane_change_state = LaneChangeState.preLaneChange # pre - elif self.lane_change_state == LaneChangeState.preLaneChange and not one_blinker: - self.lane_change_state = LaneChangeState.off - elif self.lane_change_state == LaneChangeState.preLaneChange and torque_applied: - self.lane_change_state = LaneChangeState.laneChangeStarting + elif self.lane_change_state == LaneChangeState.preLaneChange: + if not one_blinker or below_lane_change_speed: + self.lane_change_state = LaneChangeState.off + elif torque_applied: + self.lane_change_state = LaneChangeState.laneChangeStarting # starting elif self.lane_change_state == LaneChangeState.laneChangeStarting and lane_change_prob > 0.5: @@ -121,11 +125,10 @@ class PathPlanner(): # finishing elif self.lane_change_state == LaneChangeState.laneChangeFinishing and lane_change_prob < 0.2: - self.lane_change_state = LaneChangeState.preLaneChange - - # Don't allow starting lane change below 45 mph - if (v_ego < 45 * CV.MPH_TO_MS) and (self.lane_change_state == LaneChangeState.preLaneChange): - self.lane_change_state = LaneChangeState.off + if one_blinker: + self.lane_change_state = LaneChangeState.preLaneChange + else: + self.lane_change_state = LaneChangeState.off if self.lane_change_state in [LaneChangeState.off, LaneChangeState.preLaneChange]: self.lane_change_timer = 0.0 diff --git a/selfdrive/controls/lib/planner.py b/selfdrive/controls/lib/planner.py index 6646e1345..1d6140a81 100755 --- a/selfdrive/controls/lib/planner.py +++ b/selfdrive/controls/lib/planner.py @@ -27,7 +27,8 @@ _A_CRUISE_MIN_BP = [ 0., 5., 10., 20., 40.] # need fast accel at very low speed for stop and go # make sure these accelerations are smaller than mpc limits -_A_CRUISE_MAX_V = [1.6, 1.6, 0.65, .4] +_A_CRUISE_MAX_V = [1.2, 1.2, 0.65, .4] +_A_CRUISE_MAX_V_FOLLOWING = [1.6, 1.6, 0.65, .4] _A_CRUISE_MAX_BP = [0., 6.4, 22.5, 40.] # Lookup table for turns @@ -38,9 +39,13 @@ _A_TOTAL_MAX_BP = [20., 40.] SPEED_PERCENTILE_IDX = 7 -def calc_cruise_accel_limits(v_ego): +def calc_cruise_accel_limits(v_ego, following): a_cruise_min = interp(v_ego, _A_CRUISE_MIN_BP, _A_CRUISE_MIN_V) - a_cruise_max = interp(v_ego, _A_CRUISE_MAX_BP, _A_CRUISE_MAX_V) + + if following: + a_cruise_max = interp(v_ego, _A_CRUISE_MAX_BP, _A_CRUISE_MAX_V_FOLLOWING) + else: + a_cruise_max = interp(v_ego, _A_CRUISE_MAX_BP, _A_CRUISE_MAX_V) return np.vstack([a_cruise_min, a_cruise_max]) @@ -80,6 +85,7 @@ class Planner(): self.path_x = np.arange(192) self.params = Params() + self.first_loop = True def choose_solution(self, v_cruise_setpoint, enabled): if enabled: @@ -122,6 +128,7 @@ class Planner(): lead_2 = sm['radarState'].leadTwo enabled = (long_control_state == LongCtrlState.pid) or (long_control_state == LongCtrlState.stopping) + following = lead_1.status and lead_1.dRel < 45.0 and lead_1.vLeadK > v_ego and lead_1.aLeadK > 0.0 if len(sm['model'].path.poly): path = list(sm['model'].path.poly) @@ -142,8 +149,8 @@ class Planner(): model_speed = MAX_SPEED # Calculate speed for normal cruise control - if enabled: - accel_limits = [float(x) for x in calc_cruise_accel_limits(v_ego)] + if enabled and not self.first_loop: + accel_limits = [float(x) for x in calc_cruise_accel_limits(v_ego, following)] jerk_limits = [min(-0.1, accel_limits[0]), max(0.1, accel_limits[1])] # TODO: make a separate lookup for jerk tuning accel_limits_turns = limit_accel_in_turns(v_ego, sm['carState'].steeringAngle, accel_limits, self.CP) @@ -242,3 +249,5 @@ class Planner(): v_acc_sol = self.v_acc_start + CP.radarTimeStep * (a_acc_sol + self.a_acc_start) / 2.0 self.v_acc_start = v_acc_sol self.a_acc_start = a_acc_sol + + self.first_loop = False diff --git a/selfdrive/controls/tests/test_following_distance.py b/selfdrive/controls/tests/test_following_distance.py index cc70bb4d3..63545e6ee 100644 --- a/selfdrive/controls/tests/test_following_distance.py +++ b/selfdrive/controls/tests/test_following_distance.py @@ -38,7 +38,7 @@ def run_following_distance_simulation(v_lead, t_end=200.0): first = True while t < t_end: # Run cruise control - accel_limits = [float(x) for x in calc_cruise_accel_limits(v_ego)] + accel_limits = [float(x) for x in calc_cruise_accel_limits(v_ego, False)] jerk_limits = [min(-0.1, accel_limits[0]), max(0.1, accel_limits[1])] v_cruise, a_cruise = speed_smoother(v_ego, a_ego, v_cruise_setpoint, accel_limits[1], accel_limits[0], diff --git a/selfdrive/locationd/calibrationd.py b/selfdrive/locationd/calibrationd.py index 3811d24f8..cd92a5f63 100755 --- a/selfdrive/locationd/calibrationd.py +++ b/selfdrive/locationd/calibrationd.py @@ -10,19 +10,23 @@ from selfdrive.swaglog import cloudlog from common.params import Params, put_nonblocking from common.transformations.model import model_height from common.transformations.camera import view_frame_from_device_frame, get_view_frame_from_road_frame, \ - eon_intrinsics, get_calib_from_vp, H, W + get_calib_from_vp, H, W, FOCAL MPH_TO_MS = 0.44704 MIN_SPEED_FILTER = 15 * MPH_TO_MS +MAX_SPEED_STD = 1.5 MAX_YAW_RATE_FILTER = np.radians(2) # per second -INPUTS_NEEDED = 300 # allow to update VP every so many frames -INPUTS_WANTED = 600 # We want a little bit more than we need for stability -WRITE_CYCLES = 400 # write every 400 cycles + +# This is all 20Hz, blocks needed for efficiency +BLOCK_SIZE = 100 +INPUTS_NEEDED = 5 # allow to update VP every so many frames +INPUTS_WANTED = 20 # We want a little bit more than we need for stability +WRITE_CYCLES = 10 # write every 1000 cycles VP_INIT = np.array([W/2., H/2.]) # These validity corners were chosen by looking at 1000 # and taking most extreme cases with some margin. -VP_VALIDITY_CORNERS = np.array([[W//2 - 150, 280], [W//2 + 150, 540]]) +VP_VALIDITY_CORNERS = np.array([[W//2 - 120, 300], [W//2 + 120, 520]]) DEBUG = os.getenv("DEBUG") is not None @@ -31,13 +35,29 @@ def is_calibration_valid(vp): vp[1] > VP_VALIDITY_CORNERS[0,1] and vp[1] < VP_VALIDITY_CORNERS[1,1] +def sanity_clip(vp): + if np.isnan(vp).any(): + vp = VP_INIT + return [np.clip(vp[0], VP_VALIDITY_CORNERS[0,0] - 20, VP_VALIDITY_CORNERS[1,0] + 20), + np.clip(vp[1], VP_VALIDITY_CORNERS[0,1] - 20, VP_VALIDITY_CORNERS[1,1] + 20)] + + +def intrinsics_from_vp(vp): + return np.array([ + [FOCAL, 0., vp[0]], + [ 0., FOCAL, vp[1]], + [ 0., 0., 1.]]) + + class Calibrator(): def __init__(self, param_put=False): self.param_put = param_put self.vp = copy.copy(VP_INIT) - self.vps = [] + self.vps = np.zeros((INPUTS_WANTED, 2)) + self.idx = 0 + self.block_idx = 0 + self.valid_blocks = 0 self.cal_status = Calibration.UNCALIBRATED - self.write_counter = 0 self.just_calibrated = False # Read calibration @@ -46,14 +66,19 @@ class Calibrator(): try: calibration_params = json.loads(calibration_params) self.vp = np.array(calibration_params["vanishing_point"]) - self.vps = np.tile(self.vp, (calibration_params['valid_points'], 1)).tolist() + if not np.isfinite(self.vp).all(): + self.vp = copy.copy(VP_INIT) + self.vps = np.tile(self.vp, (INPUTS_WANTED, 1)) + self.valid_blocks = calibration_params['valid_blocks'] + if not np.isfinite(self.valid_blocks) or self.valid_blocks < 0: + self.valid_blocks = 0 self.update_status() except Exception: cloudlog.exception("CalibrationParams file found but error encountered") def update_status(self): start_status = self.cal_status - if len(self.vps) < INPUTS_NEEDED: + if self.valid_blocks < INPUTS_NEEDED: self.cal_status = Calibration.UNCALIBRATED else: self.cal_status = Calibration.CALIBRATED if is_calibration_valid(self.vp) else Calibration.INVALID @@ -63,19 +88,28 @@ class Calibrator(): if start_status == Calibration.UNCALIBRATED and end_status == Calibration.CALIBRATED: self.just_calibrated = True - def handle_cam_odom(self, log): - trans, rot = log.trans, log.rot - if np.linalg.norm(trans) > MIN_SPEED_FILTER and abs(rot[2]) < MAX_YAW_RATE_FILTER: - new_vp = eon_intrinsics.dot(view_frame_from_device_frame.dot(trans)) + def handle_cam_odom(self, trans, rot, trans_std, rot_std): + if ((trans[0] > MIN_SPEED_FILTER) and + (trans_std[0] < MAX_SPEED_STD) and + (abs(rot[2]) < MAX_YAW_RATE_FILTER)): + # intrinsics are not eon intrinsics, since this is calibrated frame + intrinsics = intrinsics_from_vp(self.vp) + new_vp = intrinsics.dot(view_frame_from_device_frame.dot(trans)) new_vp = new_vp[:2]/new_vp[2] - self.vps.append(new_vp) - self.vps = self.vps[-INPUTS_WANTED:] - self.vp = np.mean(self.vps, axis=0) + + self.vps[self.block_idx] = (self.idx*self.vps[self.block_idx] + (BLOCK_SIZE - self.idx) * new_vp) / float(BLOCK_SIZE) + self.idx = (self.idx + 1) % BLOCK_SIZE + if self.idx == 0: + self.block_idx += 1 + self.valid_blocks = max(self.block_idx, self.valid_blocks) + self.block_idx = self.block_idx % INPUTS_WANTED + raw_vp = np.mean(self.vps[:max(1, self.valid_blocks)], axis=0) + self.vp = sanity_clip(raw_vp) self.update_status() - self.write_counter += 1 - if self.param_put and (self.write_counter % WRITE_CYCLES == 0 or self.just_calibrated): + + if self.param_put and ((self.idx == 0 and self.block_idx == 0) or self.just_calibrated): cal_params = {"vanishing_point": list(self.vp), - "valid_points": len(self.vps)} + "valid_blocks": self.valid_blocks} put_nonblocking("CalibrationParams", json.dumps(cal_params).encode('utf8')) return new_vp else: @@ -88,7 +122,7 @@ class Calibrator(): cal_send = messaging.new_message() cal_send.init('liveCalibration') cal_send.liveCalibration.calStatus = self.cal_status - cal_send.liveCalibration.calPerc = min(len(self.vps) * 100 // INPUTS_NEEDED, 100) + cal_send.liveCalibration.calPerc = min(100 * (self.valid_blocks * BLOCK_SIZE + self.idx) // (INPUTS_NEEDED * BLOCK_SIZE), 100) cal_send.liveCalibration.extrinsicMatrix = [float(x) for x in extrinsic_matrix.flatten()] cal_send.liveCalibration.rpyCalib = [float(x) for x in calib] @@ -104,15 +138,22 @@ def calibrationd_thread(sm=None, pm=None): calibrator = Calibrator(param_put=True) - # buffer with all the messages that still need to be input into the kalman + send_counter = 0 while 1: sm.update() - new_vp = calibrator.handle_cam_odom(sm['cameraOdometry']) + if sm.updated['cameraOdometry']: + new_vp = calibrator.handle_cam_odom(sm['cameraOdometry'].trans, + sm['cameraOdometry'].rot, + sm['cameraOdometry'].transStd, + sm['cameraOdometry'].rotStd) if DEBUG and new_vp is not None: print('got new vp', new_vp) - calibrator.send_data(pm) + # decimate outputs for efficiency + if (send_counter % 5) == 0: + calibrator.send_data(pm) + send_counter += 1 def main(sm=None, pm=None): diff --git a/selfdrive/locationd/locationd_yawrate.cc b/selfdrive/locationd/locationd_yawrate.cc index 83c837d7f..b2b87557c 100644 --- a/selfdrive/locationd/locationd_yawrate.cc +++ b/selfdrive/locationd/locationd_yawrate.cc @@ -40,7 +40,7 @@ void Localizer::handle_sensor_events(capnp::List::Reade } void Localizer::handle_camera_odometry(cereal::CameraOdometry::Reader camera_odometry, double current_time) { - double R = 100.0 * pow(camera_odometry.getRotStd()[2], 2); + double R = pow(30.0 *camera_odometry.getRotStd()[2], 2); double meas = camera_odometry.getRot()[2]; update_state(C_posenet, R, current_time, meas); @@ -73,7 +73,7 @@ Localizer::Localizer() { 0, 0, 0, 0, 0, pow(0.1, 2.0), 0, 0, 0, 0, 0, 0, - 0, 0, pow(0.0005 / 100.0, 2.0), 0; + 0, 0, pow(0.005 / 100.0, 2.0), 0; P << pow(100.0, 2.0), 0, 0, 0, 0, pow(100.0, 2.0), 0, 0, diff --git a/selfdrive/loggerd/SConscript b/selfdrive/loggerd/SConscript index 6a392d15d..b319c773c 100644 --- a/selfdrive/loggerd/SConscript +++ b/selfdrive/loggerd/SConscript @@ -1,6 +1,12 @@ -Import('env', 'messaging', 'common', 'visionipc') -env.Program(['loggerd.cc', 'logger.c', 'raw_logger.cc', 'encoder.c'], LIBS=[ - 'zmq', 'czmq', 'capnp', 'kj', 'yaml-cpp', 'z', +Import('env', 'arch', 'messaging', 'common', 'visionipc') + +src = ['loggerd.cc', 'logger.c'] +libs = ['zmq', 'czmq', 'capnp', 'kj', 'yaml-cpp', 'z', 'avformat', 'avcodec', 'swscale', 'avutil', - 'OmxVenc', 'OmxCore', 'yuv', - 'bz2', 'cutils', common, 'json', messaging, visionipc]) + 'yuv', 'bz2', common, 'json', messaging, visionipc] + +if arch == "aarch64": + src += ['encoder.c', 'raw_logger.cc'] + libs += ['OmxVenc', 'OmxCore', 'cutils'] + +env.Program(src, LIBS=libs) diff --git a/selfdrive/loggerd/config.py b/selfdrive/loggerd/config.py index 14e1e67a9..0ee6fa671 100644 --- a/selfdrive/loggerd/config.py +++ b/selfdrive/loggerd/config.py @@ -9,11 +9,21 @@ else: SEGMENT_LENGTH = 60 -def get_available_percent(): +def get_available_percent(default=None): try: statvfs = os.statvfs(ROOT) available_percent = 100.0 * statvfs.f_bavail / statvfs.f_blocks except OSError: - available_percent = 100.0 + available_percent = default return available_percent + + +def get_available_bytes(default=None): + try: + statvfs = os.statvfs(ROOT) + available_bytes = statvfs.f_bavail * statvfs.f_frsize + except OSError: + available_bytes = default + + return available_bytes diff --git a/selfdrive/loggerd/deleter.py b/selfdrive/loggerd/deleter.py index 5669e2342..1c687ff4b 100644 --- a/selfdrive/loggerd/deleter.py +++ b/selfdrive/loggerd/deleter.py @@ -3,15 +3,15 @@ import os import shutil import threading from selfdrive.swaglog import cloudlog -from selfdrive.loggerd.config import ROOT, get_available_percent +from selfdrive.loggerd.config import ROOT, get_available_bytes from selfdrive.loggerd.uploader import listdir_by_creation def deleter_thread(exit_event): while not exit_event.is_set(): - available_percent = get_available_percent() + available_bytes = get_available_bytes() - if available_percent < 10.0: + if available_bytes is not None and available_bytes < (5 * 1024 * 1024 * 1024): # remove the earliest directory we can dirs = listdir_by_creation(ROOT) for delete_dir in dirs: diff --git a/selfdrive/loggerd/loggerd.cc b/selfdrive/loggerd/loggerd.cc index 4334af0f3..1b3a81d7a 100644 --- a/selfdrive/loggerd/loggerd.cc +++ b/selfdrive/loggerd/loggerd.cc @@ -25,7 +25,10 @@ #include #include #include + +#ifdef QCOM #include +#endif #include "common/version.h" #include "common/timing.h" @@ -38,6 +41,11 @@ #include "logger.h" #include "messaging.hpp" +#ifndef QCOM +// no encoder on PC +#define DISABLE_ENCODER +#endif + #ifndef DISABLE_ENCODER #include "encoder.h" @@ -420,6 +428,7 @@ kj::Array gen_init_data() { init.setKernelVersion(util::read_file("/proc/version")); +#ifdef QCOM { std::vector > properties; property_list(append_property, (void*)&properties); @@ -431,6 +440,7 @@ kj::Array gen_init_data() { lentry.setValue(properties[i].second); } } +#endif const char* dongle_id = getenv("DONGLE_ID"); if (dongle_id) { diff --git a/selfdrive/loggerd/tests/test_deleter.py b/selfdrive/loggerd/tests/test_deleter.py index adbb0f031..f06946b2f 100644 --- a/selfdrive/loggerd/tests/test_deleter.py +++ b/selfdrive/loggerd/tests/test_deleter.py @@ -1,6 +1,7 @@ import os import time import threading +import unittest from collections import namedtuple import selfdrive.loggerd.deleter as deleter @@ -8,7 +9,8 @@ from common.timeout import Timeout, TimeoutException from selfdrive.loggerd.tests.loggerd_tests_common import UploaderTestCase -Stats = namedtuple("Stats", ['f_bavail', 'f_blocks']) +Stats = namedtuple("Stats", ['f_bavail', 'f_blocks', 'f_frsize']) + class TestDeleter(UploaderTestCase): def fake_statvfs(self, d): @@ -17,7 +19,7 @@ class TestDeleter(UploaderTestCase): def setUp(self): self.f_type = "fcamera.hevc" super(TestDeleter, self).setUp() - self.fake_stats = Stats(f_bavail=0, f_blocks=10) + self.fake_stats = Stats(f_bavail=0, f_blocks=10, f_frsize=4096) deleter.os.statvfs = self.fake_statvfs deleter.ROOT = self.root @@ -68,7 +70,9 @@ class TestDeleter(UploaderTestCase): def test_no_delete_when_available_space(self): f_path = self.make_file_with_data(self.seg_dir, self.f_type) - self.fake_stats = Stats(f_bavail=10, f_blocks=10) + block_size = 4096 + available = (10 * 1024 * 1024 * 1024) / block_size # 10GB free + self.fake_stats = Stats(f_bavail=available, f_blocks=10, f_frsize=block_size) self.start_thread() @@ -98,3 +102,7 @@ class TestDeleter(UploaderTestCase): self.join_thread() self.assertTrue(os.path.exists(f_path), "File deleted when locked") + + +if __name__ == "__main__": + unittest.main() diff --git a/selfdrive/loggerd/uploader.py b/selfdrive/loggerd/uploader.py index bea384b54..8f7e0a67a 100644 --- a/selfdrive/loggerd/uploader.py +++ b/selfdrive/loggerd/uploader.py @@ -68,13 +68,15 @@ def is_on_wifi(): # ConnectivityManager.getActiveNetworkInfo() try: result = android.parse_service_call_string(["connectivity", "2"]) + if result is None: + return True return 'WIFI' in result except AttributeError: return False def is_on_hotspot(): try: - result = subprocess.check_output(["ifconfig", "wlan0"], encoding='utf8') + result = subprocess.check_output(["ifconfig", "wlan0"], stderr=subprocess.STDOUT, encoding='utf8') result = re.findall(r"inet addr:((\d+\.){3}\d+)", result)[0][0] is_android = result.startswith('192.168.43.') diff --git a/selfdrive/manager.py b/selfdrive/manager.py index b603bf3d4..358eb3893 100755 --- a/selfdrive/manager.py +++ b/selfdrive/manager.py @@ -5,10 +5,12 @@ import sys import fcntl import errno import signal +import shutil import subprocess import datetime from common.basedir import BASEDIR +from common.android import ANDROID sys.path.append(os.path.join(BASEDIR, "pyextra")) os.environ['BASEDIR'] = BASEDIR @@ -21,7 +23,7 @@ try: except FileExistsError: pass -if os.path.isfile('/EON'): +if ANDROID: os.chmod("/dev/shm", 0o777) def unblock_stdout(): @@ -74,7 +76,11 @@ if not prebuilt: # run scons env = os.environ.copy() env['SCONS_PROGRESS'] = "1" - scons = subprocess.Popen(["scons", "-j4"], cwd=BASEDIR, env=env, stderr=subprocess.PIPE) + env['SCONS_CACHE'] = "1" + + nproc = os.cpu_count() + j_flag = "" if nproc is None else "-j%d" % (nproc - 1) + scons = subprocess.Popen(["scons", j_flag], cwd=BASEDIR, env=env, stderr=subprocess.PIPE) # Read progress from stderr and update spinner while scons.poll() is None: @@ -96,8 +102,12 @@ if not prebuilt: if scons.returncode != 0: if retry: - print("scons build failed, make clean") + print("scons build failed, cleaning in") + for i in range(3,-1,-1): + print("....%d" % i) + time.sleep(1) subprocess.check_call(["scons", "-c"], cwd=BASEDIR, env=env) + shutil.rmtree("/tmp/scons_cache") else: raise RuntimeError("scons build failed") else: @@ -139,11 +149,13 @@ managed_processes = { "paramsd": ("selfdrive/locationd", ["./paramsd"]), "camerad": ("selfdrive/camerad", ["./camerad"]), "sensord": ("selfdrive/sensord", ["./sensord"]), + "clocksd": ("selfdrive/clocksd", ["./clocksd"]), "gpsd": ("selfdrive/sensord", ["./gpsd"]), "updated": "selfdrive.updated", "monitoringd": ("selfdrive/modeld", ["./monitoringd"]), "modeld": ("selfdrive/modeld", ["./modeld"]), } + daemon_processes = { "manage_athenad": ("selfdrive.athena.manage_athenad", "AthenadPid"), } @@ -161,32 +173,42 @@ interrupt_processes = [] # processes to end with SIGKILL instead of SIGTERM kill_processes = ['sensord', 'paramsd'] +# processes to end if thermal conditions exceed Green parameters +green_temp_processes = ['uploader'] + persistent_processes = [ 'thermald', 'logmessaged', - 'logcatd', - 'tombstoned', - 'uploader', 'ui', - 'updated', + 'uploader', ] +if ANDROID: + persistent_processes += [ + 'logcatd', + 'tombstoned', + 'updated', + ] car_started_processes = [ 'controlsd', 'plannerd', 'loggerd', - 'sensord', 'radard', 'calibrationd', 'paramsd', 'camerad', 'modeld', - 'monitoringd', 'proclogd', 'ubloxd', - 'gpsd', - 'deleter', ] +if ANDROID: + car_started_processes += [ + 'sensord', + 'clocksd', + 'gpsd', + 'monitoringd', + 'deleter', + ] def register_managed_process(name, desc, car_started=False): global managed_processes, car_started_processes, persistent_processes @@ -303,7 +325,8 @@ def kill_managed_process(name): def cleanup_all_processes(signal, frame): cloudlog.info("caught ctrl-c %s %s" % (signal, frame)) - pm_apply_packages('disable') + if ANDROID: + pm_apply_packages('disable') for name in list(running.keys()): kill_managed_process(name) @@ -365,8 +388,9 @@ def manager_thread(): start_managed_process(p) # start frame - pm_apply_packages('enable') - start_frame() + if ANDROID: + pm_apply_packages('enable') + start_frame() if os.getenv("NOBOARD") is None: start_managed_process("pandad") @@ -376,11 +400,15 @@ def manager_thread(): while 1: msg = messaging.recv_sock(thermal_sock, wait=True) - # uploader is gated based on the phone temperature + # heavyweight batch processes are gated on favorable thermal conditions if msg.thermal.thermalStatus >= ThermalStatus.yellow: - kill_managed_process("uploader") + for p in green_temp_processes: + if p in persistent_processes: + kill_managed_process(p) else: - start_managed_process("uploader") + for p in green_temp_processes: + if p in persistent_processes: + start_managed_process(p) if msg.thermal.freeSpace < 0.05: logger_dead = True @@ -430,13 +458,6 @@ def main(): # disable bluetooth os.system('service call bluetooth_manager 8') - # support additional internal only extensions - try: - import selfdrive.manager_extensions - selfdrive.manager_extensions.register(register_managed_process) # pylint: disable=no-member - except ImportError: - pass - params = Params() params.manager_start() @@ -480,7 +501,8 @@ def main(): if params.get("Passive") is None: raise Exception("Passive must be set to continue") - update_apks() + if ANDROID: + update_apks() manager_init() manager_prepare(spinner) spinner.close() diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index b752bb3ad..aa995fb3f 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -1,25 +1,36 @@ Import('env', 'arch', 'messaging', 'common', 'gpucommon', 'visionipc') +lenv = env.Clone() libs = [messaging, common, 'OpenCL', 'SNPE', 'capnp', 'zmq', 'kj', 'yuv', gpucommon, visionipc] +common_src = [ + "models/commonmodel.c", + "runners/snpemodel.cc", + "transforms/loadyuv.c", + "transforms/transform.c"] + if arch == "aarch64": libs += ['gsl', 'CB', 'gnustl_shared'] else: libs += ['symphony-cpu', 'pthread'] -common = env.Object([ - "models/commonmodel.c", - "runners/snpemodel.cc", - "transforms/loadyuv.c", - "transforms/transform.c"]) + if FindFile('libtensorflow.so', env['LIBPATH']): + # for tensorflow support + common_src += ['runners/tfmodel.cc'] + libs += ['tensorflow'] + # tell runners to use it + lenv['CFLAGS'].append("-DUSE_TF_MODEL") + lenv['CXXFLAGS'].append("-DUSE_TF_MODEL") -env.Program('_monitoringd', [ +common = lenv.Object(common_src) + +lenv.Program('_monitoringd', [ "monitoringd.cc", "models/monitoring.cc", ]+common, LIBS=libs) -env.Program('_modeld', [ +lenv.Program('_modeld', [ "modeld.cc", "models/driving.cc", - "models/posenet.cc", ]+common, LIBS=libs) + diff --git a/selfdrive/modeld/modeld b/selfdrive/modeld/modeld index cb7dc65e7..3629f7a71 100755 --- a/selfdrive/modeld/modeld +++ b/selfdrive/modeld/modeld @@ -1,4 +1,4 @@ #!/bin/sh -export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64-android-clang3.8/:$LD_LIBRARY_PATH" +export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64-android-clang3.8/:/home/batman/one/phonelibs/snpe/x86_64-linux-clang:$LD_LIBRARY_PATH" exec ./_modeld diff --git a/selfdrive/modeld/modeld.cc b/selfdrive/modeld/modeld.cc index 276a34792..fa49d1b5d 100644 --- a/selfdrive/modeld/modeld.cc +++ b/selfdrive/modeld/modeld.cc @@ -1,18 +1,11 @@ #include #include -#ifdef QCOM -#include -#else -#include -#endif - #include "common/visionbuf.h" #include "common/visionipc.h" #include "common/swaglog.h" #include "models/driving.h" -#include "models/posenet.h" volatile sig_atomic_t do_exit = 0; @@ -122,13 +115,26 @@ int main(int argc, char **argv) { cl_command_queue q; { // TODO: refactor this - cl_platform_id platform_id = NULL; + cl_platform_id platform_id[2]; cl_uint num_devices; cl_uint num_platforms; - err = clGetPlatformIDs(1, &platform_id, &num_platforms); + err = clGetPlatformIDs(sizeof(platform_id)/sizeof(cl_platform_id), platform_id, &num_platforms); assert(err == 0); - err = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_DEFAULT, 1, + + #ifdef QCOM + int clPlatform = 0; + #else + // don't use nvidia on pc, it's broken + // TODO: write this nicely + int clPlatform = num_platforms-1; + #endif + + char cBuffer[1024]; + clGetPlatformInfo(platform_id[clPlatform], CL_PLATFORM_NAME, sizeof(cBuffer), &cBuffer, NULL); + LOGD("got %d opencl platform(s), using %s", num_platforms, cBuffer); + + err = clGetDeviceIDs(platform_id[clPlatform], CL_DEVICE_TYPE_DEFAULT, 1, &device_id, &num_devices); assert(err == 0); @@ -141,9 +147,7 @@ int main(int argc, char **argv) { // init the models ModelState model; - PosenetState posenet; model_init(&model, device_id, context, true); - posenet_init(&posenet); LOGW("models loaded, modeld starting"); // debayering does a 2x downscale @@ -213,54 +217,18 @@ int main(int argc, char **argv) { // TODO: don't make copies! memcpy(yuv_ion.addr, buf->addr, buf_info.buf_len); - ModelData model_buf = + ModelDataRaw model_buf = model_eval_frame(&model, q, yuv_cl, buf_info.width, buf_info.height, model_transform, NULL, vec_desire); mt2 = millis_since_boot(); model_publish(model_sock, extra.frame_id, model_buf, extra.timestamp_eof); + posenet_publish(posenet_sock, extra.frame_id, model_buf, extra.timestamp_eof); LOGD("model process: %.2fms, from last %.2fms", mt2-mt1, mt1-last); last = mt1; } - // push the frame to the posenet - // TODO: This doesn't always have to run - double pt1 = 0, pt2 = 0, pt3 = 0; - pt1 = millis_since_boot(); - posenet_push(&posenet, (uint8_t*)buf->addr, buf_info.width); - pt2 = millis_since_boot(); - - // posenet runs every 5 - if (extra.frame_id % 5 == 0) { - posenet_eval(&posenet); - - // send posenet event - { - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - - auto posenetd = event.initCameraOdometry(); - kj::ArrayPtr trans_vs(&posenet.output[0], 3); - posenetd.setTrans(trans_vs); - kj::ArrayPtr rot_vs(&posenet.output[3], 3); - posenetd.setRot(rot_vs); - kj::ArrayPtr trans_std_vs(&posenet.output[6], 3); - posenetd.setTransStd(trans_std_vs); - kj::ArrayPtr rot_std_vs(&posenet.output[9], 3); - posenetd.setRotStd(rot_std_vs); - posenetd.setTimestampEof(extra.timestamp_eof); - posenetd.setFrameId(extra.frame_id); - - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); - posenet_sock->send((char*)bytes.begin(), bytes.size()); - } - pt3 = millis_since_boot(); - LOGD("pre: %.2fms | posenet: %.2fms", (pt2-pt1), (pt3-pt1)); - } - } visionbuf_free(&yuv_ion); } @@ -268,9 +236,7 @@ int main(int argc, char **argv) { visionstream_destroy(&stream); delete model_sock; - delete posenet_sock; - - posenet_free(&posenet); + model_free(&model); LOG("joining live_thread"); diff --git a/selfdrive/modeld/models/commonmodel.c b/selfdrive/modeld/models/commonmodel.c index 0369d16d5..143480fb0 100644 --- a/selfdrive/modeld/models/commonmodel.c +++ b/selfdrive/modeld/models/commonmodel.c @@ -5,57 +5,57 @@ #include "common/mat.h" #include "common/timing.h" -void model_input_init(ModelInput* s, int width, int height, +void frame_init(ModelFrame* frame, int width, int height, cl_device_id device_id, cl_context context) { int err; - s->device_id = device_id; - s->context = context; + frame->device_id = device_id; + frame->context = context; - transform_init(&s->transform, context, device_id); - s->transformed_width = width; - s->transformed_height = height; + transform_init(&frame->transform, context, device_id); + frame->transformed_width = width; + frame->transformed_height = height; - s->transformed_y_cl = clCreateBuffer(s->context, CL_MEM_READ_WRITE, - s->transformed_width*s->transformed_height, NULL, &err); + frame->transformed_y_cl = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, + frame->transformed_width*frame->transformed_height, NULL, &err); assert(err == 0); - s->transformed_u_cl = clCreateBuffer(s->context, CL_MEM_READ_WRITE, - (s->transformed_width/2)*(s->transformed_height/2), NULL, &err); + frame->transformed_u_cl = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, + (frame->transformed_width/2)*(frame->transformed_height/2), NULL, &err); assert(err == 0); - s->transformed_v_cl = clCreateBuffer(s->context, CL_MEM_READ_WRITE, - (s->transformed_width/2)*(s->transformed_height/2), NULL, &err); + frame->transformed_v_cl = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, + (frame->transformed_width/2)*(frame->transformed_height/2), NULL, &err); assert(err == 0); - s->net_input_size = ((width*height*3)/2)*sizeof(float); - s->net_input = clCreateBuffer(s->context, CL_MEM_READ_WRITE, - s->net_input_size, (void*)NULL, &err); + frame->net_input_size = ((width*height*3)/2)*sizeof(float); + frame->net_input = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, + frame->net_input_size, (void*)NULL, &err); assert(err == 0); - loadyuv_init(&s->loadyuv, context, device_id, s->transformed_width, s->transformed_height); + loadyuv_init(&frame->loadyuv, context, device_id, frame->transformed_width, frame->transformed_height); } -float *model_input_prepare(ModelInput* s, cl_command_queue q, +float *frame_prepare(ModelFrame* frame, cl_command_queue q, cl_mem yuv_cl, int width, int height, mat3 transform) { int err; int i = 0; - transform_queue(&s->transform, q, + transform_queue(&frame->transform, q, yuv_cl, width, height, - s->transformed_y_cl, s->transformed_u_cl, s->transformed_v_cl, - s->transformed_width, s->transformed_height, + frame->transformed_y_cl, frame->transformed_u_cl, frame->transformed_v_cl, + frame->transformed_width, frame->transformed_height, transform); - loadyuv_queue(&s->loadyuv, q, - s->transformed_y_cl, s->transformed_u_cl, s->transformed_v_cl, - s->net_input); - float *net_input_buf = (float *)clEnqueueMapBuffer(q, s->net_input, CL_TRUE, - CL_MAP_READ, 0, s->net_input_size, + loadyuv_queue(&frame->loadyuv, q, + frame->transformed_y_cl, frame->transformed_u_cl, frame->transformed_v_cl, + frame->net_input); + float *net_input_buf = (float *)clEnqueueMapBuffer(q, frame->net_input, CL_TRUE, + CL_MAP_READ, 0, frame->net_input_size, 0, NULL, NULL, &err); clFinish(q); return net_input_buf; } -void model_input_free(ModelInput* s) { - transform_destroy(&s->transform); - loadyuv_destroy(&s->loadyuv); +void frame_free(ModelFrame* frame) { + transform_destroy(&frame->transform); + loadyuv_destroy(&frame->loadyuv); } diff --git a/selfdrive/modeld/models/commonmodel.h b/selfdrive/modeld/models/commonmodel.h index 2a03a3d00..4e3ee448c 100644 --- a/selfdrive/modeld/models/commonmodel.h +++ b/selfdrive/modeld/models/commonmodel.h @@ -4,7 +4,6 @@ #include #include "common/mat.h" -#include "common/modeldata.h" #include "transforms/transform.h" #include "transforms/loadyuv.h" @@ -15,7 +14,7 @@ extern "C" { float softplus(float input); float sigmoid(float input); -typedef struct ModelInput { +typedef struct ModelFrame { cl_device_id device_id; cl_context context; @@ -26,14 +25,14 @@ typedef struct ModelInput { LoadYUVState loadyuv; cl_mem net_input; size_t net_input_size; -} ModelInput; +} ModelFrame; -void model_input_init(ModelInput* s, int width, int height, +void frame_init(ModelFrame* frame, int width, int height, cl_device_id device_id, cl_context context); -float *model_input_prepare(ModelInput* s, cl_command_queue q, +float *frame_prepare(ModelFrame* frame, cl_command_queue q, cl_mem yuv_cl, int width, int height, mat3 transform); -void model_input_free(ModelInput* s); +void frame_free(ModelFrame* frame); #ifdef __cplusplus } diff --git a/selfdrive/modeld/models/driving.cc b/selfdrive/modeld/models/driving.cc index 127e3c2e8..06c820ff7 100644 --- a/selfdrive/modeld/models/driving.cc +++ b/selfdrive/modeld/models/driving.cc @@ -2,26 +2,20 @@ #include #include #include - -#ifdef QCOM -#include -#else -#include -#endif - #include "common/timing.h" #include "driving.h" -#define MODEL_WIDTH 512 -#define MODEL_HEIGHT 256 -#define MODEL_NAME "driving_model_dlc" -#define LEAD_MDN_N 5 // probs for 5 groups -#define MDN_VALS 4 // output xyva for each lead group -#define SELECTION 3 //output 3 group (lead now, in 2s and 6s) -#define MDN_GROUP_SIZE 11 -#define SPEED_BUCKETS 100 -#define OUTPUT_SIZE ((MODEL_PATH_DISTANCE*2) + (2*(MODEL_PATH_DISTANCE*2 + 1)) + MDN_GROUP_SIZE*LEAD_MDN_N + SELECTION + OTHER_META_SIZE + DESIRE_PRED_SIZE) +#define PATH_IDX 0 +#define LL_IDX PATH_IDX + MODEL_PATH_DISTANCE*2 +#define RL_IDX LL_IDX + MODEL_PATH_DISTANCE*2 + 1 +#define LEAD_IDX RL_IDX + MODEL_PATH_DISTANCE*2 + 1 +#define LONG_X_IDX LEAD_IDX + MDN_GROUP_SIZE*LEAD_MDN_N + SELECTION +#define LONG_V_IDX LONG_X_IDX + TIME_DISTANCE*2 +#define LONG_A_IDX LONG_V_IDX + TIME_DISTANCE*2 +#define META_IDX LONG_A_IDX + TIME_DISTANCE*2 +#define POSE_IDX META_IDX + OTHER_META_SIZE + DESIRE_PRED_SIZE +#define OUTPUT_SIZE POSE_IDX + POSE_SIZE #ifdef TEMPORAL #define TEMPORAL_SIZE 512 #else @@ -33,15 +27,19 @@ Eigen::Matrix vander; void model_init(ModelState* s, cl_device_id device_id, cl_context context, int temporal) { - model_input_init(&s->in, MODEL_WIDTH, MODEL_HEIGHT, device_id, context); + frame_init(&s->frame, MODEL_WIDTH, MODEL_HEIGHT, device_id, context); + s->input_frames = (float*)calloc(MODEL_FRAME_SIZE * 2, sizeof(float)); + const int output_size = OUTPUT_SIZE + TEMPORAL_SIZE; - s->output = (float*)malloc(output_size * sizeof(float)); - memset(s->output, 0, output_size * sizeof(float)); - s->m = new DefaultRunModel("../../models/driving_model.dlc", s->output, output_size, USE_GPU_RUNTIME); + s->output = (float*)calloc(output_size, sizeof(float)); + + s->m = new DefaultRunModel("../../models/supercombo.dlc", s->output, output_size, USE_GPU_RUNTIME); + #ifdef TEMPORAL assert(temporal); s->m->addRecurrent(&s->output[OUTPUT_SIZE], TEMPORAL_SIZE); #endif + #ifdef DESIRE s->desire = (float*)malloc(DESIRE_SIZE * sizeof(float)); for (int i = 0; i < DESIRE_SIZE; i++) s->desire[i] = 0.0; @@ -56,18 +54,11 @@ void model_init(ModelState* s, cl_device_id device_id, cl_context context, int t } } -ModelData model_eval_frame(ModelState* s, cl_command_queue q, + + +ModelDataRaw model_eval_frame(ModelState* s, cl_command_queue q, cl_mem yuv_cl, int width, int height, mat3 transform, void* sock, float *desire_in) { - struct { - float *path; - float *left_lane; - float *right_lane; - float *lead; - float *speed; - float *meta; - } net_outputs = {NULL}; - #ifdef DESIRE if (desire_in != NULL) { for (int i = 0; i < DESIRE_SIZE; i++) s->desire[i] = desire_in[i]; @@ -76,124 +67,36 @@ ModelData model_eval_frame(ModelState* s, cl_command_queue q, //for (int i = 0; i < OUTPUT_SIZE + TEMPORAL_SIZE; i++) { printf("%f ", s->output[i]); } printf("\n"); - float *net_input_buf = model_input_prepare(&s->in, q, yuv_cl, width, height, transform); + float *new_frame_buf = frame_prepare(&s->frame, q, yuv_cl, width, height, transform); + memmove(&s->input_frames[0], &s->input_frames[MODEL_FRAME_SIZE], sizeof(float)*MODEL_FRAME_SIZE); + memmove(&s->input_frames[MODEL_FRAME_SIZE], new_frame_buf, sizeof(float)*MODEL_FRAME_SIZE); + s->m->execute(s->input_frames); #ifdef DUMP_YUV FILE *dump_yuv_file = fopen("/sdcard/dump.yuv", "wb"); - fwrite(net_input_buf, MODEL_HEIGHT*MODEL_WIDTH*3/2, sizeof(float), dump_yuv_file); + fwrite(new_frame_buf, MODEL_HEIGHT*MODEL_WIDTH*3/2, sizeof(float), dump_yuv_file); fclose(dump_yuv_file); assert(1==2); #endif - //printf("readinggggg \n"); - //FILE *f = fopen("goof_frame", "r"); - //fread(net_input_buf, sizeof(float), MODEL_HEIGHT*MODEL_WIDTH*3/2, f); - //fclose(f); - //sleep(1); - //printf("%i \n",OUTPUT_SIZE); - //printf("%i \n",MDN_GROUP_SIZE); - s->m->execute(net_input_buf); - // net outputs - net_outputs.path = &s->output[0]; - net_outputs.left_lane = &s->output[MODEL_PATH_DISTANCE*2]; - net_outputs.right_lane = &s->output[MODEL_PATH_DISTANCE*2 + MODEL_PATH_DISTANCE*2 + 1]; - net_outputs.lead = &s->output[MODEL_PATH_DISTANCE*2 + (MODEL_PATH_DISTANCE*2 + 1)*2]; - //net_outputs.speed = &s->output[OUTPUT_SIZE - SPEED_BUCKETS]; - net_outputs.meta = &s->output[OUTPUT_SIZE - OTHER_META_SIZE - DESIRE_PRED_SIZE]; - - ModelData model = {0}; - - for (int i=0; i net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 8]) { - mdn_max_idx = i; - } - } - model.lead.prob = sigmoid(net_outputs.lead[LEAD_MDN_N*MDN_GROUP_SIZE]); - model.lead.dist = net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE] * max_dist; - model.lead.std = softplus(net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS]) * max_dist; - model.lead.rel_y = net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 1]; - model.lead.rel_y_std = softplus(net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 1]); - model.lead.rel_v = net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 2] * max_rel_vel; - model.lead.rel_v_std = softplus(net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 2]) * max_rel_vel; - model.lead.rel_a = net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 3]; - model.lead.rel_a_std = softplus(net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 3]); - - // Find the distribution that corresponds to the lead in 2s - mdn_max_idx = 0; - for (int i=1; i net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 9]) { - mdn_max_idx = i; - } - } - model.lead_future.prob = sigmoid(net_outputs.lead[LEAD_MDN_N*MDN_GROUP_SIZE + 1]); - model.lead_future.dist = net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE] * max_dist; - model.lead_future.std = softplus(net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS]) * max_dist; - model.lead_future.rel_y = net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 1]; - model.lead_future.rel_y_std = softplus(net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 1]); - model.lead_future.rel_v = net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 2] * max_rel_vel; - model.lead_future.rel_v_std = softplus(net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 2]) * max_rel_vel; - model.lead_future.rel_a = net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 3]; - model.lead_future.rel_a_std = softplus(net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 3]); - - - // get speed percentiles numbers represent 5th, 15th, ... 95th percentile - for (int i=0; i < SPEED_PERCENTILES; i++) { - model.speed[i] = ((float) SPEED_BUCKETS)/2.0; - } - //float sum = 0; - //for (int idx = 0; idx < SPEED_BUCKETS; idx++) { - // sum += net_outputs.speed[idx]; - // int idx_percentile = (sum + .05) * SPEED_PERCENTILES; - // if (idx_percentile < SPEED_PERCENTILES ){ - // model.speed[idx_percentile] = ((float)idx)/2.0; - // } - //} - // make sure no percentiles are skipped - //for (int i=SPEED_PERCENTILES-1; i > 0; i--){ - // if (model.speed[i-1] > model.speed[i]){ - // model.speed[i-1] = model.speed[i]; - // } - //} - for (int i=0; ioutput[PATH_IDX]; + net_outputs.left_lane = &s->output[LL_IDX]; + net_outputs.right_lane = &s->output[RL_IDX]; + net_outputs.lead = &s->output[LEAD_IDX]; + net_outputs.long_x = &s->output[LONG_X_IDX]; + net_outputs.long_v = &s->output[LONG_V_IDX]; + net_outputs.long_a = &s->output[LONG_A_IDX]; + net_outputs.meta = &s->output[META_IDX]; + net_outputs.pose = &s->output[POSE_IDX]; + return net_outputs; } void model_free(ModelState* s) { free(s->output); - model_input_free(&s->in); + free(s->input_frames); + frame_free(&s->frame); delete s->m; } @@ -224,31 +127,52 @@ void poly_fit(float *in_pts, float *in_stds, float *out) { } -void fill_path(cereal::ModelData::PathData::Builder path, const PathData path_data) { +void fill_path(cereal::ModelData::PathData::Builder path, const float * data, bool has_prob, const float offset) { + float points_arr[MODEL_PATH_DISTANCE]; + float stds_arr[MODEL_PATH_DISTANCE]; + float poly_arr[POLYFIT_DEGREE]; + float std; + float prob; + + for (int i=0; i stds(&path_data.stds[0], ARRAYSIZE(path_data.stds)); + kj::ArrayPtr stds(&stds_arr[0], ARRAYSIZE(stds_arr)); path.setStds(stds); - kj::ArrayPtr points(&path_data.points[0], ARRAYSIZE(path_data.points)); + kj::ArrayPtr points(&points_arr[0], ARRAYSIZE(points_arr)); path.setPoints(points); } - kj::ArrayPtr poly(&path_data.poly[0], ARRAYSIZE(path_data.poly)); + kj::ArrayPtr poly(&poly_arr[0], ARRAYSIZE(poly_arr)); path.setPoly(poly); - path.setProb(path_data.prob); - path.setStd(path_data.std); + path.setProb(prob); + path.setStd(std); } -void fill_lead(cereal::ModelData::LeadData::Builder lead, const LeadData lead_data) { - lead.setDist(lead_data.dist); - lead.setProb(lead_data.prob); - lead.setStd(lead_data.std); - lead.setRelY(lead_data.rel_y); - lead.setRelYStd(lead_data.rel_y_std); - lead.setRelVel(lead_data.rel_v); - lead.setRelVelStd(lead_data.rel_v_std); - lead.setRelA(lead_data.rel_a); - lead.setRelAStd(lead_data.rel_a_std); +void fill_lead(cereal::ModelData::LeadData::Builder lead, const float * data, int mdn_max_idx) { + const double x_scale = 10.0; + const double y_scale = 10.0; + + lead.setProb(sigmoid(data[LEAD_MDN_N*MDN_GROUP_SIZE])); + lead.setDist(x_scale * data[mdn_max_idx*MDN_GROUP_SIZE]); + lead.setStd(x_scale * softplus(data[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS])); + lead.setRelY(y_scale * data[mdn_max_idx*MDN_GROUP_SIZE + 1]); + lead.setRelYStd(y_scale * softplus(data[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 1])); + lead.setRelVel(data[mdn_max_idx*MDN_GROUP_SIZE + 2]); + lead.setRelVelStd(softplus(data[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 2])); + lead.setRelA(data[mdn_max_idx*MDN_GROUP_SIZE + 3]); + lead.setRelAStd(softplus(data[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 3])); } void fill_meta(cereal::ModelData::MetaData::Builder meta, const float * meta_data) { @@ -260,39 +184,104 @@ void fill_meta(cereal::ModelData::MetaData::Builder meta, const float * meta_dat meta.setDesirePrediction(desire_pred); } +void fill_longi(cereal::ModelData::LongitudinalData::Builder longi, const float * long_v_data, const float * long_a_data) { + // just doing 10 vals, 1 every sec for now + float speed_arr[TIME_DISTANCE/10]; + float accel_arr[TIME_DISTANCE/10]; + for (int i=0; i speed(&speed_arr[0], ARRAYSIZE(speed_arr)); + longi.setSpeeds(speed); + kj::ArrayPtr accel(&accel_arr[0], ARRAYSIZE(accel_arr)); + longi.setAccelerations(accel); +} + void model_publish(PubSocket *sock, uint32_t frame_id, - const ModelData data, uint64_t timestamp_eof) { - // make msg - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); + const ModelDataRaw net_outputs, uint64_t timestamp_eof) { + // make msg + capnp::MallocMessageBuilder msg; + cereal::Event::Builder event = msg.initRoot(); + event.setLogMonoTime(nanos_since_boot()); + + auto framed = event.initModel(); + framed.setFrameId(frame_id); + framed.setTimestampEof(timestamp_eof); - auto framed = event.initModel(); - framed.setFrameId(frame_id); - framed.setTimestampEof(timestamp_eof); - - kj::ArrayPtr speed(&data.speed[0], ARRAYSIZE(data.speed)); - framed.setSpeed(speed); + auto lpath = framed.initPath(); + fill_path(lpath, net_outputs.path, false, 0); + auto left_lane = framed.initLeftLane(); + fill_path(left_lane, net_outputs.left_lane, true, 1.8); + auto right_lane = framed.initRightLane(); + fill_path(right_lane, net_outputs.right_lane, true, -1.8); + auto longi = framed.initLongitudinal(); + fill_longi(longi, net_outputs.long_v, net_outputs.long_a); - auto lpath = framed.initPath(); - fill_path(lpath, data.path); - auto left_lane = framed.initLeftLane(); - fill_path(left_lane, data.left_lane); - auto right_lane = framed.initRightLane(); - fill_path(right_lane, data.right_lane); - - auto lead = framed.initLead(); - fill_lead(lead, data.lead); - auto lead_future = framed.initLeadFuture(); - fill_lead(lead_future, data.lead_future); - - auto meta = framed.initMeta(); - fill_meta(meta, data.meta); - - - // send message - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); - sock->send((char*)bytes.begin(), bytes.size()); + // Find the distribution that corresponds to the current lead + int mdn_max_idx = 0; + for (int i=1; i net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 8]) { + mdn_max_idx = i; } + } + auto lead = framed.initLead(); + fill_lead(lead, net_outputs.lead, mdn_max_idx); + // Find the distribution that corresponds to the lead in 2s + mdn_max_idx = 0; + for (int i=1; i net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 9]) { + mdn_max_idx = i; + } + } + auto lead_future = framed.initLeadFuture(); + fill_lead(lead_future, net_outputs.lead, mdn_max_idx); + + + auto meta = framed.initMeta(); + fill_meta(meta, net_outputs.meta); + + + // send message + auto words = capnp::messageToFlatArray(msg); + auto bytes = words.asBytes(); + sock->send((char*)bytes.begin(), bytes.size()); + } + +void posenet_publish(PubSocket *sock, uint32_t frame_id, + const ModelDataRaw net_outputs, uint64_t timestamp_eof) { + capnp::MallocMessageBuilder msg; + cereal::Event::Builder event = msg.initRoot(); + event.setLogMonoTime(nanos_since_boot()); + + float trans_arr[3]; + float trans_std_arr[3]; + float rot_arr[3]; + float rot_std_arr[3]; + + for (int i =0; i < 3; i++) { + trans_arr[i] = net_outputs.pose[i]; + trans_std_arr[i] = softplus(net_outputs.pose[6 + i]) + 1e-6; + + rot_arr[i] = M_PI * net_outputs.pose[3 + i] / 180.0; + rot_std_arr[i] = M_PI * (softplus(net_outputs.pose[9 + i]) + 1e-6) / 180.0; + } + + auto posenetd = event.initCameraOdometry(); + kj::ArrayPtr trans_vs(&trans_arr[0], 3); + posenetd.setTrans(trans_vs); + kj::ArrayPtr rot_vs(&rot_arr[0], 3); + posenetd.setRot(rot_vs); + kj::ArrayPtr trans_std_vs(&trans_std_arr[0], 3); + posenetd.setTransStd(trans_std_vs); + kj::ArrayPtr rot_std_vs(&rot_std_arr[0], 3); + posenetd.setRotStd(rot_std_vs); + + posenetd.setTimestampEof(timestamp_eof); + posenetd.setFrameId(frame_id); + + auto words = capnp::messageToFlatArray(msg); + auto bytes = words.asBytes(); + sock->send((char*)bytes.begin(), bytes.size()); + } diff --git a/selfdrive/modeld/models/driving.h b/selfdrive/modeld/models/driving.h index ccf8a94be..bf46cb9d4 100644 --- a/selfdrive/modeld/models/driving.h +++ b/selfdrive/modeld/models/driving.h @@ -9,8 +9,13 @@ #define DESIRE_SIZE 8 #endif +#ifdef QCOM +#include +#else +#include +#endif + #include "common/mat.h" -#include "common/modeldata.h" #include "common/util.h" #include "commonmodel.h" @@ -21,10 +26,40 @@ #include #include "messaging.hpp" +#define MODEL_WIDTH 512 +#define MODEL_HEIGHT 256 +#define MODEL_FRAME_SIZE MODEL_WIDTH * MODEL_HEIGHT * 3 / 2 +#define MODEL_NAME "supercombo_dlc" + +#define MODEL_PATH_DISTANCE 192 +#define POLYFIT_DEGREE 4 +#define SPEED_PERCENTILES 10 +#define DESIRE_PRED_SIZE 32 +#define OTHER_META_SIZE 4 +#define LEAD_MDN_N 5 // probs for 5 groups +#define MDN_VALS 4 // output xyva for each lead group +#define SELECTION 3 //output 3 group (lead now, in 2s and 6s) +#define MDN_GROUP_SIZE 11 +#define TIME_DISTANCE 100 +#define POSE_SIZE 12 + +struct ModelDataRaw { + float *path; + float *left_lane; + float *right_lane; + float *lead; + float *long_x; + float *long_v; + float *long_a; + float *meta; + float *pose; + }; + typedef struct ModelState { - ModelInput in; + ModelFrame frame; float *output; + float *input_frames; RunModel *m; #ifdef DESIRE float *desire; @@ -33,12 +68,14 @@ typedef struct ModelState { void model_init(ModelState* s, cl_device_id device_id, cl_context context, int temporal); -ModelData model_eval_frame(ModelState* s, cl_command_queue q, +ModelDataRaw model_eval_frame(ModelState* s, cl_command_queue q, cl_mem yuv_cl, int width, int height, mat3 transform, void* sock, float *desire_in); void model_free(ModelState* s); void poly_fit(float *in_pts, float *in_stds, float *out); void model_publish(PubSocket* sock, uint32_t frame_id, - const ModelData data, uint64_t timestamp_eof); + const ModelDataRaw data, uint64_t timestamp_eof); +void posenet_publish(PubSocket* sock, uint32_t frame_id, + const ModelDataRaw data, uint64_t timestamp_eof); #endif diff --git a/selfdrive/modeld/models/posenet.cc b/selfdrive/modeld/models/posenet.cc deleted file mode 100644 index c843e81f4..000000000 --- a/selfdrive/modeld/models/posenet.cc +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include "posenet.h" - -void posenet_init(PosenetState *s) { - s->input = (float*)malloc(2*200*532*sizeof(float)); - s->m = new DefaultRunModel("../../models/posenet.dlc", s->output, sizeof(s->output)/sizeof(float), USE_GPU_RUNTIME); -} - -void posenet_push(PosenetState *s, uint8_t *yuv_ptr_y, int yuv_width) { - // move second frame to first frame - memmove(&s->input[0], &s->input[1], sizeof(float)*(200*532*2 - 1)); - - // fill posenet input - float a; - // posenet uses a half resolution cropped frame - // with upper left corner: [50, 237] and - // bottom right corner: [1114, 637] - // So the resulting crop is 532 X 200 - for (int y=237; y<637; y+=2) { - int yy = (y-237)/2; - for (int x = 50; x < 1114; x+=2) { - int xx = (x-50)/2; - a = 0; - a += yuv_ptr_y[yuv_width*(y+0) + (x+1)]; - a += yuv_ptr_y[yuv_width*(y+1) + (x+1)]; - a += yuv_ptr_y[yuv_width*(y+0) + (x+0)]; - a += yuv_ptr_y[yuv_width*(y+1) + (x+0)]; - // The posenet takes a normalized image input - // like the driving model so [0,255] is remapped - // to [-1,1] - s->input[(yy*532+xx)*2 + 1] = (a/512.0 - 1.0); - } - } -} - -void posenet_eval(PosenetState *s) { - s->m->execute(s->input); - - // fix stddevs - for (int i = 6; i < 12; i++) { - s->output[i] = log1p(exp(s->output[i])) + 1e-6; - } - // to radians - for (int i = 3; i < 6; i++) { - s->output[i] = M_PI * s->output[i] / 180.0; - } - // to radians - for (int i = 9; i < 12; i++) { - s->output[i] = M_PI * s->output[i] / 180.0; - } -} - -void posenet_free(PosenetState *s) { - delete s->m; - free(s->input); -} - diff --git a/selfdrive/modeld/models/posenet.h b/selfdrive/modeld/models/posenet.h deleted file mode 100644 index 2e6571a1d..000000000 --- a/selfdrive/modeld/models/posenet.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef POSENET_H -#define POSENET_H - -#include -#include "runners/run.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct PosenetState { - float output[12]; - float *input; - RunModel *m; -} PosenetState; - -void posenet_init(PosenetState *s); -void posenet_push(PosenetState *s, uint8_t *yuv_ptr_y, int yuv_width); -void posenet_eval(PosenetState *s); -void posenet_free(PosenetState *s); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/selfdrive/modeld/monitoringd b/selfdrive/modeld/monitoringd index 63e28b12f..419d82633 100755 --- a/selfdrive/modeld/monitoringd +++ b/selfdrive/modeld/monitoringd @@ -1,5 +1,5 @@ #!/bin/sh -export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64-android-clang3.8:$LD_LIBRARY_PATH" +export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64-android-clang3.8:/home/batman/one/phonelibs/snpe/x86_64-linux-clang:$LD_LIBRARY_PATH" export ADSP_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64-android-clang3.8/" exec ./_monitoringd diff --git a/selfdrive/modeld/runners/run.h b/selfdrive/modeld/runners/run.h index 049f06584..56e785397 100644 --- a/selfdrive/modeld/runners/run.h +++ b/selfdrive/modeld/runners/run.h @@ -7,9 +7,12 @@ #ifdef QCOM #define DefaultRunModel SNPEModel #else -#define DefaultRunModel SNPEModel - /* #include "tfmodel.h" */ - /* #define DefaultRunModel TFModel */ + #ifdef USE_TF_MODEL + #include "tfmodel.h" + #define DefaultRunModel TFModel + #else + #define DefaultRunModel SNPEModel + #endif #endif #endif diff --git a/selfdrive/pandad.py b/selfdrive/pandad.py index 2ef286fa7..7cbc704b9 100755 --- a/selfdrive/pandad.py +++ b/selfdrive/pandad.py @@ -4,19 +4,29 @@ import os import time from selfdrive.swaglog import cloudlog -from panda import Panda, PandaDFU, BASEDIR +from panda import Panda, PandaDFU, BASEDIR, build_st -def get_expected_version(): - with open(os.path.join(BASEDIR, "VERSION")) as f: - repo_version = f.read() - repo_version += "-EON" if os.path.isfile('/EON') else "-DEV" - return repo_version +def get_firmware_fn(): + signed_fn = os.path.join(BASEDIR, "board", "obj", "panda.bin.signed") + if os.path.exists(signed_fn): + cloudlog.info("Using prebuilt signed firmware") + return signed_fn + else: + cloudlog.info("Building panda firmware") + fn = "obj/panda.bin" + build_st(fn, clean=False) + return os.path.join(BASEDIR, "board", fn) + + +def get_expected_signature(fw_fn=None): + if fw_fn is None: + fw_fn = get_firmware_fn() + + return Panda.get_signature_from_firmware(fw_fn) def update_panda(): - repo_version = get_expected_version() - panda = None panda_dfu = None @@ -37,27 +47,28 @@ def update_panda(): panda_dfu = PandaDFU(panda_dfu[0]) panda_dfu.recover() - print("waiting for board...") time.sleep(1) + fw_fn = get_firmware_fn() + fw_signature = get_expected_signature(fw_fn) + try: serial = panda.get_serial()[0].decode("utf-8") except Exception: serial = None - current_version = "bootstub" if panda.bootstub else panda.get_version() - cloudlog.warning("Panda %s connected, version: %s, expected %s" % (serial, current_version, repo_version)) - if panda.bootstub or not current_version.startswith(repo_version): + panda_version = "bootstub" if panda.bootstub else panda.get_version() + panda_signature = "bootstub" if panda.bootstub else panda.get_signature() + cloudlog.warning("Panda %s connected, version: %s, signature %s, expected %s" % ( + serial, + panda_version, + panda_signature.hex(), + fw_signature.hex(), + )) + + if panda.bootstub or panda_signature != fw_signature: cloudlog.info("Panda firmware out of date, update required") - - signed_fn = os.path.join(BASEDIR, "board", "obj", "panda.bin.signed") - if os.path.exists(signed_fn): - cloudlog.info("Flashing signed firmware") - panda.flash(fn=signed_fn) - else: - cloudlog.info("Building and flashing unsigned firmware") - panda.flash() - + panda.flash(fw_fn) cloudlog.info("Done flashing") if panda.bootstub: @@ -69,8 +80,8 @@ def update_panda(): cloudlog.info("Panda still not booting, exiting") raise AssertionError - version = panda.get_version() - if not version.startswith(repo_version): + panda_signature = panda.get_signature() + if panda_signature != fw_signature: cloudlog.info("Version mismatch after flashing, exiting") raise AssertionError diff --git a/selfdrive/registration.py b/selfdrive/registration.py index 734f6f871..5de1ec99b 100644 --- a/selfdrive/registration.py +++ b/selfdrive/registration.py @@ -7,6 +7,7 @@ from selfdrive.version import version, terms_version, training_version, get_git_ from common.android import get_imei, get_serial, get_subscriber_info from common.api import api_get from common.params import Params +from common.file_helpers import mkdirs_exists_ok def register(): params = Params() @@ -18,6 +19,17 @@ def register(): params.put("GitRemote", get_git_remote()) params.put("SubscriberInfo", get_subscriber_info()) + # create a key for auth + # your private key is kept on your device persist partition and never sent to our servers + # do not erase your persist partition + if not os.path.isfile("/persist/comma/id_rsa.pub"): + cloudlog.warning("generating your personal RSA key") + mkdirs_exists_ok("/persist/comma") + assert os.system("openssl genrsa -out /persist/comma/id_rsa.tmp 2048") == 0 + assert os.system("openssl rsa -in /persist/comma/id_rsa.tmp -pubout -out /persist/comma/id_rsa.tmp.pub") == 0 + os.rename("/persist/comma/id_rsa.tmp", "/persist/comma/id_rsa") + os.rename("/persist/comma/id_rsa.tmp.pub", "/persist/comma/id_rsa.pub") + # make key readable by app users (ai.comma.plus.offroad) os.chmod('/persist/comma/', 0o755) os.chmod('/persist/comma/id_rsa', 0o744) @@ -51,5 +63,4 @@ def register(): return None if __name__ == "__main__": - print(api_get("").text) print(register()) diff --git a/selfdrive/sensord/gpsd.cc b/selfdrive/sensord/gpsd.cc index 54eb6b4dc..ae2ecec03 100644 --- a/selfdrive/sensord/gpsd.cc +++ b/selfdrive/sensord/gpsd.cc @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -31,8 +30,6 @@ volatile sig_atomic_t do_exit = 0; namespace { -pthread_t clock_thread_handle; - Context *gps_context; PubSocket *gps_publisher; PubSocket *gps_location_publisher; @@ -181,61 +178,6 @@ int64_t arm_cntpct() { return v; } -// TODO: move this out of here -void* clock_thread(void* args) { - int err = 0; - - PubSocket* clock_publisher = PubSocket::create(gps_context, "clocks"); - assert(clock_publisher != NULL); - - int timerfd = timerfd_create(CLOCK_BOOTTIME, 0); - assert(timerfd >= 0); - - struct itimerspec spec = {0}; - spec.it_interval.tv_sec = 1; - spec.it_interval.tv_nsec = 0; - spec.it_value.tv_sec = 1; - spec.it_value.tv_nsec = 0; - - err = timerfd_settime(timerfd, 0, &spec, 0); - assert(err == 0); - - uint64_t expirations = 0; - while ((err = read(timerfd, &expirations, sizeof(expirations)))) { - if (err < 0) break; - - if (do_exit) break; - - uint64_t boottime = nanos_since_boot(); - uint64_t monotonic = nanos_monotonic(); - uint64_t monotonic_raw = nanos_monotonic_raw(); - uint64_t wall_time = nanos_since_epoch(); - - uint64_t modem_uptime_v = arm_cntpct() / 19200ULL; // 19.2 mhz clock - - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(boottime); - auto clocks = event.initClocks(); - - clocks.setBootTimeNanos(boottime); - clocks.setMonotonicNanos(monotonic); - clocks.setMonotonicRawNanos(monotonic_raw); - clocks.setWallTimeNanos(wall_time); - clocks.setModemUptimeMillis(modem_uptime_v); - - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); - clock_publisher->send((char*)bytes.begin(), bytes.size()); - } - - close(timerfd); - delete clock_publisher; - - return NULL; -} - - } int main() { @@ -249,15 +191,8 @@ int main() { rawgps_init(); - err = pthread_create(&clock_thread_handle, NULL, - clock_thread, NULL); - assert(err == 0); - while(!do_exit) pause(); - err = pthread_join(clock_thread_handle, NULL); - assert(err == 0); - rawgps_destroy(); gps_destroy(); diff --git a/selfdrive/sensord/sensors.cc b/selfdrive/sensord/sensors.cc index 0f70bb66c..952a4863c 100644 --- a/selfdrive/sensord/sensors.cc +++ b/selfdrive/sensord/sensors.cc @@ -38,6 +38,7 @@ #define SENSOR_LIGHT 7 volatile sig_atomic_t do_exit = 0; +volatile sig_atomic_t re_init_sensors = 0; namespace { @@ -47,179 +48,190 @@ void set_do_exit(int sig) { void sigpipe_handler(int sig) { LOGE("SIGPIPE received"); + re_init_sensors = true; } void sensor_loop() { LOG("*** sensor loop"); - Context * c = Context::create(); - PubSocket * sensor_events_sock = PubSocket::create(c, "sensorEvents"); - assert(sensor_events_sock != NULL); - - struct sensors_poll_device_t* device; - struct sensors_module_t* module; - - hw_get_module(SENSORS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); - sensors_open(&module->common, &device); - - // required - struct sensor_t const* list; - int count = module->get_sensors_list(module, &list); - LOG("%d sensors found", count); - - if (getenv("SENSOR_TEST")) { - exit(count); - } - - for (int i = 0; i < count; i++) { - LOGD("sensor %4d: %4d %60s %d-%ld us", i, list[i].handle, list[i].name, list[i].minDelay, list[i].maxDelay); - } - - device->activate(device, SENSOR_MAGNETOMETER_UNCALIBRATED, 0); - device->activate(device, SENSOR_GYRO_UNCALIBRATED, 0); - device->activate(device, SENSOR_ACCELEROMETER, 0); - device->activate(device, SENSOR_MAGNETOMETER, 0); - device->activate(device, SENSOR_GYRO, 0); - device->activate(device, SENSOR_PROXIMITY, 0); - device->activate(device, SENSOR_LIGHT, 0); - - device->activate(device, SENSOR_MAGNETOMETER_UNCALIBRATED, 1); - device->activate(device, SENSOR_GYRO_UNCALIBRATED, 1); - device->activate(device, SENSOR_ACCELEROMETER, 1); - device->activate(device, SENSOR_MAGNETOMETER, 1); - device->activate(device, SENSOR_GYRO, 1); - device->activate(device, SENSOR_PROXIMITY, 1); - device->activate(device, SENSOR_LIGHT, 1); - - device->setDelay(device, SENSOR_GYRO_UNCALIBRATED, ms2ns(10)); - device->setDelay(device, SENSOR_MAGNETOMETER_UNCALIBRATED, ms2ns(100)); - device->setDelay(device, SENSOR_ACCELEROMETER, ms2ns(10)); - device->setDelay(device, SENSOR_GYRO, ms2ns(10)); - device->setDelay(device, SENSOR_MAGNETOMETER, ms2ns(100)); - device->setDelay(device, SENSOR_PROXIMITY, ms2ns(100)); - device->setDelay(device, SENSOR_LIGHT, ms2ns(100)); - - static const size_t numEvents = 16; - sensors_event_t buffer[numEvents]; - while (!do_exit) { - int n = device->poll(device, buffer, numEvents); - if (n == 0) continue; - if (n < 0) { - LOG("sensor_loop poll failed: %d", n); - continue; + Context * c = Context::create(); + PubSocket * sensor_events_sock = PubSocket::create(c, "sensorEvents"); + assert(sensor_events_sock != NULL); + + struct sensors_poll_device_t* device; + struct sensors_module_t* module; + + hw_get_module(SENSORS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); + sensors_open(&module->common, &device); + + // required + struct sensor_t const* list; + int count = module->get_sensors_list(module, &list); + LOG("%d sensors found", count); + + if (getenv("SENSOR_TEST")) { + exit(count); } - int log_events = 0; - for (int i=0; i < n; i++) { - switch (buffer[i].type) { - case SENSOR_TYPE_ACCELEROMETER: - case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: - case SENSOR_TYPE_MAGNETIC_FIELD: - case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: - case SENSOR_TYPE_GYROSCOPE: - case SENSOR_TYPE_PROXIMITY: - case SENSOR_TYPE_LIGHT: - log_events++; - break; - default: - continue; - } + for (int i = 0; i < count; i++) { + LOGD("sensor %4d: %4d %60s %d-%ld us", i, list[i].handle, list[i].name, list[i].minDelay, list[i].maxDelay); } - uint64_t log_time = nanos_since_boot(); + device->activate(device, SENSOR_MAGNETOMETER_UNCALIBRATED, 0); + device->activate(device, SENSOR_GYRO_UNCALIBRATED, 0); + device->activate(device, SENSOR_ACCELEROMETER, 0); + device->activate(device, SENSOR_MAGNETOMETER, 0); + device->activate(device, SENSOR_GYRO, 0); + device->activate(device, SENSOR_PROXIMITY, 0); + device->activate(device, SENSOR_LIGHT, 0); - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(log_time); + device->activate(device, SENSOR_MAGNETOMETER_UNCALIBRATED, 1); + device->activate(device, SENSOR_GYRO_UNCALIBRATED, 1); + device->activate(device, SENSOR_ACCELEROMETER, 1); + device->activate(device, SENSOR_MAGNETOMETER, 1); + device->activate(device, SENSOR_GYRO, 1); + device->activate(device, SENSOR_PROXIMITY, 1); + device->activate(device, SENSOR_LIGHT, 1); - auto sensor_events = event.initSensorEvents(log_events); + device->setDelay(device, SENSOR_GYRO_UNCALIBRATED, ms2ns(10)); + device->setDelay(device, SENSOR_MAGNETOMETER_UNCALIBRATED, ms2ns(100)); + device->setDelay(device, SENSOR_ACCELEROMETER, ms2ns(10)); + device->setDelay(device, SENSOR_GYRO, ms2ns(10)); + device->setDelay(device, SENSOR_MAGNETOMETER, ms2ns(100)); + device->setDelay(device, SENSOR_PROXIMITY, ms2ns(100)); + device->setDelay(device, SENSOR_LIGHT, ms2ns(100)); - int log_i = 0; - for (int i = 0; i < n; i++) { + static const size_t numEvents = 16; + sensors_event_t buffer[numEvents]; - const sensors_event_t& data = buffer[i]; - switch (data.type) { - case SENSOR_TYPE_ACCELEROMETER: - case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: - case SENSOR_TYPE_MAGNETIC_FIELD: - case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: - case SENSOR_TYPE_GYROSCOPE: - case SENSOR_TYPE_PROXIMITY: - case SENSOR_TYPE_LIGHT: - break; - default: + while (!do_exit) { + int n = device->poll(device, buffer, numEvents); + if (n == 0) continue; + if (n < 0) { + LOG("sensor_loop poll failed: %d", n); continue; } - auto log_event = sensor_events[log_i]; - - log_event.setSource(cereal::SensorEventData::SensorSource::ANDROID); - log_event.setVersion(data.version); - log_event.setSensor(data.sensor); - log_event.setType(data.type); - log_event.setTimestamp(data.timestamp); - - switch (data.type) { - case SENSOR_TYPE_ACCELEROMETER: { - auto svec = log_event.initAcceleration(); - kj::ArrayPtr vs(&data.acceleration.v[0], 3); - svec.setV(vs); - svec.setStatus(data.acceleration.status); - break; - } - case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: { - auto svec = log_event.initMagneticUncalibrated(); - // assuming the uncalib and bias floats are contiguous in memory - kj::ArrayPtr vs(&data.uncalibrated_magnetic.uncalib[0], 6); - svec.setV(vs); - break; - } - case SENSOR_TYPE_MAGNETIC_FIELD: { - auto svec = log_event.initMagnetic(); - kj::ArrayPtr vs(&data.magnetic.v[0], 3); - svec.setV(vs); - svec.setStatus(data.magnetic.status); - break; - } - case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: { - auto svec = log_event.initGyroUncalibrated(); - // assuming the uncalib and bias floats are contiguous in memory - kj::ArrayPtr vs(&data.uncalibrated_gyro.uncalib[0], 6); - svec.setV(vs); - break; - } - case SENSOR_TYPE_GYROSCOPE: { - auto svec = log_event.initGyro(); - kj::ArrayPtr vs(&data.gyro.v[0], 3); - svec.setV(vs); - svec.setStatus(data.gyro.status); - break; - } - case SENSOR_TYPE_PROXIMITY: { - log_event.setProximity(data.distance); - break; - } - case SENSOR_TYPE_LIGHT: - log_event.setLight(data.light); - break; + int log_events = 0; + for (int i=0; i < n; i++) { + switch (buffer[i].type) { + case SENSOR_TYPE_ACCELEROMETER: + case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: + case SENSOR_TYPE_MAGNETIC_FIELD: + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + case SENSOR_TYPE_GYROSCOPE: + case SENSOR_TYPE_PROXIMITY: + case SENSOR_TYPE_LIGHT: + log_events++; + break; + default: + continue; + } } - log_i++; + uint64_t log_time = nanos_since_boot(); + + capnp::MallocMessageBuilder msg; + cereal::Event::Builder event = msg.initRoot(); + event.setLogMonoTime(log_time); + + auto sensor_events = event.initSensorEvents(log_events); + + int log_i = 0; + for (int i = 0; i < n; i++) { + + const sensors_event_t& data = buffer[i]; + + switch (data.type) { + case SENSOR_TYPE_ACCELEROMETER: + case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: + case SENSOR_TYPE_MAGNETIC_FIELD: + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + case SENSOR_TYPE_GYROSCOPE: + case SENSOR_TYPE_PROXIMITY: + case SENSOR_TYPE_LIGHT: + break; + default: + continue; + } + + auto log_event = sensor_events[log_i]; + + log_event.setSource(cereal::SensorEventData::SensorSource::ANDROID); + log_event.setVersion(data.version); + log_event.setSensor(data.sensor); + log_event.setType(data.type); + log_event.setTimestamp(data.timestamp); + + switch (data.type) { + case SENSOR_TYPE_ACCELEROMETER: { + auto svec = log_event.initAcceleration(); + kj::ArrayPtr vs(&data.acceleration.v[0], 3); + svec.setV(vs); + svec.setStatus(data.acceleration.status); + break; + } + case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: { + auto svec = log_event.initMagneticUncalibrated(); + // assuming the uncalib and bias floats are contiguous in memory + kj::ArrayPtr vs(&data.uncalibrated_magnetic.uncalib[0], 6); + svec.setV(vs); + break; + } + case SENSOR_TYPE_MAGNETIC_FIELD: { + auto svec = log_event.initMagnetic(); + kj::ArrayPtr vs(&data.magnetic.v[0], 3); + svec.setV(vs); + svec.setStatus(data.magnetic.status); + break; + } + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: { + auto svec = log_event.initGyroUncalibrated(); + // assuming the uncalib and bias floats are contiguous in memory + kj::ArrayPtr vs(&data.uncalibrated_gyro.uncalib[0], 6); + svec.setV(vs); + break; + } + case SENSOR_TYPE_GYROSCOPE: { + auto svec = log_event.initGyro(); + kj::ArrayPtr vs(&data.gyro.v[0], 3); + svec.setV(vs); + svec.setStatus(data.gyro.status); + break; + } + case SENSOR_TYPE_PROXIMITY: { + log_event.setProximity(data.distance); + break; + } + case SENSOR_TYPE_LIGHT: + log_event.setLight(data.light); + break; + } + + log_i++; + } + + auto words = capnp::messageToFlatArray(msg); + auto bytes = words.asBytes(); + sensor_events_sock->send((char*)bytes.begin(), bytes.size()); + + if (re_init_sensors){ + LOGE("Resetting sensors"); + re_init_sensors = false; + break; + } } - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); - sensor_events_sock->send((char*)bytes.begin(), bytes.size()); + delete sensor_events_sock; + delete c; } +} - delete sensor_events_sock; - delete c; -} -} +}// Namespace end int main(int argc, char *argv[]) { setpriority(PRIO_PROCESS, 0, -13); diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py index 5dd1d0789..06b243769 100755 --- a/selfdrive/test/longitudinal_maneuvers/plant.py +++ b/selfdrive/test/longitudinal_maneuvers/plant.py @@ -272,6 +272,7 @@ class Plant(): 'INTERCEPTOR_GAS', 'INTERCEPTOR_GAS2', 'IMPERIAL_UNIT', + 'MOTOR_TORQUE', ]) vls = vls_tuple( self.speed_sensor(speed), @@ -304,7 +305,8 @@ class Plant(): 0, # Brake hold 0, # Interceptor feedback 0, # Interceptor 2 feedback - False + False, + 0, ) # TODO: publish each message at proper frequency diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index fdce596e4..14969954c 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -159,11 +159,11 @@ def get_car_params(msgs, fsm, can_sock): _, CP = get_car(can, sendcan) Params().put("CarParams", CP.to_bytes()) -def radar_rcv_callback(msg, CP): +def radar_rcv_callback(msg, CP, cfg, fsm): if msg.which() != "can": - return [] + return [], False elif CP.radarOffCan: - return ["radarState", "liveTracks"] + return ["radarState", "liveTracks"], True radar_msgs = {"honda": [0x445], "toyota": [0x19f, 0x22f], "gm": [0x474], "chrysler": [0x2d4]}.get(CP.carName, None) @@ -173,8 +173,15 @@ def radar_rcv_callback(msg, CP): for m in msg.can: if m.src == 1 and m.address in radar_msgs: - return ["radarState", "liveTracks"] - return [] + return ["radarState", "liveTracks"], True + return [], False + +def calibration_rcv_callback(msg, CP, cfg, fsm): + # calibrationd publishes 1 calibrationData every 5 cameraOdometry packets. + # should_recv always true to increment frame + recv_socks = ["liveCalibration"] if (fsm.frame + 1) % 5 == 0 else [] + return recv_socks, True + CONFIGS = [ ProcessConfig( @@ -215,7 +222,7 @@ CONFIGS = [ }, ignore=[("logMonoTime", 0), ("valid", True)], init_callback=get_car_params, - should_recv_callback=None, + should_recv_callback=calibration_rcv_callback, ), ] @@ -263,12 +270,11 @@ def replay_process(cfg, lr): log_msgs, msg_queue = [], [] for msg in tqdm(pub_msgs): if cfg.should_recv_callback is not None: - recv_socks = cfg.should_recv_callback(msg, CP) + recv_socks, should_recv = cfg.should_recv_callback(msg, CP, cfg, fsm) else: recv_socks = [s for s in cfg.pub_sub[msg.which()] if (fsm.frame + 1) % int(service_list[msg.which()].frequency / service_list[s].frequency) == 0] - - should_recv = bool(len(recv_socks)) + should_recv = bool(len(recv_socks)) if msg.which() == 'can': can_sock.send(msg.as_builder().to_bytes()) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 0c0be19b6..823608db0 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -89304bdcab73fa43a8dd39cab93bc4ea4c9cbbdb \ No newline at end of file +b60841eb6cf09037200bc2daacf0c9cf69b358fe \ No newline at end of file diff --git a/selfdrive/test/test_car_models.py b/selfdrive/test/test_car_models.py index 0d18496d5..c1aa39a7f 100755 --- a/selfdrive/test/test_car_models.py +++ b/selfdrive/test/test_car_models.py @@ -39,8 +39,6 @@ def wait_for_socket(name, timeout=10.0): break time.sleep(0.5) - if r is None: - sys.exit(-1) return r def get_route_logs(route_name): @@ -405,42 +403,48 @@ passive_routes = [ #"bfa17080b080f3ec|2018-06-28--23-27-47", ] +forced_dashcam_routes = [ + # Ford fusion + "f1b4c567731f4a1b|2018-04-18--11-29-37", + "f1b4c567731f4a1b|2018-04-30--10-15-35", +] + # TODO: replace all these with public routes # TODO: add routes for untested cars: HONDA ACCORD 2018 HYBRID TOURING and CHRYSLER PACIFICA 2018 non_public_routes = [ - "0607d2516fc2148f|2019-02-13--23-03-16", # CHRYSLER PACIFICA HYBRID 2019 - "3e9592a1c78a3d63|2018-02-08--20-28-24", # HONDA PILOT 2017 TOURING - "aa20e335f61ba898|2019-02-05--16-59-04", # BUICK REGAL ESSENCE 2018 - "1851183c395ef471|2018-05-31--18-07-21", # HONDA CR-V 2017 EX - "9d5fb4f0baa1b3e1|2019-01-14--17-45-59", # KIA SORENTO GT LINE 2018 - "b4c18bf13d5955da|2018-07-29--13-39-46", # TOYOTA C-HR HYBRID 2018 - "5a2cfe4bb362af9e|2018-02-02--23-41-07", # ACURA RDX 2018 ACURAWATCH PLUS - "362d23d4d5bea2fa|2018-08-10--13-31-40", # TOYOTA HIGHLANDER HYBRID 2018 - "aa20e335f61ba898|2018-12-17--21-10-37", # BUICK REGAL ESSENCE 2018 - "215cd70e9c349266|2018-11-25--22-22-12", # KIA STINGER GT2 2018 - "192a598e34926b1e|2019-04-04--13-27-39", # JEEP GRAND CHEROKEE 2019 - "34a84d2b765df688|2018-08-28--12-41-00", # HONDA PILOT 2019 ELITE - "b0c9d2329ad1606b|2019-01-06--10-11-23", # CHRYSLER PACIFICA HYBRID 2017 - "31390e3eb6f7c29a|2019-01-23--08-56-00", # KIA OPTIMA SX 2019 - "fd10b9a107bb2e49|2018-07-24--16-32-42", # TOYOTA C-HR 2018 - "9f7a7e50a51fb9db|2019-01-17--18-34-21", # JEEP GRAND CHEROKEE V6 2018 - "aadda448b49c99ad|2018-10-25--17-16-22", # CHEVROLET MALIBU PREMIER 2017 - "362d23d4d5bea2fa|2018-09-02--17-03-55", # TOYOTA HIGHLANDER HYBRID 2018 - "1582e1dc57175194|2018-08-15--07-46-07", # HONDA ACCORD 2018 LX 1.5T - "fd10b9a107bb2e49|2018-07-24--20-32-08", # TOYOTA C-HR 2018 - "265007255e794bce|2018-11-24--22-08-31", # CADILLAC ATS Premium Performance 2018 - "53ac3251e03f95d7|2019-01-10--13-43-32", # HYUNDAI ELANTRA LIMITED ULTIMATE 2017 - "21aa231dee2a68bd|2018-01-30--04-54-41", # HONDA ODYSSEY 2018 EX-L - "900ad17e536c3dc7|2018-04-12--22-02-36", # HONDA RIDGELINE 2017 BLACK EDITION - "975b26878285314d|2018-12-25--14-42-13", # CHRYSLER PACIFICA HYBRID 2018 - "8ae193ceb56a0efe|2018-06-18--20-07-32", # TOYOTA RAV4 HYBRID 2017 - "a893a80e5c5f72c8|2019-01-14--20-02-59", # HYUNDAI GENESIS 2018 - "49c73650e65ff465|2018-11-19--16-58-04", # HOLDEN ASTRA RS-V BK 2017 - "d2d8152227f7cb82|2018-07-25--13-40-56", # TOYOTA CAMRY 2018 - "07cb8a788c31f645|2018-06-17--18-50-29", # mock - "c9d60e5e02c04c5c|2018-01-08--16-01-49", # HONDA CR-V 2016 TOURING - "1632088eda5e6c4d|2018-06-07--08-03-18", # HONDA CIVIC HATCHBACK 2017 SEDAN/COUPE 2019 - "fbd011384db5e669|2018-07-26--20-51-48", # TOYOTA CAMRY HYBRID 2018 + "0607d2516fc2148f|2019-02-13--23-03-16", # CHRYSLER PACIFICA HYBRID 2019 + "3e9592a1c78a3d63|2018-02-08--20-28-24", # HONDA PILOT 2017 TOURING + "aa20e335f61ba898|2019-02-05--16-59-04", # BUICK REGAL ESSENCE 2018 + "1851183c395ef471|2018-05-31--18-07-21", # HONDA CR-V 2017 EX + "9d5fb4f0baa1b3e1|2019-01-14--17-45-59", # KIA SORENTO GT LINE 2018 + "b4c18bf13d5955da|2018-07-29--13-39-46", # TOYOTA C-HR HYBRID 2018 + "5a2cfe4bb362af9e|2018-02-02--23-41-07", # ACURA RDX 2018 ACURAWATCH PLUS + "362d23d4d5bea2fa|2018-08-10--13-31-40", # TOYOTA HIGHLANDER HYBRID 2018 + "aa20e335f61ba898|2018-12-17--21-10-37", # BUICK REGAL ESSENCE 2018 + "215cd70e9c349266|2018-11-25--22-22-12", # KIA STINGER GT2 2018 + "192a598e34926b1e|2019-04-04--13-27-39", # JEEP GRAND CHEROKEE 2019 + "34a84d2b765df688|2018-08-28--12-41-00", # HONDA PILOT 2019 ELITE + "b0c9d2329ad1606b|2019-01-06--10-11-23", # CHRYSLER PACIFICA HYBRID 2017 + "31390e3eb6f7c29a|2019-01-23--08-56-00", # KIA OPTIMA SX 2019 + "fd10b9a107bb2e49|2018-07-24--16-32-42", # TOYOTA C-HR 2018 + "9f7a7e50a51fb9db|2019-01-17--18-34-21", # JEEP GRAND CHEROKEE V6 2018 + "aadda448b49c99ad|2018-10-25--17-16-22", # CHEVROLET MALIBU PREMIER 2017 + "362d23d4d5bea2fa|2018-09-02--17-03-55", # TOYOTA HIGHLANDER HYBRID 2018 + "1582e1dc57175194|2018-08-15--07-46-07", # HONDA ACCORD 2018 LX 1.5T + "fd10b9a107bb2e49|2018-07-24--20-32-08", # TOYOTA C-HR 2018 + "265007255e794bce|2018-11-24--22-08-31", # CADILLAC ATS Premium Performance 2018 + "53ac3251e03f95d7|2019-01-10--13-43-32", # HYUNDAI ELANTRA LIMITED ULTIMATE 2017 + "21aa231dee2a68bd|2018-01-30--04-54-41", # HONDA ODYSSEY 2018 EX-L + "900ad17e536c3dc7|2018-04-12--22-02-36", # HONDA RIDGELINE 2017 BLACK EDITION + "975b26878285314d|2018-12-25--14-42-13", # CHRYSLER PACIFICA HYBRID 2018 + "8ae193ceb56a0efe|2018-06-18--20-07-32", # TOYOTA RAV4 HYBRID 2017 + "a893a80e5c5f72c8|2019-01-14--20-02-59", # HYUNDAI GENESIS 2018 + "49c73650e65ff465|2018-11-19--16-58-04", # HOLDEN ASTRA RS-V BK 2017 + "d2d8152227f7cb82|2018-07-25--13-40-56", # TOYOTA CAMRY 2018 + "07cb8a788c31f645|2018-06-17--18-50-29", # mock + "c9d60e5e02c04c5c|2018-01-08--16-01-49", # HONDA CR-V 2016 TOURING + "1632088eda5e6c4d|2018-06-07--08-03-18", # HONDA CIVIC HATCHBACK 2017 SEDAN/COUPE 2019 + "fbd011384db5e669|2018-07-26--20-51-48", # TOYOTA CAMRY HYBRID 2018 ] if __name__ == "__main__": @@ -458,96 +462,103 @@ if __name__ == "__main__": elif "UNLOGGER_PATH" not in os.environ: continue - for _ in range(3): - shutil.rmtree('/data/params') - manager.gctx = {} - params = Params() - params.manager_start() - if route in passive_routes: - params.put("Passive", "1") - else: - params.put("Passive", "0") + shutil.rmtree('/data/params') + manager.gctx = {} + params = Params() + params.manager_start() + params.put("OpenpilotEnabledToggle", "1") + params.put("CommunityFeaturesToggle", "1") - print("testing ", route, " ", checks['carFingerprint']) - print("Preparing processes") - manager.prepare_managed_process("radard") - manager.prepare_managed_process("controlsd") - manager.prepare_managed_process("plannerd") - print("Starting processes") - manager.start_managed_process("radard") - manager.start_managed_process("controlsd") - manager.start_managed_process("plannerd") - time.sleep(2) + if route in passive_routes: + params.put("Passive", "1") + else: + params.put("Passive", "0") - # Start unlogger - print("Start unlogger") - if route in non_public_routes: - unlogger_cmd = [os.path.join(BASEDIR, os.environ['UNLOGGER_PATH']), '%s' % route, '--disable', 'frame,plan,pathPlan,liveLongitudinalMpc,radarState,controlsState,liveTracks,liveMpc,sendcan,carState,carControl,carEvents,carParams', '--no-interactive'] - else: - unlogger_cmd = [os.path.join(BASEDIR, 'tools/replay/unlogger.py'), '%s' % route, '/tmp', '--disable', 'frame,plan,pathPlan,liveLongitudinalMpc,radarState,controlsState,liveTracks,liveMpc,sendcan,carState,carControl,carEvents,carParams', '--no-interactive'] - unlogger = subprocess.Popen(unlogger_cmd, preexec_fn=os.setsid) + print("testing ", route, " ", checks['carFingerprint']) + print("Preparing processes") + manager.prepare_managed_process("radard") + manager.prepare_managed_process("controlsd") + manager.prepare_managed_process("plannerd") + print("Starting processes") + manager.start_managed_process("radard") + manager.start_managed_process("controlsd") + manager.start_managed_process("plannerd") + time.sleep(2) - print("Check sockets") - controls_state_result = wait_for_socket('controlsState', timeout=30) - radarstate_result = wait_for_socket('radarState', timeout=30) - plan_result = wait_for_socket('plan', timeout=30) + # Start unlogger + print("Start unlogger") + if route in non_public_routes: + unlogger_cmd = [os.path.join(BASEDIR, os.environ['UNLOGGER_PATH']), '%s' % route, '--disable', 'frame,plan,pathPlan,liveLongitudinalMpc,radarState,controlsState,liveTracks,liveMpc,sendcan,carState,carControl,carEvents,carParams', '--no-interactive'] + else: + unlogger_cmd = [os.path.join(BASEDIR, 'tools/replay/unlogger.py'), '%s' % route, '/tmp', '--disable', 'frame,plan,pathPlan,liveLongitudinalMpc,radarState,controlsState,liveTracks,liveMpc,sendcan,carState,carControl,carEvents,carParams', '--no-interactive'] + unlogger = subprocess.Popen(unlogger_cmd, preexec_fn=os.setsid) - if route not in passive_routes: # TODO The passive routes have very flaky models - path_plan_result = wait_for_socket('pathPlan', timeout=30) - else: - path_plan_result = True + print("Check sockets") + controls_state_result = wait_for_socket('controlsState', timeout=30) - carstate_result = wait_for_socket('carState', timeout=30) + has_camera = checks.get('enableCamera', False) + if (route not in passive_routes) and (route not in forced_dashcam_routes) and has_camera: + controls_state_result = controls_state_result and wait_for_socket('sendcan', timeout=30) - print("Check if everything is running") - running = manager.get_running() - controlsd_running = running['controlsd'].is_alive() - radard_running = running['radard'].is_alive() - plannerd_running = running['plannerd'].is_alive() + radarstate_result = wait_for_socket('radarState', timeout=30) + plan_result = wait_for_socket('plan', timeout=30) - manager.kill_managed_process("controlsd") - manager.kill_managed_process("radard") - manager.kill_managed_process("plannerd") - os.killpg(os.getpgid(unlogger.pid), signal.SIGTERM) + if route not in passive_routes: # TODO The passive routes have very flaky models + path_plan_result = wait_for_socket('pathPlan', timeout=30) + else: + path_plan_result = True - sockets_ok = all([ - controls_state_result, radarstate_result, plan_result, path_plan_result, carstate_result, - controlsd_running, radard_running, plannerd_running - ]) - params_ok = True - failures = [] + carstate_result = wait_for_socket('carState', timeout=30) - if not controlsd_running: - failures.append('controlsd') - if not radard_running: - failures.append('radard') - if not radarstate_result: - failures.append('radarState') - if not controls_state_result: - failures.append('controlsState') - if not plan_result: - failures.append('plan') - if not path_plan_result: - failures.append('pathPlan') + print("Check if everything is running") + running = manager.get_running() + controlsd_running = running['controlsd'].is_alive() + radard_running = running['radard'].is_alive() + plannerd_running = running['plannerd'].is_alive() - try: - car_params = car.CarParams.from_bytes(params.get("CarParams")) - for k, v in checks.items(): - if not v == getattr(car_params, k): - params_ok = False - failures.append(k) - except: - params_ok = False + manager.kill_managed_process("controlsd") + manager.kill_managed_process("radard") + manager.kill_managed_process("plannerd") + os.killpg(os.getpgid(unlogger.pid), signal.SIGTERM) - if sockets_ok and params_ok: - print("Success") - results[route] = True, failures - break - else: - print("Failure") - results[route] = False, failures + sockets_ok = all([ + controls_state_result, radarstate_result, plan_result, path_plan_result, carstate_result, + controlsd_running, radard_running, plannerd_running + ]) + params_ok = True + failures = [] - time.sleep(2) + if not controlsd_running: + failures.append('controlsd') + if not radard_running: + failures.append('radard') + if not radarstate_result: + failures.append('radarState') + if not controls_state_result: + failures.append('controlsState') + if not plan_result: + failures.append('plan') + if not path_plan_result: + failures.append('pathPlan') + + try: + car_params = car.CarParams.from_bytes(params.get("CarParams")) + for k, v in checks.items(): + if not v == getattr(car_params, k): + params_ok = False + failures.append(k) + except Exception: + params_ok = False + + if sockets_ok and params_ok: + print("Success") + results[route] = True, failures + else: + print("Failure") + results[route] = False, failures + break + + time.sleep(2) for route in results: print(results[route]) diff --git a/selfdrive/test/test_openpilot.py b/selfdrive/test/test_openpilot.py index 68e35d90b..27b964c47 100644 --- a/selfdrive/test/test_openpilot.py +++ b/selfdrive/test/test_openpilot.py @@ -149,11 +149,11 @@ def test_athena(): assert False, f"Athena did not start within {timeout} seconds" time.sleep(0.5) - def athena_post(payload, max_retries=5): + def athena_post(payload, max_retries=5, wait=5): tries = 0 while 1: try: - return requests.post( + resp = requests.post( "https://athena.comma.ai/" + params.get("DongleId", encoding="utf-8"), headers={ "Authorization": "JWT " + os.getenv("COMMA_JWT"), @@ -162,29 +162,26 @@ def test_athena(): data=json.dumps(payload), timeout=30 ) + resp_json = resp.json() + if resp_json.get('error'): + raise Exception(resp_json['error']) + return resp_json except Exception as e: - print(e) - time.sleep(5.0) + time.sleep(wait) tries += 1 if tries == max_retries: raise + else: + print(f'athena_post failed {e}. retrying...') - def expect_athena_registers(timeout=60): - now = time.time() - while 1: - resp = athena_post({ - "method": "echo", - "params": ["hello"], - "id": 0, - "jsonrpc": "2.0" - }) - resp_json = resp.json() - if resp_json.get('result') == "hello": - break - elif time.time() - now > timeout: - assert False, f"Athena did not become available within {timeout} seconds." - else: - time.sleep(5.0) + def expect_athena_registers(): + resp = athena_post({ + "method": "echo", + "params": ["hello"], + "id": 0, + "jsonrpc": "2.0" + }, max_retries=12, wait=5) + assert resp.get('result') == "hello", f'Athena failed to register ({resp})' try: athenad_pid = expect_athena_starts() @@ -204,9 +201,8 @@ def test_athena(): "id": 0, "jsonrpc": "2.0" }) - resp_json = resp.json() - assert resp_json.get('result'), resp_json - assert 'sim_id' in resp_json['result'], resp_json['result'] + assert resp.get('result'), resp + assert 'sim_id' in resp['result'], resp['result'] print("ATHENA: takeSnapshot") resp = athena_post({ @@ -214,9 +210,8 @@ def test_athena(): "id": 0, "jsonrpc": "2.0" }) - resp_json = resp.json() - assert resp_json.get('result'), resp_json - assert resp_json['result']['jpegBack'], resp_json['result'] + assert resp.get('result'), resp + assert resp['result']['jpegBack'], resp['result'] @with_processes(["thermald"]) def test_athena_thermal(): @@ -227,9 +222,8 @@ def test_athena(): "id": 0, "jsonrpc": "2.0" }) - resp_json = resp.json() - assert resp_json.get('result'), resp_json - assert resp_json['result']['thermal'], resp_json['result'] + assert resp.get('result'), resp + assert resp['result']['thermal'], resp['result'] test_athena_thermal() finally: try: diff --git a/selfdrive/thermald.py b/selfdrive/thermald.py index d78e0011d..aa4c47a6e 100755 --- a/selfdrive/thermald.py +++ b/selfdrive/thermald.py @@ -6,6 +6,7 @@ import datetime import psutil from smbus2 import SMBus from cereal import log +from common.android import ANDROID from common.basedir import BASEDIR from common.params import Params from common.realtime import sec_since_boot, DT_TRML @@ -15,9 +16,9 @@ from selfdrive.version import terms_version, training_version from selfdrive.swaglog import cloudlog import cereal.messaging as messaging from selfdrive.loggerd.config import get_available_percent -from selfdrive.pandad import get_expected_version +from selfdrive.pandad import get_expected_signature -FW_VERSION = get_expected_version() +FW_SIGNATURE = get_expected_signature() ThermalStatus = log.ThermalData.ThermalStatus CURRENT_TAU = 15. # 15s time constant @@ -29,10 +30,13 @@ with open(BASEDIR + "/selfdrive/controls/lib/alerts_offroad.json") as json_file: OFFROAD_ALERTS = json.load(json_file) def read_tz(x, clip=True): - with open("/sys/devices/virtual/thermal/thermal_zone%d/temp" % x) as f: - ret = int(f.read()) - if clip: - ret = max(0, ret) + try: + with open("/sys/devices/virtual/thermal/thermal_zone%d/temp" % x) as f: + ret = int(f.read()) + if clip: + ret = max(0, ret) + except FileNotFoundError: + return 0 return ret @@ -156,7 +160,7 @@ def thermald_thread(): should_start_prev = False is_uno = (read_tz(29, clip=False) < -1000) - if is_uno: + if is_uno or not ANDROID: handle_fan = handle_fan_uno else: setup_eon_fan() @@ -178,20 +182,28 @@ def thermald_thread(): if health is not None: usb_power = health.health.usbPowerMode != log.HealthData.UsbPowerMode.client - msg.thermal.freeSpace = get_available_percent() / 100.0 # disk space + msg.thermal.freeSpace = get_available_percent(default=100.0) / 100.0 msg.thermal.memUsedPercent = int(round(psutil.virtual_memory().percent)) msg.thermal.cpuPerc = int(round(psutil.cpu_percent())) - with open("/sys/class/power_supply/battery/capacity") as f: - msg.thermal.batteryPercent = int(f.read()) - with open("/sys/class/power_supply/battery/status") as f: - msg.thermal.batteryStatus = f.read().strip() - with open("/sys/class/power_supply/battery/current_now") as f: - msg.thermal.batteryCurrent = int(f.read()) - with open("/sys/class/power_supply/battery/voltage_now") as f: - msg.thermal.batteryVoltage = int(f.read()) - with open("/sys/class/power_supply/usb/present") as f: - msg.thermal.usbOnline = bool(int(f.read())) + try: + with open("/sys/class/power_supply/battery/capacity") as f: + msg.thermal.batteryPercent = int(f.read()) + with open("/sys/class/power_supply/battery/status") as f: + msg.thermal.batteryStatus = f.read().strip() + with open("/sys/class/power_supply/battery/current_now") as f: + msg.thermal.batteryCurrent = int(f.read()) + with open("/sys/class/power_supply/battery/voltage_now") as f: + msg.thermal.batteryVoltage = int(f.read()) + with open("/sys/class/power_supply/usb/present") as f: + msg.thermal.usbOnline = bool(int(f.read())) + except FileNotFoundError: + pass + + # Fake battery levels on uno for frame + if is_uno: + msg.thermal.batteryPercent = 100 + msg.thermal.batteryStatus = "Charging" current_filter.update(msg.thermal.batteryCurrent / 1e6) @@ -268,8 +280,9 @@ def thermald_thread(): do_uninstall = params.get("DoUninstall") == b"1" accepted_terms = params.get("HasAcceptedTerms") == terms_version completed_training = params.get("CompletedTrainingVersion") == training_version - fw_version = params.get("PandaFirmware", encoding="utf8") - fw_version_match = fw_version is None or fw_version.startswith(FW_VERSION) # don't show alert is no panda is connected (None) + + panda_signature = params.get("PandaFirmware") + fw_version_match = (panda_signature is None) or (panda_signature == FW_SIGNATURE) # don't show alert is no panda is connected (None) should_start = ignition @@ -306,12 +319,18 @@ def thermald_thread(): params.delete("Offroad_TemperatureTooHigh") if should_start: + if not should_start_prev: + params.delete("IsOffroad") + off_ts = None if started_ts is None: started_ts = sec_since_boot() started_seen = True os.system('echo performance > /sys/class/devfreq/soc:qcom,cpubw/governor') else: + if should_start_prev or (count == 0): + params.put("IsOffroad", "1") + started_ts = None if off_ts is None: off_ts = sec_since_boot() diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index b3e96e79a..ad96cb0a4 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,5 +1,15 @@ -Import('env', 'common', 'messaging', 'gpucommon', 'visionipc', 'cereal') +Import('env', 'arch', 'common', 'messaging', 'gpucommon', 'visionipc', 'cereal') -env.Program('_ui', ['ui.cc', 'slplay.c', '#phonelibs/nanovg/nanovg.c'], +src = ['ui.cc', 'paint.cc', '#phonelibs/nanovg/nanovg.c'] +libs = [common, 'zmq', 'czmq', 'capnp', 'capnp_c', 'm', cereal, 'json', messaging, 'OpenCL', gpucommon, visionipc] + +if arch == "aarch64": + src += ['sound.cc', 'slplay.c'] + libs += ['EGL', 'GLESv3', 'gnustl_shared', 'log', 'utils', 'gui', 'hardware', 'ui', 'CB', 'gsl', 'adreno_utils', 'OpenSLES', 'cutils', 'uuid'] +else: + src += ['linux.cc'] + libs += ['EGL', 'pthread', 'X11-xcb', 'xcb', 'X11', 'glfw'] + +env.Program('_ui', src, LINKFLAGS=['-Wl,-rpath=/system/lib64,-rpath=/system/comma/usr/lib'], - LIBS=[common, 'zmq', 'czmq', 'capnp', 'capnp_c', 'm', 'GLESv3', 'EGL', cereal, 'gnustl_shared', 'log', 'utils', 'gui', 'hardware', 'ui', 'json', messaging, 'CB', 'OpenCL', 'gsl', 'adreno_utils', 'OpenSLES', 'cutils', 'uuid', gpucommon, visionipc]) + LIBS=libs) diff --git a/selfdrive/ui/linux.cc b/selfdrive/ui/linux.cc new file mode 100644 index 000000000..62531e356 --- /dev/null +++ b/selfdrive/ui/linux.cc @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +#include "ui.hpp" + +#define GLFW_INCLUDE_ES2 +#define GLFW_INCLUDE_GLEXT +#include + +typedef struct FramebufferState FramebufferState; +typedef struct TouchState TouchState; + +#define FALSE 0 +#define TRUE 1 + +#include +#include + +extern "C" { + +FramebufferState* framebuffer_init( + const char* name, int32_t layer, int alpha, + int *out_w, int *out_h) { + glfwInit(); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_RESIZABLE, 0); + GLFWwindow* window; + window = glfwCreateWindow(1920, 1080, "ui", NULL, NULL); + if (!window) { + printf("glfwCreateWindow failed\n"); + } + + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + + // clear screen + glClearColor(0.2f, 0.2f, 0.2f, 1.0f ); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + framebuffer_swap((FramebufferState*)window); + + if (out_w) *out_w = 1920; + if (out_h) *out_h = 1080; + + return (FramebufferState*)window; +} + +void framebuffer_set_power(FramebufferState *s, int mode) { +} + +void framebuffer_swap(FramebufferState *s) { + glfwSwapBuffers((GLFWwindow*)s); +} + +void touch_init(TouchState *s) { + printf("touch_init\n"); +} + +int touch_poll(TouchState *s, int* out_x, int* out_y, int timeout) { + return -1; +} + +int touch_read(TouchState *s, int* out_x, int* out_y) { + return -1; +} + +} + +#include "sound.hpp" + +void ui_sound_init() {} +void ui_sound_destroy() {} + +void set_volume(int volume) {} + +void play_alert_sound(AudibleAlert alert) {} +void stop_alert_sound(AudibleAlert alert) {} + +#include "common/visionimg.h" +#include + +GLuint visionimg_to_gl(const VisionImg *img, EGLImageKHR *pkhr, void **pph) { + unsigned int texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img->width, img->height, 0, GL_RGB, GL_UNSIGNED_BYTE, *pph); + glGenerateMipmap(GL_TEXTURE_2D); + *pkhr = (EGLImageKHR *)1; // not NULL + return texture; +} + +void visionimg_destroy_gl(EGLImageKHR khr, void *ph) { + // empty +} + diff --git a/selfdrive/ui/paint.cc b/selfdrive/ui/paint.cc new file mode 100644 index 000000000..0728a5eab --- /dev/null +++ b/selfdrive/ui/paint.cc @@ -0,0 +1,1050 @@ +#include +#include "ui.hpp" + +#include "common/util.h" + +#define NANOVG_GLES3_IMPLEMENTATION + +#include "nanovg_gl.h" +#include "nanovg_gl_utils.h" + +extern "C"{ +#include "common/glutil.h" +} + +// TODO: this is also hardcoded in common/transformations/camera.py +const mat3 intrinsic_matrix = (mat3){{ + 910., 0., 582., + 0., 910., 437., + 0., 0., 1. +}}; + +const uint8_t alert_colors[][4] = { + [STATUS_STOPPED] = {0x07, 0x23, 0x39, 0xf1}, + [STATUS_DISENGAGED] = {0x17, 0x33, 0x49, 0xc8}, + [STATUS_ENGAGED] = {0x17, 0x86, 0x44, 0xf1}, + [STATUS_WARNING] = {0xDA, 0x6F, 0x25, 0xf1}, + [STATUS_ALERT] = {0xC9, 0x22, 0x31, 0xf1}, +}; + +const int alert_sizes[] = { + [ALERTSIZE_NONE] = 0, + [ALERTSIZE_SMALL] = 241, + [ALERTSIZE_MID] = 390, + [ALERTSIZE_FULL] = vwp_h, +}; + +// Projects a point in car to space to the corresponding point in full frame +// image space. +vec3 car_space_to_full_frame(const UIState *s, vec4 car_space_projective) { + const UIScene *scene = &s->scene; + + // We'll call the car space point p. + // First project into normalized image coordinates with the extrinsics matrix. + const vec4 Ep4 = matvecmul(scene->extrinsic_matrix, car_space_projective); + + // The last entry is zero because of how we store E (to use matvecmul). + const vec3 Ep = {{Ep4.v[0], Ep4.v[1], Ep4.v[2]}}; + const vec3 KEp = matvecmul3(intrinsic_matrix, Ep); + + // Project. + const vec3 p_image = {{KEp.v[0] / KEp.v[2], KEp.v[1] / KEp.v[2], 1.}}; + return p_image; +} + +// Calculate an interpolation between two numbers at a specific increment +static float lerp(float v0, float v1, float t) { + return (1 - t) * v0 + t * v1; +} + +static void draw_chevron(UIState *s, float x_in, float y_in, float sz, + NVGcolor fillColor, NVGcolor glowColor) { + const UIScene *scene = &s->scene; + + nvgSave(s->vg); + + nvgTranslate(s->vg, 240.0f, 0.0); + nvgTranslate(s->vg, -1440.0f / 2, -1080.0f / 2); + nvgScale(s->vg, 2.0, 2.0); + nvgScale(s->vg, 1440.0f / s->rgb_width, 1080.0f / s->rgb_height); + + const vec4 p_car_space = (vec4){{x_in, y_in, 0., 1.}}; + const vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); + + sz *= 30; + sz /= (x_in / 3 + 30); + if (sz > 30) sz = 30; + if (sz < 15) sz = 15; + + float x = p_full_frame.v[0]; + float y = p_full_frame.v[1]; + + // glow + nvgBeginPath(s->vg); + float g_xo = sz/5; + float g_yo = sz/10; + if (x >= 0 && y >= 0.) { + nvgMoveTo(s->vg, x+(sz*1.35)+g_xo, y+sz+g_yo); + nvgLineTo(s->vg, x, y-g_xo); + nvgLineTo(s->vg, x-(sz*1.35)-g_xo, y+sz+g_yo); + nvgLineTo(s->vg, x+(sz*1.35)+g_xo, y+sz+g_yo); + nvgClosePath(s->vg); + } + nvgFillColor(s->vg, glowColor); + nvgFill(s->vg); + + // chevron + nvgBeginPath(s->vg); + if (x >= 0 && y >= 0.) { + nvgMoveTo(s->vg, x+(sz*1.25), y+sz); + nvgLineTo(s->vg, x, y); + nvgLineTo(s->vg, x-(sz*1.25), y+sz); + nvgLineTo(s->vg, x+(sz*1.25), y+sz); + nvgClosePath(s->vg); + } + nvgFillColor(s->vg, fillColor); + nvgFill(s->vg); + + nvgRestore(s->vg); +} + +static void ui_draw_lane_line(UIState *s, const model_path_vertices_data *pvd, NVGcolor color) { + const UIScene *scene = &s->scene; + + nvgSave(s->vg); + nvgTranslate(s->vg, 240.0f, 0.0); // rgb-box space + nvgTranslate(s->vg, -1440.0f / 2, -1080.0f / 2); // zoom 2x + nvgScale(s->vg, 2.0, 2.0); + nvgScale(s->vg, 1440.0f / s->rgb_width, 1080.0f / s->rgb_height); + nvgBeginPath(s->vg); + + bool started = false; + for (int i=0; icnt; i++) { + if (pvd->v[i].x < 0 || pvd->v[i].y < 0.) { + continue; + } + if (!started) { + nvgMoveTo(s->vg, pvd->v[i].x, pvd->v[i].y); + started = true; + } else { + nvgLineTo(s->vg, pvd->v[i].x, pvd->v[i].y); + } + } + + nvgClosePath(s->vg); + nvgFillColor(s->vg, color); + nvgFill(s->vg); + nvgRestore(s->vg); +} + +static void update_track_data(UIState *s, bool is_mpc, track_vertices_data *pvd) { + const UIScene *scene = &s->scene; + const PathData path = scene->model.path; + const float *mpc_x_coords = &scene->mpc_x[0]; + const float *mpc_y_coords = &scene->mpc_y[0]; + + bool started = false; + float off = is_mpc?0.3:0.5; + float lead_d = scene->lead_d_rel*2.; + float path_height = is_mpc?(lead_d>5.)?fmin(lead_d, 25.)-fmin(lead_d*0.35, 10.):20. + :(lead_d>0.)?fmin(lead_d, 50.)-fmin(lead_d*0.35, 10.):49.; + pvd->cnt = 0; + // left side up + for (int i=0; i<=path_height; i++) { + float px, py, mpx; + if (is_mpc) { + mpx = i==0?0.0:mpc_x_coords[i]; + px = lerp(mpx+1.0, mpx, i/100.0); + py = mpc_y_coords[i] - off; + } else { + px = lerp(i+1.0, i, i/100.0); + py = path.points[i] - off; + } + + vec4 p_car_space = (vec4){{px, py, 0., 1.}}; + vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); + if (p_full_frame.v[0] < 0. || p_full_frame.v[1] < 0.) { + continue; + } + pvd->v[pvd->cnt].x = p_full_frame.v[0]; + pvd->v[pvd->cnt].y = p_full_frame.v[1]; + pvd->cnt += 1; + } + + // right side down + for (int i=path_height; i>=0; i--) { + float px, py, mpx; + if (is_mpc) { + mpx = i==0?0.0:mpc_x_coords[i]; + px = lerp(mpx+1.0, mpx, i/100.0); + py = mpc_y_coords[i] + off; + } else { + px = lerp(i+1.0, i, i/100.0); + py = path.points[i] + off; + } + + vec4 p_car_space = (vec4){{px, py, 0., 1.}}; + vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); + pvd->v[pvd->cnt].x = p_full_frame.v[0]; + pvd->v[pvd->cnt].y = p_full_frame.v[1]; + pvd->cnt += 1; + } +} + +static void update_all_track_data(UIState *s) { + const UIScene *scene = &s->scene; + // Draw vision path + update_track_data(s, false, &s->track_vertices[0]); + + if (scene->engaged) { + // Draw MPC path when engaged + update_track_data(s, true, &s->track_vertices[1]); + } +} + + +static void ui_draw_track(UIState *s, bool is_mpc, track_vertices_data *pvd) { +const UIScene *scene = &s->scene; + const PathData path = scene->model.path; + const float *mpc_x_coords = &scene->mpc_x[0]; + const float *mpc_y_coords = &scene->mpc_y[0]; + + nvgSave(s->vg); + nvgTranslate(s->vg, 240.0f, 0.0); // rgb-box space + nvgTranslate(s->vg, -1440.0f / 2, -1080.0f / 2); // zoom 2x + nvgScale(s->vg, 2.0, 2.0); + nvgScale(s->vg, 1440.0f / s->rgb_width, 1080.0f / s->rgb_height); + nvgBeginPath(s->vg); + + bool started = false; + float off = is_mpc?0.3:0.5; + float lead_d = scene->lead_d_rel*2.; + float path_height = is_mpc?(lead_d>5.)?fmin(lead_d, 25.)-fmin(lead_d*0.35, 10.):20. + :(lead_d>0.)?fmin(lead_d, 50.)-fmin(lead_d*0.35, 10.):49.; + int vi = 0; + for(int i = 0;i < pvd->cnt;i++) { + if (pvd->v[i].x < 0 || pvd->v[i].y < 0) { + continue; + } + + if (!started) { + nvgMoveTo(s->vg, pvd->v[i].x, pvd->v[i].y); + started = true; + } else { + nvgLineTo(s->vg, pvd->v[i].x, pvd->v[i].y); + } + } + + nvgClosePath(s->vg); + + NVGpaint track_bg; + if (is_mpc) { + // Draw colored MPC track + const uint8_t *clr = bg_colors[s->status]; + track_bg = nvgLinearGradient(s->vg, vwp_w, vwp_h, vwp_w, vwp_h*.4, + nvgRGBA(clr[0], clr[1], clr[2], 255), nvgRGBA(clr[0], clr[1], clr[2], 255/2)); + } else { + // Draw white vision track + track_bg = nvgLinearGradient(s->vg, vwp_w, vwp_h, vwp_w, vwp_h*.4, + nvgRGBA(255, 255, 255, 255), nvgRGBA(255, 255, 255, 0)); + } + + nvgFillPaint(s->vg, track_bg); + nvgFill(s->vg); + nvgRestore(s->vg); +} + +static void draw_steering(UIState *s, float curvature) { + float points[50]; + for (int i = 0; i < 50; i++) { + float y_actual = i * tan(asin(clamp(i * curvature, -0.999, 0.999)) / 2.); + points[i] = y_actual; + } + + // ui_draw_lane_edge(s, points, 0.0, nvgRGBA(0, 0, 255, 128), 5); +} + +static void draw_frame(UIState *s) { + const UIScene *scene = &s->scene; + + float x1, x2, y1, y2; + if (s->scene.frontview) { + glBindVertexArray(s->frame_vao[1]); + } else { + glBindVertexArray(s->frame_vao[0]); + } + + mat4 *out_mat; + if (s->scene.frontview || s->scene.fullview) { + out_mat = &s->front_frame_mat; + } else { + out_mat = &s->rear_frame_mat; + } + glActiveTexture(GL_TEXTURE0); + if (s->scene.frontview && s->cur_vision_front_idx >= 0) { + glBindTexture(GL_TEXTURE_2D, s->frame_front_texs[s->cur_vision_front_idx]); + } else if (!scene->frontview && s->cur_vision_idx >= 0) { + glBindTexture(GL_TEXTURE_2D, s->frame_texs[s->cur_vision_idx]); + #ifndef QCOM + // TODO: a better way to do this? + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1164, 874, 0, GL_RGB, GL_UNSIGNED_BYTE, s->priv_hnds[s->cur_vision_idx]); + #endif + } + + glUseProgram(s->frame_program); + glUniform1i(s->frame_texture_loc, 0); + glUniformMatrix4fv(s->frame_transform_loc, 1, GL_TRUE, out_mat->v); + + assert(glGetError() == GL_NO_ERROR); + glEnableVertexAttribArray(0); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void*)0); + glDisableVertexAttribArray(0); + glBindVertexArray(0); +} + +static inline bool valid_frame_pt(UIState *s, float x, float y) { + return x >= 0 && x <= s->rgb_width && y >= 0 && y <= s->rgb_height; + +} +static void update_lane_line_data(UIState *s, const float *points, float off, bool is_ghost, model_path_vertices_data *pvd) { + pvd->cnt = 0; + for (int i = 0; i < MODEL_PATH_MAX_VERTICES_CNT / 2; i++) { + float px = (float)i; + float py = points[i] - off; + const vec4 p_car_space = (vec4){{px, py, 0., 1.}}; + const vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); + if(!valid_frame_pt(s, p_full_frame.v[0], p_full_frame.v[1])) + continue; + pvd->v[pvd->cnt].x = p_full_frame.v[0]; + pvd->v[pvd->cnt].y = p_full_frame.v[1]; + pvd->cnt += 1; + } + for (int i = MODEL_PATH_MAX_VERTICES_CNT / 2; i > 0; i--) { + float px = (float)i; + float py = is_ghost?(points[i]-off):(points[i]+off); + const vec4 p_car_space = (vec4){{px, py, 0., 1.}}; + const vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); + if(!valid_frame_pt(s, p_full_frame.v[0], p_full_frame.v[1])) + continue; + pvd->v[pvd->cnt].x = p_full_frame.v[0]; + pvd->v[pvd->cnt].y = p_full_frame.v[1]; + pvd->cnt += 1; + } +} + +static void update_all_lane_lines_data(UIState *s, const PathData path, model_path_vertices_data *pstart) { + update_lane_line_data(s, path.points, 0.025*path.prob, false, pstart); + float var = fmin(path.std, 0.7); + update_lane_line_data(s, path.points, -var, true, pstart + 1); + update_lane_line_data(s, path.points, var, true, pstart + 2); +} + +static void ui_draw_lane(UIState *s, const PathData *path, model_path_vertices_data *pstart, NVGcolor color) { + ui_draw_lane_line(s, pstart, color); + float var = fmin(path->std, 0.7); + color.a /= 4; + ui_draw_lane_line(s, pstart + 1, color); + ui_draw_lane_line(s, pstart + 2, color); +} + +static void ui_draw_vision_lanes(UIState *s) { + const UIScene *scene = &s->scene; + model_path_vertices_data *pvd = &s->model_path_vertices[0]; + if(s->model_changed) { + update_all_lane_lines_data(s, scene->model.left_lane, pvd); + update_all_lane_lines_data(s, scene->model.right_lane, pvd + MODEL_LANE_PATH_CNT); + s->model_changed = false; + } + // Draw left lane edge + ui_draw_lane( + s, &scene->model.left_lane, + pvd, + nvgRGBAf(1.0, 1.0, 1.0, scene->model.left_lane.prob)); + + // Draw right lane edge + ui_draw_lane( + s, &scene->model.right_lane, + pvd + MODEL_LANE_PATH_CNT, + nvgRGBAf(1.0, 1.0, 1.0, scene->model.right_lane.prob)); + + if(s->livempc_or_radarstate_changed) { + update_all_track_data(s); + s->livempc_or_radarstate_changed = false; + } + // Draw vision path + ui_draw_track(s, false, &s->track_vertices[0]); + if (scene->engaged) { + // Draw MPC path when engaged + ui_draw_track(s, true, &s->track_vertices[1]); + } +} + +// Draw all world space objects. +static void ui_draw_world(UIState *s) { + const UIScene *scene = &s->scene; + if (!scene->world_objects_visible) { + return; + } + + // Draw lane edges and vision/mpc tracks + ui_draw_vision_lanes(s); + + if (scene->lead_status) { + // Draw lead car indicator + float fillAlpha = 0; + float speedBuff = 10.; + float leadBuff = 40.; + if (scene->lead_d_rel < leadBuff) { + fillAlpha = 255*(1.0-(scene->lead_d_rel/leadBuff)); + if (scene->lead_v_rel < 0) { + fillAlpha += 255*(-1*(scene->lead_v_rel/speedBuff)); + } + fillAlpha = (int)(fmin(fillAlpha, 255)); + } + draw_chevron(s, scene->lead_d_rel+2.7, scene->lead_y_rel, 25, + nvgRGBA(201, 34, 49, fillAlpha), nvgRGBA(218, 202, 37, 255)); + } +} + +static void ui_draw_vision_maxspeed(UIState *s) { + /*if (!s->longitudinal_control){ + return; + }*/ + + const UIScene *scene = &s->scene; + int ui_viz_rx = scene->ui_viz_rx; + int ui_viz_rw = scene->ui_viz_rw; + + char maxspeed_str[32]; + float maxspeed = s->scene.v_cruise; + int maxspeed_calc = maxspeed * 0.6225 + 0.5; + float speedlimit = s->scene.speedlimit; + int speedlim_calc = speedlimit * 2.2369363 + 0.5; + int speed_lim_off = s->speed_lim_off * 2.2369363 + 0.5; + if (s->is_metric) { + maxspeed_calc = maxspeed + 0.5; + speedlim_calc = speedlimit * 3.6 + 0.5; + speed_lim_off = s->speed_lim_off * 3.6 + 0.5; + } + + bool is_cruise_set = (maxspeed != 0 && maxspeed != SET_SPEED_NA); + bool is_speedlim_valid = s->scene.speedlimit_valid; + bool is_set_over_limit = is_speedlim_valid && s->scene.engaged && + is_cruise_set && maxspeed_calc > (speedlim_calc + speed_lim_off); + + int viz_maxspeed_w = 184; + int viz_maxspeed_h = 202; + int viz_maxspeed_x = (ui_viz_rx + (bdr_s*2)); + int viz_maxspeed_y = (box_y + (bdr_s*1.5)); + int viz_maxspeed_xo = 180; + +#ifdef SHOW_SPEEDLIMIT + viz_maxspeed_w += viz_maxspeed_xo; + viz_maxspeed_x += viz_maxspeed_w - (viz_maxspeed_xo * 2); +#else + viz_maxspeed_xo = 0; +#endif + + // Draw Background + nvgBeginPath(s->vg); + nvgRoundedRect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, 30); + if (is_set_over_limit) { + nvgFillColor(s->vg, nvgRGBA(218, 111, 37, 180)); + } else { + nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 100)); + } + nvgFill(s->vg); + + // Draw Border + nvgBeginPath(s->vg); + nvgRoundedRect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, 20); + if (is_set_over_limit) { + nvgStrokeColor(s->vg, nvgRGBA(218, 111, 37, 255)); + } else if (is_speedlim_valid && !s->is_ego_over_limit) { + nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 255)); + } else if (is_speedlim_valid && s->is_ego_over_limit) { + nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 20)); + } else { + nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 100)); + } + nvgStrokeWidth(s->vg, 10); + nvgStroke(s->vg); + + // Draw "MAX" Text + nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); + nvgFontFace(s->vg, "sans-regular"); + nvgFontSize(s->vg, 26*2.5); + if (is_cruise_set) { + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 200)); + } else { + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 100)); + } + nvgText(s->vg, viz_maxspeed_x+(viz_maxspeed_xo/2)+(viz_maxspeed_w/2), 148, "MAX", NULL); + + // Draw Speed Text + nvgFontFace(s->vg, "sans-bold"); + nvgFontSize(s->vg, 48*2.5); + if (is_cruise_set) { + snprintf(maxspeed_str, sizeof(maxspeed_str), "%d", maxspeed_calc); + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); + nvgText(s->vg, viz_maxspeed_x+(viz_maxspeed_xo/2)+(viz_maxspeed_w/2), 242, maxspeed_str, NULL); + } else { + nvgFontFace(s->vg, "sans-semibold"); + nvgFontSize(s->vg, 42*2.5); + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 100)); + nvgText(s->vg, viz_maxspeed_x+(viz_maxspeed_xo/2)+(viz_maxspeed_w/2), 242, "N/A", NULL); + } + +} + +static void ui_draw_vision_speedlimit(UIState *s) { + const UIScene *scene = &s->scene; + int ui_viz_rx = scene->ui_viz_rx; + int ui_viz_rw = scene->ui_viz_rw; + + char speedlim_str[32]; + float speedlimit = s->scene.speedlimit; + int speedlim_calc = speedlimit * 2.2369363 + 0.5; + if (s->is_metric) { + speedlim_calc = speedlimit * 3.6 + 0.5; + } + + bool is_speedlim_valid = s->scene.speedlimit_valid; + float hysteresis_offset = 0.5; + if (s->is_ego_over_limit) { + hysteresis_offset = 0.0; + } + s->is_ego_over_limit = is_speedlim_valid && s->scene.v_ego > (speedlimit + s->speed_lim_off + hysteresis_offset); + + int viz_speedlim_w = 180; + int viz_speedlim_h = 202; + int viz_speedlim_x = (ui_viz_rx + (bdr_s*2)); + int viz_speedlim_y = (box_y + (bdr_s*1.5)); + if (!is_speedlim_valid) { + viz_speedlim_w -= 5; + viz_speedlim_h -= 10; + viz_speedlim_x += 9; + viz_speedlim_y += 5; + } + int viz_speedlim_bdr = is_speedlim_valid ? 30 : 15; + + // Draw Background + nvgBeginPath(s->vg); + nvgRoundedRect(s->vg, viz_speedlim_x, viz_speedlim_y, viz_speedlim_w, viz_speedlim_h, viz_speedlim_bdr); + if (is_speedlim_valid && s->is_ego_over_limit) { + nvgFillColor(s->vg, nvgRGBA(218, 111, 37, 180)); + } else if (is_speedlim_valid) { + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); + } else { + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 100)); + } + nvgFill(s->vg); + + // Draw Border + if (is_speedlim_valid) { + nvgStrokeWidth(s->vg, 10); + nvgStroke(s->vg); + nvgBeginPath(s->vg); + nvgRoundedRect(s->vg, viz_speedlim_x, viz_speedlim_y, viz_speedlim_w, viz_speedlim_h, 20); + if (s->is_ego_over_limit) { + nvgStrokeColor(s->vg, nvgRGBA(218, 111, 37, 255)); + } else if (is_speedlim_valid) { + nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 255)); + } + } + + // Draw "Speed Limit" Text + nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); + nvgFontFace(s->vg, "sans-semibold"); + nvgFontSize(s->vg, 50); + nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 255)); + if (is_speedlim_valid && s->is_ego_over_limit) { + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); + } + nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2 + (is_speedlim_valid ? 6 : 0), viz_speedlim_y + (is_speedlim_valid ? 50 : 45), "SMART", NULL); + nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2 + (is_speedlim_valid ? 6 : 0), viz_speedlim_y + (is_speedlim_valid ? 90 : 85), "SPEED", NULL); + + // Draw Speed Text + nvgFontFace(s->vg, "sans-bold"); + nvgFontSize(s->vg, 48*2.5); + if (s->is_ego_over_limit) { + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); + } else { + nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 255)); + } + if (is_speedlim_valid) { + snprintf(speedlim_str, sizeof(speedlim_str), "%d", speedlim_calc); + nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2, viz_speedlim_y + (is_speedlim_valid ? 170 : 165), speedlim_str, NULL); + } else { + nvgFontFace(s->vg, "sans-semibold"); + nvgFontSize(s->vg, 42*2.5); + nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2, viz_speedlim_y + (is_speedlim_valid ? 170 : 165), "N/A", NULL); + } +} + +static void ui_draw_vision_speed(UIState *s) { + const UIScene *scene = &s->scene; + int ui_viz_rx = scene->ui_viz_rx; + int ui_viz_rw = scene->ui_viz_rw; + float speed = s->scene.v_ego; + + const int viz_speed_w = 280; + const int viz_speed_x = ui_viz_rx+((ui_viz_rw/2)-(viz_speed_w/2)); + char speed_str[32]; + + nvgBeginPath(s->vg); + nvgRect(s->vg, viz_speed_x, box_y, viz_speed_w, header_h); + nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); + + if (s->is_metric) { + snprintf(speed_str, sizeof(speed_str), "%d", (int)(speed * 3.6 + 0.5)); + } else { + snprintf(speed_str, sizeof(speed_str), "%d", (int)(speed * 2.2369363 + 0.5)); + } + nvgFontFace(s->vg, "sans-bold"); + nvgFontSize(s->vg, 96*2.5); + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); + nvgText(s->vg, viz_speed_x+viz_speed_w/2, 240, speed_str, NULL); + + nvgFontFace(s->vg, "sans-regular"); + nvgFontSize(s->vg, 36*2.5); + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 200)); + + if (s->is_metric) { + nvgText(s->vg, viz_speed_x+viz_speed_w/2, 320, "kph", NULL); + } else { + nvgText(s->vg, viz_speed_x+viz_speed_w/2, 320, "mph", NULL); + } +} + +static void ui_draw_vision_event(UIState *s) { + const UIScene *scene = &s->scene; + const int ui_viz_rx = scene->ui_viz_rx; + const int ui_viz_rw = scene->ui_viz_rw; + const int viz_event_w = 220; + const int viz_event_x = ((ui_viz_rx + ui_viz_rw) - (viz_event_w + (bdr_s*2))); + const int viz_event_y = (box_y + (bdr_s*1.5)); + const int viz_event_h = (header_h - (bdr_s*1.5)); + if (s->scene.decel_for_model && s->scene.engaged) { + // draw winding road sign + const int img_turn_size = 160*1.5; + const int img_turn_x = viz_event_x-(img_turn_size/4); + const int img_turn_y = viz_event_y+bdr_s-25; + float img_turn_alpha = 1.0f; + nvgBeginPath(s->vg); + NVGpaint imgPaint = nvgImagePattern(s->vg, img_turn_x, img_turn_y, + img_turn_size, img_turn_size, 0, s->img_turn, img_turn_alpha); + nvgRect(s->vg, img_turn_x, img_turn_y, img_turn_size, img_turn_size); + nvgFillPaint(s->vg, imgPaint); + nvgFill(s->vg); + } else { + // draw steering wheel + const int bg_wheel_size = 96; + const int bg_wheel_x = viz_event_x + (viz_event_w-bg_wheel_size); + const int bg_wheel_y = viz_event_y + (bg_wheel_size/2); + const int img_wheel_size = bg_wheel_size*1.5; + const int img_wheel_x = bg_wheel_x-(img_wheel_size/2); + const int img_wheel_y = bg_wheel_y-25; + float img_wheel_alpha = 0.1f; + bool is_engaged = (s->status == STATUS_ENGAGED); + bool is_warning = (s->status == STATUS_WARNING); + bool is_engageable = scene->engageable; + if (is_engaged || is_warning || is_engageable) { + nvgBeginPath(s->vg); + nvgCircle(s->vg, bg_wheel_x, (bg_wheel_y + (bdr_s*1.5)), bg_wheel_size); + if (is_engaged) { + nvgFillColor(s->vg, nvgRGBA(23, 134, 68, 255)); + } else if (is_warning) { + nvgFillColor(s->vg, nvgRGBA(218, 111, 37, 255)); + } else if (is_engageable) { + nvgFillColor(s->vg, nvgRGBA(23, 51, 73, 255)); + } + nvgFill(s->vg); + img_wheel_alpha = 1.0f; + } + nvgBeginPath(s->vg); + NVGpaint imgPaint = nvgImagePattern(s->vg, img_wheel_x, img_wheel_y, + img_wheel_size, img_wheel_size, 0, s->img_wheel, img_wheel_alpha); + nvgRect(s->vg, img_wheel_x, img_wheel_y, img_wheel_size, img_wheel_size); + nvgFillPaint(s->vg, imgPaint); + nvgFill(s->vg); + } +} + +static void ui_draw_vision_map(UIState *s) { + const UIScene *scene = &s->scene; + const int map_size = 96; + const int map_x = (scene->ui_viz_rx + (map_size * 3) + (bdr_s * 3)); + const int map_y = (footer_y + ((footer_h - map_size) / 2)); + const int map_img_size = (map_size * 1.5); + const int map_img_x = (map_x - (map_img_size / 2)); + const int map_img_y = (map_y - (map_size / 4)); + + bool map_valid = s->scene.map_valid; + float map_img_alpha = map_valid ? 1.0f : 0.15f; + float map_bg_alpha = map_valid ? 0.3f : 0.1f; + NVGcolor map_bg = nvgRGBA(0, 0, 0, (255 * map_bg_alpha)); + NVGpaint map_img = nvgImagePattern(s->vg, map_img_x, map_img_y, + map_img_size, map_img_size, 0, s->img_map, map_img_alpha); + + nvgBeginPath(s->vg); + nvgCircle(s->vg, map_x, (map_y + (bdr_s * 1.5)), map_size); + nvgFillColor(s->vg, map_bg); + nvgFill(s->vg); + + nvgBeginPath(s->vg); + nvgRect(s->vg, map_img_x, map_img_y, map_img_size, map_img_size); + nvgFillPaint(s->vg, map_img); + nvgFill(s->vg); +} + +static void ui_draw_vision_face(UIState *s) { + const UIScene *scene = &s->scene; + const int face_size = 96; + const int face_x = (scene->ui_viz_rx + face_size + (bdr_s * 2)); + const int face_y = (footer_y + ((footer_h - face_size) / 2)); + const int face_img_size = (face_size * 1.5); + const int face_img_x = (face_x - (face_img_size / 2)); + const int face_img_y = (face_y - (face_size / 4)); + float face_img_alpha = scene->monitoring_active ? 1.0f : 0.15f; + float face_bg_alpha = scene->monitoring_active ? 0.3f : 0.1f; + NVGcolor face_bg = nvgRGBA(0, 0, 0, (255 * face_bg_alpha)); + NVGpaint face_img = nvgImagePattern(s->vg, face_img_x, face_img_y, + face_img_size, face_img_size, 0, s->img_face, face_img_alpha); + + nvgBeginPath(s->vg); + nvgCircle(s->vg, face_x, (face_y + (bdr_s * 1.5)), face_size); + nvgFillColor(s->vg, face_bg); + nvgFill(s->vg); + + nvgBeginPath(s->vg); + nvgRect(s->vg, face_img_x, face_img_y, face_img_size, face_img_size); + nvgFillPaint(s->vg, face_img); + nvgFill(s->vg); +} + +static void ui_draw_vision_header(UIState *s) { + const UIScene *scene = &s->scene; + int ui_viz_rx = scene->ui_viz_rx; + int ui_viz_rw = scene->ui_viz_rw; + + nvgBeginPath(s->vg); + NVGpaint gradient = nvgLinearGradient(s->vg, ui_viz_rx, + (box_y+(header_h-(header_h/2.5))), + ui_viz_rx, box_y+header_h, + nvgRGBAf(0,0,0,0.45), nvgRGBAf(0,0,0,0)); + nvgFillPaint(s->vg, gradient); + nvgRect(s->vg, ui_viz_rx, box_y, ui_viz_rw, header_h); + nvgFill(s->vg); + + ui_draw_vision_maxspeed(s); + +#ifdef SHOW_SPEEDLIMIT + ui_draw_vision_speedlimit(s); +#endif + ui_draw_vision_speed(s); + ui_draw_vision_event(s); +} + +static void ui_draw_vision_footer(UIState *s) { + const UIScene *scene = &s->scene; + int ui_viz_rx = scene->ui_viz_rx; + int ui_viz_rw = scene->ui_viz_rw; + + nvgBeginPath(s->vg); + nvgRect(s->vg, ui_viz_rx, footer_y, ui_viz_rw, footer_h); + + ui_draw_vision_face(s); + +#ifdef SHOW_SPEEDLIMIT + // ui_draw_vision_map(s); +#endif +} + +void ui_draw_vision_alert(UIState *s, int va_size, int va_color, + const char* va_text1, const char* va_text2) { + const UIScene *scene = &s->scene; + int ui_viz_rx = scene->ui_viz_rx; + int ui_viz_rw = scene->ui_viz_rw; + bool hasSidebar = !s->scene.uilayout_sidebarcollapsed; + bool mapEnabled = s->scene.uilayout_mapenabled; + bool longAlert1 = strlen(va_text1) > 15; + + const uint8_t *color = alert_colors[va_color]; + const int alr_s = alert_sizes[va_size]; + const int alr_x = ui_viz_rx-(mapEnabled?(hasSidebar?nav_w:(nav_ww)):0)-bdr_s; + const int alr_w = ui_viz_rw+(mapEnabled?(hasSidebar?nav_w:(nav_ww)):0)+(bdr_s*2); + const int alr_h = alr_s+(va_size==ALERTSIZE_NONE?0:bdr_s); + const int alr_y = vwp_h-alr_h; + + nvgBeginPath(s->vg); + nvgRect(s->vg, alr_x, alr_y, alr_w, alr_h); + nvgFillColor(s->vg, nvgRGBA(color[0],color[1],color[2],(color[3]*s->alert_blinking_alpha))); + nvgFill(s->vg); + + nvgBeginPath(s->vg); + NVGpaint gradient = nvgLinearGradient(s->vg, alr_x, alr_y, alr_x, alr_y+alr_h, + nvgRGBAf(0.0,0.0,0.0,0.05), nvgRGBAf(0.0,0.0,0.0,0.35)); + nvgFillPaint(s->vg, gradient); + nvgRect(s->vg, alr_x, alr_y, alr_w, alr_h); + nvgFill(s->vg); + + nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); + nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); + + if (va_size == ALERTSIZE_SMALL) { + nvgFontFace(s->vg, "sans-semibold"); + nvgFontSize(s->vg, 40*2.5); + nvgText(s->vg, alr_x+alr_w/2, alr_y+alr_h/2+15, va_text1, NULL); + } else if (va_size== ALERTSIZE_MID) { + nvgFontFace(s->vg, "sans-bold"); + nvgFontSize(s->vg, 48*2.5); + nvgText(s->vg, alr_x+alr_w/2, alr_y+alr_h/2-45, va_text1, NULL); + nvgFontFace(s->vg, "sans-regular"); + nvgFontSize(s->vg, 36*2.5); + nvgText(s->vg, alr_x+alr_w/2, alr_y+alr_h/2+75, va_text2, NULL); + } else if (va_size== ALERTSIZE_FULL) { + nvgFontSize(s->vg, (longAlert1?72:96)*2.5); + nvgFontFace(s->vg, "sans-bold"); + nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); + nvgTextBox(s->vg, alr_x, alr_y+(longAlert1?360:420), alr_w-60, va_text1, NULL); + nvgFontSize(s->vg, 48*2.5); + nvgFontFace(s->vg, "sans-regular"); + nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BOTTOM); + nvgTextBox(s->vg, alr_x, alr_h-(longAlert1?300:360), alr_w-60, va_text2, NULL); + } +} + +static void ui_draw_vision(UIState *s) { + const UIScene *scene = &s->scene; + int ui_viz_rx = scene->ui_viz_rx; + int ui_viz_rw = scene->ui_viz_rw; + int ui_viz_ro = scene->ui_viz_ro; + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + // Draw video frames + glEnable(GL_SCISSOR_TEST); + glViewport(ui_viz_rx+ui_viz_ro, s->fb_h-(box_y+box_h), viz_w, box_h); + glScissor(ui_viz_rx, s->fb_h-(box_y+box_h), ui_viz_rw, box_h); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + draw_frame(s); + glViewport(0, 0, s->fb_w, s->fb_h); + glDisable(GL_SCISSOR_TEST); + + glClear(GL_STENCIL_BUFFER_BIT); + + nvgBeginFrame(s->vg, s->fb_w, s->fb_h, 1.0f); + nvgSave(s->vg); + + // Draw augmented elements + const int inner_height = viz_w*9/16; + nvgScissor(s->vg, ui_viz_rx, box_y, ui_viz_rw, box_h); + nvgTranslate(s->vg, ui_viz_rx+ui_viz_ro, box_y + (box_h-inner_height)/2.0); + nvgScale(s->vg, (float)viz_w / s->fb_w, (float)inner_height / s->fb_h); + if (!scene->frontview && !scene->fullview) { + ui_draw_world(s); + } + + nvgRestore(s->vg); + + // Set Speed, Current Speed, Status/Events + ui_draw_vision_header(s); + + if (s->scene.alert_size != ALERTSIZE_NONE) { + // Controls Alerts + ui_draw_vision_alert(s, s->scene.alert_size, s->status, + s->scene.alert_text1, s->scene.alert_text2); + } else { + ui_draw_vision_footer(s); + } + + + nvgEndFrame(s->vg); + glDisable(GL_BLEND); +} + +static void ui_draw_blank(UIState *s) { + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); +} + +void ui_draw(UIState *s) { + if (s->vision_connected && s->active_app == cereal_UiLayoutState_App_home && s->status != STATUS_STOPPED) { + ui_draw_vision(s); + } else { + ui_draw_blank(s); + } + + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClear(GL_STENCIL_BUFFER_BIT); + + nvgBeginFrame(s->vg, s->fb_w, s->fb_h, 1.0f); + + nvgEndFrame(s->vg); + glDisable(GL_BLEND); + } +} + +static const char frame_vertex_shader[] = + "attribute vec4 aPosition;\n" + "attribute vec4 aTexCoord;\n" + "uniform mat4 uTransform;\n" + "varying vec4 vTexCoord;\n" + "void main() {\n" + " gl_Position = uTransform * aPosition;\n" + " vTexCoord = aTexCoord;\n" + "}\n"; + +static const char frame_fragment_shader[] = + "precision mediump float;\n" + "uniform sampler2D uTexture;\n" + "varying vec4 vTexCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTexture, vTexCoord.xy);\n" + "}\n"; + +static const char line_vertex_shader[] = + "attribute vec4 aPosition;\n" + "attribute vec4 aColor;\n" + "uniform mat4 uTransform;\n" + "varying vec4 vColor;\n" + "void main() {\n" + " gl_Position = uTransform * aPosition;\n" + " vColor = aColor;\n" + "}\n"; + +static const char line_fragment_shader[] = + "precision mediump float;\n" + "uniform sampler2D uTexture;\n" + "varying vec4 vColor;\n" + "void main() {\n" + " gl_FragColor = vColor;\n" + "}\n"; + +static const mat4 device_transform = {{ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, +}}; + +// frame from 4/3 to box size with a 2x zoom +static const mat4 frame_transform = {{ + 2*(4./3.)/((float)viz_w/box_h), 0.0, 0.0, 0.0, + 0.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, +}}; + +// frame from 4/3 to 16/9 display +static const mat4 full_to_wide_frame_transform = {{ + .75, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, +}}; + +void ui_nvg_init(UIState *s) { + // init drawing + s->vg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG); + assert(s->vg); + + s->font_courbd = nvgCreateFont(s->vg, "courbd", "../assets/fonts/courbd.ttf"); + assert(s->font_courbd >= 0); + s->font_sans_regular = nvgCreateFont(s->vg, "sans-regular", "../assets/fonts/opensans_regular.ttf"); + assert(s->font_sans_regular >= 0); + s->font_sans_semibold = nvgCreateFont(s->vg, "sans-semibold", "../assets/fonts/opensans_semibold.ttf"); + assert(s->font_sans_semibold >= 0); + s->font_sans_bold = nvgCreateFont(s->vg, "sans-bold", "../assets/fonts/opensans_bold.ttf"); + assert(s->font_sans_bold >= 0); + + assert(s->img_wheel >= 0); + s->img_wheel = nvgCreateImage(s->vg, "../assets/img_chffr_wheel.png", 1); + + assert(s->img_turn >= 0); + s->img_turn = nvgCreateImage(s->vg, "../assets/img_trafficSign_turn.png", 1); + + assert(s->img_face >= 0); + s->img_face = nvgCreateImage(s->vg, "../assets/img_driver_face.png", 1); + + assert(s->img_map >= 0); + s->img_map = nvgCreateImage(s->vg, "../assets/img_map.png", 1); + + // init gl + s->frame_program = load_program(frame_vertex_shader, frame_fragment_shader); + assert(s->frame_program); + + s->frame_pos_loc = glGetAttribLocation(s->frame_program, "aPosition"); + s->frame_texcoord_loc = glGetAttribLocation(s->frame_program, "aTexCoord"); + + s->frame_texture_loc = glGetUniformLocation(s->frame_program, "uTexture"); + s->frame_transform_loc = glGetUniformLocation(s->frame_program, "uTransform"); + + s->line_program = load_program(line_vertex_shader, line_fragment_shader); + assert(s->line_program); + + s->line_pos_loc = glGetAttribLocation(s->line_program, "aPosition"); + s->line_color_loc = glGetAttribLocation(s->line_program, "aColor"); + s->line_transform_loc = glGetUniformLocation(s->line_program, "uTransform"); + + glViewport(0, 0, s->fb_w, s->fb_h); + + glDisable(GL_DEPTH_TEST); + + assert(glGetError() == GL_NO_ERROR); + + for(int i = 0; i < 2; i++) { + float x1, x2, y1, y2; + if (i == 1) { + // flip horizontally so it looks like a mirror + x1 = 0.0; + x2 = 1.0; + y1 = 1.0; + y2 = 0.0; + } else { + x1 = 1.0; + x2 = 0.0; + y1 = 1.0; + y2 = 0.0; + } + const uint8_t frame_indicies[] = {0, 1, 2, 0, 2, 3}; + const float frame_coords[4][4] = { + {-1.0, -1.0, x2, y1}, //bl + {-1.0, 1.0, x2, y2}, //tl + { 1.0, 1.0, x1, y2}, //tr + { 1.0, -1.0, x1, y1}, //br + }; + + glGenVertexArrays(1,&s->frame_vao[i]); + glBindVertexArray(s->frame_vao[i]); + glGenBuffers(1, &s->frame_vbo[i]); + glBindBuffer(GL_ARRAY_BUFFER, s->frame_vbo[i]); + glBufferData(GL_ARRAY_BUFFER, sizeof(frame_coords), frame_coords, GL_STATIC_DRAW); + glEnableVertexAttribArray(s->frame_pos_loc); + glVertexAttribPointer(s->frame_pos_loc, 2, GL_FLOAT, GL_FALSE, + sizeof(frame_coords[0]), (const void *)0); + glEnableVertexAttribArray(s->frame_texcoord_loc); + glVertexAttribPointer(s->frame_texcoord_loc, 2, GL_FLOAT, GL_FALSE, + sizeof(frame_coords[0]), (const void *)(sizeof(float) * 2)); + glGenBuffers(1, &s->frame_ibo[i]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->frame_ibo[i]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(frame_indicies), frame_indicies, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER,0); + glBindVertexArray(0); + } + + s->front_frame_mat = matmul(device_transform, full_to_wide_frame_transform); + s->rear_frame_mat = matmul(device_transform, frame_transform); + + for(int i = 0;i < UI_BUF_COUNT; i++) { + s->khr[i] = NULL; + s->priv_hnds[i] = NULL; + s->khr_front[i] = NULL; + s->priv_hnds_front[i] = NULL; + } +} diff --git a/selfdrive/ui/sound.cc b/selfdrive/ui/sound.cc new file mode 100644 index 000000000..701fa56b4 --- /dev/null +++ b/selfdrive/ui/sound.cc @@ -0,0 +1,85 @@ +#include +#include "sound.hpp" + +#include "common/swaglog.h" + +typedef struct { + AudibleAlert alert; + const char* uri; + bool loop; +} sound_file; + +extern "C"{ +#include "slplay.h" +} + +void set_volume(int volume) { + char volume_change_cmd[64]; + sprintf(volume_change_cmd, "service call audio 3 i32 3 i32 %d i32 1 &", volume); + + // 5 second timeout at 60fps + int volume_changed = system(volume_change_cmd); +} + + +sound_file sound_table[] = { + { cereal_CarControl_HUDControl_AudibleAlert_chimeDisengage, "../assets/sounds/disengaged.wav", false }, + { cereal_CarControl_HUDControl_AudibleAlert_chimeEngage, "../assets/sounds/engaged.wav", false }, + { cereal_CarControl_HUDControl_AudibleAlert_chimeWarning1, "../assets/sounds/warning_1.wav", false }, + { cereal_CarControl_HUDControl_AudibleAlert_chimeWarning2, "../assets/sounds/warning_2.wav", false }, + { cereal_CarControl_HUDControl_AudibleAlert_chimeWarningRepeat, "../assets/sounds/warning_repeat.wav", true }, + { cereal_CarControl_HUDControl_AudibleAlert_chimeError, "../assets/sounds/error.wav", false }, + { cereal_CarControl_HUDControl_AudibleAlert_chimePrompt, "../assets/sounds/error.wav", false }, + { cereal_CarControl_HUDControl_AudibleAlert_none, NULL, false }, +}; + +sound_file* get_sound_file(AudibleAlert alert) { + for (sound_file *s = sound_table; s->alert != cereal_CarControl_HUDControl_AudibleAlert_none; s++) { + if (s->alert == alert) { + return s; + } + } + + return NULL; +} + +void play_alert_sound(AudibleAlert alert) { + sound_file* sound = get_sound_file(alert); + char* error = NULL; + + slplay_play(sound->uri, sound->loop, &error); + if(error) { + LOGW("error playing sound: %s", error); + } +} + +void stop_alert_sound(AudibleAlert alert) { + sound_file* sound = get_sound_file(alert); + char* error = NULL; + + slplay_stop_uri(sound->uri, &error); + if(error) { + LOGW("error stopping sound: %s", error); + } +} + +void ui_sound_init() { + char *error = NULL; + slplay_setup(&error); + if (error) goto fail; + + for (sound_file *s = sound_table; s->alert != cereal_CarControl_HUDControl_AudibleAlert_none; s++) { + slplay_create_player_for_uri(s->uri, &error); + if (error) goto fail; + } + return; + +fail: + LOGW(error); + exit(1); +} + +void ui_sound_destroy() { + slplay_destroy(); +} + diff --git a/selfdrive/ui/sound.hpp b/selfdrive/ui/sound.hpp new file mode 100644 index 000000000..cc44c1b4b --- /dev/null +++ b/selfdrive/ui/sound.hpp @@ -0,0 +1,17 @@ +#ifndef __SOUND_HPP +#define __SOUND_HPP + +#include "cereal/gen/c/log.capnp.h" + +typedef enum cereal_CarControl_HUDControl_AudibleAlert AudibleAlert; + +void ui_sound_init(); +void ui_sound_destroy(); + +void set_volume(int volume); + +void play_alert_sound(AudibleAlert alert); +void stop_alert_sound(AudibleAlert alert); + +#endif + diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index fb22ad790..6a36f4919 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -6,289 +6,19 @@ #include #include -#include - -#include -#include - #include #include -#include "nanovg.h" -#define NANOVG_GLES3_IMPLEMENTATION -#include "nanovg_gl.h" -#include "nanovg_gl_utils.h" - +#include "common/util.h" #include "common/messaging.h" #include "common/timing.h" -#include "common/util.h" #include "common/swaglog.h" -#include "common/mat.h" - -extern "C"{ -#include "common/glutil.h" -} - #include "common/touch.h" -#include "common/framebuffer.h" -#include "common/visionipc.h" #include "common/visionimg.h" -#include "common/modeldata.h" #include "common/params.h" -#include "cereal/gen/c/log.capnp.h" - -extern "C"{ -#include "slplay.h" -} - -#include "messaging.hpp" - -#define STATUS_STOPPED 0 -#define STATUS_DISENGAGED 1 -#define STATUS_ENGAGED 2 -#define STATUS_WARNING 3 -#define STATUS_ALERT 4 - -#define ALERTSIZE_NONE 0 -#define ALERTSIZE_SMALL 1 -#define ALERTSIZE_MID 2 -#define ALERTSIZE_FULL 3 - -//#define UI_60FPS - -#define UI_BUF_COUNT 4 -//#define SHOW_SPEEDLIMIT 1 -//#define DEBUG_TURN - -const int vwp_w = 1920; -const int vwp_h = 1080; -const int nav_w = 640; -const int nav_ww= 760; -const int sbr_w = 300; -const int bdr_s = 30; -const int box_x = sbr_w+bdr_s; -const int box_y = bdr_s; -const int box_w = vwp_w-sbr_w-(bdr_s*2); -const int box_h = vwp_h-(bdr_s*2); -const int viz_w = vwp_w-(bdr_s*2); -const int header_h = 420; -const int footer_h = 280; -const int footer_y = vwp_h-bdr_s-footer_h; - -const int UI_FREQ = 30; // Hz - -const int MODEL_PATH_MAX_VERTICES_CNT = 98; -const int MODEL_LANE_PATH_CNT = 3; -const int TRACK_POINTS_MAX_CNT = 50 * 2; - -const uint8_t bg_colors[][4] = { - [STATUS_STOPPED] = {0x07, 0x23, 0x39, 0xff}, - [STATUS_DISENGAGED] = {0x17, 0x33, 0x49, 0xff}, - [STATUS_ENGAGED] = {0x17, 0x86, 0x44, 0xff}, - [STATUS_WARNING] = {0xDA, 0x6F, 0x25, 0xff}, - [STATUS_ALERT] = {0xC9, 0x22, 0x31, 0xff}, -}; - -const uint8_t alert_colors[][4] = { - [STATUS_STOPPED] = {0x07, 0x23, 0x39, 0xf1}, - [STATUS_DISENGAGED] = {0x17, 0x33, 0x49, 0xc8}, - [STATUS_ENGAGED] = {0x17, 0x86, 0x44, 0xf1}, - [STATUS_WARNING] = {0xDA, 0x6F, 0x25, 0xf1}, - [STATUS_ALERT] = {0xC9, 0x22, 0x31, 0xf1}, -}; - -const int alert_sizes[] = { - [ALERTSIZE_NONE] = 0, - [ALERTSIZE_SMALL] = 241, - [ALERTSIZE_MID] = 390, - [ALERTSIZE_FULL] = vwp_h, -}; - -const int SET_SPEED_NA = 255; - -// TODO: this is also hardcoded in common/transformations/camera.py -const mat3 intrinsic_matrix = (mat3){{ - 910., 0., 582., - 0., 910., 437., - 0., 0., 1. -}}; - -typedef enum cereal_CarControl_HUDControl_AudibleAlert AudibleAlert; - -typedef struct UIScene { - int frontview; - int fullview; - - int transformed_width, transformed_height; - - ModelData model; - - float mpc_x[50]; - float mpc_y[50]; - - bool world_objects_visible; - mat4 extrinsic_matrix; // Last row is 0 so we can use mat4. - - float v_cruise; - uint64_t v_cruise_update_ts; - float v_ego; - bool decel_for_model; - - float speedlimit; - bool speedlimit_valid; - bool map_valid; - - float curvature; - int engaged; - bool engageable; - bool monitoring_active; - - bool uilayout_sidebarcollapsed; - bool uilayout_mapenabled; - // responsive layout - int ui_viz_rx; - int ui_viz_rw; - int ui_viz_ro; - - int lead_status; - float lead_d_rel, lead_y_rel, lead_v_rel; - - int front_box_x, front_box_y, front_box_width, front_box_height; - - uint64_t alert_ts; - char alert_text1[1024]; - char alert_text2[1024]; - uint8_t alert_size; - float alert_blinkingrate; - - float awareness_status; - - // Used to show gps planner status - bool gps_planner_active; -} UIScene; - -typedef struct { - float x, y; -}vertex_data; - -typedef struct { - vertex_data v[MODEL_PATH_MAX_VERTICES_CNT]; - int cnt; -} model_path_vertices_data; - -typedef struct { - vertex_data v[TRACK_POINTS_MAX_CNT]; - int cnt; -} track_vertices_data; - - -typedef struct UIState { - pthread_mutex_t lock; - pthread_cond_t bg_cond; - - FramebufferState *fb; - int fb_w, fb_h; - EGLDisplay display; - EGLSurface surface; - - NVGcontext *vg; - - int font_courbd; - int font_sans_regular; - int font_sans_semibold; - int font_sans_bold; - int img_wheel; - int img_turn; - int img_face; - int img_map; - - // Sockets - Context *ctx; - SubSocket *model_sock; - SubSocket *controlsstate_sock; - SubSocket *livecalibration_sock; - SubSocket *radarstate_sock; - SubSocket *map_data_sock; - SubSocket *uilayout_sock; - Poller * poller; - - int active_app; - - // vision state - bool vision_connected; - bool vision_connect_firstrun; - int ipc_fd; - - VIPCBuf bufs[UI_BUF_COUNT]; - VIPCBuf front_bufs[UI_BUF_COUNT]; - int cur_vision_idx; - int cur_vision_front_idx; - - GLuint frame_program; - GLuint frame_texs[UI_BUF_COUNT]; - EGLImageKHR khr[UI_BUF_COUNT]; - void *priv_hnds[UI_BUF_COUNT]; - GLuint frame_front_texs[UI_BUF_COUNT]; - EGLImageKHR khr_front[UI_BUF_COUNT]; - void *priv_hnds_front[UI_BUF_COUNT]; - - GLint frame_pos_loc, frame_texcoord_loc; - GLint frame_texture_loc, frame_transform_loc; - - GLuint line_program; - GLint line_pos_loc, line_color_loc; - GLint line_transform_loc; - - int rgb_width, rgb_height, rgb_stride; - size_t rgb_buf_len; - mat4 rgb_transform; - - int rgb_front_width, rgb_front_height, rgb_front_stride; - size_t rgb_front_buf_len; - - UIScene scene; - - bool awake; - int awake_timeout; - - int volume_timeout; - int controls_timeout; - int alert_sound_timeout; - int speed_lim_off_timeout; - int is_metric_timeout; - int longitudinal_control_timeout; - int limit_set_speed_timeout; - - bool controls_seen; - - int status; - bool is_metric; - bool longitudinal_control; - bool limit_set_speed; - float speed_lim_off; - bool is_ego_over_limit; - char alert_type[64]; - AudibleAlert alert_sound; - int alert_size; - float alert_blinking_alpha; - bool alert_blinked; - - float light_sensor; - - int touch_fd; - - // Hints for re-calculations and redrawing - bool model_changed; - bool livempc_or_radarstate_changed; - - GLuint frame_vao[2], frame_vbo[2], frame_ibo[2]; - mat4 rear_frame_mat, front_frame_mat; - - model_path_vertices_data model_path_vertices[MODEL_LANE_PATH_CNT * 2]; - - track_vertices_data track_vertices[2]; -} UIState; +#include "ui.hpp" +#include "sound.hpp" static int last_brightness = -1; static void set_brightness(UIState *s, int brightness) { @@ -303,6 +33,7 @@ static void set_brightness(UIState *s, int brightness) { } static void set_awake(UIState *s, bool awake) { +#ifdef QCOM if (awake) { // 30 second timeout at 30 fps s->awake_timeout = 30*30; @@ -322,15 +53,10 @@ static void set_awake(UIState *s, bool awake) { framebuffer_set_power(s->fb, HWC_POWER_MODE_OFF); } } -} - -static void set_volume(UIState *s, int volume) { - char volume_change_cmd[64]; - sprintf(volume_change_cmd, "service call audio 3 i32 3 i32 %d i32 1 &", volume); - - // 5 second timeout at 60fps - s->volume_timeout = 5 * UI_FREQ; - int volume_changed = system(volume_change_cmd); +#else + // computer UI doesn't sleep + s->awake = true; +#endif } volatile sig_atomic_t do_exit = 0; @@ -374,123 +100,6 @@ static void read_param_float_timeout(float* param, const char* param_name, int* } } -static const char frame_vertex_shader[] = - "attribute vec4 aPosition;\n" - "attribute vec4 aTexCoord;\n" - "uniform mat4 uTransform;\n" - "varying vec4 vTexCoord;\n" - "void main() {\n" - " gl_Position = uTransform * aPosition;\n" - " vTexCoord = aTexCoord;\n" - "}\n"; - -static const char frame_fragment_shader[] = - "precision mediump float;\n" - "uniform sampler2D uTexture;\n" - "varying vec4 vTexCoord;\n" - "void main() {\n" - " gl_FragColor = texture2D(uTexture, vTexCoord.xy);\n" - "}\n"; - -static const char line_vertex_shader[] = - "attribute vec4 aPosition;\n" - "attribute vec4 aColor;\n" - "uniform mat4 uTransform;\n" - "varying vec4 vColor;\n" - "void main() {\n" - " gl_Position = uTransform * aPosition;\n" - " vColor = aColor;\n" - "}\n"; - -static const char line_fragment_shader[] = - "precision mediump float;\n" - "uniform sampler2D uTexture;\n" - "varying vec4 vColor;\n" - "void main() {\n" - " gl_FragColor = vColor;\n" - "}\n"; - - -static const mat4 device_transform = {{ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, -}}; - -// frame from 4/3 to box size with a 2x zoom -static const mat4 frame_transform = {{ - 2*(4./3.)/((float)viz_w/box_h), 0.0, 0.0, 0.0, - 0.0, 2.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, -}}; - -// frame from 4/3 to 16/9 display -static const mat4 full_to_wide_frame_transform = {{ - .75, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, -}}; - -typedef struct { - AudibleAlert alert; - const char* uri; - bool loop; -} sound_file; - -sound_file sound_table[] = { - { cereal_CarControl_HUDControl_AudibleAlert_chimeDisengage, "../assets/sounds/disengaged.wav", false }, - { cereal_CarControl_HUDControl_AudibleAlert_chimeEngage, "../assets/sounds/engaged.wav", false }, - { cereal_CarControl_HUDControl_AudibleAlert_chimeWarning1, "../assets/sounds/warning_1.wav", false }, - { cereal_CarControl_HUDControl_AudibleAlert_chimeWarning2, "../assets/sounds/warning_2.wav", false }, - { cereal_CarControl_HUDControl_AudibleAlert_chimeWarningRepeat, "../assets/sounds/warning_repeat.wav", true }, - { cereal_CarControl_HUDControl_AudibleAlert_chimeError, "../assets/sounds/error.wav", false }, - { cereal_CarControl_HUDControl_AudibleAlert_chimePrompt, "../assets/sounds/error.wav", false }, - { cereal_CarControl_HUDControl_AudibleAlert_none, NULL, false }, -}; - -sound_file* get_sound_file(AudibleAlert alert) { - for (sound_file *s = sound_table; s->alert != cereal_CarControl_HUDControl_AudibleAlert_none; s++) { - if (s->alert == alert) { - return s; - } - } - - return NULL; -} - -void play_alert_sound(AudibleAlert alert) { - sound_file* sound = get_sound_file(alert); - char* error = NULL; - - slplay_play(sound->uri, sound->loop, &error); - if(error) { - LOGW("error playing sound: %s", error); - } -} - -void stop_alert_sound(AudibleAlert alert) { - sound_file* sound = get_sound_file(alert); - char* error = NULL; - - slplay_stop_uri(sound->uri, &error); - if(error) { - LOGW("error stopping sound: %s", error); - } -} - -void ui_sound_init(char **error) { - slplay_setup(error); - if (*error) return; - - for (sound_file *s = sound_table; s->alert != cereal_CarControl_HUDControl_AudibleAlert_none; s++) { - slplay_create_player_for_uri(s->uri, error); - if (*error) return; - } -} - static void ui_init(UIState *s) { memset(s, 0, sizeof(UIState)); @@ -527,112 +136,15 @@ static void ui_init(UIState *s) { s->ipc_fd = -1; // init display - s->fb = framebuffer_init("ui", 0x00010000, true, - &s->display, &s->surface, &s->fb_w, &s->fb_h); + s->fb = framebuffer_init("ui", 0x00010000, true, &s->fb_w, &s->fb_h); assert(s->fb); set_awake(s, true); - // init drawing - s->vg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG); - assert(s->vg); - - s->font_courbd = nvgCreateFont(s->vg, "courbd", "../assets/fonts/courbd.ttf"); - assert(s->font_courbd >= 0); - s->font_sans_regular = nvgCreateFont(s->vg, "sans-regular", "../assets/fonts/opensans_regular.ttf"); - assert(s->font_sans_regular >= 0); - s->font_sans_semibold = nvgCreateFont(s->vg, "sans-semibold", "../assets/fonts/opensans_semibold.ttf"); - assert(s->font_sans_semibold >= 0); - s->font_sans_bold = nvgCreateFont(s->vg, "sans-bold", "../assets/fonts/opensans_bold.ttf"); - assert(s->font_sans_bold >= 0); - - assert(s->img_wheel >= 0); - s->img_wheel = nvgCreateImage(s->vg, "../assets/img_chffr_wheel.png", 1); - - assert(s->img_turn >= 0); - s->img_turn = nvgCreateImage(s->vg, "../assets/img_trafficSign_turn.png", 1); - - assert(s->img_face >= 0); - s->img_face = nvgCreateImage(s->vg, "../assets/img_driver_face.png", 1); - - assert(s->img_map >= 0); - s->img_map = nvgCreateImage(s->vg, "../assets/img_map.png", 1); - - // init gl - s->frame_program = load_program(frame_vertex_shader, frame_fragment_shader); - assert(s->frame_program); - - s->frame_pos_loc = glGetAttribLocation(s->frame_program, "aPosition"); - s->frame_texcoord_loc = glGetAttribLocation(s->frame_program, "aTexCoord"); - - s->frame_texture_loc = glGetUniformLocation(s->frame_program, "uTexture"); - s->frame_transform_loc = glGetUniformLocation(s->frame_program, "uTransform"); - - s->line_program = load_program(line_vertex_shader, line_fragment_shader); - assert(s->line_program); - - s->line_pos_loc = glGetAttribLocation(s->line_program, "aPosition"); - s->line_color_loc = glGetAttribLocation(s->line_program, "aColor"); - s->line_transform_loc = glGetUniformLocation(s->line_program, "uTransform"); - - glViewport(0, 0, s->fb_w, s->fb_h); - - glDisable(GL_DEPTH_TEST); - - assert(glGetError() == GL_NO_ERROR); - - for(int i = 0; i < 2; i++) { - float x1, x2, y1, y2; - if (i == 1) { - // flip horizontally so it looks like a mirror - x1 = 0.0; - x2 = 1.0; - y1 = 1.0; - y2 = 0.0; - } else { - x1 = 1.0; - x2 = 0.0; - y1 = 1.0; - y2 = 0.0; - } - const uint8_t frame_indicies[] = {0, 1, 2, 0, 2, 3}; - const float frame_coords[4][4] = { - {-1.0, -1.0, x2, y1}, //bl - {-1.0, 1.0, x2, y2}, //tl - { 1.0, 1.0, x1, y2}, //tr - { 1.0, -1.0, x1, y1}, //br - }; - - glGenVertexArrays(1,&s->frame_vao[i]); - glBindVertexArray(s->frame_vao[i]); - glGenBuffers(1, &s->frame_vbo[i]); - glBindBuffer(GL_ARRAY_BUFFER, s->frame_vbo[i]); - glBufferData(GL_ARRAY_BUFFER, sizeof(frame_coords), frame_coords, GL_STATIC_DRAW); - glEnableVertexAttribArray(s->frame_pos_loc); - glVertexAttribPointer(s->frame_pos_loc, 2, GL_FLOAT, GL_FALSE, - sizeof(frame_coords[0]), (const void *)0); - glEnableVertexAttribArray(s->frame_texcoord_loc); - glVertexAttribPointer(s->frame_texcoord_loc, 2, GL_FLOAT, GL_FALSE, - sizeof(frame_coords[0]), (const void *)(sizeof(float) * 2)); - glGenBuffers(1, &s->frame_ibo[i]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->frame_ibo[i]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(frame_indicies), frame_indicies, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER,0); - glBindVertexArray(0); - } - s->model_changed = false; s->livempc_or_radarstate_changed = false; - s->front_frame_mat = matmul(device_transform, full_to_wide_frame_transform); - s->rear_frame_mat = matmul(device_transform, frame_transform); - - for(int i = 0;i < UI_BUF_COUNT; i++) { - s->khr[i] = NULL; - s->priv_hnds[i] = NULL; - s->khr_front[i] = NULL; - s->priv_hnds_front[i] = NULL; - } + ui_nvg_init(s); } static void ui_init_vision(UIState *s, const VisionStreamBufs back_bufs, @@ -691,858 +203,6 @@ static void ui_init_vision(UIState *s, const VisionStreamBufs back_bufs, s->limit_set_speed_timeout = UI_FREQ; } -// Projects a point in car to space to the corresponding point in full frame -// image space. -vec3 car_space_to_full_frame(const UIState *s, vec4 car_space_projective) { - const UIScene *scene = &s->scene; - - // We'll call the car space point p. - // First project into normalized image coordinates with the extrinsics matrix. - const vec4 Ep4 = matvecmul(scene->extrinsic_matrix, car_space_projective); - - // The last entry is zero because of how we store E (to use matvecmul). - const vec3 Ep = {{Ep4.v[0], Ep4.v[1], Ep4.v[2]}}; - const vec3 KEp = matvecmul3(intrinsic_matrix, Ep); - - // Project. - const vec3 p_image = {{KEp.v[0] / KEp.v[2], KEp.v[1] / KEp.v[2], 1.}}; - return p_image; -} - -// Calculate an interpolation between two numbers at a specific increment -static float lerp(float v0, float v1, float t) { - return (1 - t) * v0 + t * v1; -} - -static void draw_chevron(UIState *s, float x_in, float y_in, float sz, - NVGcolor fillColor, NVGcolor glowColor) { - const UIScene *scene = &s->scene; - - nvgSave(s->vg); - - nvgTranslate(s->vg, 240.0f, 0.0); - nvgTranslate(s->vg, -1440.0f / 2, -1080.0f / 2); - nvgScale(s->vg, 2.0, 2.0); - nvgScale(s->vg, 1440.0f / s->rgb_width, 1080.0f / s->rgb_height); - - const vec4 p_car_space = (vec4){{x_in, y_in, 0., 1.}}; - const vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); - - sz *= 30; - sz /= (x_in / 3 + 30); - if (sz > 30) sz = 30; - if (sz < 15) sz = 15; - - float x = p_full_frame.v[0]; - float y = p_full_frame.v[1]; - - // glow - nvgBeginPath(s->vg); - float g_xo = sz/5; - float g_yo = sz/10; - if (x >= 0 && y >= 0.) { - nvgMoveTo(s->vg, x+(sz*1.35)+g_xo, y+sz+g_yo); - nvgLineTo(s->vg, x, y-g_xo); - nvgLineTo(s->vg, x-(sz*1.35)-g_xo, y+sz+g_yo); - nvgLineTo(s->vg, x+(sz*1.35)+g_xo, y+sz+g_yo); - nvgClosePath(s->vg); - } - nvgFillColor(s->vg, glowColor); - nvgFill(s->vg); - - // chevron - nvgBeginPath(s->vg); - if (x >= 0 && y >= 0.) { - nvgMoveTo(s->vg, x+(sz*1.25), y+sz); - nvgLineTo(s->vg, x, y); - nvgLineTo(s->vg, x-(sz*1.25), y+sz); - nvgLineTo(s->vg, x+(sz*1.25), y+sz); - nvgClosePath(s->vg); - } - nvgFillColor(s->vg, fillColor); - nvgFill(s->vg); - - nvgRestore(s->vg); -} - -static void ui_draw_lane_line(UIState *s, const model_path_vertices_data *pvd, NVGcolor color) { - const UIScene *scene = &s->scene; - - nvgSave(s->vg); - nvgTranslate(s->vg, 240.0f, 0.0); // rgb-box space - nvgTranslate(s->vg, -1440.0f / 2, -1080.0f / 2); // zoom 2x - nvgScale(s->vg, 2.0, 2.0); - nvgScale(s->vg, 1440.0f / s->rgb_width, 1080.0f / s->rgb_height); - nvgBeginPath(s->vg); - - bool started = false; - for (int i=0; icnt; i++) { - if (pvd->v[i].x < 0 || pvd->v[i].y < 0.) { - continue; - } - if (!started) { - nvgMoveTo(s->vg, pvd->v[i].x, pvd->v[i].y); - started = true; - } else { - nvgLineTo(s->vg, pvd->v[i].x, pvd->v[i].y); - } - } - - nvgClosePath(s->vg); - nvgFillColor(s->vg, color); - nvgFill(s->vg); - nvgRestore(s->vg); -} - -static void update_track_data(UIState *s, bool is_mpc, track_vertices_data *pvd) { - const UIScene *scene = &s->scene; - const PathData path = scene->model.path; - const float *mpc_x_coords = &scene->mpc_x[0]; - const float *mpc_y_coords = &scene->mpc_y[0]; - - bool started = false; - float off = is_mpc?0.3:0.5; - float lead_d = scene->lead_d_rel*2.; - float path_height = is_mpc?(lead_d>5.)?fmin(lead_d, 25.)-fmin(lead_d*0.35, 10.):20. - :(lead_d>0.)?fmin(lead_d, 50.)-fmin(lead_d*0.35, 10.):49.; - pvd->cnt = 0; - // left side up - for (int i=0; i<=path_height; i++) { - float px, py, mpx; - if (is_mpc) { - mpx = i==0?0.0:mpc_x_coords[i]; - px = lerp(mpx+1.0, mpx, i/100.0); - py = mpc_y_coords[i] - off; - } else { - px = lerp(i+1.0, i, i/100.0); - py = path.points[i] - off; - } - - vec4 p_car_space = (vec4){{px, py, 0., 1.}}; - vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); - if (p_full_frame.v[0] < 0. || p_full_frame.v[1] < 0.) { - continue; - } - pvd->v[pvd->cnt].x = p_full_frame.v[0]; - pvd->v[pvd->cnt].y = p_full_frame.v[1]; - pvd->cnt += 1; - } - - // right side down - for (int i=path_height; i>=0; i--) { - float px, py, mpx; - if (is_mpc) { - mpx = i==0?0.0:mpc_x_coords[i]; - px = lerp(mpx+1.0, mpx, i/100.0); - py = mpc_y_coords[i] + off; - } else { - px = lerp(i+1.0, i, i/100.0); - py = path.points[i] + off; - } - - vec4 p_car_space = (vec4){{px, py, 0., 1.}}; - vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); - pvd->v[pvd->cnt].x = p_full_frame.v[0]; - pvd->v[pvd->cnt].y = p_full_frame.v[1]; - pvd->cnt += 1; - } -} - -static void update_all_track_data(UIState *s) { - const UIScene *scene = &s->scene; - // Draw vision path - update_track_data(s, false, &s->track_vertices[0]); - - if (scene->engaged) { - // Draw MPC path when engaged - update_track_data(s, true, &s->track_vertices[1]); - } -} - - -static void ui_draw_track(UIState *s, bool is_mpc, track_vertices_data *pvd) { -const UIScene *scene = &s->scene; - const PathData path = scene->model.path; - const float *mpc_x_coords = &scene->mpc_x[0]; - const float *mpc_y_coords = &scene->mpc_y[0]; - - nvgSave(s->vg); - nvgTranslate(s->vg, 240.0f, 0.0); // rgb-box space - nvgTranslate(s->vg, -1440.0f / 2, -1080.0f / 2); // zoom 2x - nvgScale(s->vg, 2.0, 2.0); - nvgScale(s->vg, 1440.0f / s->rgb_width, 1080.0f / s->rgb_height); - nvgBeginPath(s->vg); - - bool started = false; - float off = is_mpc?0.3:0.5; - float lead_d = scene->lead_d_rel*2.; - float path_height = is_mpc?(lead_d>5.)?fmin(lead_d, 25.)-fmin(lead_d*0.35, 10.):20. - :(lead_d>0.)?fmin(lead_d, 50.)-fmin(lead_d*0.35, 10.):49.; - int vi = 0; - for(int i = 0;i < pvd->cnt;i++) { - if (pvd->v[i].x < 0 || pvd->v[i].y < 0) { - continue; - } - - if (!started) { - nvgMoveTo(s->vg, pvd->v[i].x, pvd->v[i].y); - started = true; - } else { - nvgLineTo(s->vg, pvd->v[i].x, pvd->v[i].y); - } - } - - nvgClosePath(s->vg); - - NVGpaint track_bg; - if (is_mpc) { - // Draw colored MPC track - const uint8_t *clr = bg_colors[s->status]; - track_bg = nvgLinearGradient(s->vg, vwp_w, vwp_h, vwp_w, vwp_h*.4, - nvgRGBA(clr[0], clr[1], clr[2], 255), nvgRGBA(clr[0], clr[1], clr[2], 255/2)); - } else { - // Draw white vision track - track_bg = nvgLinearGradient(s->vg, vwp_w, vwp_h, vwp_w, vwp_h*.4, - nvgRGBA(255, 255, 255, 255), nvgRGBA(255, 255, 255, 0)); - } - - nvgFillPaint(s->vg, track_bg); - nvgFill(s->vg); - nvgRestore(s->vg); -} - -static void draw_steering(UIState *s, float curvature) { - float points[50]; - for (int i = 0; i < 50; i++) { - float y_actual = i * tan(asin(clamp(i * curvature, -0.999, 0.999)) / 2.); - points[i] = y_actual; - } - - // ui_draw_lane_edge(s, points, 0.0, nvgRGBA(0, 0, 255, 128), 5); -} - -static void draw_frame(UIState *s) { - const UIScene *scene = &s->scene; - - float x1, x2, y1, y2; - if (s->scene.frontview) { - glBindVertexArray(s->frame_vao[1]); - } else { - glBindVertexArray(s->frame_vao[0]); - } - - mat4 *out_mat; - if (s->scene.frontview || s->scene.fullview) { - out_mat = &s->front_frame_mat; - } else { - out_mat = &s->rear_frame_mat; - } - glActiveTexture(GL_TEXTURE0); - if (s->scene.frontview && s->cur_vision_front_idx >= 0) { - glBindTexture(GL_TEXTURE_2D, s->frame_front_texs[s->cur_vision_front_idx]); - } else if (!scene->frontview && s->cur_vision_idx >= 0) { - glBindTexture(GL_TEXTURE_2D, s->frame_texs[s->cur_vision_idx]); - } - - glUseProgram(s->frame_program); - glUniform1i(s->frame_texture_loc, 0); - glUniformMatrix4fv(s->frame_transform_loc, 1, GL_TRUE, out_mat->v); - - assert(glGetError() == GL_NO_ERROR); - glEnableVertexAttribArray(0); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void*)0); - glDisableVertexAttribArray(0); - glBindVertexArray(0); -} - -static inline bool valid_frame_pt(UIState *s, float x, float y) { - return x >= 0 && x <= s->rgb_width && y >= 0 && y <= s->rgb_height; - -} -static void update_lane_line_data(UIState *s, const float *points, float off, bool is_ghost, model_path_vertices_data *pvd) { - pvd->cnt = 0; - for (int i = 0; i < MODEL_PATH_MAX_VERTICES_CNT / 2; i++) { - float px = (float)i; - float py = points[i] - off; - const vec4 p_car_space = (vec4){{px, py, 0., 1.}}; - const vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); - if(!valid_frame_pt(s, p_full_frame.v[0], p_full_frame.v[1])) - continue; - pvd->v[pvd->cnt].x = p_full_frame.v[0]; - pvd->v[pvd->cnt].y = p_full_frame.v[1]; - pvd->cnt += 1; - } - for (int i = MODEL_PATH_MAX_VERTICES_CNT / 2; i > 0; i--) { - float px = (float)i; - float py = is_ghost?(points[i]-off):(points[i]+off); - const vec4 p_car_space = (vec4){{px, py, 0., 1.}}; - const vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); - if(!valid_frame_pt(s, p_full_frame.v[0], p_full_frame.v[1])) - continue; - pvd->v[pvd->cnt].x = p_full_frame.v[0]; - pvd->v[pvd->cnt].y = p_full_frame.v[1]; - pvd->cnt += 1; - } -} - -static void update_all_lane_lines_data(UIState *s, const PathData path, model_path_vertices_data *pstart) { - update_lane_line_data(s, path.points, 0.025*path.prob, false, pstart); - float var = fmin(path.std, 0.7); - update_lane_line_data(s, path.points, -var, true, pstart + 1); - update_lane_line_data(s, path.points, var, true, pstart + 2); -} - -static void ui_draw_lane(UIState *s, const PathData *path, model_path_vertices_data *pstart, NVGcolor color) { - ui_draw_lane_line(s, pstart, color); - float var = fmin(path->std, 0.7); - color.a /= 4; - ui_draw_lane_line(s, pstart + 1, color); - ui_draw_lane_line(s, pstart + 2, color); -} - -static void ui_draw_vision_lanes(UIState *s) { - const UIScene *scene = &s->scene; - model_path_vertices_data *pvd = &s->model_path_vertices[0]; - if(s->model_changed) { - update_all_lane_lines_data(s, scene->model.left_lane, pvd); - update_all_lane_lines_data(s, scene->model.right_lane, pvd + MODEL_LANE_PATH_CNT); - s->model_changed = false; - } - // Draw left lane edge - ui_draw_lane( - s, &scene->model.left_lane, - pvd, - nvgRGBAf(1.0, 1.0, 1.0, scene->model.left_lane.prob)); - - // Draw right lane edge - ui_draw_lane( - s, &scene->model.right_lane, - pvd + MODEL_LANE_PATH_CNT, - nvgRGBAf(1.0, 1.0, 1.0, scene->model.right_lane.prob)); - - if(s->livempc_or_radarstate_changed) { - update_all_track_data(s); - s->livempc_or_radarstate_changed = false; - } - // Draw vision path - ui_draw_track(s, false, &s->track_vertices[0]); - if (scene->engaged) { - // Draw MPC path when engaged - ui_draw_track(s, true, &s->track_vertices[1]); - } -} - -// Draw all world space objects. -static void ui_draw_world(UIState *s) { - const UIScene *scene = &s->scene; - if (!scene->world_objects_visible) { - return; - } - - // Draw lane edges and vision/mpc tracks - ui_draw_vision_lanes(s); - - if (scene->lead_status) { - // Draw lead car indicator - float fillAlpha = 0; - float speedBuff = 10.; - float leadBuff = 40.; - if (scene->lead_d_rel < leadBuff) { - fillAlpha = 255*(1.0-(scene->lead_d_rel/leadBuff)); - if (scene->lead_v_rel < 0) { - fillAlpha += 255*(-1*(scene->lead_v_rel/speedBuff)); - } - fillAlpha = (int)(fmin(fillAlpha, 255)); - } - draw_chevron(s, scene->lead_d_rel+2.7, scene->lead_y_rel, 25, - nvgRGBA(201, 34, 49, fillAlpha), nvgRGBA(218, 202, 37, 255)); - } -} - -static void ui_draw_vision_maxspeed(UIState *s) { - /*if (!s->longitudinal_control){ - return; - }*/ - - const UIScene *scene = &s->scene; - int ui_viz_rx = scene->ui_viz_rx; - int ui_viz_rw = scene->ui_viz_rw; - - char maxspeed_str[32]; - float maxspeed = s->scene.v_cruise; - int maxspeed_calc = maxspeed * 0.6225 + 0.5; - float speedlimit = s->scene.speedlimit; - int speedlim_calc = speedlimit * 2.2369363 + 0.5; - int speed_lim_off = s->speed_lim_off * 2.2369363 + 0.5; - if (s->is_metric) { - maxspeed_calc = maxspeed + 0.5; - speedlim_calc = speedlimit * 3.6 + 0.5; - speed_lim_off = s->speed_lim_off * 3.6 + 0.5; - } - - bool is_cruise_set = (maxspeed != 0 && maxspeed != SET_SPEED_NA); - bool is_speedlim_valid = s->scene.speedlimit_valid; - bool is_set_over_limit = is_speedlim_valid && s->scene.engaged && - is_cruise_set && maxspeed_calc > (speedlim_calc + speed_lim_off); - - int viz_maxspeed_w = 184; - int viz_maxspeed_h = 202; - int viz_maxspeed_x = (ui_viz_rx + (bdr_s*2)); - int viz_maxspeed_y = (box_y + (bdr_s*1.5)); - int viz_maxspeed_xo = 180; - -#ifdef SHOW_SPEEDLIMIT - viz_maxspeed_w += viz_maxspeed_xo; - viz_maxspeed_x += viz_maxspeed_w - (viz_maxspeed_xo * 2); -#else - viz_maxspeed_xo = 0; -#endif - - // Draw Background - nvgBeginPath(s->vg); - nvgRoundedRect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, 30); - if (is_set_over_limit) { - nvgFillColor(s->vg, nvgRGBA(218, 111, 37, 180)); - } else { - nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 100)); - } - nvgFill(s->vg); - - // Draw Border - nvgBeginPath(s->vg); - nvgRoundedRect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, 20); - if (is_set_over_limit) { - nvgStrokeColor(s->vg, nvgRGBA(218, 111, 37, 255)); - } else if (is_speedlim_valid && !s->is_ego_over_limit) { - nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 255)); - } else if (is_speedlim_valid && s->is_ego_over_limit) { - nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 20)); - } else { - nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 100)); - } - nvgStrokeWidth(s->vg, 10); - nvgStroke(s->vg); - - // Draw "MAX" Text - nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); - nvgFontFace(s->vg, "sans-regular"); - nvgFontSize(s->vg, 26*2.5); - if (is_cruise_set) { - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 200)); - } else { - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 100)); - } - nvgText(s->vg, viz_maxspeed_x+(viz_maxspeed_xo/2)+(viz_maxspeed_w/2), 148, "MAX", NULL); - - // Draw Speed Text - nvgFontFace(s->vg, "sans-bold"); - nvgFontSize(s->vg, 48*2.5); - if (is_cruise_set) { - snprintf(maxspeed_str, sizeof(maxspeed_str), "%d", maxspeed_calc); - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); - nvgText(s->vg, viz_maxspeed_x+(viz_maxspeed_xo/2)+(viz_maxspeed_w/2), 242, maxspeed_str, NULL); - } else { - nvgFontFace(s->vg, "sans-semibold"); - nvgFontSize(s->vg, 42*2.5); - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 100)); - nvgText(s->vg, viz_maxspeed_x+(viz_maxspeed_xo/2)+(viz_maxspeed_w/2), 242, "N/A", NULL); - } - -} - -static void ui_draw_vision_speedlimit(UIState *s) { - const UIScene *scene = &s->scene; - int ui_viz_rx = scene->ui_viz_rx; - int ui_viz_rw = scene->ui_viz_rw; - - char speedlim_str[32]; - float speedlimit = s->scene.speedlimit; - int speedlim_calc = speedlimit * 2.2369363 + 0.5; - if (s->is_metric) { - speedlim_calc = speedlimit * 3.6 + 0.5; - } - - bool is_speedlim_valid = s->scene.speedlimit_valid; - float hysteresis_offset = 0.5; - if (s->is_ego_over_limit) { - hysteresis_offset = 0.0; - } - s->is_ego_over_limit = is_speedlim_valid && s->scene.v_ego > (speedlimit + s->speed_lim_off + hysteresis_offset); - - int viz_speedlim_w = 180; - int viz_speedlim_h = 202; - int viz_speedlim_x = (ui_viz_rx + (bdr_s*2)); - int viz_speedlim_y = (box_y + (bdr_s*1.5)); - if (!is_speedlim_valid) { - viz_speedlim_w -= 5; - viz_speedlim_h -= 10; - viz_speedlim_x += 9; - viz_speedlim_y += 5; - } - int viz_speedlim_bdr = is_speedlim_valid ? 30 : 15; - - // Draw Background - nvgBeginPath(s->vg); - nvgRoundedRect(s->vg, viz_speedlim_x, viz_speedlim_y, viz_speedlim_w, viz_speedlim_h, viz_speedlim_bdr); - if (is_speedlim_valid && s->is_ego_over_limit) { - nvgFillColor(s->vg, nvgRGBA(218, 111, 37, 180)); - } else if (is_speedlim_valid) { - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); - } else { - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 100)); - } - nvgFill(s->vg); - - // Draw Border - if (is_speedlim_valid) { - nvgStrokeWidth(s->vg, 10); - nvgStroke(s->vg); - nvgBeginPath(s->vg); - nvgRoundedRect(s->vg, viz_speedlim_x, viz_speedlim_y, viz_speedlim_w, viz_speedlim_h, 20); - if (s->is_ego_over_limit) { - nvgStrokeColor(s->vg, nvgRGBA(218, 111, 37, 255)); - } else if (is_speedlim_valid) { - nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 255)); - } - } - - // Draw "Speed Limit" Text - nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); - nvgFontFace(s->vg, "sans-semibold"); - nvgFontSize(s->vg, 50); - nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 255)); - if (is_speedlim_valid && s->is_ego_over_limit) { - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); - } - nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2 + (is_speedlim_valid ? 6 : 0), viz_speedlim_y + (is_speedlim_valid ? 50 : 45), "SMART", NULL); - nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2 + (is_speedlim_valid ? 6 : 0), viz_speedlim_y + (is_speedlim_valid ? 90 : 85), "SPEED", NULL); - - // Draw Speed Text - nvgFontFace(s->vg, "sans-bold"); - nvgFontSize(s->vg, 48*2.5); - if (s->is_ego_over_limit) { - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); - } else { - nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 255)); - } - if (is_speedlim_valid) { - snprintf(speedlim_str, sizeof(speedlim_str), "%d", speedlim_calc); - nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2, viz_speedlim_y + (is_speedlim_valid ? 170 : 165), speedlim_str, NULL); - } else { - nvgFontFace(s->vg, "sans-semibold"); - nvgFontSize(s->vg, 42*2.5); - nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2, viz_speedlim_y + (is_speedlim_valid ? 170 : 165), "N/A", NULL); - } -} - -static void ui_draw_vision_speed(UIState *s) { - const UIScene *scene = &s->scene; - int ui_viz_rx = scene->ui_viz_rx; - int ui_viz_rw = scene->ui_viz_rw; - float speed = s->scene.v_ego; - - const int viz_speed_w = 280; - const int viz_speed_x = ui_viz_rx+((ui_viz_rw/2)-(viz_speed_w/2)); - char speed_str[32]; - - nvgBeginPath(s->vg); - nvgRect(s->vg, viz_speed_x, box_y, viz_speed_w, header_h); - nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); - - if (s->is_metric) { - snprintf(speed_str, sizeof(speed_str), "%d", (int)(speed * 3.6 + 0.5)); - } else { - snprintf(speed_str, sizeof(speed_str), "%d", (int)(speed * 2.2369363 + 0.5)); - } - nvgFontFace(s->vg, "sans-bold"); - nvgFontSize(s->vg, 96*2.5); - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); - nvgText(s->vg, viz_speed_x+viz_speed_w/2, 240, speed_str, NULL); - - nvgFontFace(s->vg, "sans-regular"); - nvgFontSize(s->vg, 36*2.5); - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 200)); - - if (s->is_metric) { - nvgText(s->vg, viz_speed_x+viz_speed_w/2, 320, "kph", NULL); - } else { - nvgText(s->vg, viz_speed_x+viz_speed_w/2, 320, "mph", NULL); - } -} - -static void ui_draw_vision_event(UIState *s) { - const UIScene *scene = &s->scene; - const int ui_viz_rx = scene->ui_viz_rx; - const int ui_viz_rw = scene->ui_viz_rw; - const int viz_event_w = 220; - const int viz_event_x = ((ui_viz_rx + ui_viz_rw) - (viz_event_w + (bdr_s*2))); - const int viz_event_y = (box_y + (bdr_s*1.5)); - const int viz_event_h = (header_h - (bdr_s*1.5)); - if (s->scene.decel_for_model && s->scene.engaged) { - // draw winding road sign - const int img_turn_size = 160*1.5; - const int img_turn_x = viz_event_x-(img_turn_size/4); - const int img_turn_y = viz_event_y+bdr_s-25; - float img_turn_alpha = 1.0f; - nvgBeginPath(s->vg); - NVGpaint imgPaint = nvgImagePattern(s->vg, img_turn_x, img_turn_y, - img_turn_size, img_turn_size, 0, s->img_turn, img_turn_alpha); - nvgRect(s->vg, img_turn_x, img_turn_y, img_turn_size, img_turn_size); - nvgFillPaint(s->vg, imgPaint); - nvgFill(s->vg); - } else { - // draw steering wheel - const int bg_wheel_size = 96; - const int bg_wheel_x = viz_event_x + (viz_event_w-bg_wheel_size); - const int bg_wheel_y = viz_event_y + (bg_wheel_size/2); - const int img_wheel_size = bg_wheel_size*1.5; - const int img_wheel_x = bg_wheel_x-(img_wheel_size/2); - const int img_wheel_y = bg_wheel_y-25; - float img_wheel_alpha = 0.1f; - bool is_engaged = (s->status == STATUS_ENGAGED); - bool is_warning = (s->status == STATUS_WARNING); - bool is_engageable = scene->engageable; - if (is_engaged || is_warning || is_engageable) { - nvgBeginPath(s->vg); - nvgCircle(s->vg, bg_wheel_x, (bg_wheel_y + (bdr_s*1.5)), bg_wheel_size); - if (is_engaged) { - nvgFillColor(s->vg, nvgRGBA(23, 134, 68, 255)); - } else if (is_warning) { - nvgFillColor(s->vg, nvgRGBA(218, 111, 37, 255)); - } else if (is_engageable) { - nvgFillColor(s->vg, nvgRGBA(23, 51, 73, 255)); - } - nvgFill(s->vg); - img_wheel_alpha = 1.0f; - } - nvgBeginPath(s->vg); - NVGpaint imgPaint = nvgImagePattern(s->vg, img_wheel_x, img_wheel_y, - img_wheel_size, img_wheel_size, 0, s->img_wheel, img_wheel_alpha); - nvgRect(s->vg, img_wheel_x, img_wheel_y, img_wheel_size, img_wheel_size); - nvgFillPaint(s->vg, imgPaint); - nvgFill(s->vg); - } -} - -static void ui_draw_vision_map(UIState *s) { - const UIScene *scene = &s->scene; - const int map_size = 96; - const int map_x = (scene->ui_viz_rx + (map_size * 3) + (bdr_s * 3)); - const int map_y = (footer_y + ((footer_h - map_size) / 2)); - const int map_img_size = (map_size * 1.5); - const int map_img_x = (map_x - (map_img_size / 2)); - const int map_img_y = (map_y - (map_size / 4)); - - bool map_valid = s->scene.map_valid; - float map_img_alpha = map_valid ? 1.0f : 0.15f; - float map_bg_alpha = map_valid ? 0.3f : 0.1f; - NVGcolor map_bg = nvgRGBA(0, 0, 0, (255 * map_bg_alpha)); - NVGpaint map_img = nvgImagePattern(s->vg, map_img_x, map_img_y, - map_img_size, map_img_size, 0, s->img_map, map_img_alpha); - - nvgBeginPath(s->vg); - nvgCircle(s->vg, map_x, (map_y + (bdr_s * 1.5)), map_size); - nvgFillColor(s->vg, map_bg); - nvgFill(s->vg); - - nvgBeginPath(s->vg); - nvgRect(s->vg, map_img_x, map_img_y, map_img_size, map_img_size); - nvgFillPaint(s->vg, map_img); - nvgFill(s->vg); -} - -static void ui_draw_vision_face(UIState *s) { - const UIScene *scene = &s->scene; - const int face_size = 96; - const int face_x = (scene->ui_viz_rx + face_size + (bdr_s * 2)); - const int face_y = (footer_y + ((footer_h - face_size) / 2)); - const int face_img_size = (face_size * 1.5); - const int face_img_x = (face_x - (face_img_size / 2)); - const int face_img_y = (face_y - (face_size / 4)); - float face_img_alpha = scene->monitoring_active ? 1.0f : 0.15f; - float face_bg_alpha = scene->monitoring_active ? 0.3f : 0.1f; - NVGcolor face_bg = nvgRGBA(0, 0, 0, (255 * face_bg_alpha)); - NVGpaint face_img = nvgImagePattern(s->vg, face_img_x, face_img_y, - face_img_size, face_img_size, 0, s->img_face, face_img_alpha); - - nvgBeginPath(s->vg); - nvgCircle(s->vg, face_x, (face_y + (bdr_s * 1.5)), face_size); - nvgFillColor(s->vg, face_bg); - nvgFill(s->vg); - - nvgBeginPath(s->vg); - nvgRect(s->vg, face_img_x, face_img_y, face_img_size, face_img_size); - nvgFillPaint(s->vg, face_img); - nvgFill(s->vg); -} - -static void ui_draw_vision_header(UIState *s) { - const UIScene *scene = &s->scene; - int ui_viz_rx = scene->ui_viz_rx; - int ui_viz_rw = scene->ui_viz_rw; - - nvgBeginPath(s->vg); - NVGpaint gradient = nvgLinearGradient(s->vg, ui_viz_rx, - (box_y+(header_h-(header_h/2.5))), - ui_viz_rx, box_y+header_h, - nvgRGBAf(0,0,0,0.45), nvgRGBAf(0,0,0,0)); - nvgFillPaint(s->vg, gradient); - nvgRect(s->vg, ui_viz_rx, box_y, ui_viz_rw, header_h); - nvgFill(s->vg); - - ui_draw_vision_maxspeed(s); - -#ifdef SHOW_SPEEDLIMIT - ui_draw_vision_speedlimit(s); -#endif - ui_draw_vision_speed(s); - ui_draw_vision_event(s); -} - -static void ui_draw_vision_footer(UIState *s) { - const UIScene *scene = &s->scene; - int ui_viz_rx = scene->ui_viz_rx; - int ui_viz_rw = scene->ui_viz_rw; - - nvgBeginPath(s->vg); - nvgRect(s->vg, ui_viz_rx, footer_y, ui_viz_rw, footer_h); - - ui_draw_vision_face(s); - -#ifdef SHOW_SPEEDLIMIT - // ui_draw_vision_map(s); -#endif -} - -static void ui_draw_vision_alert(UIState *s, int va_size, int va_color, - const char* va_text1, const char* va_text2) { - const UIScene *scene = &s->scene; - int ui_viz_rx = scene->ui_viz_rx; - int ui_viz_rw = scene->ui_viz_rw; - bool hasSidebar = !s->scene.uilayout_sidebarcollapsed; - bool mapEnabled = s->scene.uilayout_mapenabled; - bool longAlert1 = strlen(va_text1) > 15; - - const uint8_t *color = alert_colors[va_color]; - const int alr_s = alert_sizes[va_size]; - const int alr_x = ui_viz_rx-(mapEnabled?(hasSidebar?nav_w:(nav_ww)):0)-bdr_s; - const int alr_w = ui_viz_rw+(mapEnabled?(hasSidebar?nav_w:(nav_ww)):0)+(bdr_s*2); - const int alr_h = alr_s+(va_size==ALERTSIZE_NONE?0:bdr_s); - const int alr_y = vwp_h-alr_h; - - nvgBeginPath(s->vg); - nvgRect(s->vg, alr_x, alr_y, alr_w, alr_h); - nvgFillColor(s->vg, nvgRGBA(color[0],color[1],color[2],(color[3]*s->alert_blinking_alpha))); - nvgFill(s->vg); - - nvgBeginPath(s->vg); - NVGpaint gradient = nvgLinearGradient(s->vg, alr_x, alr_y, alr_x, alr_y+alr_h, - nvgRGBAf(0.0,0.0,0.0,0.05), nvgRGBAf(0.0,0.0,0.0,0.35)); - nvgFillPaint(s->vg, gradient); - nvgRect(s->vg, alr_x, alr_y, alr_w, alr_h); - nvgFill(s->vg); - - nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); - nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); - - if (va_size == ALERTSIZE_SMALL) { - nvgFontFace(s->vg, "sans-semibold"); - nvgFontSize(s->vg, 40*2.5); - nvgText(s->vg, alr_x+alr_w/2, alr_y+alr_h/2+15, va_text1, NULL); - } else if (va_size== ALERTSIZE_MID) { - nvgFontFace(s->vg, "sans-bold"); - nvgFontSize(s->vg, 48*2.5); - nvgText(s->vg, alr_x+alr_w/2, alr_y+alr_h/2-45, va_text1, NULL); - nvgFontFace(s->vg, "sans-regular"); - nvgFontSize(s->vg, 36*2.5); - nvgText(s->vg, alr_x+alr_w/2, alr_y+alr_h/2+75, va_text2, NULL); - } else if (va_size== ALERTSIZE_FULL) { - nvgFontSize(s->vg, (longAlert1?72:96)*2.5); - nvgFontFace(s->vg, "sans-bold"); - nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); - nvgTextBox(s->vg, alr_x, alr_y+(longAlert1?360:420), alr_w-60, va_text1, NULL); - nvgFontSize(s->vg, 48*2.5); - nvgFontFace(s->vg, "sans-regular"); - nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BOTTOM); - nvgTextBox(s->vg, alr_x, alr_h-(longAlert1?300:360), alr_w-60, va_text2, NULL); - } -} - -static void ui_draw_vision(UIState *s) { - const UIScene *scene = &s->scene; - int ui_viz_rx = scene->ui_viz_rx; - int ui_viz_rw = scene->ui_viz_rw; - int ui_viz_ro = scene->ui_viz_ro; - - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - - // Draw video frames - glEnable(GL_SCISSOR_TEST); - glViewport(ui_viz_rx+ui_viz_ro, s->fb_h-(box_y+box_h), viz_w, box_h); - glScissor(ui_viz_rx, s->fb_h-(box_y+box_h), ui_viz_rw, box_h); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - draw_frame(s); - glViewport(0, 0, s->fb_w, s->fb_h); - glDisable(GL_SCISSOR_TEST); - - glClear(GL_STENCIL_BUFFER_BIT); - - nvgBeginFrame(s->vg, s->fb_w, s->fb_h, 1.0f); - nvgSave(s->vg); - - // Draw augmented elements - const int inner_height = viz_w*9/16; - nvgScissor(s->vg, ui_viz_rx, box_y, ui_viz_rw, box_h); - nvgTranslate(s->vg, ui_viz_rx+ui_viz_ro, box_y + (box_h-inner_height)/2.0); - nvgScale(s->vg, (float)viz_w / s->fb_w, (float)inner_height / s->fb_h); - if (!scene->frontview && !scene->fullview) { - ui_draw_world(s); - } - - nvgRestore(s->vg); - - // Set Speed, Current Speed, Status/Events - ui_draw_vision_header(s); - - if (s->scene.alert_size != ALERTSIZE_NONE) { - // Controls Alerts - ui_draw_vision_alert(s, s->scene.alert_size, s->status, - s->scene.alert_text1, s->scene.alert_text2); - } else { - ui_draw_vision_footer(s); - } - - - nvgEndFrame(s->vg); - glDisable(GL_BLEND); -} - -static void ui_draw_blank(UIState *s) { - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); -} - -static void ui_draw(UIState *s) { - if (s->vision_connected && s->active_app == cereal_UiLayoutState_App_home && s->status != STATUS_STOPPED) { - ui_draw_vision(s); - } else { - ui_draw_blank(s); - } - - { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glClear(GL_STENCIL_BUFFER_BIT); - - nvgBeginFrame(s->vg, s->fb_w, s->fb_h, 1.0f); - - nvgEndFrame(s->vg); - glDisable(GL_BLEND); - } -} - static PathData read_path(cereal_ModelData_PathData_ptr pathp) { PathData ret = {0}; @@ -1603,11 +263,6 @@ void handle_message(UIState *s, Message * msg) { struct cereal_Event eventd; cereal_read_Event(&eventd, eventp); - // Skip messages from previous run - if (nanos_since_boot() - eventd.logMonoTime > 1e9) { - return; - } - if (eventd.which == cereal_Event_controlsState) { struct cereal_ControlsState datad; cereal_read_ControlsState(&datad, eventd.controlsState); @@ -1789,6 +444,9 @@ static void ui_update(UIState *s) { .bpp = 3, .size = s->rgb_buf_len, }; + #ifndef QCOM + s->priv_hnds[i] = s->bufs[i].addr; + #endif s->frame_texs[i] = visionimg_to_gl(&img, &s->khr[i], &s->priv_hnds[i]); glBindTexture(GL_TEXTURE_2D, s->frame_texs[i]); @@ -1816,6 +474,9 @@ static void ui_update(UIState *s) { .bpp = 3, .size = s->rgb_front_buf_len, }; + #ifndef QCOM + s->priv_hnds_front[i] = s->bufs[i].addr; + #endif s->frame_front_texs[i] = visionimg_to_gl(&img, &s->khr_front[i], &s->priv_hnds_front[i]); glBindTexture(GL_TEXTURE_2D, s->frame_front_texs[i]); @@ -1996,12 +657,28 @@ static void* vision_connect_thread(void *args) { s->vision_connected = true; s->vision_connect_firstrun = true; + + // Drain sockets + while (true){ + auto polls = s->poller->poll(0); + if (polls.size() == 0) + break; + + for (auto sock : polls){ + Message * msg = sock->receive(); + if (msg == NULL) continue; + delete msg; + } + } + pthread_mutex_unlock(&s->lock); } return NULL; } +#ifdef QCOM +#include #include #include @@ -2057,11 +734,7 @@ static void* bg_thread(void* args) { UIState *s = (UIState*)args; set_thread_name("bg"); - EGLDisplay bg_display; - EGLSurface bg_surface; - - FramebufferState *bg_fb = framebuffer_init("bg", 0x00001000, false, - &bg_display, &bg_surface, NULL, NULL); + FramebufferState *bg_fb = framebuffer_init("bg", 0x00001000, false, NULL, NULL); assert(bg_fb); int bg_status = -1; @@ -2080,13 +753,14 @@ static void* bg_thread(void* args) { glClearColor(color[0]/256.0, color[1]/256.0, color[2]/256.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(bg_display, bg_surface); - assert(glGetError() == GL_NO_ERROR); + framebuffer_swap(bg_fb); } return NULL; } +#endif + int is_leon() { #define MAXCHAR 1000 FILE *fp; @@ -2121,6 +795,7 @@ int main(int argc, char* argv[]) { vision_connect_thread, s); assert(err == 0); +#ifdef QCOM pthread_t light_sensor_thread_handle; err = pthread_create(&light_sensor_thread_handle, NULL, light_sensor_thread, s); @@ -2130,17 +805,13 @@ int main(int argc, char* argv[]) { err = pthread_create(&bg_thread_handle, NULL, bg_thread, s); assert(err == 0); +#endif TouchState touch = {0}; touch_init(&touch); s->touch_fd = touch.fd; - char* error = NULL; - ui_sound_init(&error); - if (error) { - LOGW(error); - exit(1); - } + ui_sound_init(); // light sensor scaling params const int LEON = is_leon(); @@ -2153,7 +824,8 @@ int main(int argc, char* argv[]) { const int MIN_VOLUME = LEON ? 12 : 9; const int MAX_VOLUME = LEON ? 15 : 12; - set_volume(s, MIN_VOLUME); + set_volume(MIN_VOLUME); + s->volume_timeout = 5 * UI_FREQ; int draws = 0; while (!do_exit) { bool should_swap = false; @@ -2224,7 +896,8 @@ int main(int argc, char* argv[]) { s->volume_timeout--; } else { int volume = fmin(MAX_VOLUME, MIN_VOLUME + s->scene.v_ego / 5); // up one notch every 5 m/s - set_volume(s, volume); + set_volume(volume); + s->volume_timeout = 5 * UI_FREQ; } if (s->controls_timeout > 0) { @@ -2238,6 +911,7 @@ int main(int argc, char* argv[]) { } // if visiond is still running and controlsState times out, display an alert + // TODO: refactor this to not be here if (s->controls_seen && s->vision_connected && strcmp(s->scene.alert_text2, "Controls Unresponsive") != 0) { s->scene.alert_size = ALERTSIZE_FULL; if (s->status != STATUS_STOPPED) { @@ -2272,20 +946,24 @@ int main(int argc, char* argv[]) { LOGW("slow frame(%d) time: %.2f", draws, u2-u1); } draws++; - eglSwapBuffers(s->display, s->surface); + framebuffer_swap(s->fb); } } set_awake(s, true); - - slplay_destroy(); + ui_sound_destroy(); // wake up bg thread to exit pthread_mutex_lock(&s->lock); pthread_cond_signal(&s->bg_cond); pthread_mutex_unlock(&s->lock); + +#ifdef QCOM + // join light_sensor_thread? + err = pthread_join(bg_thread_handle, NULL); assert(err == 0); +#endif err = pthread_join(connect_thread_handle, NULL); assert(err == 0); diff --git a/selfdrive/ui/ui.hpp b/selfdrive/ui/ui.hpp new file mode 100644 index 000000000..e520f6eae --- /dev/null +++ b/selfdrive/ui/ui.hpp @@ -0,0 +1,253 @@ +#ifndef _UI_H +#define _UI_H + +#include +#include + +#include "nanovg.h" + +#include "common/mat.h" +#include "common/visionipc.h" +#include "common/framebuffer.h" +#include "common/modeldata.h" +#include "messaging.hpp" + +#include "cereal/gen/c/log.capnp.h" + +#include "sound.hpp" + +#define STATUS_STOPPED 0 +#define STATUS_DISENGAGED 1 +#define STATUS_ENGAGED 2 +#define STATUS_WARNING 3 +#define STATUS_ALERT 4 + +#define ALERTSIZE_NONE 0 +#define ALERTSIZE_SMALL 1 +#define ALERTSIZE_MID 2 +#define ALERTSIZE_FULL 3 + +#ifndef QCOM + #define UI_60FPS +#endif + +#define UI_BUF_COUNT 4 +//#define SHOW_SPEEDLIMIT 1 +//#define DEBUG_TURN + +const int vwp_w = 1920; +const int vwp_h = 1080; +const int nav_w = 640; +const int nav_ww= 760; +const int sbr_w = 300; +const int bdr_s = 30; +const int box_x = sbr_w+bdr_s; +const int box_y = bdr_s; +const int box_w = vwp_w-sbr_w-(bdr_s*2); +const int box_h = vwp_h-(bdr_s*2); +const int viz_w = vwp_w-(bdr_s*2); +const int header_h = 420; +const int footer_h = 280; +const int footer_y = vwp_h-bdr_s-footer_h; + +const int UI_FREQ = 30; // Hz + +const int MODEL_PATH_MAX_VERTICES_CNT = 98; +const int MODEL_LANE_PATH_CNT = 3; +const int TRACK_POINTS_MAX_CNT = 50 * 2; + +const int SET_SPEED_NA = 255; + +const uint8_t bg_colors[][4] = { + [STATUS_STOPPED] = {0x07, 0x23, 0x39, 0xff}, + [STATUS_DISENGAGED] = {0x17, 0x33, 0x49, 0xff}, + [STATUS_ENGAGED] = {0x17, 0x86, 0x44, 0xff}, + [STATUS_WARNING] = {0xDA, 0x6F, 0x25, 0xff}, + [STATUS_ALERT] = {0xC9, 0x22, 0x31, 0xff}, +}; + + +typedef struct UIScene { + int frontview; + int fullview; + + int transformed_width, transformed_height; + + ModelData model; + + float mpc_x[50]; + float mpc_y[50]; + + bool world_objects_visible; + mat4 extrinsic_matrix; // Last row is 0 so we can use mat4. + + float v_cruise; + uint64_t v_cruise_update_ts; + float v_ego; + bool decel_for_model; + + float speedlimit; + bool speedlimit_valid; + bool map_valid; + + float curvature; + int engaged; + bool engageable; + bool monitoring_active; + + bool uilayout_sidebarcollapsed; + bool uilayout_mapenabled; + // responsive layout + int ui_viz_rx; + int ui_viz_rw; + int ui_viz_ro; + + int lead_status; + float lead_d_rel, lead_y_rel, lead_v_rel; + + int front_box_x, front_box_y, front_box_width, front_box_height; + + uint64_t alert_ts; + char alert_text1[1024]; + char alert_text2[1024]; + uint8_t alert_size; + float alert_blinkingrate; + + float awareness_status; + + // Used to show gps planner status + bool gps_planner_active; +} UIScene; + +typedef struct { + float x, y; +}vertex_data; + +typedef struct { + vertex_data v[MODEL_PATH_MAX_VERTICES_CNT]; + int cnt; +} model_path_vertices_data; + +typedef struct { + vertex_data v[TRACK_POINTS_MAX_CNT]; + int cnt; +} track_vertices_data; + + +typedef struct UIState { + pthread_mutex_t lock; + pthread_cond_t bg_cond; + + // framebuffer + FramebufferState *fb; + int fb_w, fb_h; + EGLDisplay display; + EGLSurface surface; + + // NVG + NVGcontext *vg; + + // fonts and images + int font_courbd; + int font_sans_regular; + int font_sans_semibold; + int font_sans_bold; + int img_wheel; + int img_turn; + int img_face; + int img_map; + + // sockets + Context *ctx; + SubSocket *model_sock; + SubSocket *controlsstate_sock; + SubSocket *livecalibration_sock; + SubSocket *radarstate_sock; + SubSocket *map_data_sock; + SubSocket *uilayout_sock; + Poller * poller; + + int active_app; + + // vision state + bool vision_connected; + bool vision_connect_firstrun; + int ipc_fd; + + VIPCBuf bufs[UI_BUF_COUNT]; + VIPCBuf front_bufs[UI_BUF_COUNT]; + int cur_vision_idx; + int cur_vision_front_idx; + + GLuint frame_program; + GLuint frame_texs[UI_BUF_COUNT]; + EGLImageKHR khr[UI_BUF_COUNT]; + void *priv_hnds[UI_BUF_COUNT]; + GLuint frame_front_texs[UI_BUF_COUNT]; + EGLImageKHR khr_front[UI_BUF_COUNT]; + void *priv_hnds_front[UI_BUF_COUNT]; + + GLint frame_pos_loc, frame_texcoord_loc; + GLint frame_texture_loc, frame_transform_loc; + + GLuint line_program; + GLint line_pos_loc, line_color_loc; + GLint line_transform_loc; + + int rgb_width, rgb_height, rgb_stride; + size_t rgb_buf_len; + mat4 rgb_transform; + + int rgb_front_width, rgb_front_height, rgb_front_stride; + size_t rgb_front_buf_len; + + UIScene scene; + bool awake; + + // timeouts + int awake_timeout; + int volume_timeout; + int controls_timeout; + int alert_sound_timeout; + int speed_lim_off_timeout; + int is_metric_timeout; + int longitudinal_control_timeout; + int limit_set_speed_timeout; + + bool controls_seen; + + int status; + bool is_metric; + bool longitudinal_control; + bool limit_set_speed; + float speed_lim_off; + bool is_ego_over_limit; + char alert_type[64]; + AudibleAlert alert_sound; + int alert_size; + float alert_blinking_alpha; + bool alert_blinked; + + float light_sensor; + + int touch_fd; + + // Hints for re-calculations and redrawing + bool model_changed; + bool livempc_or_radarstate_changed; + + GLuint frame_vao[2], frame_vbo[2], frame_ibo[2]; + mat4 rear_frame_mat, front_frame_mat; + + model_path_vertices_data model_path_vertices[MODEL_LANE_PATH_CNT * 2]; + + track_vertices_data track_vertices[2]; +} UIState; + +// API +void ui_draw_vision_alert(UIState *s, int va_size, int va_color, + const char* va_text1, const char* va_text2); +void ui_draw(UIState *s); +void ui_nvg_init(UIState *s); + +#endif diff --git a/selfdrive/updated.py b/selfdrive/updated.py index 1ac216c4f..efde9adcc 100755 --- a/selfdrive/updated.py +++ b/selfdrive/updated.py @@ -1,57 +1,361 @@ #!/usr/bin/env python3 -# simple service that waits for network access and tries to update every hour +# Safe Update: A simple service that waits for network access and tries to +# update every 10 minutes. It's intended to make the OP update process more +# robust against Git repository corruption. This service DOES NOT try to fix +# an already-corrupt BASEDIR Git repo, only prevent it from happening. +# +# During normal operation, both onroad and offroad, the update process makes +# no changes to the BASEDIR install of OP. All update attempts are performed +# in a disposable staging area provided by OverlayFS. It assumes the deleter +# process provides enough disk space to carry out the process. +# +# If an update succeeds, a flag is set, and the update is swapped in at the +# next reboot. If an update is interrupted or otherwise fails, the OverlayFS +# upper layer and metadata can be discarded before trying again. +# +# The swap on boot is triggered by launch_chffrplus.sh +# gated on the existence of $FINALIZED/.overlay_consistent and also the +# existence and mtime of $BASEDIR/.overlay_init. +# +# Other than build byproducts, BASEDIR should not be modified while this +# service is running. Developers modifying code directly in BASEDIR should +# disable this service. +import os import datetime import subprocess -import time +import psutil +from stat import S_ISREG, S_ISDIR, S_ISLNK, S_IMODE, ST_MODE, ST_INO, ST_UID, ST_GID, ST_ATIME, ST_MTIME +import shutil +import signal +from pathlib import Path +import fcntl +import threading +from cffi import FFI +from common.basedir import BASEDIR from common.params import Params from selfdrive.swaglog import cloudlog +STAGING_ROOT = "/data/safe_staging" + +OVERLAY_UPPER = os.path.join(STAGING_ROOT, "upper") +OVERLAY_METADATA = os.path.join(STAGING_ROOT, "metadata") +OVERLAY_MERGED = os.path.join(STAGING_ROOT, "merged") +FINALIZED = os.path.join(STAGING_ROOT, "finalized") + NICE_LOW_PRIORITY = ["nice", "-n", "19"] -def main(gctx=None): +SHORT = os.getenv("SHORT") is not None + +# Workaround for the EON/termux build of Python having os.link removed. +ffi = FFI() +ffi.cdef("int link(const char *oldpath, const char *newpath);") +libc = ffi.dlopen(None) + + +class WaitTimeHelper: + ready_event = threading.Event() + shutdown = False + + def __init__(self): + signal.signal(signal.SIGTERM, self.graceful_shutdown) + signal.signal(signal.SIGINT, self.graceful_shutdown) + signal.signal(signal.SIGHUP, self.update_now) + + def graceful_shutdown(self, signum, frame): + # umount -f doesn't appear effective in avoiding "device busy" on EON, + # so don't actually die until the next convenient opportunity in main(). + cloudlog.info("caught SIGINT/SIGTERM, dismounting overlay at next opportunity") + self.shutdown = True + self.ready_event.set() + + def update_now(self, signum, frame): + cloudlog.info("caught SIGHUP, running update check immediately") + self.ready_event.set() + + +def wait_between_updates(ready_event): + ready_event.clear() + if SHORT: + ready_event.wait(timeout=10) + else: + ready_event.wait(timeout=60 * 10) + + +def link(src, dest): + # Workaround for the EON/termux build of Python having os.link removed. + return libc.link(src.encode(), dest.encode()) + + +def run(cmd, cwd=None): + return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT, encoding='utf8') + + +def remove_consistent_flag(): + os.system("sync") + consistent_file = Path(os.path.join(FINALIZED, ".overlay_consistent")) + try: + consistent_file.unlink() + except FileNotFoundError: + pass + os.system("sync") + + +def set_consistent_flag(): + consistent_file = Path(os.path.join(FINALIZED, ".overlay_consistent")) + os.system("sync") + consistent_file.touch() + os.system("sync") + + +def set_update_available_params(new_version=False): params = Params() + t = datetime.datetime.now().isoformat() + params.put("LastUpdateTime", t.encode('utf8')) + + if new_version: + try: + with open(os.path.join(FINALIZED, "RELEASES.md"), "rb") as f: + r = f.read() + r = r[:r.find(b'\n\n')] # Slice latest release notes + params.put("ReleaseNotes", r + b"\n") + except Exception: + params.put("ReleaseNotes", "") + params.put("UpdateAvailable", "1") + + +def dismount_ovfs(): + if os.path.ismount(OVERLAY_MERGED): + cloudlog.error("unmounting existing overlay") + run(["umount", "-l", OVERLAY_MERGED]) + + +def init_ovfs(): + cloudlog.info("preparing new safe staging area") + Params().put("UpdateAvailable", "0") + + remove_consistent_flag() + + dismount_ovfs() + if os.path.isdir(STAGING_ROOT): + shutil.rmtree(STAGING_ROOT) + + for dirname in [STAGING_ROOT, OVERLAY_UPPER, OVERLAY_METADATA, OVERLAY_MERGED, FINALIZED]: + os.mkdir(dirname, 0o755) + if not os.lstat(BASEDIR).st_dev == os.lstat(OVERLAY_MERGED).st_dev: + raise RuntimeError("base and overlay merge directories are on different filesystems; not valid for overlay FS!") + + # Remove consistent flag from current BASEDIR so it's not copied over + if os.path.isfile(os.path.join(BASEDIR, ".overlay_consistent")): + os.remove(os.path.join(BASEDIR, ".overlay_consistent")) + + # We sync FS object atimes (which EON doesn't use) and mtimes, but ctimes + # are outside user control. Make sure Git is set up to ignore system ctimes, + # because they change when we make hard links during finalize. Otherwise, + # there is a lot of unnecessary churn. This appears to be a common need on + # OSX as well: https://www.git-tower.com/blog/make-git-rebase-safe-on-osx/ + run(["git", "config", "core.trustctime", "false"], BASEDIR) + + # We are temporarily using copytree to copy the directory, which also changes + # inode numbers. Ignore those changes too. + run(["git", "config", "core.checkStat", "minimal"], BASEDIR) + + # Leave a timestamped canary in BASEDIR to check at startup. The EON clock + # should be correct by the time we get here. If the init file disappears, or + # critical mtimes in BASEDIR are newer than .overlay_init, continue.sh can + # assume that BASEDIR has used for local development or otherwise modified, + # and skips the update activation attempt. + Path(os.path.join(BASEDIR, ".overlay_init")).touch() + + overlay_opts = f"lowerdir={BASEDIR},upperdir={OVERLAY_UPPER},workdir={OVERLAY_METADATA}" + run(["mount", "-t", "overlay", "-o", overlay_opts, "none", OVERLAY_MERGED]) + + +def inodes_in_tree(search_dir): + """Given a search root, produce a dictionary mapping of inodes to relative + pathnames of regular files (no directories, symlinks, or special files).""" + inode_map = {} + for root, dirs, files in os.walk(search_dir, topdown=True): + for file_name in files: + full_path_name = os.path.join(root, file_name) + st = os.lstat(full_path_name) + if S_ISREG(st[ST_MODE]): + inode_map[st[ST_INO]] = full_path_name + return inode_map + + +def dup_ovfs_object(inode_map, source_obj, target_dir): + """Given a relative pathname to copy, and a new target root, duplicate the + source object in the target root, using hardlinks for regular files.""" + + source_full_path = os.path.join(OVERLAY_MERGED, source_obj) + st = os.lstat(source_full_path) + target_full_path = os.path.join(target_dir, source_obj) + + if S_ISREG(st[ST_MODE]): + # Hardlink all regular files; ownership and permissions are shared. + link(inode_map[st[ST_INO]], target_full_path) + else: + # Recreate all directories and symlinks; copy ownership and permissions. + if S_ISDIR(st[ST_MODE]): + os.mkdir(os.path.join(FINALIZED, source_obj), S_IMODE(st[ST_MODE])) + elif S_ISLNK(st[ST_MODE]): + os.symlink(os.readlink(source_full_path), target_full_path) + os.chmod(target_full_path, S_IMODE(st[ST_MODE]), follow_symlinks=False) + else: + # Ran into a FIFO, socket, etc. Should not happen in OP install dir. + # Ignore without copying for the time being; revisit later if needed. + cloudlog.error("can't copy this file type: %s" % source_full_path) + os.chown(target_full_path, st[ST_UID], st[ST_GID], follow_symlinks=False) + + # Sync target mtimes to the cached lstat() value from each source object. + # Restores shared inode mtimes after linking, fixes symlinks and dirs. + os.utime(target_full_path, (st[ST_ATIME], st[ST_MTIME]), follow_symlinks=False) + + +def finalize_from_ovfs_hardlink(): + """Take the current OverlayFS merged view and finalize a copy outside of + OverlayFS, ready to be swapped-in at BASEDIR. Copy using hardlinks""" + + cloudlog.info("creating finalized version of the overlay") + + # The "copy" is done with hardlinks, but since the OverlayFS merge looks + # like a different filesystem, and hardlinks can't cross filesystems, we + # have to borrow a source pathname from the upper or lower layer. + inode_map = inodes_in_tree(BASEDIR) + inode_map.update(inodes_in_tree(OVERLAY_UPPER)) + + shutil.rmtree(FINALIZED) + os.umask(0o077) + os.mkdir(FINALIZED) + for root, dirs, files in os.walk(OVERLAY_MERGED, topdown=True): + for obj_name in dirs: + relative_path_name = os.path.relpath(os.path.join(root, obj_name), OVERLAY_MERGED) + dup_ovfs_object(inode_map, relative_path_name, FINALIZED) + for obj_name in files: + relative_path_name = os.path.relpath(os.path.join(root, obj_name), OVERLAY_MERGED) + dup_ovfs_object(inode_map, relative_path_name, FINALIZED) + cloudlog.info("done finalizing overlay") + + +def finalize_from_ovfs_copy(): + """Take the current OverlayFS merged view and finalize a copy outside of + OverlayFS, ready to be swapped-in at BASEDIR. Copy using shutil.copytree""" + + cloudlog.info("creating finalized version of the overlay") + shutil.rmtree(FINALIZED) + shutil.copytree(OVERLAY_MERGED, FINALIZED, symlinks=True) + cloudlog.info("done finalizing overlay") + + +def attempt_update(): + cloudlog.info("attempting git update inside staging overlay") + + git_fetch_output = run(NICE_LOW_PRIORITY + ["git", "fetch"], OVERLAY_MERGED) + cloudlog.info("git fetch success: %s", git_fetch_output) + + cur_hash = run(["git", "rev-parse", "HEAD"], OVERLAY_MERGED).rstrip() + upstream_hash = run(["git", "rev-parse", "@{u}"], OVERLAY_MERGED).rstrip() + new_version = cur_hash != upstream_hash + + git_fetch_result = len(git_fetch_output) > 0 and (git_fetch_output != "Failed to add the host to the list of known hosts (/data/data/com.termux/files/home/.ssh/known_hosts).\n") + + cloudlog.info("comparing %s to %s" % (cur_hash, upstream_hash)) + if new_version or git_fetch_result: + cloudlog.info("Running update") + if new_version: + cloudlog.info("git reset in progress") + r = [ + run(NICE_LOW_PRIORITY + ["git", "reset", "--hard", "@{u}"], OVERLAY_MERGED), + run(NICE_LOW_PRIORITY + ["git", "clean", "-xdf"], OVERLAY_MERGED), + run(NICE_LOW_PRIORITY + ["git", "submodule", "init"], OVERLAY_MERGED), + run(NICE_LOW_PRIORITY + ["git", "submodule", "update"], OVERLAY_MERGED), + ] + cloudlog.info("git reset success: %s", '\n'.join(r)) + + # Un-set the validity flag to prevent the finalized tree from being + # activated later if the finalize step is interrupted + remove_consistent_flag() + + finalize_from_ovfs_copy() + + # Make sure the validity flag lands on disk LAST, only when the local git + # repo and OP install are in a consistent state. + set_consistent_flag() + + cloudlog.info("update successful!") + else: + cloudlog.info("nothing new from git at this time") + + set_update_available_params(new_version=new_version) + + +def main(gctx=None): + overlay_init_done = False + wait_helper = WaitTimeHelper() + params = Params() + + if not os.geteuid() == 0: + raise RuntimeError("updated must be launched as root!") + + # Set low io priority + p = psutil.Process() + if psutil.LINUX: + p.ionice(psutil.IOPRIO_CLASS_BE, value=7) + + ov_lock_fd = open('/tmp/safe_staging_overlay.lock', 'w') + try: + fcntl.flock(ov_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + raise RuntimeError("couldn't get overlay lock; is another updated running?") + while True: time_wrong = datetime.datetime.now().year < 2019 ping_failed = subprocess.call(["ping", "-W", "4", "-c", "1", "8.8.8.8"]) - if ping_failed or time_wrong: - time.sleep(60) - continue - # download application update - try: - r = subprocess.check_output(NICE_LOW_PRIORITY + ["git", "fetch"], stderr=subprocess.STDOUT).decode('utf8') - except subprocess.CalledProcessError as e: - cloudlog.event("git fetch failed", - cmd=e.cmd, - output=e.output, - returncode=e.returncode) - time.sleep(60) - continue - cloudlog.info("git fetch success: %s", r) + # Wait until we have a valid datetime to initialize the overlay + if not (ping_failed or time_wrong): + try: + # If the git directory has modifcations after we created the overlay + # we need to recreate the overlay + if overlay_init_done: + overlay_init_fn = os.path.join(BASEDIR, ".overlay_init") + git_dir_path = os.path.join(BASEDIR, ".git") + new_files = run(["find", git_dir_path, "-newer", overlay_init_fn]) - # Write update available param - try: - cur_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).rstrip() - upstream_hash = subprocess.check_output(["git", "rev-parse", "@{u}"]).rstrip() - params.put("UpdateAvailable", str(int(cur_hash != upstream_hash))) - except: - params.put("UpdateAvailable", "0") + if len(new_files.splitlines()): + cloudlog.info(".git directory changed, recreating overlay") + overlay_init_done = False - # Write latest release notes to param - try: - r = subprocess.check_output(["git", "--no-pager", "show", "@{u}:RELEASES.md"]) - r = r[:r.find(b'\n\n')] # Slice latest release notes - params.put("ReleaseNotes", r + b"\n") - except: - params.put("ReleaseNotes", "") + if not overlay_init_done: + init_ovfs() + overlay_init_done = True - t = datetime.datetime.now().isoformat() - params.put("LastUpdateTime", t.encode('utf8')) + if params.get("IsOffroad") == b"1": + attempt_update() + else: + cloudlog.info("not running updater, openpilot running") - time.sleep(60*60) + except subprocess.CalledProcessError as e: + cloudlog.event( + "update process failed", + cmd=e.cmd, + output=e.output, + returncode=e.returncode + ) + overlay_init_done = False + except Exception: + cloudlog.exception("uncaught updated exception, shouldn't happen") + overlay_init_done = False + + wait_between_updates(wait_helper.ready_event) + if wait_helper.shutdown: + break + + # We've been signaled to shut down + dismount_ovfs() if __name__ == "__main__": main() diff --git a/selfdrive/version.py b/selfdrive/version.py index 21c042923..d2f665196 100644 --- a/selfdrive/version.py +++ b/selfdrive/version.py @@ -48,7 +48,7 @@ try: dirty_files = subprocess.check_output(["git", "diff-index", branch, "--"], encoding='utf8') commit = subprocess.check_output(["git", "rev-parse", "--verify", "HEAD"], encoding='utf8').rstrip() origin_commit = subprocess.check_output(["git", "rev-parse", "--verify", branch], encoding='utf8').rstrip() - cloudlog.event("dirty comma branch", vesion=version, dirty=dirty, origin=origin, branch=branch, dirty_files=dirty_files, commit=commit, origin_commit=origin_commit) + cloudlog.event("dirty comma branch", version=version, dirty=dirty, origin=origin, branch=branch, dirty_files=dirty_files, commit=commit, origin_commit=origin_commit) else: dirty = True @@ -59,7 +59,7 @@ except subprocess.CalledProcessError: pass dirty = True -training_version = b"0.1.0" +training_version = b"0.2.0" terms_version = b"2" if __name__ == "__main__":