mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-10 10:13:45 +08:00
Compare commits
1265 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eae2a73e0a | |||
| 08401a96c2 | |||
| f37fd3ea34 | |||
| dc4dae6794 | |||
| f170440f4a | |||
| 310ba9d2c0 | |||
| f0053d4619 | |||
| 052692b25d | |||
| 55c3885742 | |||
| cb32793300 | |||
| d8569b07eb | |||
| efd5301f65 | |||
| 5dcaf3bef8 | |||
| bf43c7e8c7 | |||
| 1dec68014f | |||
| 8badc7d813 | |||
| 9be7a48ccd | |||
| 6b94c47c6a | |||
| b706673e1c | |||
| e4813645fa | |||
| 12f1be19cc | |||
| e5ebd45576 | |||
| 0870e26fb6 | |||
| d75b8f4540 | |||
| f4b8384332 | |||
| 5766202763 | |||
| 6871203c45 | |||
| 1d48cbdffa | |||
| 54db569c2c | |||
| 31e4fe55ac | |||
| a8b5c74507 | |||
| 470c3f4a92 | |||
| af09b7a45b | |||
| 7fae59167e | |||
| 08d8bb9975 | |||
| 240e0036d2 | |||
| d5e75dd0af | |||
| e53cc41b47 | |||
| d0382e2d48 | |||
| 78b15773c9 | |||
| f95959afdb | |||
| 1c14375796 | |||
| a68ea44af3 | |||
| 5e7f5dd840 | |||
| cc4f786846 | |||
| f4657aa2d5 | |||
| 46bbe6890a | |||
| 380d91c8f7 | |||
| 24121f8abf | |||
| 9d19cca006 | |||
| ee9da82aab | |||
| 06630e8a39 | |||
| 2cc70ef2e4 | |||
| 5908b7cda0 | |||
| d0375942b8 | |||
| bbed1a2551 | |||
| 2b0aab3a38 | |||
| d8ae8c201a | |||
| 9bcd965f0b | |||
| 6e7587a75c | |||
| c631a22eb6 | |||
| 7dfb7967b6 | |||
| 58d6211bc2 | |||
| 4e239dbc22 | |||
| 3469d9aadb | |||
| 18da21e65b | |||
| 50f0cf25a6 | |||
| bea040095c | |||
| 3584523a93 | |||
| d3bcc80d28 | |||
| 0ce679f687 | |||
| d55ccba5fe | |||
| f85b3473a2 | |||
| b750229e70 | |||
| 40b61a8212 | |||
| 5927316788 | |||
| dd89bc30fa | |||
| bf4bf0e5b7 | |||
| 9164148d48 | |||
| ac3dcbe62f | |||
| ba19527181 | |||
| 4acf0438c8 | |||
| bd5fbbabda | |||
| 1777d548bf | |||
| 095d96fbe0 | |||
| 2ca6f893df | |||
| a17a8daad5 | |||
| acace97ef8 | |||
| 0208d26845 | |||
| dd8aa4a21e | |||
| d6c85abcd3 | |||
| 56d1961625 | |||
| 1dbae159a8 | |||
| 76458d175f | |||
| ad181ba501 | |||
| 71290f3805 | |||
| e42ee228c2 | |||
| 9510e05dc0 | |||
| 6e87e66bc5 | |||
| 1197ea9ab9 | |||
| 9d7edbf57a | |||
| acec60d19e | |||
| 6a3dcc74e8 | |||
| 6e851ff886 | |||
| 7a5d8a813b | |||
| 4742bf0230 | |||
| 4bf2bfb122 | |||
| 797b769478 | |||
| 024e2af269 | |||
| e35513afc4 | |||
| 6607283cec | |||
| 08162be765 | |||
| 7061c18cee | |||
| c36c30e74b | |||
| 1f9ec135a4 | |||
| 0557283e3d | |||
| 793f8fee32 | |||
| 5e1a576f3d | |||
| fd98db72ab | |||
| 2f1a58f991 | |||
| 4cc68f57cf | |||
| 5e2a5b5355 | |||
| 44ec08c112 | |||
| 60ec7dc7b6 | |||
| af1fb2644e | |||
| 4651bc6a1f | |||
| ac1dd692af | |||
| 363735f7ce | |||
| 5303afb0dc | |||
| 118d903e2d | |||
| 93eb8418b7 | |||
| 6922d58762 | |||
| b4b747e5cb | |||
| 2d53f4cf01 | |||
| 4a1101c032 | |||
| 41bba2b55a | |||
| d801cebb2e | |||
| 3a19f85512 | |||
| dcc166343f | |||
| 4f5df6589d | |||
| 3cc9d89d45 | |||
| e59f675715 | |||
| 5beae930e4 | |||
| 0274b73760 | |||
| 055b29b226 | |||
| 6330a9c53a | |||
| 2c4e114b51 | |||
| e264b4269f | |||
| fef89d1039 | |||
| fc372e2ae1 | |||
| cd22ee3327 | |||
| e97a1d1a44 | |||
| 6795b09d0a | |||
| 20d484c7cb | |||
| 7e1a8d41a1 | |||
| 0c452dbafe | |||
| 56ed377197 | |||
| 92f9684fdb | |||
| 91b7752268 | |||
| 2ebf09eb07 | |||
| 90af6be9b8 | |||
| 3504ccb639 | |||
| 443cd795a3 | |||
| 06b2c68e03 | |||
| 3478ac1338 | |||
| ce04d25f7d | |||
| 0c7abf3855 | |||
| 0b9ab8bb91 | |||
| 6b52ee7ef2 | |||
| c3d5c5f016 | |||
| 0374979397 | |||
| f9b5d1e9e5 | |||
| 041606de4c | |||
| c244a5d485 | |||
| 308475fcc9 | |||
| a7de971334 | |||
| 61658fbfe3 | |||
| 5ef0040ac6 | |||
| 24d3f07a2f | |||
| b10c2ada79 | |||
| d44fde7117 | |||
| d634894300 | |||
| 8856585129 | |||
| e7cc70f3fa | |||
| a6b562e0c1 | |||
| ca5234a32f | |||
| a27efe5796 | |||
| 870430e19f | |||
| 6cbef7bc13 | |||
| 9cc0d7a1c9 | |||
| e244aabe88 | |||
| b15390d351 | |||
| 2af7b3441e | |||
| 87c495b761 | |||
| d016071df3 | |||
| 940c5b3b3f | |||
| 256ee6cf6f | |||
| b5855bcade | |||
| 8f328f17fc | |||
| b6f3692b56 | |||
| 6266feeed2 | |||
| 876ac69047 | |||
| 47ca2c9381 | |||
| 10f3f56801 | |||
| 2e42bf9fa4 | |||
| 3a958b882a | |||
| 1b17bf40cd | |||
| 6e8f325024 | |||
| cc21fd3ac3 | |||
| 010a32bb9b | |||
| 276713ddf9 | |||
| e1a4189c1f | |||
| a1f4ba55bf | |||
| d899834b63 | |||
| c5372e9041 | |||
| 1bf0fb3851 | |||
| fe39ffa55a | |||
| 0437998bce | |||
| de8f7c4584 | |||
| 286c4f8403 | |||
| 0977a91d65 | |||
| 245d5bba9c | |||
| 2ef29967e8 | |||
| 6d559c4219 | |||
| 3cc4683eb7 | |||
| ac08c79139 | |||
| 04dcdf46bc | |||
| 7f1def00b2 | |||
| 94ee6b0f43 | |||
| 91696ba6c8 | |||
| 608a1c2baa | |||
| 93a96695ea | |||
| 146c64b0f1 | |||
| 4cd5c1b3c2 | |||
| 811363cab9 | |||
| b2e94548b9 | |||
| cf5ae3cbca | |||
| 561c490b2a | |||
| 496ae85f67 | |||
| 5c630b20a9 | |||
| c2a7437972 | |||
| 1550520b63 | |||
| bcb4a6a3e3 | |||
| 7835b9aa17 | |||
| f2c4749420 | |||
| 238fca2334 | |||
| 72ecc330e2 | |||
| d9b5a1e30b | |||
| 1792a60053 | |||
| 571937da84 | |||
| 6442752486 | |||
| ed34c4cfd6 | |||
| 8810948eca | |||
| 0b6da2077f | |||
| 159d3a30e3 | |||
| 6db6d79211 | |||
| a064de7ceb | |||
| c787507449 | |||
| 3352e48c51 | |||
| 081bb51e58 | |||
| faa23595af | |||
| cf083711bb | |||
| 9f7002fdf1 | |||
| 761c349490 | |||
| 542e14306e | |||
| 79bc6c3a52 | |||
| 8952c947d1 | |||
| a1e9cf9df9 | |||
| 2ddf95d47f | |||
| 8bd8066589 | |||
| 44cf6b358e | |||
| ded5e5c8d0 | |||
| 21b8189a45 | |||
| 76a552715f | |||
| bd3b7a1d87 | |||
| 8543afc78a | |||
| 12f923445b | |||
| 0e127cf88b | |||
| c5b65d072d | |||
| ed8d1a65c3 | |||
| 91930c2d0d | |||
| 19459d2b2e | |||
| 5af3f32157 | |||
| 16dda06a0c | |||
| 76d084d877 | |||
| 90a9ef277c | |||
| b32227e69f | |||
| 7cc237aa4c | |||
| 0a14e19808 | |||
| c16879f2b8 | |||
| 8d0cb9c8cf | |||
| 2ecdd2810c | |||
| 35e38f5fe4 | |||
| 2a0ac63fa5 | |||
| ca058bcc81 | |||
| f96406b13f | |||
| 0738c05d9f | |||
| 08b76d3de6 | |||
| cef81da1e9 | |||
| f4a36f7f74 | |||
| f911493177 | |||
| fa2050ab1a | |||
| 4bffe422e4 | |||
| f881a9ba68 | |||
| afa9ec1138 | |||
| ddf8abc14a | |||
| 5f722d2c93 | |||
| 31ac5a216d | |||
| 1b262a5a52 | |||
| 517289f3a5 | |||
| 6fcd2187e1 | |||
| 8fa3f60de7 | |||
| a3f40dbac3 | |||
| f99dc2eab2 | |||
| cdcc2f6766 | |||
| 1304f95978 | |||
| c4393277fb | |||
| 7cd9ab27e6 | |||
| ece999c548 | |||
| f41d77b24f | |||
| f45f239774 | |||
| 02e550e2cb | |||
| 06298b28f1 | |||
| a694d051b3 | |||
| 468a50b6f6 | |||
| 4e8a4f87f4 | |||
| 30350f4207 | |||
| c98ba4ff4c | |||
| c46cf9f536 | |||
| 23e1c4f49e | |||
| d6af0e6eb5 | |||
| 09926bf5b5 | |||
| f9f33c4dc4 | |||
| 5fc6fe68f6 | |||
| 806655b052 | |||
| 5d54743d8b | |||
| b28ff40d4d | |||
| 07163f793b | |||
| 66687746f9 | |||
| b27fa58444 | |||
| 8bca2ca758 | |||
| cefddf4b9b | |||
| f829c90de6 | |||
| 6bd3cab8a8 | |||
| e54c0091bc | |||
| 48568cba0b | |||
| 6ecb1060be | |||
| 93977e2ee2 | |||
| 8650ca837f | |||
| 6853f1db29 | |||
| 140aa95523 | |||
| 69544c57fd | |||
| a3f2452fa7 | |||
| c736d43cce | |||
| a28cc71b8b | |||
| 612c518dd6 | |||
| 3c4ddba992 | |||
| 488d84c664 | |||
| b6a0c89dc5 | |||
| a6f4cdb319 | |||
| b80d3e113b | |||
| d80cde6e41 | |||
| c6db0cd4b6 | |||
| 489afc3842 | |||
| b5f86446d4 | |||
| 62b5fd54e6 | |||
| 7aeb7085a3 | |||
| edafe139a4 | |||
| 80f4becabf | |||
| c8e10139c2 | |||
| 966bb6cc54 | |||
| 887ea25b6d | |||
| 735c2fb48e | |||
| 028f5ca1f4 | |||
| d6238c285a | |||
| 4f407dabcd | |||
| fd34659dc3 | |||
| 1f85860f7e | |||
| 14f3f6dd1a | |||
| e527b463a5 | |||
| 7dc56dc064 | |||
| 0a98ee9e4f | |||
| 43d162e8fb | |||
| 037e6e749a | |||
| d984fb1bae | |||
| 3661a01489 | |||
| 7fd131e01c | |||
| 5d8e54ae3e | |||
| 422885dce6 | |||
| 136574fbcb | |||
| a5f9c2fc23 | |||
| 6704f63a3d | |||
| 8e13d8abd0 | |||
| 8831b11a24 | |||
| 03a4f7ef9a | |||
| c393973916 | |||
| 27f89e6634 | |||
| 4166c9fccb | |||
| ced5f417b8 | |||
| f67f84109e | |||
| eea07462fa | |||
| 96d1b876bb | |||
| 56d3014298 | |||
| ae6aa0f008 | |||
| ecde604198 | |||
| 4af41ffce6 | |||
| 2dac616bef | |||
| 5a9fdde156 | |||
| 9bb6e997aa | |||
| cd03aa19a1 | |||
| 10065c8c28 | |||
| 1b426a3160 | |||
| c91225b52e | |||
| 49a611df59 | |||
| 2ba6df2506 | |||
| 9b7bf4a101 | |||
| a61badb564 | |||
| 2e21deeae8 | |||
| 132f10365a | |||
| 98bc70344f | |||
| 0fa8e01d1f | |||
| f142f1cd70 | |||
| eb5cd542d9 | |||
| 1257d31a56 | |||
| 2e9b980fc2 | |||
| 2b7f91d151 | |||
| 7665045fc6 | |||
| af1583cdfc | |||
| a46007d84d | |||
| 13b71b4e81 | |||
| b084294dc0 | |||
| 6cf95918c5 | |||
| 0072449b01 | |||
| f03efab907 | |||
| cddc3b9e8f | |||
| d977a5bd62 | |||
| 99c2fcc797 | |||
| 5b98ea04ad | |||
| b9344af9bb | |||
| 1e0f1a8abc | |||
| 8ba36b76a0 | |||
| 3f382d6e69 | |||
| 10edb90ac6 | |||
| 45099e7fcd | |||
| 77f069cbe5 | |||
| 1070dda3ee | |||
| fcd5897650 | |||
| f1785c245a | |||
| 6892b62761 | |||
| a18ddf12eb | |||
| 46ae67b607 | |||
| 4d3aeaba6d | |||
| ba67e468ab | |||
| e946e9de0b | |||
| 7d2563880a | |||
| 43edc51cb6 | |||
| 9476a8a7f6 | |||
| 053441fbb3 | |||
| e35a1eca99 | |||
| 3d11e8ef36 | |||
| 73f720220b | |||
| a1202eaf2a | |||
| a941e8f78f | |||
| 9aca13cf2d | |||
| ac087d085e | |||
| 46d65095af | |||
| 667f3bb32f | |||
| c65cf18c75 | |||
| 55a31d7657 | |||
| ac17c35cfe | |||
| bcb13a7229 | |||
| 0ce28803ec | |||
| db8cd9f411 | |||
| 51312afd3d | |||
| efc23644c7 | |||
| e531f844f6 | |||
| 46f74753cd | |||
| d5cbb89d84 | |||
| 4ce701150a | |||
| 96fded0399 | |||
| 12597856da | |||
| 593c3a0c8e | |||
| 187d3a079c | |||
| 9755520b87 | |||
| 7099bd18e3 | |||
| cb670c2c3d | |||
| f5b84e74f4 | |||
| 2b8e887e44 | |||
| 64f74dad27 | |||
| 7e959c5a3e | |||
| 28795d3065 | |||
| 8aed5a1a89 | |||
| a5c973be89 | |||
| 39dcc88330 | |||
| fc6afd5ab8 | |||
| 177a1a3c8b | |||
| 1979a6d8e8 | |||
| 944c23f369 | |||
| 2230933d4b | |||
| 3ea474dd58 | |||
| 8879d481e5 | |||
| 5e35feb3ab | |||
| 5b6436a90c | |||
| ee7601ae9d | |||
| 54cf8d6a5e | |||
| aac90dd11b | |||
| 85b9f8962e | |||
| a668bc9eda | |||
| fd50941cff | |||
| 831f2396d9 | |||
| 5fc4c2b25c | |||
| b03e7821d4 | |||
| 35241a5fb8 | |||
| 5da6bf9e03 | |||
| 948d42b3e5 | |||
| 422de59898 | |||
| 7a990b99f7 | |||
| 0a84b00406 | |||
| e76e1e500c | |||
| cd70e23dc3 | |||
| 1dfef69a3c | |||
| c35df583a5 | |||
| db3df61c34 | |||
| 32f0a2cbbc | |||
| 569099eb70 | |||
| df7f426405 | |||
| bddd20c425 | |||
| e89e4407c5 | |||
| d849d6f1d7 | |||
| 0b958f7c9a | |||
| 2fc10e8299 | |||
| bf8cae5e7c | |||
| 93015c1c17 | |||
| 97329e46ae | |||
| d76f756f42 | |||
| 71a418d166 | |||
| de024fd4a7 | |||
| 7c90c0669a | |||
| 560ed80123 | |||
| 12220ec82d | |||
| 3715fe85aa | |||
| ba6e5f125d | |||
| 1459d3519d | |||
| c9cfe2c727 | |||
| bc979ea6aa | |||
| 79472cdf83 | |||
| adf6f28ebf | |||
| a7dfd36c00 | |||
| 6c7f3751e7 | |||
| c179a3ccb7 | |||
| 13efc421c4 | |||
| 10db1edc7f | |||
| 039b85f355 | |||
| 0b41b42f7b | |||
| a46ff01cab | |||
| 1f9efd9311 | |||
| 7f8dbf24e7 | |||
| 5e4b88201e | |||
| 1252188b4b | |||
| 15d3a166f7 | |||
| f51c2aeced | |||
| 3edb3243f6 | |||
| c693bc1247 | |||
| 5d3ab260e1 | |||
| ea64c4c0ae | |||
| 84bce8ae02 | |||
| 3c5974930a | |||
| be854df32d | |||
| adbf68f771 | |||
| f62177a827 | |||
| bb40d161e8 | |||
| 84b1f363e4 | |||
| a5348b8679 | |||
| 63c9a85c6a | |||
| adf9ec5360 | |||
| 883d1232d3 | |||
| b58fddb83e | |||
| ce1491df9c | |||
| ea01a53711 | |||
| 85cdb2ed9a | |||
| 368947c88c | |||
| 3f1f7ad89c | |||
| a8cfa2e2fe | |||
| c8eed43538 | |||
| 29a2f576f5 | |||
| 31ec0096e4 | |||
| 8728c7dde3 | |||
| e9a37d99c3 | |||
| 67742699cc | |||
| de975d5af9 | |||
| c3143f3833 | |||
| 1c2f9e6190 | |||
| 654338f9c7 | |||
| dfd7a8c8d7 | |||
| bb8a5bd476 | |||
| e4359e9acb | |||
| 13b8a67ae2 | |||
| 89d9fdca82 | |||
| a478b64ff3 | |||
| b3c2daf9e5 | |||
| 792a9b715c | |||
| 631d6d9ef4 | |||
| 6249211745 | |||
| 2213f8f8a4 | |||
| 4e85568370 | |||
| 88a4f2baf1 | |||
| 4bfc28dec0 | |||
| be2818a131 | |||
| 1646fd94b8 | |||
| 3fbd928b98 | |||
| d2125aafd4 | |||
| b9c3b1219a | |||
| 99983d39c3 | |||
| 31e46f929d | |||
| cecce82015 | |||
| a112e6e882 | |||
| c69c076acb | |||
| 6069c87b07 | |||
| 90ed6d739c | |||
| 95350ad854 | |||
| 9768109ec1 | |||
| 4fa4237e3f | |||
| 4624d8f936 | |||
| bcdeec3133 | |||
| 545f7c6f2a | |||
| 507f420927 | |||
| 752ef8696a | |||
| 9e4c2bcacf | |||
| 9d9e5aa02d | |||
| e2fd6f34e9 | |||
| a3c638697f | |||
| f287d487e5 | |||
| 7cabab69a1 | |||
| 716ad288bb | |||
| 1c135f7ff2 | |||
| 1504e10380 | |||
| 65008d281f | |||
| 2e8586fab5 | |||
| 6c5be6ddab | |||
| 7a324fc377 | |||
| f4dea7977b | |||
| e9255d1e9c | |||
| 350dc6a50f | |||
| 3206784dd8 | |||
| 0dd59d0404 | |||
| cc119b2a37 | |||
| 9421e1cbfe | |||
| 9947206ccd | |||
| 0871a35c10 | |||
| 2d91aa5abc | |||
| 13693e3a0a | |||
| edede31c32 | |||
| c61ed10015 | |||
| 1391434f54 | |||
| d8125f50d2 | |||
| a49273d9d4 | |||
| ff5b75d164 | |||
| 53b7adedc2 | |||
| f78bacf96b | |||
| dfd56a46d2 | |||
| 6bbc3f4d1c | |||
| 34fed9f908 | |||
| c85db43705 | |||
| 8d9e203130 | |||
| d5f6946502 | |||
| 48a42a9c53 | |||
| fb807cc007 | |||
| 7119412d35 | |||
| fadf7ff1e5 | |||
| cce2e4d357 | |||
| 4e74e0f755 | |||
| a6645a1be1 | |||
| 239d690a43 | |||
| d4d6134d3b | |||
| 0965650f61 | |||
| 224e2c271b | |||
| e72e5d4ebe | |||
| f962a36fd8 | |||
| 2947af42fc | |||
| cd9b08492e | |||
| 45b7d60263 | |||
| 93f2076c7e | |||
| 4edbc7d0cf | |||
| cc7dd066d2 | |||
| 9e55577cc7 | |||
| 7ea6cfcbdf | |||
| 83dad85cdd | |||
| 5fd0906164 | |||
| e7d349bf36 | |||
| 5393308d03 | |||
| dc02a2d385 | |||
| 63563c3561 | |||
| ae6250e685 | |||
| ae402d3ac7 | |||
| cfb0a1c18c | |||
| fa18bb9261 | |||
| cabfa7b735 | |||
| 65e551c671 | |||
| 62b7abcd91 | |||
| dc654b439a | |||
| 693c83f74c | |||
| 8ffe3f287e | |||
| 749e236bc0 | |||
| 151d256dd6 | |||
| 436e3dec3e | |||
| 7521fd11e2 | |||
| ff755ed4bf | |||
| 6d04251517 | |||
| 970afa9683 | |||
| cd7e362333 | |||
| 85a162dd43 | |||
| 8de8946374 | |||
| f1c2b1df7f | |||
| 1b20567c98 | |||
| 6c39f6bb53 | |||
| 22003fd10a | |||
| 088fc1cab1 | |||
| cb718618d1 | |||
| d6de3572ca | |||
| d8c316faef | |||
| 65f18c363b | |||
| c32e2898ac | |||
| d3532d7d6f | |||
| f07a40deb4 | |||
| 1052435391 | |||
| ce596424cf | |||
| 3959200a5b | |||
| f8d0f22344 | |||
| 0a0fadb16a | |||
| d0489062b5 | |||
| 630e14fd7f | |||
| 394f580f16 | |||
| 4ef82c4119 | |||
| 4bd6fb0995 | |||
| ae534ddeab | |||
| ae6ada4162 | |||
| b8d55987c2 | |||
| 26261387f8 | |||
| 946fd3f387 | |||
| 50a797b0be | |||
| 302e448b93 | |||
| dd51bf2021 | |||
| 49178539f3 | |||
| f01391a7d9 | |||
| c67afb45ae | |||
| d0c3972cc7 | |||
| be8c5491b1 | |||
| ebc11fdbc8 | |||
| a981f78e2f | |||
| a49c68806a | |||
| 8bc6becce1 | |||
| 5151bb8bf2 | |||
| f19ff793f5 | |||
| 4378c4b8bb | |||
| 61fffb9578 | |||
| a46af06baa | |||
| b6bfa13b70 | |||
| 6f89473f33 | |||
| be700bc825 | |||
| 38697ac628 | |||
| fa56d539a7 | |||
| 5c10e7f6cf | |||
| a29fdbd024 | |||
| f0d8ebd851 | |||
| 3a001dd71c | |||
| c8effae4eb | |||
| 9d0ab68f3b | |||
| eeddfc058a | |||
| 0d9b4cdaed | |||
| 1398bdb10e | |||
| b89c717643 | |||
| 85301e3a67 | |||
| b0b6bf8702 | |||
| 85b05998ed | |||
| e899f46727 | |||
| 3aaf249236 | |||
| b73be441b3 | |||
| e449ffcc36 | |||
| bca727a3cb | |||
| e1e41be1a9 | |||
| ad7f3d2b24 | |||
| 4ef0d3ee99 | |||
| 67cbeebc7b | |||
| 16abf93be8 | |||
| d3cc32ddca | |||
| 689f884810 | |||
| f653c1c0c5 | |||
| b8a845fe93 | |||
| a6d2297545 | |||
| d0c6f845da | |||
| 81be78cd4d | |||
| b778da1d7c | |||
| a1795f80dd | |||
| 9c19ec8409 | |||
| fc253fe1ee | |||
| d72a01d739 | |||
| f93b3f51c9 | |||
| 3d08a5048b | |||
| 9ee66008db | |||
| 6a257fe2de | |||
| dad7bb53a2 | |||
| 47ba86af33 | |||
| 9689de426b | |||
| 124eb42758 | |||
| 85404c184b | |||
| ed42cfe699 | |||
| 3099f4f12d | |||
| 8fceb9d957 | |||
| d4185a5d57 | |||
| 1262fca36b | |||
| 890b1cf512 | |||
| 1633641055 | |||
| 2dcb67091f | |||
| fb34601d5a | |||
| b6bcc8cca3 | |||
| e5a7deb6ad | |||
| 10100e34e1 | |||
| 2d31b422c8 | |||
| 89919c8832 | |||
| dc5f5eaf65 | |||
| ee8970dc42 | |||
| 0a44b48e21 | |||
| 36e53c7394 | |||
| 38eb400e41 | |||
| 5198b1b079 | |||
| e8a11591a8 | |||
| cbc8f98682 | |||
| ecdcb5d0c6 | |||
| c7494aed0f | |||
| 215ef16803 | |||
| 350b846d3a | |||
| 9ce9920ff7 | |||
| 1c0b087105 | |||
| 137d4b89b4 | |||
| 2cc4885a2e | |||
| 736e1fa7b7 | |||
| 177c7f1cf3 | |||
| 9bf904e8a6 | |||
| 5ea5f6f267 | |||
| 525b6e48e9 | |||
| c7b115b68e | |||
| 62aef9cd34 | |||
| f57617c944 | |||
| c4a0e57046 | |||
| 76c5cb6d87 | |||
| fc4e5007fd | |||
| af24fd6842 | |||
| 002a22a097 | |||
| 9f20eb8ce6 | |||
| 2e636458a6 | |||
| 47d0a95fd6 | |||
| 5d142326f5 | |||
| ef9683ee79 | |||
| 8a77534d02 | |||
| 73ed45f9d7 | |||
| 2d6df2e125 | |||
| e754b738ad | |||
| 1dadb3fcc9 | |||
| 4e88245745 | |||
| debc9bf7cf | |||
| e03673485b | |||
| 6efe4e1998 | |||
| ff6ed7055d | |||
| 6c85e2c697 | |||
| 2d0340cefd | |||
| a974deeb59 | |||
| cf5bb4e16e | |||
| 0d4b0ee116 | |||
| 03cb3e9dc0 | |||
| f0dd0b5c8c | |||
| 94ca077e69 | |||
| e92e59ca78 | |||
| e0cabc1174 | |||
| 5e2f142704 | |||
| 2beb0ffad1 | |||
| fa373af9b5 | |||
| 7909716c1f | |||
| c1cb971bca | |||
| 538ec25ad9 | |||
| 17152484c2 | |||
| 954b567b9b | |||
| 6061476d8e | |||
| ad903aeaa1 | |||
| c8c1b0f781 | |||
| 534f096bb8 | |||
| 7da36b2470 | |||
| f2db7f7665 | |||
| 40a1af97b9 | |||
| 53ff5413cd | |||
| dc889587ce | |||
| 6486ab6cab | |||
| ab234c72a3 | |||
| 485c7b2725 | |||
| 4861d15056 | |||
| 1e73025f86 | |||
| 378212e5ab | |||
| 4f52f3f3c5 | |||
| a0d48b6c63 | |||
| b14270bd71 | |||
| 8f720a54f6 | |||
| 2c41dbc472 | |||
| a8660b5b4f | |||
| 4ccafff123 | |||
| 856f8d3d47 | |||
| 00e20f1524 | |||
| 215acefbb4 | |||
| c33c9ff22a | |||
| 99fdd59042 | |||
| 5af1099fbf | |||
| f983df0c70 | |||
| 936740201c | |||
| 4489517eeb | |||
| b1b7c505a1 | |||
| a2e7f3788f | |||
| d2bb8fe537 | |||
| 5289b08bcf | |||
| cc8f6eadfe | |||
| 9b2f7341d8 | |||
| 650946cd2a | |||
| 9801e486d9 | |||
| 3381192297 | |||
| b2e3dd17ea | |||
| 01715f6f9a | |||
| 8720e5d712 | |||
| 8752093801 | |||
| 3c957c6e9d | |||
| 3ef5037c16 | |||
| 7534b2a160 | |||
| b28425b8c3 | |||
| 1f5e0b6f68 | |||
| 646f6a1006 | |||
| cc683f2040 | |||
| 18e8f648c2 | |||
| 821e4da2c7 | |||
| 13d98fd2d5 | |||
| 92cd656c68 | |||
| 727a750b34 | |||
| 5dabb678ce | |||
| ef988aca28 | |||
| 64f3759fd0 | |||
| d71d2bd2d0 | |||
| 702bebf176 | |||
| 25da8e9d44 | |||
| 845f6ec8cf | |||
| e1ad4daf8d | |||
| 783b717af8 | |||
| 65e1fd299e | |||
| b29b1964ba | |||
| 80a8df0643 | |||
| d9fc6c0086 | |||
| cb612a4b90 | |||
| 36d77debd0 | |||
| 530ad2925d | |||
| ec33519dc7 | |||
| 2fd4b53aaf | |||
| a2c4fe1c90 | |||
| 3e56612990 | |||
| 75858673c4 | |||
| 57223958b5 | |||
| 3553a754a4 | |||
| f290fb1e05 | |||
| 0c64818f52 | |||
| c44548ba0f | |||
| 59bddfba8d | |||
| 8a1fcd8991 | |||
| 3546b625e7 | |||
| 87443cd34d | |||
| 5f0e9fce61 | |||
| a2cce7f897 | |||
| f4041dc1f0 | |||
| 8e3757ac87 | |||
| 41fa0cdf82 | |||
| 692f5fdd72 | |||
| de0a1e66d8 | |||
| 129445cd1d | |||
| 13d0aefd7c | |||
| 5f7b05e808 | |||
| 32f65bae55 | |||
| 49d9b8bb00 | |||
| b3eba70b7a | |||
| cec7a5dc98 | |||
| 14993f58e3 | |||
| e8a17b4963 | |||
| fb77212221 | |||
| aa7f6973c0 | |||
| c2af5a82ff | |||
| 348114e5bd | |||
| a6e28ac2ee | |||
| 0e6f78a656 | |||
| 2305fb59a2 | |||
| cc816043c1 | |||
| b6dbb0fd8d | |||
| fdcf8b592e | |||
| 4ff77a4752 | |||
| f04ee80452 | |||
| ddbbcc6f5d | |||
| 0f40afa357 | |||
| cac8d3f405 | |||
| b521a913ab | |||
| d6651ccd82 | |||
| 2976798852 | |||
| 1b90b42647 | |||
| de805e4af7 | |||
| 4d085424f8 | |||
| d07981ea3c | |||
| 22d5cbd0fa | |||
| 4c9ca91b98 | |||
| 0736f325fc | |||
| dcc5afa8fa | |||
| 226465e882 | |||
| 0b62dbe16b | |||
| 2deb4e6f65 | |||
| 9f32f217e6 | |||
| e62781cccb | |||
| e1912fa5be | |||
| a7fe9db773 | |||
| 35296a8692 | |||
| 31801a7312 | |||
| cc7ecd53c7 | |||
| 586e49cab3 | |||
| ebe47a580c | |||
| 7933c10c97 | |||
| 2bc97ee23f | |||
| c88ab5cd12 | |||
| 943aaef76a | |||
| 3fd9e94a34 | |||
| e423f8f605 | |||
| 0eb90ecb3e | |||
| 703f3d0573 | |||
| 2337704602 | |||
| bd9888a439 | |||
| 12b3d0e08d | |||
| 89d350a791 | |||
| 99a83e5522 | |||
| 4d53a26a06 | |||
| a8328cb5ff | |||
| 844c328625 | |||
| 39b97d4e18 | |||
| 45f497e8f6 | |||
| edc5a0412c | |||
| 9670e3a5eb | |||
| 7b2b10bc9e | |||
| bd357adb8b | |||
| 670b6011da | |||
| 150ff72646 | |||
| d567442136 | |||
| 540fff5226 | |||
| 21273c921e | |||
| 75e52427d1 | |||
| 21fd3d0320 | |||
| 1ee798439a | |||
| cc52f980b3 | |||
| ec7e3192bb | |||
| 3fd352a7ef | |||
| 49570c11c6 | |||
| b8ae62a0b1 | |||
| 29a6f0504a | |||
| eadab06f59 | |||
| 9493f2a0eb | |||
| b593b7cc43 | |||
| 5c0c2a17b0 | |||
| 5f33b2fb2d | |||
| 63e0e038fa | |||
| d24a14cb39 | |||
| 3efa52f53b | |||
| 16a4206720 | |||
| e4784d44f6 | |||
| aaf2aac050 | |||
| b5ec0e9744 | |||
| 070a13096b | |||
| 7ccab2bdb9 | |||
| e9434befaa | |||
| 56c77fd5fa | |||
| e6bd88371e | |||
| bc30b01eb7 | |||
| ef93981bfa | |||
| 35e2fc7dd9 | |||
| 2feddf32b2 | |||
| ed185e90f6 | |||
| 19fc66f88a | |||
| 5cbfc7705b | |||
| 8de8c3eb00 | |||
| 04365f12ff | |||
| 9297cd2f3e | |||
| 0711160b1c | |||
| 33f01084d1 | |||
| 1fbec6f601 | |||
| cf5b743de6 | |||
| 2c377e534f | |||
| 1ca9fe35c2 | |||
| 56c49b3b42 | |||
| 5429748767 | |||
| 6aecf59536 | |||
| afc7ff1b7a | |||
| 222e880561 | |||
| 6901e3417b | |||
| cd33562379 | |||
| 073503a6f2 | |||
| 61d5a50534 | |||
| b6e0d4807a | |||
| c7a37c06d8 | |||
| efbd0b9ea0 | |||
| 6ed8f07cb6 | |||
| 5164555c4f | |||
| c5999702ae | |||
| 2a5de8e0f8 | |||
| d05cb31e2e | |||
| c7a9ea2bf4 | |||
| b637ad49d9 | |||
| c6a2c99123 | |||
| 852598fa0a | |||
| 3751d9cf51 | |||
| 30c388aea8 | |||
| b622e3e0a7 | |||
| 086e33dd6e | |||
| 889ce4c4fb | |||
| 96c00271e3 | |||
| a6adedf6e0 | |||
| eb821ceb5c | |||
| 98d61982f9 | |||
| 04a26ada69 | |||
| 3e0dd06374 | |||
| f18828228a | |||
| c812c3192d | |||
| 8d3b919ef6 | |||
| 63df46bf22 | |||
| 826c5e96a1 | |||
| 1870d4905b | |||
| 347b23055d | |||
| cbea5f198f | |||
| be379e188b | |||
| 42d9bd0516 | |||
| 3ca9f351a0 | |||
| a1d6a062a9 | |||
| c9dbf97649 | |||
| 2c04a27a2a | |||
| 70c0592e84 | |||
| 572c03dbac | |||
| fa498221da | |||
| 67238d5045 | |||
| 994170ddb5 | |||
| 3c28188d7a | |||
| 4ccd17903b | |||
| 0e1b573f89 | |||
| 10580aca92 | |||
| 6b13175338 | |||
| d0171084b5 | |||
| 2bfdd0d61d | |||
| ea53111afc | |||
| 0739d4ac2d | |||
| 8dca43881a | |||
| a885111c0c | |||
| bd73664f4c | |||
| 608c16007e | |||
| 275abc1eb5 | |||
| ff34b8af76 | |||
| 03e9777c3f | |||
| 1033d3d80e | |||
| 1f1efec4c9 | |||
| f0f04d4b5b | |||
| 2b7707ecf6 | |||
| ef870d5533 | |||
| a5044302a2 | |||
| 6a4f685d04 | |||
| 355499a8de | |||
| 67fd6c80dd | |||
| c990515eaf | |||
| 76e91da3ad | |||
| 9fcac06297 | |||
| 3e2549f2b8 | |||
| 63961dec45 | |||
| 7a19a11001 | |||
| 93f7925c4d | |||
| a2c5fca787 | |||
| bb06468ead | |||
| 1d8dc8a69a | |||
| a254a05df0 | |||
| 2aa7648bb8 | |||
| b309bf4173 | |||
| a3fcde2ae8 | |||
| f8ff156869 | |||
| 375dfe16a8 | |||
| b976135d2f | |||
| f40f7f9ece | |||
| ea6677c464 | |||
| 8258257658 | |||
| f5d67b5eee | |||
| 8ee3c7b485 | |||
| 8450f9f333 | |||
| 4cd76f4966 | |||
| ec254074d1 | |||
| 8059106cae | |||
| 23b4aaf2a5 | |||
| 5359f6d354 | |||
| a70e4c3074 | |||
| 7a2f2ddf32 | |||
| 2dc0f97c93 | |||
| 15fcbf24f1 | |||
| e89c6b3b88 | |||
| c4a7f25b62 | |||
| 1d74a97ba6 | |||
| aea467ff02 | |||
| b501ad4d51 | |||
| dd7de180ea | |||
| 2b46e1450a | |||
| 7ed8abb66c | |||
| ef2bb7f2fc | |||
| ae3b74245f | |||
| c0a74f7a20 | |||
| cea3572b74 | |||
| cd9ec6b240 | |||
| b4cc4ea8e2 | |||
| 154f655335 | |||
| 2ff707d82f | |||
| 8320934d91 | |||
| 63441c048c | |||
| d0069c136b | |||
| 870d19f33d | |||
| 60c34a0837 | |||
| 22e79479d2 | |||
| 3570022b9a | |||
| dd5f5fdb98 | |||
| 18b7ddef8f | |||
| 5ec9aee216 | |||
| 927548621b | |||
| 6005b12f94 | |||
| 09aa21390d | |||
| d097a0c201 | |||
| 560c503871 | |||
| 3d24225cc1 | |||
| 51314fa9fe | |||
| 803b54ebdb | |||
| c085b8af19 | |||
| 2cec2587be | |||
| 2148e2dff2 | |||
| f55f3bb7cd | |||
| 4d55671b17 | |||
| dfc66d7807 | |||
| 31101ecaab | |||
| 3d879dd1ae | |||
| ef9e430992 | |||
| 63fa250f29 | |||
| ceb557058c | |||
| 6a67f9e56f | |||
| 372682d4a9 | |||
| ab44c9a4ff | |||
| 1805a47139 | |||
| 5417efaa1d | |||
| 4536719353 | |||
| b54d5997de | |||
| 385ad9e839 | |||
| 7c6bc70312 | |||
| 8ec61991ee | |||
| 1eef956cad | |||
| daef43f620 | |||
| aa91a02db8 | |||
| a6d0a88b1e | |||
| f2c806f8a0 | |||
| 349c0ec662 | |||
| 741ea44aba | |||
| 56a89eb4fb | |||
| 3f830827b2 | |||
| a2a385336e | |||
| be934b3881 | |||
| 3d6dfc864d |
-19
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
Checks: '
|
|
||||||
bugprone-*,
|
|
||||||
-bugprone-integer-division,
|
|
||||||
-bugprone-narrowing-conversions,
|
|
||||||
performance-*,
|
|
||||||
clang-analyzer-*,
|
|
||||||
misc-*,
|
|
||||||
-misc-unused-parameters,
|
|
||||||
modernize-*,
|
|
||||||
-modernize-avoid-c-arrays,
|
|
||||||
-modernize-deprecated-headers,
|
|
||||||
-modernize-use-auto,
|
|
||||||
-modernize-use-using,
|
|
||||||
-modernize-use-nullptr,
|
|
||||||
-modernize-use-trailing-return-type,
|
|
||||||
'
|
|
||||||
CheckOptions:
|
|
||||||
...
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
Wen
|
|
||||||
REGIST
|
|
||||||
PullRequest
|
|
||||||
cancelled
|
|
||||||
FOF
|
|
||||||
@@ -13,27 +13,6 @@
|
|||||||
*.o-*
|
*.o-*
|
||||||
*.os
|
*.os
|
||||||
*.os-*
|
*.os-*
|
||||||
*.so
|
|
||||||
*.a
|
|
||||||
|
|
||||||
venv/
|
venv/
|
||||||
.venv/
|
.venv/
|
||||||
|
|
||||||
notebooks
|
|
||||||
phone
|
|
||||||
massivemap
|
|
||||||
neos
|
|
||||||
installer
|
|
||||||
chffr/app2
|
|
||||||
chffr/backend/env
|
|
||||||
selfdrive/nav
|
|
||||||
selfdrive/baseui
|
|
||||||
selfdrive/test/simulator2
|
|
||||||
**/cache_data
|
|
||||||
xx/plus
|
|
||||||
xx/community
|
|
||||||
xx/projects
|
|
||||||
!xx/projects/eon_testing_master
|
|
||||||
!xx/projects/map3d
|
|
||||||
xx/ops
|
|
||||||
xx/junk
|
|
||||||
|
|||||||
+3
-1
@@ -7,10 +7,12 @@
|
|||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.otf filter=lfs diff=lfs merge=lfs -text
|
||||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
||||||
selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
|
selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
|
||||||
system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text
|
system/hardware/tici/updater_weston filter=lfs diff=lfs merge=lfs -text
|
||||||
|
system/hardware/tici/updater_magic filter=lfs diff=lfs merge=lfs -text
|
||||||
third_party/**/*.a filter=lfs diff=lfs merge=lfs -text
|
third_party/**/*.a filter=lfs diff=lfs merge=lfs -text
|
||||||
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text
|
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text
|
||||||
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text
|
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
* @sunnypilot/dev-internal
|
|
||||||
/.github/ @devtekve @sunnyhaibin
|
|
||||||
/release/ci/ @devtekve @sunnyhaibin
|
|
||||||
/tinygrad_repo @devtekve @Discountchubbs
|
|
||||||
/tinygrad/ @devtekve @Discountchubbs
|
|
||||||
/selfdrive/controls/lib/longitudinal_planner.py @devtekve @Discountchubbs
|
|
||||||
/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @devtekve @Discountchubbs
|
|
||||||
/selfdrive/modeld/ @devtekve @Discountchubbs
|
|
||||||
/sunnypilot/model* @devtekve @Discountchubbs
|
|
||||||
/sunnypilot/sunnylink/ @devtekve
|
|
||||||
/system/athena/ @devtekve
|
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
ci:
|
CI / testing:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-all-files: "{.github/**,**/test_*,Jenkinsfile}"
|
- any-glob-to-all-files: "{.github/**,**/test_*,**/test/**,Jenkinsfile}"
|
||||||
|
|
||||||
chore:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-all-files: "{.github/**}"
|
|
||||||
|
|
||||||
car:
|
car:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
@@ -16,7 +12,7 @@ simulation:
|
|||||||
|
|
||||||
ui:
|
ui:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-all-files: '{selfdrive/ui/**,system/ui/**}'
|
- any-glob-to-all-files: '{selfdrive/assets/**,selfdrive/ui/**,system/ui/**}'
|
||||||
|
|
||||||
tools:
|
tools:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
@@ -28,4 +24,4 @@ multilanguage:
|
|||||||
|
|
||||||
autonomy:
|
autonomy:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-all-files: "{selfdrive/modeld/models/**,selfdrive/test/process_replay/model_replay_ref_commit,sunnypilot/modeld*/models/**}"
|
- any-glob-to-all-files: "{selfdrive/modeld/models/**,selfdrive/test/process_replay/model_replay_ref_commit}"
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
exclude-labels:
|
|
||||||
- 'no-changelog'
|
|
||||||
categories:
|
|
||||||
- title: '🚀 Features'
|
|
||||||
labels:
|
|
||||||
- 'feature'
|
|
||||||
- 'enhancement'
|
|
||||||
- title: '🐛 Bug Fixes'
|
|
||||||
collapse-after: 5
|
|
||||||
labels:
|
|
||||||
- 'fix'
|
|
||||||
- 'bugfix'
|
|
||||||
- 'bug'
|
|
||||||
- title: '🧰 Maintenance'
|
|
||||||
collapse-after: 5
|
|
||||||
label: 'chore'
|
|
||||||
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
|
||||||
change-title-escapes: '\<*_&'
|
|
||||||
replacers:
|
|
||||||
- search: '/[Ss][Uu][Nn][Nn][Yy][Pp][Ii][Ll][Oo][Tt]/g'
|
|
||||||
replace: 'sunnypilot'
|
|
||||||
- search: '/\b[Ss][Pp]\b/g'
|
|
||||||
replace: 'SP'
|
|
||||||
version-resolver:
|
|
||||||
major:
|
|
||||||
labels:
|
|
||||||
- 'major'
|
|
||||||
minor:
|
|
||||||
labels:
|
|
||||||
- 'minor'
|
|
||||||
patch:
|
|
||||||
labels:
|
|
||||||
- 'patch'
|
|
||||||
default: patch
|
|
||||||
name-template: 'v$RESOLVED_VERSION 🚀'
|
|
||||||
tag-template: 'v$RESOLVED_VERSION'
|
|
||||||
version-template: "0.$MAJOR.$MINOR.$PATCH" # The day OP becomes v1, we need to bump this
|
|
||||||
tag-prefix: "v0." # The day OP becomes v1, we need to bump this
|
|
||||||
prerelease-identifier: "staging"
|
|
||||||
template: |
|
|
||||||
## Changes
|
|
||||||
|
|
||||||
$CHANGES
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
name: 'automatically cache based on current runner'
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
path:
|
|
||||||
description: 'path to cache'
|
|
||||||
required: true
|
|
||||||
key:
|
|
||||||
description: 'key'
|
|
||||||
required: true
|
|
||||||
restore-keys:
|
|
||||||
description: 'restore-keys'
|
|
||||||
required: true
|
|
||||||
save:
|
|
||||||
description: 'whether to save the cache'
|
|
||||||
default: 'true'
|
|
||||||
required: false
|
|
||||||
outputs:
|
|
||||||
cache-hit:
|
|
||||||
description: 'cache hit occurred'
|
|
||||||
value: ${{ (contains(runner.name, 'nsc') && steps.ns-cache.outputs.cache-hit) ||
|
|
||||||
(!contains(runner.name, 'nsc') && inputs.save != 'false' && steps.gha-cache.outputs.cache-hit) ||
|
|
||||||
(!contains(runner.name, 'nsc') && inputs.save == 'false' && steps.gha-cache-ro.outputs.cache-hit) }}
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
- name: setup namespace cache
|
|
||||||
id: ns-cache
|
|
||||||
if: ${{ contains(runner.name, 'nsc') }}
|
|
||||||
uses: namespacelabs/nscloud-cache-action@v1
|
|
||||||
with:
|
|
||||||
path: ${{ inputs.path }}
|
|
||||||
|
|
||||||
- name: setup github cache
|
|
||||||
id: gha-cache
|
|
||||||
if: ${{ !contains(runner.name, 'nsc') && inputs.save != 'false' }}
|
|
||||||
uses: 'actions/cache@v4'
|
|
||||||
with:
|
|
||||||
path: ${{ inputs.path }}
|
|
||||||
key: ${{ inputs.key }}
|
|
||||||
restore-keys: ${{ inputs.restore-keys }}
|
|
||||||
|
|
||||||
- name: setup github cache
|
|
||||||
id: gha-cache-ro
|
|
||||||
if: ${{ !contains(runner.name, 'nsc') && inputs.save == 'false' }}
|
|
||||||
uses: 'actions/cache/restore@v4'
|
|
||||||
with:
|
|
||||||
path: ${{ inputs.path }}
|
|
||||||
key: ${{ inputs.key }}
|
|
||||||
restore-keys: ${{ inputs.restore-keys }}
|
|
||||||
|
|
||||||
# make the directory manually in case we didn't get a hit, so it doesn't fail on future steps
|
|
||||||
- id: scons-cache-setup
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
mkdir -p ${{ inputs.path }}
|
|
||||||
sudo chmod -R 777 ${{ inputs.path }}
|
|
||||||
sudo chown -R $USER ${{ inputs.path }}
|
|
||||||
@@ -9,15 +9,14 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
issues: write
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: false
|
submodules: false
|
||||||
|
|
||||||
# Label PRs
|
# Label PRs
|
||||||
- uses: actions/labeler@v5.0.0
|
- uses: actions/labeler@v6
|
||||||
with:
|
with:
|
||||||
dot: true
|
dot: true
|
||||||
configuration-path: .github/labeler.yaml
|
configuration-path: .github/labeler.yaml
|
||||||
@@ -25,67 +24,12 @@ jobs:
|
|||||||
# Check PR target branch
|
# Check PR target branch
|
||||||
- name: check branch
|
- name: check branch
|
||||||
uses: Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
|
uses: Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'commaai/openpilot'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
target: /^(?!master-new$).*/
|
target: /^(?!master$).*/
|
||||||
exclude: /sunnypilot:.*/
|
exclude: /commaai:.*/
|
||||||
change-to: ${{ github.base_ref }}
|
change-to: ${{ github.base_ref }}
|
||||||
already-exists-action: close_this
|
already-exists-action: close_this
|
||||||
already-exists-comment: "Your PR should be made against the `master-new` branch"
|
already-exists-comment: "Your PR should be made against the `master` branch"
|
||||||
|
|
||||||
update-pr-labels:
|
|
||||||
name: Update fork's PR Labels
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: (github.event.pull_request.head.repo.fork && (contains(github.event_name, 'pull_request') && github.event.action == 'synchronize'))
|
|
||||||
env:
|
|
||||||
PR_LABEL: 'dev-c3'
|
|
||||||
TRUST_FORK_PR_LABEL: 'trust-fork-pr'
|
|
||||||
steps:
|
|
||||||
- name: Check if PR has dev-c3 label
|
|
||||||
id: check-labels
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
const prNumber = context.payload.pull_request.number;
|
|
||||||
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: prNumber
|
|
||||||
});
|
|
||||||
|
|
||||||
const hasDevC3Label = labels.some(label => label.name === process.env.PR_LABEL);
|
|
||||||
const hasTrustLabel = labels.some(label => label.name === process.env.TRUST_FORK_PR_LABEL);
|
|
||||||
|
|
||||||
console.log(`PR #${prNumber} has ${process.env.PR_LABEL} label: ${hasDevC3Label}`);
|
|
||||||
console.log(`PR #${prNumber} has ${process.env.TRUST_FORK_PR_LABEL} label: ${hasTrustLabel}`);
|
|
||||||
|
|
||||||
core.setOutput('has-dev-c3', hasDevC3Label ? 'true' : 'false');
|
|
||||||
core.setOutput('has-trust', hasTrustLabel ? 'true' : 'false');
|
|
||||||
|
|
||||||
- name: Remove trust-fork-pr label if present
|
|
||||||
if: steps.check-labels.outputs.has-dev-c3 == 'true' && steps.check-labels.outputs.has-trust == 'true'
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
const prNumber = context.payload.pull_request.number;
|
|
||||||
|
|
||||||
await github.rest.issues.removeLabel({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: prNumber,
|
|
||||||
name: process.env.TRUST_FORK_PR_LABEL
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Removed '${process.env.TRUST_FORK_PR_LABEL}' label from PR #${prNumber} as it received new commits`);
|
|
||||||
|
|
||||||
// Add a comment to the PR
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: prNumber,
|
|
||||||
body: `The \`${process.env.TRUST_FORK_PR_LABEL}\` label has been automatically removed because new commits were pushed to this PR. This PR will need to be re-reviewed before the label can be applied again.`
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -5,31 +5,29 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BASE_IMAGE: openpilot-base
|
PYTHONPATH: ${{ github.workspace }}
|
||||||
DOCKER_REGISTRY: ghcr.io/commaai
|
|
||||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/bash -c
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
badges:
|
badges:
|
||||||
name: create badges
|
name: create badges
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'commaai/openpilot'
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- run: ./tools/op.sh setup
|
||||||
- name: Push badges
|
- name: Push badges
|
||||||
run: |
|
run: |
|
||||||
${{ env.RUN }} "scons -j$(nproc) && python3 selfdrive/ui/translations/create_badges.py"
|
python3 selfdrive/ui/translations/create_badges.py
|
||||||
|
|
||||||
rm .gitattributes
|
rm .gitattributes
|
||||||
|
|
||||||
git checkout --orphan badges
|
git checkout --orphan badges
|
||||||
git rm -rf --cached .
|
git rm -rf --cached .
|
||||||
git config user.email "badge-researcher@sunnypilot.ai"
|
git config user.email "badge-researcher@comma.ai"
|
||||||
git config user.name "Badge Researcher"
|
git config user.name "Badge Researcher"
|
||||||
|
|
||||||
git add translation_badge.svg
|
git add translation_badge.svg
|
||||||
|
|||||||
@@ -1,226 +0,0 @@
|
|||||||
name: Build All Tinygrad Models and Push to GitLab
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
branch:
|
|
||||||
description: 'Branch to run workflow from'
|
|
||||||
required: false
|
|
||||||
default: 'master-new'
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
setup:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
json_file: ${{ steps.get-json.outputs.json_file }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout docs repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: sunnypilot/sunnypilot-docs
|
|
||||||
ref: gh-pages
|
|
||||||
path: docs
|
|
||||||
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Get next JSON version to use
|
|
||||||
id: get-json
|
|
||||||
run: |
|
|
||||||
cd docs/docs
|
|
||||||
latest=$(ls driving_models_v*.json | sed -E 's/.*_v([0-9]+)\.json/\1/' | sort -n | tail -1)
|
|
||||||
next=$((latest+1))
|
|
||||||
json_file="driving_models_v${next}.json"
|
|
||||||
cp "driving_models_v${latest}.json" "$json_file"
|
|
||||||
echo "json_file=$json_file" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Upload context for next jobs
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: context
|
|
||||||
path: docs
|
|
||||||
|
|
||||||
build-all:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: setup
|
|
||||||
env:
|
|
||||||
JSON_FILE: docs/docs/${{ needs.setup.outputs.json_file }}
|
|
||||||
steps:
|
|
||||||
- name: Set up SSH
|
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Add GitLab.com SSH key to known_hosts
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
- name: Clone GitLab docs repo
|
|
||||||
env:
|
|
||||||
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
|
||||||
run: |
|
|
||||||
echo "Cloning GitLab"
|
|
||||||
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/docs.sunnypilot.ai2.git gitlab_docs
|
|
||||||
cd gitlab_docs
|
|
||||||
git checkout main
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
- name: Set next recompiled dir
|
|
||||||
id: set-recompiled
|
|
||||||
run: |
|
|
||||||
cd gitlab_docs/models
|
|
||||||
latest_dir=$(ls -d recompiled* 2>/dev/null | sed -E 's/recompiled([0-9]+)/\1/' | sort -n | tail -1)
|
|
||||||
if [[ -z "$latest_dir" ]]; then
|
|
||||||
next_dir=1
|
|
||||||
else
|
|
||||||
next_dir=$((latest_dir+1))
|
|
||||||
fi
|
|
||||||
recompiled_dir="recompiled${next_dir}"
|
|
||||||
mkdir -p "$recompiled_dir"
|
|
||||||
echo "RECOMPILED_DIR=$recompiled_dir" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Download context
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: context
|
|
||||||
path: .
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y jq gh
|
|
||||||
|
|
||||||
- name: Build all tinygrad models
|
|
||||||
id: trigger-builds
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
> triggered_run_ids.txt
|
|
||||||
BRANCH="${{ github.event.inputs.branch }}"
|
|
||||||
jq -c '.bundles[] | select(.runner=="tinygrad")' "$JSON_FILE" | while read -r bundle; do
|
|
||||||
ref=$(echo "$bundle" | jq -r '.ref')
|
|
||||||
display_name=$(echo "$bundle" | jq -r '.display_name' | sed 's/ ([^)]*)//g')
|
|
||||||
is_20hz=$(echo "$bundle" | jq -r '.is_20hz')
|
|
||||||
echo "Triggering build for: $display_name ($ref) [20Hz: $is_20hz]"
|
|
||||||
START_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
||||||
gh workflow run sunnypilot-build-model.yaml \
|
|
||||||
--repo sunnypilot/sunnypilot \
|
|
||||||
--ref "$BRANCH" \
|
|
||||||
-f upstream_branch="$ref" \
|
|
||||||
-f custom_name="$display_name" \
|
|
||||||
-f is_20hz="$is_20hz"
|
|
||||||
for i in {1..24}; do
|
|
||||||
RUN_ID=$(gh run list --repo sunnypilot/sunnypilot --workflow=sunnypilot-build-model.yaml --branch="$BRANCH" --created ">$START_TIME" --limit=1 --json databaseId --jq '.[0].databaseId')
|
|
||||||
if [ -n "$RUN_ID" ]; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
if [ -z "$RUN_ID" ]; then
|
|
||||||
echo "ould not find the triggered workflow run for $display_name ($ref)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "$RUN_ID" >> triggered_run_ids.txt
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Wait for all model builds to finish
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
SUCCESS_RUNS=()
|
|
||||||
FAILED_RUNS=()
|
|
||||||
declare -A RUN_ID_TO_NAME
|
|
||||||
|
|
||||||
while read -r RUN_ID; do
|
|
||||||
ARTIFACT_NAME=$(gh api repos/sunnypilot/sunnypilot/actions/runs/$RUN_ID/artifacts --jq '.artifacts[] | select(.name | startswith("model-")) | .name' || echo "unknown")
|
|
||||||
RUN_ID_TO_NAME["$RUN_ID"]="$ARTIFACT_NAME"
|
|
||||||
done < triggered_run_ids.txt
|
|
||||||
|
|
||||||
while read -r RUN_ID; do
|
|
||||||
echo "Watching run ID: $RUN_ID"
|
|
||||||
gh run watch "$RUN_ID" --repo sunnypilot/sunnypilot
|
|
||||||
CONCLUSION=$(gh run view "$RUN_ID" --repo sunnypilot/sunnypilot --json conclusion --jq '.conclusion')
|
|
||||||
ARTIFACT_NAME="${RUN_ID_TO_NAME[$RUN_ID]}"
|
|
||||||
echo "Run $RUN_ID ($ARTIFACT_NAME) concluded with: $CONCLUSION"
|
|
||||||
if [[ "$CONCLUSION" == "success" ]]; then
|
|
||||||
SUCCESS_RUNS+=("$RUN_ID")
|
|
||||||
else
|
|
||||||
FAILED_RUNS+=("$RUN_ID")
|
|
||||||
fi
|
|
||||||
done < triggered_run_ids.txt
|
|
||||||
|
|
||||||
if [[ ${#SUCCESS_RUNS[@]} -eq 0 ]]; then
|
|
||||||
echo "All model builds failed. Aborting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ${#FAILED_RUNS[@]} -gt 0 ]]; then
|
|
||||||
echo "WARNING: The following model builds failed:"
|
|
||||||
for RUN_ID in "${FAILED_RUNS[@]}"; do
|
|
||||||
echo "- $RUN_ID (${RUN_ID_TO_NAME[$RUN_ID]})"
|
|
||||||
done
|
|
||||||
echo "You may want to rerun these models manually."
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Download and extract all model artifacts
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
ARTIFACT_DIR="gitlab_docs/models/$RECOMPILED_DIR"
|
|
||||||
SUCCESS_RUNS=()
|
|
||||||
while read -r RUN_ID; do
|
|
||||||
CONCLUSION=$(gh run view "$RUN_ID" --repo sunnypilot/sunnypilot --json conclusion --jq '.conclusion')
|
|
||||||
if [[ "$CONCLUSION" == "success" ]]; then
|
|
||||||
SUCCESS_RUNS+=("$RUN_ID")
|
|
||||||
fi
|
|
||||||
done < triggered_run_ids.txt
|
|
||||||
|
|
||||||
for RUN_ID in "${SUCCESS_RUNS[@]}"; do
|
|
||||||
ARTIFACT_NAME=$(gh api repos/sunnypilot/sunnypilot/actions/runs/$RUN_ID/artifacts --jq '.artifacts[] | select(.name | startswith("model-")) | .name')
|
|
||||||
echo "Downloading artifact: $ARTIFACT_NAME from run: $RUN_ID"
|
|
||||||
mkdir -p "$ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
echo "Created directory: $ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
gh run download "$RUN_ID" --repo sunnypilot/sunnypilot -n "$ARTIFACT_NAME" --dir "$ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
echo "Downloaded artifact zip(s) to: $ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
ZIP_PATH=$(find "$ARTIFACT_DIR/$ARTIFACT_NAME" -type f -name '*.zip' | head -n1)
|
|
||||||
if [ -n "$ZIP_PATH" ]; then
|
|
||||||
echo "Unzipping $ZIP_PATH to $ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
unzip -o "$ZIP_PATH" -d "$ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
rm -f "$ZIP_PATH"
|
|
||||||
echo "Unzipped and removed $ZIP_PATH"
|
|
||||||
else
|
|
||||||
echo "No zip file found in $ARTIFACT_DIR/$ARTIFACT_NAME (This is NOT an error)."
|
|
||||||
fi
|
|
||||||
echo "Done processing $ARTIFACT_NAME"
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Push recompiled dir to GitLab
|
|
||||||
env:
|
|
||||||
GITLAB_SSH_PRIVATE_KEY: ${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
|
|
||||||
run: |
|
|
||||||
cd gitlab_docs
|
|
||||||
git checkout main
|
|
||||||
mkdir -p models/"$(basename $RECOMPILED_DIR)"
|
|
||||||
git add models/"$(basename $RECOMPILED_DIR)"
|
|
||||||
git config --global user.name "GitHub Action"
|
|
||||||
git config --global user.email "action@github.com"
|
|
||||||
git commit -m "Add $(basename $RECOMPILED_DIR) from build-all-tinygrad-models"
|
|
||||||
git push origin main
|
|
||||||
|
|
||||||
- name: Run json_parser.py to update JSON
|
|
||||||
run: |
|
|
||||||
python3 docs/json_parser.py \
|
|
||||||
--json-path "$JSON_FILE" \
|
|
||||||
--recompiled-dir "gitlab_docs/models/$RECOMPILED_DIR"
|
|
||||||
|
|
||||||
- name: Push updated JSON to GitHub docs repo
|
|
||||||
run: |
|
|
||||||
cd docs
|
|
||||||
git config --global user.name "GitHub Action"
|
|
||||||
git config --global user.email "action@github.com"
|
|
||||||
git checkout gh-pages
|
|
||||||
git add docs/"$(basename $JSON_FILE)"
|
|
||||||
git commit -m "Update $(basename $JSON_FILE) after recompiling models" || echo "No changes to commit"
|
|
||||||
git push origin gh-pages
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
name: Build Single Tinygrad Model and Push
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
build_model_ref:
|
|
||||||
description: 'Branch to use for build-model workflow'
|
|
||||||
required: false
|
|
||||||
default: 'master-new'
|
|
||||||
type: string
|
|
||||||
upstream_branch:
|
|
||||||
description: 'Upstream commit to build from'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
custom_name:
|
|
||||||
description: 'Custom name for the model (no date, only name)'
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
recompiled_dir:
|
|
||||||
description: 'Existing recompiled directory number (e.g. 3 for recompiled3)'
|
|
||||||
required: true
|
|
||||||
type: number
|
|
||||||
json_version:
|
|
||||||
description: 'driving_models version number to update (e.g. 5 for driving_models_v5.json)'
|
|
||||||
required: true
|
|
||||||
type: number
|
|
||||||
model_folder:
|
|
||||||
description: 'Model folder'
|
|
||||||
required: true
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- Simple Plan Models
|
|
||||||
- TR Models
|
|
||||||
- DTR Models
|
|
||||||
- Custom Merge Models
|
|
||||||
- FOF series models
|
|
||||||
- Other
|
|
||||||
custom_model_folder:
|
|
||||||
description: 'Custom model folder name (if "Other" selected)'
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
generation:
|
|
||||||
description: 'Model generation'
|
|
||||||
required: false
|
|
||||||
type: number
|
|
||||||
version:
|
|
||||||
description: 'Minimum selector version'
|
|
||||||
required: false
|
|
||||||
type: number
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-single:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
RECOMPILED_DIR: recompiled${{ github.event.inputs.recompiled_dir }}
|
|
||||||
JSON_FILE: docs/docs/driving_models_v${{ github.event.inputs.json_version }}.json
|
|
||||||
steps:
|
|
||||||
- name: Set up SSH
|
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Add GitLab.com SSH key to known_hosts
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
- name: Clone GitLab docs repo
|
|
||||||
env:
|
|
||||||
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
|
||||||
run: |
|
|
||||||
echo "Cloning GitLab"
|
|
||||||
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/docs.sunnypilot.ai2.git gitlab_docs
|
|
||||||
cd gitlab_docs
|
|
||||||
echo "checkout models/${RECOMPILED_DIR}"
|
|
||||||
git sparse-checkout set --no-cone models/${RECOMPILED_DIR}
|
|
||||||
git checkout main
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
- name: Checkout docs repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: sunnypilot/sunnypilot-docs
|
|
||||||
ref: gh-pages
|
|
||||||
path: docs
|
|
||||||
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Validate recompiled dir and JSON version
|
|
||||||
run: |
|
|
||||||
if [ ! -d "gitlab_docs/models/$RECOMPILED_DIR" ]; then
|
|
||||||
echo "Recompiled dir $RECOMPILED_DIR does not exist in GitLab repo"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ ! -f "$JSON_FILE" ]; then
|
|
||||||
echo "JSON file $JSON_FILE does not exist!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y jq gh
|
|
||||||
|
|
||||||
- name: Build model
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
START_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
||||||
gh workflow run sunnypilot-build-model.yaml \
|
|
||||||
--repo sunnypilot/sunnypilot \
|
|
||||||
--ref "${{ github.event.inputs.build_model_ref }}" \
|
|
||||||
-f upstream_branch="${{ github.event.inputs.upstream_branch }}" \
|
|
||||||
-f custom_name="${{ github.event.inputs.custom_name }}"
|
|
||||||
|
|
||||||
for i in {1..24}; do
|
|
||||||
RUN_ID=$(gh run list --repo sunnypilot/sunnypilot --workflow=sunnypilot-build-model.yaml --branch="${{ github.event.inputs.build_model_ref }}" --created ">$START_TIME" --limit=1 --json databaseId --jq '.[0].databaseId')
|
|
||||||
if [ -n "$RUN_ID" ]; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "$RUN_ID" ]; then
|
|
||||||
echo "Could not find the triggered workflow run."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Watching run ID: $RUN_ID"
|
|
||||||
gh run watch "$RUN_ID" --repo sunnypilot/sunnypilot
|
|
||||||
CONCLUSION=$(gh run view "$RUN_ID" --repo sunnypilot/sunnypilot --json conclusion --jq '.conclusion')
|
|
||||||
echo "Run concluded with: $CONCLUSION"
|
|
||||||
if [[ "$CONCLUSION" != "success" ]]; then
|
|
||||||
echo "Workflow run failed with conclusion: $CONCLUSION"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Download and extract model artifact
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
ARTIFACT_DIR="gitlab_docs/models/$RECOMPILED_DIR"
|
|
||||||
RUN_ID=$(gh run list --repo sunnypilot/sunnypilot --workflow=sunnypilot-build-model.yaml --branch="${{ github.event.inputs.build_model_ref }}" --limit=1 --json databaseId --jq '.[0].databaseId')
|
|
||||||
ARTIFACT_NAME=$(gh api repos/sunnypilot/sunnypilot/actions/runs/$RUN_ID/artifacts --jq '.artifacts[] | select(.name | startswith("model-")) | .name')
|
|
||||||
echo "Downloading artifact: $ARTIFACT_NAME from run: $RUN_ID"
|
|
||||||
mkdir -p "$ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
echo "Created directory: $ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
gh run download "$RUN_ID" --repo sunnypilot/sunnypilot -n "$ARTIFACT_NAME" --dir "$ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
echo "Downloaded artifact zip(s) to: $ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
ZIP_PATH=$(find "$ARTIFACT_DIR/$ARTIFACT_NAME" -type f -name '*.zip' | head -n1)
|
|
||||||
if [ -n "$ZIP_PATH" ]; then
|
|
||||||
echo "Unzipping $ZIP_PATH to $ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
unzip -o "$ZIP_PATH" -d "$ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
rm -f "$ZIP_PATH"
|
|
||||||
echo "Unzipped and removed $ZIP_PATH"
|
|
||||||
else
|
|
||||||
echo "No zip file found in $ARTIFACT_DIR/$ARTIFACT_NAME"
|
|
||||||
fi
|
|
||||||
echo "Done processing $ARTIFACT_NAME"
|
|
||||||
|
|
||||||
- name: Push recompiled dir to GitLab
|
|
||||||
env:
|
|
||||||
GITLAB_SSH_PRIVATE_KEY: ${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
|
|
||||||
run: |
|
|
||||||
cd gitlab_docs
|
|
||||||
git checkout main
|
|
||||||
for d in models/"$RECOMPILED_DIR"/*/; do
|
|
||||||
git sparse-checkout add "$d"
|
|
||||||
done
|
|
||||||
git add models/"$RECOMPILED_DIR"
|
|
||||||
git config --global user.name "GitHub Action"
|
|
||||||
git config --global user.email "action@github.com"
|
|
||||||
git commit -m "Update $RECOMPILED_DIR with new/updated model from build-single-tinygrad-model" || echo "No changes to commit"
|
|
||||||
git push origin main
|
|
||||||
|
|
||||||
- name: Run json_parser.py to update JSON
|
|
||||||
run: |
|
|
||||||
FOLDER="${{ github.event.inputs.model_folder }}"
|
|
||||||
if [ "$FOLDER" = "Other" ]; then
|
|
||||||
FOLDER="${{ github.event.inputs.custom_model_folder }}"
|
|
||||||
fi
|
|
||||||
ARGS=""
|
|
||||||
[ -n "$FOLDER" ] && ARGS="$ARGS --model-folder \"$FOLDER\""
|
|
||||||
[ -n "${{ github.event.inputs.generation }}" ] && ARGS="$ARGS --generation \"${{ github.event.inputs.generation }}\""
|
|
||||||
[ -n "${{ github.event.inputs.version }}" ] && ARGS="$ARGS --version \"${{ github.event.inputs.version }}\""
|
|
||||||
eval python3 docs/json_parser.py \
|
|
||||||
--json-path "$JSON_FILE" \
|
|
||||||
--recompiled-dir "gitlab_docs/models/$RECOMPILED_DIR" \
|
|
||||||
$ARGS
|
|
||||||
|
|
||||||
- name: Push updated JSON to GitHub docs repo
|
|
||||||
run: |
|
|
||||||
cd docs
|
|
||||||
git config --global user.name "GitHub Action"
|
|
||||||
git config --global user.email "action@github.com"
|
|
||||||
git checkout gh-pages
|
|
||||||
git add docs/"$(basename $JSON_FILE)"
|
|
||||||
git commit -m "Update $(basename $JSON_FILE) after recompiling model" || echo "No changes to commit"
|
|
||||||
git push origin gh-pages
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
name: cereal validation
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- master-new
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'cereal/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
run_number:
|
|
||||||
default: '1'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: cereal-validation-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
PYTHONWARNINGS: error
|
|
||||||
BASE_IMAGE: openpilot-base
|
|
||||||
BUILD: selfdrive/test/docker_build.sh base
|
|
||||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
generate_cereal_artifact:
|
|
||||||
name: Generate cereal validation artifacts
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Build openpilot
|
|
||||||
run: ${{ env.RUN }} "scons -j$(nproc) cereal"
|
|
||||||
- name: Generate the log file
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "cereal/messaging/tests/validate_sp_cereal_upstream.py -g -f schema_instances.bin" && \
|
|
||||||
ls -la
|
|
||||||
ls -la cereal/messaging/tests
|
|
||||||
- name: 'Prepare artifact'
|
|
||||||
run: |
|
|
||||||
mkdir -p "cereal/messaging/tests/cereal_validations"
|
|
||||||
cp cereal/messaging/tests/validate_sp_cereal_upstream.py "cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py"
|
|
||||||
cp schema_instances.bin "cereal/messaging/tests/cereal_validations/schema_instances.bin"
|
|
||||||
- name: 'Upload Artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: cereal_validations
|
|
||||||
path: cereal/messaging/tests/cereal_validations
|
|
||||||
|
|
||||||
validate_cereal_with_upstream:
|
|
||||||
name: Validate cereal with Upstream
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs: generate_cereal_artifact
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: 'commaai/openpilot'
|
|
||||||
submodules: true
|
|
||||||
ref: "refs/heads/master"
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Build openpilot
|
|
||||||
run: ${{ env.RUN }} "scons -j$(nproc) cereal"
|
|
||||||
- name: Download build artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: cereal_validations
|
|
||||||
path: cereal/messaging/tests/cereal_validations
|
|
||||||
- name: 'Run the validation'
|
|
||||||
run: |
|
|
||||||
chmod +x cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py
|
|
||||||
${{ env.RUN }} "cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py -r -f cereal/messaging/tests/cereal_validations/schema_instances.bin"
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
name: weekly CI test report
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '37 9 * * 1' # 9:37AM UTC -> 2:37AM PST every monday
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
ci_runs:
|
|
||||||
description: 'The amount of runs to trigger in CI test report'
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
CI_RUNS: ${{ github.event.inputs.ci_runs || '50' }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
setup:
|
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
ci_runs: ${{ steps.ci_runs_setup.outputs.matrix }}
|
|
||||||
steps:
|
|
||||||
- id: ci_runs_setup
|
|
||||||
name: CI_RUNS=${{ env.CI_RUNS }}
|
|
||||||
run: |
|
|
||||||
matrix=$(python3 -c "import json; print(json.dumps({ 'run_number' : list(range(${{ env.CI_RUNS }})) }))")
|
|
||||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
ci_matrix_run:
|
|
||||||
needs: [ setup ]
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix: ${{fromJSON(needs.setup.outputs.ci_runs)}}
|
|
||||||
uses: sunnypilot/sunnypilot/.github/workflows/ci_weekly_run.yaml@master
|
|
||||||
with:
|
|
||||||
run_number: ${{ matrix.run_number }}
|
|
||||||
|
|
||||||
report:
|
|
||||||
needs: [ci_matrix_run]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: always()
|
|
||||||
steps:
|
|
||||||
- name: Get job results
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
id: get-job-results
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const jobs = await github
|
|
||||||
.paginate("GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt}/jobs", {
|
|
||||||
owner: "commaai",
|
|
||||||
repo: "${{ github.event.repository.name }}",
|
|
||||||
run_id: "${{ github.run_id }}",
|
|
||||||
attempt: "${{ github.run_attempt }}",
|
|
||||||
})
|
|
||||||
var report = {}
|
|
||||||
jobs.slice(1, jobs.length-1).forEach(job => {
|
|
||||||
if (job.conclusion === "skipped") return;
|
|
||||||
const jobName = job.name.split(" / ")[2];
|
|
||||||
const runRegex = /\((.*?)\)/;
|
|
||||||
const run = job.name.match(runRegex)[1];
|
|
||||||
report[jobName] = report[jobName] || { successes: [], failures: [], canceled: [] };
|
|
||||||
switch (job.conclusion) {
|
|
||||||
case "success":
|
|
||||||
report[jobName].successes.push({ "run_number": run, "link": job.html_url}); break;
|
|
||||||
case "failure":
|
|
||||||
report[jobName].failures.push({ "run_number": run, "link": job.html_url }); break;
|
|
||||||
case "canceled":
|
|
||||||
report[jobName].canceled.push({ "run_number": run, "link": job.html_url }); break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return JSON.stringify({"jobs": report});
|
|
||||||
|
|
||||||
- name: Add job results to summary
|
|
||||||
env:
|
|
||||||
JOB_RESULTS: ${{ fromJSON(steps.get-job-results.outputs.result) }}
|
|
||||||
run: |
|
|
||||||
cat <<EOF >> template.html
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th></th>
|
|
||||||
<th>Job</th>
|
|
||||||
<th>✅ Passing</th>
|
|
||||||
<th>❌ Failure Details</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for key in jobs.keys() %}<tr>
|
|
||||||
<td>{% for i in range(5) %}{% if i+1 <= (5 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }}) %}🟩{% else %}🟥{% endif %}{% endfor%}</td>
|
|
||||||
<td>{{ key }}</td>
|
|
||||||
<td>{{ 100 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }} }}%</td>
|
|
||||||
<td>{% if jobs[key]["failures"]|length > 0 %}<details>{% for failure in jobs[key]["failures"] %}<a href="{{ failure['link'] }}">Log for run #{{ failure['run_number'] }}</a><br>{% endfor %}</details>{% else %}{% endif %}</td>
|
|
||||||
</td>
|
|
||||||
</tr>{% endfor %}
|
|
||||||
</table>
|
|
||||||
EOF
|
|
||||||
|
|
||||||
pip install jinja2-cli
|
|
||||||
echo $JOB_RESULTS | jinja2 template.html > report.html
|
|
||||||
echo "# CI Test Report - ${{ env.CI_RUNS }} Runs" >> $GITHUB_STEP_SUMMARY
|
|
||||||
cat report.html >> $GITHUB_STEP_SUMMARY
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
name: weekly CI test run
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
run_number:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ci-run-${{ inputs.run_number }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
selfdrive_tests:
|
|
||||||
uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master
|
|
||||||
with:
|
|
||||||
run_number: ${{ inputs.run_number }}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
name: 'compile openpilot'
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
- shell: bash
|
|
||||||
name: Build openpilot with all flags
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "scons -j$(nproc)"
|
|
||||||
${{ env.RUN }} "release/check-dirty.sh"
|
|
||||||
- shell: bash
|
|
||||||
name: Cleanup scons cache and rebuild
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
|
|
||||||
scons -j$(nproc) --cache-populate"
|
|
||||||
- name: Save scons cache
|
|
||||||
uses: actions/cache/save@v4
|
|
||||||
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new')
|
|
||||||
with:
|
|
||||||
path: .ci_cache/scons_cache
|
|
||||||
key: scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
name: diff report
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
comment:
|
||||||
|
name: comment
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
actions: read
|
||||||
|
steps:
|
||||||
|
- name: Wait for process replay
|
||||||
|
id: wait
|
||||||
|
continue-on-error: true
|
||||||
|
uses: lewagon/wait-on-check-action@v1.3.4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
check-name: process replay
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
allowed-conclusions: success,failure
|
||||||
|
wait-interval: 20
|
||||||
|
- name: Download diff
|
||||||
|
if: steps.wait.outcome == 'success'
|
||||||
|
uses: dawidd6/action-download-artifact@v6
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
workflow: tests.yaml
|
||||||
|
workflow_conclusion: ''
|
||||||
|
pr: ${{ github.event.number }}
|
||||||
|
name: diff_report_${{ github.event.number }}
|
||||||
|
path: .
|
||||||
|
allow_forks: true
|
||||||
|
- name: Comment on PR
|
||||||
|
if: steps.wait.outcome == 'success'
|
||||||
|
uses: thollander/actions-comment-pull-request@v2
|
||||||
|
with:
|
||||||
|
filePath: diff_report.txt
|
||||||
|
comment_tag: diff_report
|
||||||
|
pr_number: ${{ github.event.number }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: commaai/timeout@v1
|
- uses: commaai/timeout@v1
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
@@ -34,14 +34,14 @@ jobs:
|
|||||||
mkdocs build
|
mkdocs build
|
||||||
|
|
||||||
# Push to docs.comma.ai
|
# Push to docs.comma.ai
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
|
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
|
||||||
with:
|
with:
|
||||||
path: openpilot-docs
|
path: openpilot-docs
|
||||||
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
|
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
|
||||||
repository: sunnypilot/sunnypilot-docs
|
repository: commaai/openpilot-docs
|
||||||
- name: Push
|
- name: Push
|
||||||
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
|
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
|
||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,54 @@ on:
|
|||||||
types: [created, edited]
|
types: [created, edited]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# TODO: gc old branches in a separate job in this workflow
|
cleanup-branches:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Delete stale Jenkins branches
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
||||||
|
const prefixes = ['tmp-jenkins', '__jenkins'];
|
||||||
|
|
||||||
|
for await (const response of github.paginate.iterator(github.rest.repos.listBranches, {
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
per_page: 100,
|
||||||
|
})) {
|
||||||
|
for (const branch of response.data) {
|
||||||
|
if (!prefixes.some(p => branch.name.startsWith(p))) continue;
|
||||||
|
|
||||||
|
const { data: commit } = await github.rest.repos.getCommit({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: branch.commit.sha,
|
||||||
|
});
|
||||||
|
|
||||||
|
const commitDate = new Date(commit.commit.committer.date).getTime();
|
||||||
|
if (commitDate < cutoff) {
|
||||||
|
console.log(`Deleting branch: ${branch.name} (last commit: ${commit.commit.committer.date})`);
|
||||||
|
await github.rest.git.deleteRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: `heads/${branch.name}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scan-comments:
|
scan-comments:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.issue.pull_request }}
|
if: ${{ github.event.issue.pull_request }}
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
steps:
|
steps:
|
||||||
- name: Check for trigger phrase
|
- name: Check for trigger phrase
|
||||||
id: check_comment
|
id: check_comment
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const triggerPhrase = "trigger-jenkins";
|
const triggerPhrase = "trigger-jenkins";
|
||||||
@@ -32,7 +72,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
if: steps.check_comment.outputs.result == 'true'
|
if: steps.check_comment.outputs.result == 'true'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: refs/pull/${{ github.event.issue.number }}/head
|
ref: refs/pull/${{ github.event.issue.number }}/head
|
||||||
|
|
||||||
@@ -43,3 +83,14 @@ jobs:
|
|||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
git checkout -b tmp-jenkins-${{ github.event.issue.number }}
|
git checkout -b tmp-jenkins-${{ github.event.issue.number }}
|
||||||
GIT_LFS_SKIP_PUSH=1 git push -f origin tmp-jenkins-${{ github.event.issue.number }}
|
GIT_LFS_SKIP_PUSH=1 git push -f origin tmp-jenkins-${{ github.event.issue.number }}
|
||||||
|
|
||||||
|
- name: Delete trigger comment
|
||||||
|
if: steps.check_comment.outputs.result == 'true' && always()
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
await github.rest.issues.deleteComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
comment_id: context.payload.comment.id,
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
name: Sync comma's LFS
|
|
||||||
|
|
||||||
env:
|
|
||||||
LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs'
|
|
||||||
LFS_PUSH_URL: 'ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git'
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * *' # Runs at 00:00 UTC every day
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'master-new'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- 'master-new'
|
|
||||||
workflow_dispatch: # enables manual triggering
|
|
||||||
inputs:
|
|
||||||
upstream_branch:
|
|
||||||
default: 'master'
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
sync:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# Skip if PR is in draft mode
|
|
||||||
if: (github.event_name != 'pull_request' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false)) && !github.event.pull_request.head.repo.fork
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: 'commaai/openpilot'
|
|
||||||
ref: ${{ inputs.upstream_branch }}
|
|
||||||
|
|
||||||
- name: LFS Fetch
|
|
||||||
run: |
|
|
||||||
git lfs fetch
|
|
||||||
|
|
||||||
- name: Set up Git
|
|
||||||
run: |
|
|
||||||
git config --global user.name 'GitHub Action'
|
|
||||||
git config --global user.email 'action@github.com'
|
|
||||||
|
|
||||||
- name: Set up SSH
|
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Add GitLab public keys
|
|
||||||
run: |
|
|
||||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
- name: Ensure branch
|
|
||||||
run: |
|
|
||||||
if git symbolic-ref -q HEAD >/dev/null; then
|
|
||||||
echo "Already on a branch, proceeding with push"
|
|
||||||
else
|
|
||||||
echo "Detached HEAD state detected, creating temporary branch"
|
|
||||||
git checkout -b temp_branch
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Update LFS Config
|
|
||||||
run: |
|
|
||||||
echo '[lfs]' > .lfsconfig
|
|
||||||
echo ' url = ${{ env.LFS_URL }}' >> .lfsconfig
|
|
||||||
echo ' pushurl = ${{ env.LFS_PUSH_URL }}' >> .lfsconfig
|
|
||||||
echo ' locksverify = false' >> .lfsconfig
|
|
||||||
|
|
||||||
- name: Push LFS
|
|
||||||
id: sync-and-commit
|
|
||||||
run: |
|
|
||||||
git lfs ls-files -l
|
|
||||||
git lfs push --all origin
|
|
||||||
@@ -16,23 +16,23 @@ jobs:
|
|||||||
if: github.repository == 'commaai/openpilot'
|
if: github.repository == 'commaai/openpilot'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
- name: Checkout master
|
- name: Checkout master
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
path: base
|
path: base
|
||||||
- run: git lfs pull
|
- run: git lfs pull
|
||||||
- run: cd base && git lfs pull
|
- run: cd base && git lfs pull
|
||||||
|
|
||||||
- run: pip install onnx
|
|
||||||
|
|
||||||
- name: scripts/reporter.py
|
- name: scripts/reporter.py
|
||||||
id: report
|
id: report
|
||||||
run: |
|
run: |
|
||||||
echo "content<<EOF" >> $GITHUB_OUTPUT
|
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||||
echo "## Model Review" >> $GITHUB_OUTPUT
|
echo "## Model Review" >> $GITHUB_OUTPUT
|
||||||
MASTER_PATH=${{ github.workspace }}/base python scripts/reporter.py >> $GITHUB_OUTPUT
|
PYTHONPATH=${{ github.workspace }} MASTER_PATH=${{ github.workspace }}/base python scripts/reporter.py >> $GITHUB_OUTPUT
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Post model report comment
|
- name: Post model report comment
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||||
BUILD: selfdrive/test/docker_build.sh prebuilt
|
BUILD: selfdrive/test/docker_build.sh
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_prebuilt:
|
build_prebuilt:
|
||||||
name: build prebuilt
|
name: build prebuilt
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'commaai/openpilot'
|
||||||
env:
|
env:
|
||||||
PUSH_IMAGE: true
|
PUSH_IMAGE: true
|
||||||
permissions:
|
permissions:
|
||||||
@@ -28,8 +28,8 @@ jobs:
|
|||||||
wait-interval: 30
|
wait-interval: 30
|
||||||
running-workflow-name: 'build prebuilt'
|
running-workflow-name: 'build prebuilt'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
check-regexp: ^((?!.*(build master-ci).*).)*$
|
check-regexp: ^((?!.*(build master-ci|create badges).*).)*$
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: git lfs pull
|
- run: git lfs pull
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
name: Release Drafter
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master-new
|
|
||||||
- master
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, reopened, synchronize]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update_release_draft:
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: release-drafter/release-drafter@v6
|
|
||||||
with:
|
|
||||||
config-name: release-drafter.yml
|
|
||||||
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -7,20 +7,12 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build_masterci:
|
build_masterci:
|
||||||
name: build master-ci
|
name: build master-ci
|
||||||
env:
|
|
||||||
ImageOS: ubuntu24
|
|
||||||
container:
|
|
||||||
image: ghcr.io/commaai/openpilot-base:latest
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'commaai/openpilot'
|
||||||
permissions:
|
permissions:
|
||||||
checks: read
|
checks: read
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Install wait-on-check-action dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libyaml-dev
|
|
||||||
- name: Wait for green check mark
|
- name: Wait for green check mark
|
||||||
if: ${{ github.event_name == 'schedule' }}
|
if: ${{ github.event_name == 'schedule' }}
|
||||||
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
|
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
|
||||||
@@ -29,14 +21,11 @@ jobs:
|
|||||||
wait-interval: 30
|
wait-interval: 30
|
||||||
running-workflow-name: 'build master-ci'
|
running-workflow-name: 'build master-ci'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
check-regexp: ^((?!.*(build prebuilt).*).)*$
|
check-regexp: ^((?!.*(build prebuilt|create badges).*).)*$
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Pull LFS
|
- run: ./tools/op.sh setup
|
||||||
run: |
|
|
||||||
git config --global --add safe.directory '*'
|
|
||||||
git lfs pull
|
|
||||||
- name: Push master-ci
|
- name: Push master-ci
|
||||||
run: BRANCH=__nightly release/build_devel.sh
|
run: BRANCH=__nightly release/build_stripped.sh
|
||||||
|
|||||||
@@ -6,67 +6,68 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BASE_IMAGE: openpilot-base
|
PYTHONPATH: ${{ github.workspace }}
|
||||||
BUILD: selfdrive/test/docker_build.sh base
|
|
||||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update_translations:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Update translations
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "python3 selfdrive/ui/update_translations.py --vanish"
|
|
||||||
- name: Create Pull Request
|
|
||||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
|
||||||
with:
|
|
||||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
|
||||||
commit-message: "Update translations"
|
|
||||||
title: "[bot] Update translations"
|
|
||||||
body: "Automatic PR from repo-maintenance -> update_translations"
|
|
||||||
branch: "update-translations"
|
|
||||||
base: "master-new"
|
|
||||||
delete-branch: true
|
|
||||||
labels: bot
|
|
||||||
|
|
||||||
package_updates:
|
package_updates:
|
||||||
name: package_updates
|
name: package_updates
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
if: github.repository == 'commaai/openpilot'
|
||||||
image: ghcr.io/commaai/openpilot-base:latest
|
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
- name: uv lock
|
- name: uv lock
|
||||||
|
run: uv lock --upgrade
|
||||||
|
- name: uv pip tree
|
||||||
|
id: pip_tree
|
||||||
run: |
|
run: |
|
||||||
python3 -m ensurepip --upgrade
|
echo 'PIP_TREE<<EOF' >> $GITHUB_OUTPUT
|
||||||
pip3 install uv
|
uv pip tree >> $GITHUB_OUTPUT
|
||||||
uv lock --upgrade
|
echo 'EOF' >> $GITHUB_OUTPUT
|
||||||
|
- name: venv size
|
||||||
|
id: venv_size
|
||||||
|
run: |
|
||||||
|
echo 'VENV_SIZE<<EOF' >> $GITHUB_OUTPUT
|
||||||
|
echo "Total: $(du -sh .venv | cut -f1)" >> $GITHUB_OUTPUT
|
||||||
|
echo "" >> $GITHUB_OUTPUT
|
||||||
|
echo "Top 10 by size:" >> $GITHUB_OUTPUT
|
||||||
|
du -sh .venv/lib/python*/site-packages/* 2>/dev/null \
|
||||||
|
| grep -v '\.dist-info' \
|
||||||
|
| grep -v '__pycache__' \
|
||||||
|
| sort -rh \
|
||||||
|
| head -10 \
|
||||||
|
| while IFS=$'\t' read size path; do echo "$size ${path##*/}"; done >> $GITHUB_OUTPUT
|
||||||
|
echo 'EOF' >> $GITHUB_OUTPUT
|
||||||
- name: bump submodules
|
- name: bump submodules
|
||||||
run: |
|
run: |
|
||||||
git config --global --add safe.directory '*'
|
|
||||||
git submodule update --remote
|
git submodule update --remote
|
||||||
git add .
|
git add .
|
||||||
- name: update car docs
|
- name: update car docs
|
||||||
run: |
|
run: |
|
||||||
export PYTHONPATH="$PWD"
|
|
||||||
scons -j$(nproc) --minimal opendbc_repo
|
|
||||||
python selfdrive/car/docs.py
|
python selfdrive/car/docs.py
|
||||||
git add docs/CARS.md
|
git add docs/CARS.md
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
||||||
with:
|
with:
|
||||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
author: Vehicle Researcher <user@comma.ai>
|
||||||
token: ${{ github.repository == 'commaai/openpilot' && secrets.ACTIONS_CREATE_PR_PAT || secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
|
||||||
commit-message: Update Python packages
|
commit-message: Update Python packages
|
||||||
title: '[bot] Update Python packages'
|
title: '[bot] Update Python packages'
|
||||||
branch: auto-package-updates
|
branch: auto-package-updates
|
||||||
base: master-new
|
base: master
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
body: 'Automatic PR from repo-maintenance -> package_updates'
|
body: |
|
||||||
|
Automatic PR from repo-maintenance -> package_updates
|
||||||
|
|
||||||
|
```
|
||||||
|
$ du -sh .venv && du -sh .venv/lib/python*/site-packages/* | sort -rh | head -10
|
||||||
|
${{ steps.venv_size.outputs.VENV_SIZE }}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ uv pip tree
|
||||||
|
${{ steps.pip_tree.outputs.PIP_TREE }}
|
||||||
|
```
|
||||||
labels: bot
|
labels: bot
|
||||||
|
|||||||
@@ -1,346 +0,0 @@
|
|||||||
name: selfdrive
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- master-new
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
run_number:
|
|
||||||
default: '1'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: selfdrive-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
REPORT_NAME: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && 'master' || github.event.number }}
|
|
||||||
PYTHONWARNINGS: error
|
|
||||||
BASE_IMAGE: openpilot-base
|
|
||||||
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
|
|
||||||
|
|
||||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
BUILD: selfdrive/test/docker_build.sh base
|
|
||||||
|
|
||||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
|
||||||
|
|
||||||
PYTEST: pytest --continue-on-collection-errors --durations=0 --durations-min=5 -n logical
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_release:
|
|
||||||
if: github.repository == 'commaai/openpilot' # build_release blocked for the time being to only comma as we may have a different process.
|
|
||||||
name: build release
|
|
||||||
runs-on:
|
|
||||||
- 'ubuntu-24.04'
|
|
||||||
env:
|
|
||||||
STRIPPED_DIR: /tmp/releasepilot
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- name: Getting LFS files
|
|
||||||
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e
|
|
||||||
with:
|
|
||||||
timeout_minutes: 2
|
|
||||||
max_attempts: 3
|
|
||||||
command: git lfs pull
|
|
||||||
- name: Build devel
|
|
||||||
timeout-minutes: 1
|
|
||||||
run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Build openpilot and run checks
|
|
||||||
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
|
|
||||||
run: |
|
|
||||||
cd $STRIPPED_DIR
|
|
||||||
${{ env.RUN }} "python3 system/manager/build.py"
|
|
||||||
- name: Run tests
|
|
||||||
timeout-minutes: 1
|
|
||||||
run: |
|
|
||||||
cd $STRIPPED_DIR
|
|
||||||
${{ env.RUN }} "release/check-dirty.sh"
|
|
||||||
- name: Check submodules
|
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
|
||||||
timeout-minutes: 3
|
|
||||||
run: release/check-submodules.sh
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on:
|
|
||||||
- 'ubuntu-24.04'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- name: Setup docker push
|
|
||||||
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
|
|
||||||
run: |
|
|
||||||
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
|
|
||||||
$DOCKER_LOGIN
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- uses: ./.github/workflows/compile-openpilot
|
|
||||||
timeout-minutes: 30
|
|
||||||
|
|
||||||
build_mac:
|
|
||||||
name: build macOS
|
|
||||||
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
|
||||||
- name: Homebrew cache
|
|
||||||
uses: ./.github/workflows/auto-cache
|
|
||||||
with:
|
|
||||||
path: ~/Library/Caches/Homebrew
|
|
||||||
key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
brew-macos-${{ env.CACHE_COMMIT_DATE }}
|
|
||||||
brew-macos
|
|
||||||
- name: Install dependencies
|
|
||||||
run: ./tools/mac_setup.sh
|
|
||||||
env:
|
|
||||||
# package install has DeprecationWarnings
|
|
||||||
PYTHONWARNINGS: default
|
|
||||||
- name: Save Homebrew cache
|
|
||||||
uses: actions/cache/save@v4
|
|
||||||
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new')
|
|
||||||
with:
|
|
||||||
path: ~/Library/Caches/Homebrew
|
|
||||||
key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
|
||||||
- run: git lfs pull
|
|
||||||
- name: Getting scons cache
|
|
||||||
uses: ./.github/workflows/auto-cache
|
|
||||||
with:
|
|
||||||
path: /tmp/scons_cache
|
|
||||||
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}
|
|
||||||
scons-${{ runner.arch }}-macos
|
|
||||||
- name: Building openpilot
|
|
||||||
run: . .venv/bin/activate && scons -j$(nproc)
|
|
||||||
- name: Save scons cache
|
|
||||||
uses: actions/cache/save@v4
|
|
||||||
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new')
|
|
||||||
with:
|
|
||||||
path: /tmp/scons_cache
|
|
||||||
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
|
||||||
|
|
||||||
static_analysis:
|
|
||||||
name: static analysis
|
|
||||||
runs-on:
|
|
||||||
- 'ubuntu-latest'
|
|
||||||
env:
|
|
||||||
PYTHONWARNINGS: default
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Static analysis
|
|
||||||
timeout-minutes: 1
|
|
||||||
run: ${{ env.RUN }} "scripts/lint/lint.sh"
|
|
||||||
|
|
||||||
unit_tests:
|
|
||||||
name: unit tests
|
|
||||||
runs-on:
|
|
||||||
- 'ubuntu-24.04'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Build openpilot
|
|
||||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
|
||||||
- name: Run unit tests
|
|
||||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && 1 || 20 }}
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "$PYTEST --collect-only -m 'not slow' &> /dev/null && \
|
|
||||||
MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \
|
|
||||||
./selfdrive/ui/tests/create_test_translations.sh && \
|
|
||||||
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
|
|
||||||
chmod -R 777 /tmp/comma_download_cache"
|
|
||||||
|
|
||||||
process_replay:
|
|
||||||
name: process replay
|
|
||||||
if: github.repository == 'commaai/openpilot' # disable process_replay for forks
|
|
||||||
runs-on:
|
|
||||||
- 'ubuntu-24.04'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Cache test routes
|
|
||||||
id: dependency-cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: .ci_cache/comma_download_cache
|
|
||||||
key: proc-replay-${{ hashFiles('selfdrive/test/process_replay/ref_commit', 'selfdrive/test/process_replay/test_processes.py') }}
|
|
||||||
- name: Build openpilot
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "scons -j$(nproc)"
|
|
||||||
- name: Run replay
|
|
||||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.dependency-cache.outputs.cache-hit == 'true') && 1 || 20 }}
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
|
|
||||||
chmod -R 777 /tmp/comma_download_cache"
|
|
||||||
- name: Print diff
|
|
||||||
id: print-diff
|
|
||||||
if: always()
|
|
||||||
run: cat selfdrive/test/process_replay/diff.txt
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
if: always()
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
name: process_replay_diff.txt
|
|
||||||
path: selfdrive/test/process_replay/diff.txt
|
|
||||||
- name: Upload reference logs
|
|
||||||
if: false # TODO: move this to github instead of azure
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "unset PYTHONWARNINGS && AZURE_TOKEN='$AZURE_TOKEN' python3 selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
|
|
||||||
- name: Run regen
|
|
||||||
if: false
|
|
||||||
timeout-minutes: 4
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "ONNXCPU=1 $PYTEST selfdrive/test/process_replay/test_regen.py && \
|
|
||||||
chmod -R 777 /tmp/comma_download_cache"
|
|
||||||
|
|
||||||
test_cars:
|
|
||||||
name: cars
|
|
||||||
runs-on:
|
|
||||||
- 'ubuntu-24.04'
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
job: [0, 1, 2, 3]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Cache test routes
|
|
||||||
id: routes-cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: .ci_cache/comma_download_cache
|
|
||||||
key: car_models-${{ hashFiles('selfdrive/car/tests/test_models.py', 'opendbc/car/tests/routes.py') }}-${{ matrix.job }}
|
|
||||||
- name: Build openpilot
|
|
||||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
|
||||||
- name: Test car models
|
|
||||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.routes-cache.outputs.cache-hit == 'true') && 1 || 6 }}
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "MAX_EXAMPLES=1 $PYTEST selfdrive/car/tests/test_models.py && \
|
|
||||||
chmod -R 777 /tmp/comma_download_cache"
|
|
||||||
env:
|
|
||||||
NUM_JOBS: 4
|
|
||||||
JOB_ID: ${{ matrix.job }}
|
|
||||||
|
|
||||||
car_docs_diff:
|
|
||||||
name: PR comments
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
#if: github.event_name == 'pull_request'
|
|
||||||
if: false # TODO: run this in opendbc?
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
ref: ${{ github.event.pull_request.base.ref }}
|
|
||||||
- run: git lfs pull
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Get base car info
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "scons -j$(nproc) && python3 selfdrive/debug/dump_car_docs.py --path /tmp/openpilot_cache/base_car_docs"
|
|
||||||
sudo chown -R $USER:$USER ${{ github.workspace }}
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
path: current
|
|
||||||
- run: cd current && git lfs pull
|
|
||||||
- name: Save car docs diff
|
|
||||||
id: save_diff
|
|
||||||
run: |
|
|
||||||
cd current
|
|
||||||
${{ env.RUN }} "scons -j$(nproc)"
|
|
||||||
output=$(${{ env.RUN }} "python3 selfdrive/debug/print_docs_diff.py --path /tmp/openpilot_cache/base_car_docs")
|
|
||||||
output="${output//$'\n'/'%0A'}"
|
|
||||||
echo "::set-output name=diff::$output"
|
|
||||||
- name: Find comment
|
|
||||||
if: ${{ env.AZURE_TOKEN != '' }}
|
|
||||||
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e
|
|
||||||
id: fc
|
|
||||||
with:
|
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
|
||||||
body-includes: This PR makes changes to
|
|
||||||
- name: Update comment
|
|
||||||
if: ${{ steps.save_diff.outputs.diff != '' && env.AZURE_TOKEN != '' }}
|
|
||||||
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043
|
|
||||||
with:
|
|
||||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
|
||||||
body: "${{ steps.save_diff.outputs.diff }}"
|
|
||||||
edit-mode: replace
|
|
||||||
- name: Delete comment
|
|
||||||
if: ${{ steps.fc.outputs.comment-id != '' && steps.save_diff.outputs.diff == '' && env.AZURE_TOKEN != '' }}
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
github.rest.issues.deleteComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
comment_id: ${{ steps.fc.outputs.comment-id }}
|
|
||||||
})
|
|
||||||
|
|
||||||
simulator_driving:
|
|
||||||
name: simulator driving
|
|
||||||
runs-on:
|
|
||||||
- 'ubuntu-24.04'
|
|
||||||
if: (github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: Build openpilot
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "scons -j$(nproc)"
|
|
||||||
- name: Driving test
|
|
||||||
timeout-minutes: 1
|
|
||||||
run: |
|
|
||||||
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
|
|
||||||
source selfdrive/test/setup_vsound.sh && \
|
|
||||||
CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py"
|
|
||||||
|
|
||||||
create_ui_report:
|
|
||||||
# This job name needs to be the same as UI_JOB_NAME in ui_preview.yaml
|
|
||||||
name: Create UI Report
|
|
||||||
runs-on:
|
|
||||||
- 'ubuntu-24.04'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
|
||||||
- name: caching frames
|
|
||||||
id: frames-cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: .ci_cache/comma_download_cache
|
|
||||||
key: ui_screenshots_test_${{ hashFiles('selfdrive/ui/tests/test_ui/run.py') }}
|
|
||||||
- name: Build openpilot
|
|
||||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
|
||||||
- name: Create Test Report
|
|
||||||
timeout-minutes: ${{ ((steps.frames-cache.outputs.cache-hit == 'true') && 2 || 4) }}
|
|
||||||
run: >
|
|
||||||
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
|
||||||
source selfdrive/test/setup_xvfb.sh &&
|
|
||||||
CACHE_ROOT=/tmp/comma_download_cache python3 selfdrive/ui/tests/test_ui/run.py &&
|
|
||||||
chmod -R 777 /tmp/comma_download_cache"
|
|
||||||
- name: Upload Test Report
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ${{ env.REPORT_NAME }}
|
|
||||||
path: selfdrive/ui/tests/test_ui/report_1/screenshots
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
name: 'openpilot env setup, with retry on failure'
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
docker_hub_pat:
|
|
||||||
description: 'Auth token for Docker Hub, required for BuildJet jobs'
|
|
||||||
required: false
|
|
||||||
default: ''
|
|
||||||
sleep_time:
|
|
||||||
description: 'Time to sleep between retries'
|
|
||||||
required: false
|
|
||||||
default: 30
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
- id: setup1
|
|
||||||
uses: ./.github/workflows/setup
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
is_retried: true
|
|
||||||
- if: steps.setup1.outcome == 'failure'
|
|
||||||
shell: bash
|
|
||||||
run: sleep ${{ inputs.sleep_time }}
|
|
||||||
- id: setup2
|
|
||||||
if: steps.setup1.outcome == 'failure'
|
|
||||||
uses: ./.github/workflows/setup
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
is_retried: true
|
|
||||||
- if: steps.setup2.outcome == 'failure'
|
|
||||||
shell: bash
|
|
||||||
run: sleep ${{ inputs.sleep_time }}
|
|
||||||
- id: setup3
|
|
||||||
if: steps.setup2.outcome == 'failure'
|
|
||||||
uses: ./.github/workflows/setup
|
|
||||||
with:
|
|
||||||
is_retried: true
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
name: 'openpilot env setup'
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
is_retried:
|
|
||||||
description: 'A mock param that asserts that we use the setup-with-retry instead of this action directly'
|
|
||||||
required: false
|
|
||||||
default: 'false'
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
# assert that this action is retried using the setup-with-retry
|
|
||||||
- shell: bash
|
|
||||||
if: ${{ inputs.is_retried == 'false' }}
|
|
||||||
run: |
|
|
||||||
echo "You should not run this action directly. Use setup-with-retry instead"
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- shell: bash
|
|
||||||
name: No retries!
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.run_attempt }}" -gt ${{ github.event.pull_request.head.repo.fork && github.event.pull_request.author_association == 'NONE' && 2 || 1}} ]; then
|
|
||||||
echo -e "\033[0;31m##################################################"
|
|
||||||
echo -e "\033[0;31m Retries not allowed! Fix the flaky test! "
|
|
||||||
echo -e "\033[0;31m##################################################\033[0m"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# do this after checkout to ensure our custom LFS config is used to pull from GitLab
|
|
||||||
- shell: bash
|
|
||||||
run: git lfs pull
|
|
||||||
|
|
||||||
# build cache
|
|
||||||
- id: date
|
|
||||||
shell: bash
|
|
||||||
run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
|
||||||
- shell: bash
|
|
||||||
run: echo "$CACHE_COMMIT_DATE"
|
|
||||||
- id: scons-cache
|
|
||||||
uses: ./.github/workflows/auto-cache
|
|
||||||
with:
|
|
||||||
path: .ci_cache/scons_cache
|
|
||||||
key: scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}
|
|
||||||
scons-${{ runner.arch }}
|
|
||||||
# as suggested here: https://github.com/moby/moby/issues/32816#issuecomment-910030001
|
|
||||||
- id: normalize-file-permissions
|
|
||||||
shell: bash
|
|
||||||
name: Normalize file permissions to ensure a consistent docker build cache
|
|
||||||
run: |
|
|
||||||
find . -type f -executable -not -perm 755 -exec chmod 755 {} \;
|
|
||||||
find . -type f -not -executable -not -perm 644 -exec chmod 644 {} \;
|
|
||||||
# build our docker image
|
|
||||||
- shell: bash
|
|
||||||
run: eval ${{ env.BUILD }}
|
|
||||||
@@ -5,15 +5,15 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DAYS_BEFORE_PR_CLOSE: 2
|
DAYS_BEFORE_PR_CLOSE: 7
|
||||||
DAYS_BEFORE_PR_STALE: 9
|
DAYS_BEFORE_PR_STALE: 24
|
||||||
DAYS_BEFORE_PR_STALE_DRAFT: 30
|
DAYS_BEFORE_PR_STALE_DRAFT: 30
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v10
|
||||||
with:
|
with:
|
||||||
exempt-all-milestones: true
|
exempt-all-milestones: true
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
|
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
|
||||||
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
|
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
|
||||||
stale-pr-label: stale
|
stale-pr-label: stale
|
||||||
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'sunnypilot/sunnypilot' }} # only delete branches on the main repo
|
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'commaai/openpilot' }} # only delete branches on the main repo
|
||||||
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
|
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
|
||||||
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE }}
|
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE }}
|
||||||
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
|
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
stale_drafts:
|
stale_drafts:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v10
|
||||||
with:
|
with:
|
||||||
exempt-all-milestones: true
|
exempt-all-milestones: true
|
||||||
|
|
||||||
|
|||||||
@@ -1,169 +0,0 @@
|
|||||||
name: Build Model from Upstream
|
|
||||||
|
|
||||||
env:
|
|
||||||
BUILD_DIR: "/data/openpilot"
|
|
||||||
OUTPUT_DIR: ${{ github.workspace }}/output
|
|
||||||
SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache
|
|
||||||
UPSTREAM_REPO: "commaai/openpilot"
|
|
||||||
TINYGRAD_PATH: ${{ github.workspace }}/tinygrad_repo
|
|
||||||
MODELS_DIR: ${{ github.workspace }}/selfdrive/modeld/models
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
upstream_branch:
|
|
||||||
description: 'Upstream branch to build from'
|
|
||||||
required: true
|
|
||||||
default: 'master'
|
|
||||||
type: string
|
|
||||||
custom_name:
|
|
||||||
description: 'Custom name for the model (no date, only name)'
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
is_20hz:
|
|
||||||
description: 'Is this a 20Hz model'
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
|
|
||||||
|
|
||||||
run-name: Build model [${{ inputs.custom_name || inputs.upstream_branch }}] from ref [${{ inputs.upstream_branch }}]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
get_model:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
model_date: ${{ steps.commit-date.outputs.model_date }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: ${{ env.UPSTREAM_REPO }}
|
|
||||||
ref: ${{ github.event.inputs.upstream_branch }}
|
|
||||||
submodules: recursive
|
|
||||||
- name: Get commit date
|
|
||||||
id: commit-date
|
|
||||||
run: |
|
|
||||||
# Get the commit date in YYYY-MM-DD format
|
|
||||||
commit_date=$(git log -1 --format=%cd --date=format:'%B %d, %Y')
|
|
||||||
echo "model_date=${commit_date}" >> $GITHUB_OUTPUT
|
|
||||||
cat $GITHUB_OUTPUT
|
|
||||||
- run: git lfs pull
|
|
||||||
- name: 'Upload Artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: models
|
|
||||||
path: ${{ github.workspace }}/selfdrive/modeld/models/*.onnx
|
|
||||||
|
|
||||||
build_model:
|
|
||||||
runs-on: self-hosted
|
|
||||||
needs: get_model
|
|
||||||
env:
|
|
||||||
MODEL_NAME: ${{ inputs.custom_name || inputs.upstream_branch }} (${{ needs.get_model.outputs.model_date }})
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- run: git lfs pull
|
|
||||||
- name: Cache SCons
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{env.SCONS_CACHE_DIR}}
|
|
||||||
key: scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}-model-${{ github.sha }}
|
|
||||||
# Note: GitHub Actions enforces cache isolation between different build sources (PR builds, workflow dispatches, etc.)
|
|
||||||
# for security. Only caches from the default branch are shared across all builds. This is by design and cannot be overridden.
|
|
||||||
restore-keys: |
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}-model
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_NEW_BRANCH }}-model
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }}-model
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_NEW_BRANCH }}
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }}
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}
|
|
||||||
|
|
||||||
- name: Set environment variables
|
|
||||||
id: set-env
|
|
||||||
run: |
|
|
||||||
# Set up common environment
|
|
||||||
source /etc/profile;
|
|
||||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
|
||||||
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
|
||||||
printenv >> $GITHUB_ENV
|
|
||||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
|
||||||
cat $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Setup build environment
|
|
||||||
run: |
|
|
||||||
mkdir -p "${BUILD_DIR}/"
|
|
||||||
sudo find $BUILD_DIR/ -mindepth 1 -delete
|
|
||||||
echo "Starting build stage..."
|
|
||||||
echo "BUILD_DIR: ${BUILD_DIR}"
|
|
||||||
echo "CI_DIR: ${CI_DIR}"
|
|
||||||
echo "VERSION: ${{ steps.set-env.outputs.version }}"
|
|
||||||
echo "UV_PROJECT_ENVIRONMENT: ${UV_PROJECT_ENVIRONMENT}"
|
|
||||||
echo "VIRTUAL_ENV: ${VIRTUAL_ENV}"
|
|
||||||
echo "-------"
|
|
||||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
|
||||||
printenv
|
|
||||||
fi
|
|
||||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable
|
|
||||||
rm -rf ${{ env.MODELS_DIR }}/*.onnx
|
|
||||||
|
|
||||||
- name: Download model artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: models
|
|
||||||
path: ${{ github.workspace }}/selfdrive/modeld/models
|
|
||||||
|
|
||||||
- name: Build Model
|
|
||||||
run: |
|
|
||||||
source /etc/profile
|
|
||||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
|
||||||
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
|
||||||
export PYTHONPATH="${PYTHONPATH}:${{ env.TINYGRAD_PATH }}"
|
|
||||||
|
|
||||||
# Loop through all .onnx files
|
|
||||||
find "${{ env.MODELS_DIR }}" -maxdepth 1 -name '*.onnx' | while IFS= read -r onnx_file; do
|
|
||||||
base_name=$(basename "$onnx_file" .onnx)
|
|
||||||
output_file="${{ env.MODELS_DIR }}/${base_name}_tinygrad.pkl"
|
|
||||||
|
|
||||||
echo "Compiling: $onnx_file -> $output_file"
|
|
||||||
QCOM=1 python3 "${{ env.TINYGRAD_PATH }}/examples/openpilot/compile3.py" "$onnx_file" "$output_file"
|
|
||||||
QCOM=1 python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Prepare Output
|
|
||||||
run: |
|
|
||||||
sudo rm -rf ${{ env.OUTPUT_DIR }}
|
|
||||||
mkdir -p ${{ env.OUTPUT_DIR }}
|
|
||||||
|
|
||||||
# Copy the model files
|
|
||||||
rsync -avm \
|
|
||||||
--include='*.dlc' \
|
|
||||||
--include='*.thneed' \
|
|
||||||
--include='*.pkl' \
|
|
||||||
--include='*.onnx' \
|
|
||||||
--exclude='*' \
|
|
||||||
--delete-excluded \
|
|
||||||
--chown=comma:comma \
|
|
||||||
${{ env.MODELS_DIR }}/ ${{ env.OUTPUT_DIR }}/
|
|
||||||
|
|
||||||
python3 "${{ github.workspace }}/release/ci/model_generator.py" \
|
|
||||||
--model-dir "${{ env.MODELS_DIR }}" \
|
|
||||||
--output-dir "${{ env.OUTPUT_DIR }}" \
|
|
||||||
--custom-name "${{ env.MODEL_NAME }}" \
|
|
||||||
--upstream-branch "${{ inputs.upstream_branch }}" \
|
|
||||||
${{ inputs.is_20hz && '--is-20hz' || '' }}
|
|
||||||
|
|
||||||
- name: Upload Build Artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: model-${{ env.MODEL_NAME }}-${{ github.run_number }}
|
|
||||||
path: ${{ env.OUTPUT_DIR }}
|
|
||||||
|
|
||||||
- name: Re-enable powersave
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --enable
|
|
||||||
@@ -1,332 +0,0 @@
|
|||||||
name: sunnypilot prebuilt action
|
|
||||||
|
|
||||||
env:
|
|
||||||
BUILD_DIR: "/data/openpilot"
|
|
||||||
OUTPUT_DIR: ${{ github.workspace }}/output
|
|
||||||
CI_DIR: ${{ github.workspace }}/release/ci
|
|
||||||
SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache
|
|
||||||
PUBLIC_REPO_URL: "https://github.com/sunnypilot/sunnypilot"
|
|
||||||
|
|
||||||
# Branch configurations
|
|
||||||
STAGING_C3_SOURCE_BRANCH: ${{ vars.STAGING_C3_SOURCE_BRANCH || 'master-new' }} # vars are set on repo settings.
|
|
||||||
DEV_C3_SOURCE_BRANCH: ${{ vars.DEV_C3_SOURCE_BRANCH || 'master-dev-c3-new' }} # vars are set on repo settings.
|
|
||||||
|
|
||||||
# Target branch configurations
|
|
||||||
STAGING_TARGET_BRANCH: ${{ vars.STAGING_TARGET_BRANCH || 'staging-c3-new' }} # vars are set on repo settings.
|
|
||||||
DEV_TARGET_BRANCH: ${{ vars.DEV_TARGET_BRANCH || 'dev-c3-new' }} # vars are set on repo settings.
|
|
||||||
RELEASE_TARGET_BRANCH: ${{ vars.RELEASE_TARGET_BRANCH || 'release-c3-new' }} # vars are set on repo settings.
|
|
||||||
|
|
||||||
# Runtime configuration
|
|
||||||
SOURCE_BRANCH: "${{ github.head_ref || github.ref_name }}"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master, master-new, master-dev-c3-new ]
|
|
||||||
tags: [ '*' ]
|
|
||||||
pull_request_target:
|
|
||||||
types: [ labeled ]
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
wait_for_tests:
|
|
||||||
description: 'Wait for selfdrive_tests to finish'
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate_tests:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
if: ((github.event_name == 'workflow_dispatch' && inputs.wait_for_tests) || contains(github.event_name, 'pull_request') && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt'))
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Wait for Tests
|
|
||||||
uses: ./.github/workflows/wait-for-action # Path to where you place the action
|
|
||||||
with:
|
|
||||||
workflow: selfdrive_tests.yaml # The workflow file to monitor
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
build:
|
|
||||||
needs: [ validate_tests ]
|
|
||||||
concurrency:
|
|
||||||
group: build-${{ github.head_ref || github.ref_name }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
runs-on: self-hosted
|
|
||||||
outputs:
|
|
||||||
new_branch: ${{ steps.set-env.outputs.new_branch }}
|
|
||||||
version: ${{ steps.set-env.outputs.version }}
|
|
||||||
extra_version_identifier: ${{ steps.set-env.outputs.extra_version_identifier }}
|
|
||||||
commit_sha: ${{ steps.set-env.outputs.commit_sha }}
|
|
||||||
if: ${{ (always() && !failure() && !cancelled()) && (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
ref: ${{ env.SOURCE_BRANCH }}
|
|
||||||
repository: ${{ github.event.pull_request.head.repo.fork && github.event.pull_request.head.repo.full_name || github.repository }}
|
|
||||||
- run: git lfs pull
|
|
||||||
|
|
||||||
- name: Cache SCons
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{env.SCONS_CACHE_DIR}}
|
|
||||||
key: scons-${{ runner.os }}-${{ runner.arch }}-${{ env.SOURCE_BRANCH }}-${{ github.sha }}
|
|
||||||
# Note: GitHub Actions enforces cache isolation between different build sources (PR builds, workflow dispatches, etc.)
|
|
||||||
# for security. Only caches from the default branch are shared across all builds. This is by design and cannot be overridden.
|
|
||||||
restore-keys: |
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.SOURCE_BRANCH }}
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}-${{ env.STAGING_C3_SOURCE_BRANCH }}
|
|
||||||
scons-${{ runner.os }}-${{ runner.arch }}
|
|
||||||
|
|
||||||
- name: Set Feature Branch Prebuilt Configuration
|
|
||||||
id: set_feature_configuration
|
|
||||||
if: (
|
|
||||||
!(env.SOURCE_BRANCH == env.DEV_C3_SOURCE_BRANCH) &&
|
|
||||||
!(env.SOURCE_BRANCH == env.STAGING_C3_SOURCE_BRANCH) &&
|
|
||||||
!(startsWith(github.ref, 'refs/tags/'))
|
|
||||||
)
|
|
||||||
run: |
|
|
||||||
echo "NEW_BRANCH=${{ env.SOURCE_BRANCH }}${{ github.event.pull_request.head.repo.fork && '-fork' || '' }}-prebuilt" >> $GITHUB_ENV
|
|
||||||
echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set dev-c3-new prebuilt Configuration
|
|
||||||
id: set_dev_configuration
|
|
||||||
if: (
|
|
||||||
steps.set_feature_configuration.outcome == 'skipped' &&
|
|
||||||
env.SOURCE_BRANCH == env.DEV_C3_SOURCE_BRANCH
|
|
||||||
)
|
|
||||||
run: |
|
|
||||||
echo "NEW_BRANCH=${{ env.DEV_TARGET_BRANCH }}" >> $GITHUB_ENV
|
|
||||||
echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV
|
|
||||||
echo "EXTRA_VERSION_IDENTIFIER=${{ github.run_number }}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set staging-c3-new prebuilt Configuration
|
|
||||||
id: set_staging_configuration
|
|
||||||
if: (
|
|
||||||
steps.set_feature_configuration.outcome == 'skipped' &&
|
|
||||||
!contains(github.event_name, 'pull_request') &&
|
|
||||||
steps.set_dev_configuration.outcome == 'skipped' &&
|
|
||||||
(env.SOURCE_BRANCH == env.STAGING_C3_SOURCE_BRANCH)
|
|
||||||
)
|
|
||||||
run: |
|
|
||||||
echo "NEW_BRANCH=${{ env.STAGING_TARGET_BRANCH }}" >> $GITHUB_ENV
|
|
||||||
echo "EXTRA_VERSION_IDENTIFIER=staging" >> $GITHUB_ENV
|
|
||||||
echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-staging" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set release-c3-new prebuilt Configuration
|
|
||||||
id: set_tag_configuration
|
|
||||||
if: (
|
|
||||||
steps.set_feature_configuration.outcome == 'skipped' &&
|
|
||||||
!contains(github.event_name, 'pull_request') &&
|
|
||||||
steps.set_staging_configuration.outcome == 'skipped' &&
|
|
||||||
startsWith(github.ref, 'refs/tags/')
|
|
||||||
)
|
|
||||||
run: |
|
|
||||||
echo "NEW_BRANCH=${{ env.RELEASE_TARGET_BRANCH }}" >> $GITHUB_ENV
|
|
||||||
echo "EXTRA_VERSION_IDENTIFIER=release" >> $GITHUB_ENV
|
|
||||||
echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-release" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set environment variables
|
|
||||||
id: set-env
|
|
||||||
run: |
|
|
||||||
# Write to GITHUB_OUTPUT from environment variables
|
|
||||||
echo "new_branch=$NEW_BRANCH" >> $GITHUB_OUTPUT
|
|
||||||
[[ ! -z "$EXTRA_VERSION_IDENTIFIER" ]] && echo "extra_version_identifier=$EXTRA_VERSION_IDENTIFIER" >> $GITHUB_OUTPUT
|
|
||||||
[[ ! -z "$VERSION" ]] && echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
echo "commit_sha=${{ github.sha }}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Set up common environment
|
|
||||||
source /etc/profile;
|
|
||||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
|
||||||
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
|
||||||
printenv >> $GITHUB_ENV
|
|
||||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
|
||||||
cat $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Setup build environment
|
|
||||||
run: |
|
|
||||||
mkdir -p "${BUILD_DIR}/"
|
|
||||||
sudo find $BUILD_DIR/ -mindepth 1 -delete
|
|
||||||
echo "Starting build stage..."
|
|
||||||
echo "BUILD_DIR: ${BUILD_DIR}"
|
|
||||||
echo "CI_DIR: ${CI_DIR}"
|
|
||||||
echo "VERSION: ${{ steps.set-env.outputs.version }}"
|
|
||||||
echo "UV_PROJECT_ENVIRONMENT: ${UV_PROJECT_ENVIRONMENT}"
|
|
||||||
echo "VIRTUAL_ENV: ${VIRTUAL_ENV}"
|
|
||||||
echo "-------"
|
|
||||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
|
||||||
printenv
|
|
||||||
fi
|
|
||||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable
|
|
||||||
|
|
||||||
- name: Build Main Project
|
|
||||||
run: |
|
|
||||||
export PYTHONPATH="$BUILD_DIR"
|
|
||||||
./release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/
|
|
||||||
cd $BUILD_DIR
|
|
||||||
sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py
|
|
||||||
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal
|
|
||||||
touch ${BUILD_DIR}/prebuilt
|
|
||||||
if [[ "${{ runner.debug }}" == "1" ]]; then
|
|
||||||
ls -la ${BUILD_DIR}
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Prepare Output
|
|
||||||
run: |
|
|
||||||
sudo rm -rf ${OUTPUT_DIR}
|
|
||||||
mkdir -p ${OUTPUT_DIR}
|
|
||||||
rsync -am${RUNNER_DEBUG:+v} \
|
|
||||||
--include='**/panda/board/' \
|
|
||||||
--include='**/panda/board/obj' \
|
|
||||||
--include='**/panda/board/obj/panda.bin.signed' \
|
|
||||||
--include='**/panda/board/obj/panda_h7.bin.signed' \
|
|
||||||
--include='**/panda/board/obj/bootstub.panda.bin' \
|
|
||||||
--include='**/panda/board/obj/bootstub.panda_h7.bin' \
|
|
||||||
--exclude='.sconsign.dblite' \
|
|
||||||
--exclude='*.a' \
|
|
||||||
--exclude='*.o' \
|
|
||||||
--exclude='*.os' \
|
|
||||||
--exclude='*.pyc' \
|
|
||||||
--exclude='moc_*' \
|
|
||||||
--exclude='*.cc' \
|
|
||||||
--exclude='Jenkinsfile' \
|
|
||||||
--exclude='supercombo.onnx' \
|
|
||||||
--exclude='**/panda/board/*' \
|
|
||||||
--exclude='**/panda/board/obj/**' \
|
|
||||||
--exclude='**/panda/certs/' \
|
|
||||||
--exclude='**/panda/crypto/' \
|
|
||||||
--exclude='**/release/' \
|
|
||||||
--exclude='**/.github/' \
|
|
||||||
--exclude='**/selfdrive/ui/replay/' \
|
|
||||||
--exclude='**/__pycache__/' \
|
|
||||||
--exclude='**/selfdrive/ui/*.h' \
|
|
||||||
--exclude='**/selfdrive/ui/**/*.h' \
|
|
||||||
--exclude='**/selfdrive/ui/qt/offroad/sunnypilot/' \
|
|
||||||
--exclude='${{env.SCONS_CACHE_DIR}}' \
|
|
||||||
--exclude='**/.git/' \
|
|
||||||
--exclude='**/SConstruct' \
|
|
||||||
--exclude='**/SConscript' \
|
|
||||||
--exclude='**/.venv/' \
|
|
||||||
--delete-excluded \
|
|
||||||
--chown=comma:comma \
|
|
||||||
${BUILD_DIR}/ ${OUTPUT_DIR}/
|
|
||||||
|
|
||||||
- name: 'Tar.gz files'
|
|
||||||
run: |
|
|
||||||
tar czf prebuilt.tar.gz -C ${{ env.OUTPUT_DIR }} .
|
|
||||||
ls -la prebuilt.tar.gz
|
|
||||||
|
|
||||||
- name: 'Upload Artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: prebuilt
|
|
||||||
path: prebuilt.tar.gz
|
|
||||||
|
|
||||||
- name: Re-enable powersave
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --enable
|
|
||||||
|
|
||||||
|
|
||||||
publish:
|
|
||||||
concurrency:
|
|
||||||
group: publish-${{ github.head_ref || github.ref_name }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
if: ${{ (always() && !failure() && !cancelled()) && (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) }}
|
|
||||||
needs: [ build ]
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
environment: ${{ (contains(fromJSON(vars.AUTO_DEPLOY_PREBUILT_BRANCHES), github.head_ref || github.ref_name) || contains(github.event.pull_request.labels.*.name, 'prebuilt')) && 'auto-deploy' || 'feature-branch' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Download build artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: prebuilt
|
|
||||||
|
|
||||||
- name: Untar prebuilt
|
|
||||||
run: |
|
|
||||||
mkdir -p ${{ env.OUTPUT_DIR }}
|
|
||||||
tar xzf prebuilt.tar.gz -C ${{ env.OUTPUT_DIR }}
|
|
||||||
|
|
||||||
- name: Configure Git
|
|
||||||
run: |
|
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git config --global user.name "github-actions[bot]"
|
|
||||||
|
|
||||||
- name: Publish to Public Repository
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo '${{ toJSON(needs.build.outputs) }}'
|
|
||||||
ls -la ${{ env.OUTPUT_DIR }}
|
|
||||||
|
|
||||||
${{ env.CI_DIR }}/publish.sh \
|
|
||||||
"${{ github.workspace }}" \
|
|
||||||
"${{ env.OUTPUT_DIR }}" \
|
|
||||||
"${{ needs.build.outputs.new_branch }}" \
|
|
||||||
"${{ needs.build.outputs.version }}" \
|
|
||||||
"https://x-access-token:${{github.token}}@github.com/sunnypilot/sunnypilot.git" \
|
|
||||||
"-${{ needs.build.outputs.extra_version_identifier }}"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "---- ℹ️ To update the list of branches that auto deploy prebuilts -----"
|
|
||||||
echo ""
|
|
||||||
echo "1. Go to: ${{ github.server_url }}/${{ github.repository }}/settings/variables/actions/AUTO_DEPLOY_PREBUILT_BRANCHES"
|
|
||||||
echo "2. Current value: ${{ vars.AUTO_DEPLOY_PREBUILT_BRANCHES }}"
|
|
||||||
echo "3. Update as needed (JSON array with no spaces)"
|
|
||||||
|
|
||||||
notify:
|
|
||||||
needs: [ build, publish ]
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
if: ${{ (always() && !failure() && !cancelled()) && (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Setup Alpine Linux environment
|
|
||||||
uses: jirutka/setup-alpine@v1.2.0
|
|
||||||
with:
|
|
||||||
packages: 'jq gettext curl'
|
|
||||||
|
|
||||||
- name: Send Discord Notification
|
|
||||||
env:
|
|
||||||
DISCORD_WEBHOOK: ${{ contains(fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES), env.SOURCE_BRANCH) && secrets.DISCORD_DEV_FEEDBACK_CHANNEL_WEBHOOK || secrets.DISCORD_DEV_PRIVATE_CHANNEL_WEBHOOK }}
|
|
||||||
run: |
|
|
||||||
TEMPLATE='${{ vars.DISCORD_GENERAL_UPDATE_NOTICE }}'
|
|
||||||
export EXTRA_VERSION_IDENTIFIER="${{ needs.build.outputs.extra_version_identifier }}"
|
|
||||||
export VERSION="${{ needs.build.outputs.version }}"
|
|
||||||
export branch_name=${{ env.SOURCE_BRANCH }}
|
|
||||||
export new_branch=${{ needs.build.outputs.new_branch }}
|
|
||||||
export extra_version_identifier=${{ needs.build.outputs.extra_version_identifier || github.run_number}}
|
|
||||||
echo ${TEMPLATE} | envsubst | jq -c '.' | tee payload.json
|
|
||||||
curl -X POST -H "Content-Type: application/json" -d @payload.json $DISCORD_WEBHOOK
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "---- ℹ️ To update the list of branches that notify to dev-feedback -----"
|
|
||||||
echo ""
|
|
||||||
echo "1. Go to: ${{ github.server_url }}/${{ github.repository }}/settings/variables/actions/DEV_FEEDBACK_NOTIFICATION_BRANCHES"
|
|
||||||
echo "2. Current value: ${{ vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES }}"
|
|
||||||
echo "3. Update as needed (JSON array with no spaces)"
|
|
||||||
shell: alpine.sh {0}
|
|
||||||
|
|
||||||
manage-pr-labels:
|
|
||||||
name: Remove prebuilt label
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: (always() && contains(github.event_name, 'pull_request') && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt'))
|
|
||||||
env:
|
|
||||||
LABEL: prebuilt
|
|
||||||
steps:
|
|
||||||
- name: Remove trust-fork-pr label if present
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
const prNumber = context.payload.pull_request.number;
|
|
||||||
|
|
||||||
await github.rest.issues.removeLabel({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: prNumber,
|
|
||||||
name: process.env.LABEL
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Removed '${process.env.LABEL}' label from PR #${prNumber}`);
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
name: Build dev-c3-new
|
|
||||||
|
|
||||||
env:
|
|
||||||
DEFAULT_SOURCE_BRANCH: "master-new"
|
|
||||||
DEFAULT_TARGET_BRANCH: "master-dev-c3-new"
|
|
||||||
PR_LABEL: "dev-c3"
|
|
||||||
LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs'
|
|
||||||
LFS_PUSH_URL: 'ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git'
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- master-new
|
|
||||||
pull_request_target:
|
|
||||||
types: [ labeled ]
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
- 'master-new'
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
source_branch:
|
|
||||||
description: 'Source branch to reset from'
|
|
||||||
required: true
|
|
||||||
default: 'master-new'
|
|
||||||
type: string
|
|
||||||
target_branch:
|
|
||||||
description: 'Target branch to reset and squash into'
|
|
||||||
required: true
|
|
||||||
default: 'master-dev-c3-new'
|
|
||||||
type: string
|
|
||||||
cancel_in_progress:
|
|
||||||
description: 'Cancel any in-progress runs of this workflow'
|
|
||||||
required: false
|
|
||||||
default: true
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}
|
|
||||||
cancel-in-progress: ${{ inputs.cancel_in_progress || github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
reset-and-squash:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: (
|
|
||||||
(github.event_name == 'workflow_dispatch')
|
|
||||||
|| (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|
|
||||||
|| (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3'))))
|
|
||||||
)
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # Fetch all history for all branches
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Wait for Tests
|
|
||||||
uses: ./.github/workflows/wait-for-action # Path to where you place the action
|
|
||||||
if: (
|
|
||||||
(github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|
|
||||||
|| (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3'))))
|
|
||||||
)
|
|
||||||
with:
|
|
||||||
workflow: selfdrive_tests.yaml # The workflow file to monitor
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Configure Git
|
|
||||||
run: |
|
|
||||||
git config --global user.name 'github-actions[bot]'
|
|
||||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
|
||||||
|
|
||||||
- name: Set up SSH
|
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Add GitLab public keys
|
|
||||||
run: |
|
|
||||||
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.10'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install PyGithub
|
|
||||||
|
|
||||||
- name: Check branches exist
|
|
||||||
run: |
|
|
||||||
# Check if source branch exists
|
|
||||||
if ! git ls-remote --heads origin ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} | grep -q "${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"; then
|
|
||||||
echo "Source branch ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} does not exist!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Make sure we have the latest source branch
|
|
||||||
git fetch origin ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}
|
|
||||||
|
|
||||||
# Check if target branch exists
|
|
||||||
if ! git ls-remote --heads origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} | grep -q "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"; then
|
|
||||||
echo "Target branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} does not exist, creating it from ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"
|
|
||||||
git checkout -b ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} origin/${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}
|
|
||||||
git push origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}
|
|
||||||
else
|
|
||||||
# Fetch target branch if it exists
|
|
||||||
git fetch origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Reset target branch
|
|
||||||
run: |
|
|
||||||
echo "Resetting ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} to match ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"
|
|
||||||
# Delete if exists and recreate pointing to source
|
|
||||||
git branch -D ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} || true
|
|
||||||
git branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} origin/${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}
|
|
||||||
|
|
||||||
- name: Get PRs to squash
|
|
||||||
id: get-prs
|
|
||||||
run: |
|
|
||||||
# Use GitHub API to get PRs with specific label, ordered by creation date
|
|
||||||
PR_LIST=$(gh api graphql -f query='
|
|
||||||
query($label:String!) {
|
|
||||||
search(query: $label, type:ISSUE, first:100) {
|
|
||||||
nodes {
|
|
||||||
... on PullRequest {
|
|
||||||
number
|
|
||||||
headRefName
|
|
||||||
title
|
|
||||||
createdAt
|
|
||||||
labels(last:10) {
|
|
||||||
nodes {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
headRepository {
|
|
||||||
name
|
|
||||||
nameWithOwner
|
|
||||||
url
|
|
||||||
isFork
|
|
||||||
}
|
|
||||||
commits(last: 1) {
|
|
||||||
nodes {
|
|
||||||
commit {
|
|
||||||
statusCheckRollup {
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' -F label="is:pr is:open label:${PR_LABEL} draft:false sort:created-asc")
|
|
||||||
|
|
||||||
echo "PR_LIST=${PR_LIST}" >> $GITHUB_OUTPUT
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Process PRs
|
|
||||||
run: |
|
|
||||||
cp ${{ github.workspace }}/release/ci/squash_and_merge.py /tmp/squash_and_merge.py && \
|
|
||||||
chmod +x /tmp/squash_and_merge.py && \
|
|
||||||
python3 ${{ github.workspace }}/release/ci/squash_and_merge_prs.py \
|
|
||||||
--pr-data '${{ steps.get-prs.outputs.PR_LIST }}' \
|
|
||||||
--target-branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} \
|
|
||||||
--squash-script-path '/tmp/squash_and_merge.py'
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Update LFS Config
|
|
||||||
run: |
|
|
||||||
echo '[lfs]' > .lfsconfig
|
|
||||||
echo ' url = ${{ env.LFS_URL }}' >> .lfsconfig
|
|
||||||
echo ' pushurl = ${{ env.LFS_PUSH_URL }}' >> .lfsconfig
|
|
||||||
echo ' locksverify = false' >> .lfsconfig
|
|
||||||
|
|
||||||
- name: Push changes if there are diffs
|
|
||||||
id: push-changes # Add an id so we can reference this step
|
|
||||||
run: |
|
|
||||||
TARGET_BRANCH="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
|
||||||
|
|
||||||
# Fetch the latest from remote
|
|
||||||
git fetch origin $TARGET_BRANCH
|
|
||||||
|
|
||||||
# Check for diffs between local and remote
|
|
||||||
if git diff $TARGET_BRANCH origin/$TARGET_BRANCH --quiet; then
|
|
||||||
echo "No changes to push - local and remote branches are identical"
|
|
||||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If we get here, there are diffs, so push
|
|
||||||
if ! git push origin $TARGET_BRANCH --force; then
|
|
||||||
echo "Failed to push changes to $TARGET_BRANCH"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Branch $TARGET_BRANCH has been reset and updated with squashed PRs"
|
|
||||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Trigger and wait for selfdrive tests
|
|
||||||
if: steps.push-changes.outputs.has_changes == 'true'
|
|
||||||
run: |
|
|
||||||
echo "Triggering selfdrive tests..."
|
|
||||||
gh workflow run selfdrive_tests.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
|
||||||
|
|
||||||
echo "Sleeping for 120s to give plenty of time for the action to start and then we wait"
|
|
||||||
sleep 120
|
|
||||||
|
|
||||||
echo "Getting latest run ID..."
|
|
||||||
RUN_ID=$(gh run list --workflow=selfdrive_tests.yaml --branch="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}" --limit=1 --json databaseId --jq '.[0].databaseId')
|
|
||||||
|
|
||||||
echo "Watching run ID: $RUN_ID"
|
|
||||||
gh run watch "$RUN_ID"
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Trigger prebuilt workflow
|
|
||||||
if: success() && steps.push-changes.outputs.has_changes == 'true'
|
|
||||||
run: |
|
|
||||||
gh workflow run sunnypilot-build-prebuilt.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
name: tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
run_number:
|
||||||
|
default: '1'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CI: 1
|
||||||
|
PYTHONPATH: ${{ github.workspace }}
|
||||||
|
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_release:
|
||||||
|
name: build release
|
||||||
|
runs-on: ${{
|
||||||
|
(github.repository == 'commaai/openpilot') &&
|
||||||
|
((github.event_name != 'pull_request') ||
|
||||||
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
|
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||||
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
|
env:
|
||||||
|
STRIPPED_DIR: /tmp/releasepilot
|
||||||
|
PYTHONPATH: /tmp/releasepilot
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Getting LFS files
|
||||||
|
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e
|
||||||
|
with:
|
||||||
|
timeout_minutes: 2
|
||||||
|
max_attempts: 3
|
||||||
|
command: git lfs pull
|
||||||
|
- name: Build devel
|
||||||
|
timeout-minutes: 1
|
||||||
|
run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
|
- name: Build openpilot and run checks
|
||||||
|
timeout-minutes: 30
|
||||||
|
working-directory: ${{ env.STRIPPED_DIR }}
|
||||||
|
run: python3 system/manager/build.py
|
||||||
|
- name: Run tests
|
||||||
|
timeout-minutes: 1
|
||||||
|
working-directory: ${{ env.STRIPPED_DIR }}
|
||||||
|
run: release/check-dirty.sh
|
||||||
|
- name: Check submodules
|
||||||
|
if: github.repository == 'commaai/openpilot'
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: release/check-submodules.sh
|
||||||
|
|
||||||
|
build_mac:
|
||||||
|
name: build macOS
|
||||||
|
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Remove Homebrew from environment
|
||||||
|
run: |
|
||||||
|
FILTERED=$(echo "$PATH" | tr ':' '\n' | grep -v '/opt/homebrew' | tr '\n' ':')
|
||||||
|
echo "PATH=${FILTERED}/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" >> $GITHUB_ENV
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
|
- name: Building openpilot
|
||||||
|
run: scons
|
||||||
|
|
||||||
|
static_analysis:
|
||||||
|
name: static analysis
|
||||||
|
runs-on: ${{
|
||||||
|
(github.repository == 'commaai/openpilot') &&
|
||||||
|
((github.event_name != 'pull_request') ||
|
||||||
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
|
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||||
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
|
- name: Static analysis
|
||||||
|
timeout-minutes: 1
|
||||||
|
run: scripts/lint/lint.sh
|
||||||
|
|
||||||
|
unit_tests:
|
||||||
|
name: unit tests
|
||||||
|
runs-on: ${{
|
||||||
|
(github.repository == 'commaai/openpilot') &&
|
||||||
|
((github.event_name != 'pull_request') ||
|
||||||
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
|
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||||
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
|
- name: Build openpilot
|
||||||
|
run: scons -j$(nproc)
|
||||||
|
- name: Run unit tests
|
||||||
|
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }}
|
||||||
|
run: |
|
||||||
|
source selfdrive/test/setup_xvfb.sh
|
||||||
|
# Pre-compile Python bytecode so each pytest worker doesn't need to
|
||||||
|
$PYTEST --collect-only -m 'not slow' -qq
|
||||||
|
MAX_EXAMPLES=1 $PYTEST -m 'not slow'
|
||||||
|
|
||||||
|
process_replay:
|
||||||
|
name: process replay
|
||||||
|
runs-on: ${{
|
||||||
|
(github.repository == 'commaai/openpilot') &&
|
||||||
|
((github.event_name != 'pull_request') ||
|
||||||
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
|
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||||
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
|
- name: Build openpilot
|
||||||
|
run: scons -j$(nproc)
|
||||||
|
- name: Run replay
|
||||||
|
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }}
|
||||||
|
continue-on-error: ${{ github.ref == 'refs/heads/master' }}
|
||||||
|
run: selfdrive/test/process_replay/test_processes.py -j$(nproc)
|
||||||
|
- name: Print diff
|
||||||
|
id: print-diff
|
||||||
|
if: always()
|
||||||
|
run: cat selfdrive/test/process_replay/diff.txt
|
||||||
|
- name: Print diff report
|
||||||
|
if: always()
|
||||||
|
run: cat selfdrive/test/process_replay/diff_report.txt
|
||||||
|
- uses: actions/upload-artifact@v6
|
||||||
|
if: always()
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
name: process_replay_diff.txt
|
||||||
|
path: selfdrive/test/process_replay/diff.txt
|
||||||
|
- name: Upload diff report
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
if: always() && github.event_name == 'pull_request'
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
name: diff_report_${{ github.event.number }}
|
||||||
|
path: selfdrive/test/process_replay/diff_report.txt
|
||||||
|
- name: Checkout ci-artifacts
|
||||||
|
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: commaai/ci-artifacts
|
||||||
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
|
path: ${{ github.workspace }}/ci-artifacts
|
||||||
|
- name: Push refs
|
||||||
|
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
||||||
|
working-directory: ${{ github.workspace }}/ci-artifacts
|
||||||
|
run: |
|
||||||
|
git config user.name "GitHub Actions Bot"
|
||||||
|
git config user.email "<>"
|
||||||
|
git fetch origin process-replay || true
|
||||||
|
git checkout process-replay 2>/dev/null || git checkout --orphan process-replay
|
||||||
|
cp ${{ github.workspace }}/selfdrive/test/process_replay/fakedata/*.zst .
|
||||||
|
echo "${{ github.sha }}" > ref_commit
|
||||||
|
git add .
|
||||||
|
git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}" || echo "No changes to commit"
|
||||||
|
git push origin process-replay --force
|
||||||
|
- name: Run regen
|
||||||
|
if: false
|
||||||
|
timeout-minutes: 4
|
||||||
|
env:
|
||||||
|
ONNXCPU: 1
|
||||||
|
run: $PYTEST selfdrive/test/process_replay/test_regen.py
|
||||||
|
|
||||||
|
simulator_driving:
|
||||||
|
name: simulator driving
|
||||||
|
runs-on: ${{
|
||||||
|
(github.repository == 'commaai/openpilot') &&
|
||||||
|
((github.event_name != 'pull_request') ||
|
||||||
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
|
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||||
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
|
if: false # FIXME: Started to timeout recently
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
|
- name: Build openpilot
|
||||||
|
run: scons -j$(nproc)
|
||||||
|
- name: Driving test
|
||||||
|
timeout-minutes: 2
|
||||||
|
run: |
|
||||||
|
source selfdrive/test/setup_xvfb.sh
|
||||||
|
pytest -s tools/sim/tests/test_metadrive_bridge.py
|
||||||
|
|
||||||
|
create_ui_report:
|
||||||
|
name: Create UI Report
|
||||||
|
runs-on: ${{
|
||||||
|
(github.repository == 'commaai/openpilot') &&
|
||||||
|
((github.event_name != 'pull_request') ||
|
||||||
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
|
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||||
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
|
- name: Build openpilot
|
||||||
|
run: scons -j$(nproc)
|
||||||
|
- name: Create UI Report
|
||||||
|
run: |
|
||||||
|
source selfdrive/test/setup_xvfb.sh
|
||||||
|
python3 selfdrive/ui/tests/diff/replay.py
|
||||||
|
python3 selfdrive/ui/tests/diff/replay.py --big
|
||||||
|
- name: Upload UI Report
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: ui-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
|
path: selfdrive/ui/tests/diff/report
|
||||||
@@ -3,25 +3,29 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- master-new
|
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [assigned, opened, synchronize, reopened, edited]
|
types: [assigned, opened, synchronize, reopened, edited]
|
||||||
branches:
|
branches:
|
||||||
- 'master'
|
- 'master'
|
||||||
- 'master-new'
|
|
||||||
paths:
|
paths:
|
||||||
|
- 'selfdrive/assets/**'
|
||||||
- 'selfdrive/ui/**'
|
- 'selfdrive/ui/**'
|
||||||
|
- 'system/ui/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
UI_JOB_NAME: "Create UI Report"
|
UI_JOB_NAME: "Create UI Report"
|
||||||
REPORT_NAME: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && 'master' || github.event.number }}
|
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
SHA: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && github.sha || github.event.pull_request.head.sha }}
|
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}"
|
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-ui-preview"
|
||||||
|
REPORT_FILES_BRANCH_NAME: "mici-raylib-ui-reports"
|
||||||
|
|
||||||
|
# variant:video_prefix:master_branch
|
||||||
|
VARIANTS: "mici:mici_ui_replay:openpilot_master_ui_mici_raylib big:tizi_ui_replay:openpilot_master_ui_big_raylib"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
preview:
|
preview:
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'commaai/openpilot'
|
||||||
name: preview
|
name: preview
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
@@ -30,8 +34,9 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
actions: read
|
actions: read
|
||||||
steps:
|
steps:
|
||||||
- name: Waiting for ui generation to start
|
- uses: actions/checkout@v6
|
||||||
run: sleep 30
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
- name: Waiting for ui generation to end
|
- name: Waiting for ui generation to end
|
||||||
uses: lewagon/wait-on-check-action@v1.3.4
|
uses: lewagon/wait-on-check-action@v1.3.4
|
||||||
@@ -48,110 +53,93 @@ jobs:
|
|||||||
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Getting proposed ui
|
- name: Getting proposed ui
|
||||||
id: download-artifact
|
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v6
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||||
search_artifacts: true
|
search_artifacts: true
|
||||||
name: report-1-${{ env.REPORT_NAME }}
|
name: ui-report-1-${{ env.REPORT_NAME }}
|
||||||
path: ${{ github.workspace }}/pr_ui
|
path: ${{ github.workspace }}/pr_ui
|
||||||
|
|
||||||
- name: Getting master ui
|
- name: Getting mici master ui
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: sunnypilot/ci-artifacts
|
repository: commaai/ci-artifacts
|
||||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
path: ${{ github.workspace }}/master_ui
|
path: ${{ github.workspace }}/master_mici
|
||||||
ref: openpilot_master_ui
|
ref: openpilot_master_ui_mici_raylib
|
||||||
|
|
||||||
|
- name: Getting big master ui
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: commaai/ci-artifacts
|
||||||
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
|
path: ${{ github.workspace }}/master_big
|
||||||
|
ref: openpilot_master_ui_big_raylib
|
||||||
|
|
||||||
- name: Saving new master ui
|
- name: Saving new master ui
|
||||||
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/master-new') && github.event_name == 'push'
|
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||||
working-directory: ${{ github.workspace }}/master_ui
|
|
||||||
run: |
|
run: |
|
||||||
git checkout --orphan=new_master_ui
|
for variant in $VARIANTS; do
|
||||||
|
IFS=':' read -r name video branch <<< "$variant"
|
||||||
|
master_dir="${{ github.workspace }}/master_${name}"
|
||||||
|
cd "$master_dir"
|
||||||
|
git checkout --orphan=new_branch
|
||||||
git rm -rf *
|
git rm -rf *
|
||||||
git branch -D openpilot_master_ui
|
git branch -D "$branch"
|
||||||
git branch -m openpilot_master_ui
|
git branch -m "$branch"
|
||||||
git config user.name "GitHub Actions Bot"
|
git config user.name "GitHub Actions Bot"
|
||||||
git config user.email "<>"
|
git config user.email "<>"
|
||||||
mv ${{ github.workspace }}/pr_ui/*.png .
|
cp "${{ github.workspace }}/pr_ui/${video}.mp4" .
|
||||||
git add .
|
git add .
|
||||||
git commit -m "screenshots for commit ${{ env.SHA }}"
|
git commit -m "${name} video for commit ${{ env.SHA }}"
|
||||||
git push origin openpilot_master_ui --force
|
git push origin "$branch" --force
|
||||||
|
done
|
||||||
|
|
||||||
- name: Finding diff
|
- name: Setup FFmpeg
|
||||||
|
uses: AnimMouse/setup-ffmpeg@ae28d57dabbb148eff63170b6bf7f2b60062cbae
|
||||||
|
|
||||||
|
- name: Finding diffs
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
id: find_diff
|
id: find_diff
|
||||||
run: >-
|
run: |
|
||||||
sudo apt-get update && sudo apt-get install -y imagemagick
|
export PYTHONPATH=${{ github.workspace }}
|
||||||
|
baseurl="https://github.com/commaai/ci-artifacts/raw/refs/heads/${{ env.BRANCH_NAME }}"
|
||||||
|
|
||||||
scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device')
|
COMMENT=""
|
||||||
A=($scenes)
|
for variant in $VARIANTS; do
|
||||||
|
IFS=':' read -r name video _ <<< "$variant"
|
||||||
|
diff_name="${name}_diff"
|
||||||
|
|
||||||
DIFF=""
|
mv "${{ github.workspace }}/pr_ui/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_proposed.mp4"
|
||||||
TABLE="<details><summary>All Screenshots</summary>"
|
cp "${{ github.workspace }}/master_${name}/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_master.mp4"
|
||||||
TABLE="${TABLE}<table>"
|
|
||||||
|
|
||||||
for ((i=0; i<${#A[*]}; i=i+1));
|
diff_exit_code=0
|
||||||
do
|
python3 ${{ github.workspace }}/selfdrive/ui/tests/diff/diff.py \
|
||||||
# Check if the master file exists
|
"${{ github.workspace }}/pr_ui/${video}_master.mp4" \
|
||||||
if [ ! -f "${{ github.workspace }}/master_ui/${A[$i]}.png" ]; then
|
"${{ github.workspace }}/pr_ui/${video}_proposed.mp4" \
|
||||||
# This is a new file in PR UI that doesn't exist in master
|
"${diff_name}.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
|
||||||
DIFF="${DIFF}<details open>"
|
|
||||||
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>"
|
|
||||||
DIFF="${DIFF}<table>"
|
|
||||||
|
|
||||||
DIFF="${DIFF}<tr>"
|
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${{ github.workspace }}/pr_ui/"
|
||||||
DIFF="${DIFF} <td> <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.mp4" "${{ github.workspace }}/pr_ui/"
|
||||||
DIFF="${DIFF}</tr>"
|
|
||||||
|
|
||||||
DIFF="${DIFF}</table>"
|
REPORT_URL="https://commaai.github.io/ci-artifacts/${diff_name}_pr_${{ github.event.number }}.html"
|
||||||
DIFF="${DIFF}</details>"
|
if [ $diff_exit_code -eq 0 ]; then
|
||||||
elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
|
COMMENT+="**${name}**: Videos are identical! [View Diff Report]($REPORT_URL)"$'\n'
|
||||||
convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png
|
|
||||||
composite mask.png ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png
|
|
||||||
convert -delay 100 ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
|
|
||||||
|
|
||||||
mv ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png
|
|
||||||
|
|
||||||
DIFF="${DIFF}<details open>"
|
|
||||||
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$</summary>"
|
|
||||||
DIFF="${DIFF}<table>"
|
|
||||||
|
|
||||||
DIFF="${DIFF}<tr>"
|
|
||||||
DIFF="${DIFF} <td> master <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
|
|
||||||
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
|
||||||
DIFF="${DIFF}</tr>"
|
|
||||||
|
|
||||||
DIFF="${DIFF}<tr>"
|
|
||||||
DIFF="${DIFF} <td> diff <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.png\"> </td>"
|
|
||||||
DIFF="${DIFF} <td> composite diff <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.gif\"> </td>"
|
|
||||||
DIFF="${DIFF}</tr>"
|
|
||||||
|
|
||||||
DIFF="${DIFF}</table>"
|
|
||||||
DIFF="${DIFF}</details>"
|
|
||||||
else
|
else
|
||||||
rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png
|
COMMENT+="**${name}**: ⚠️ <strong>Videos differ!</strong> [View Diff Report]($REPORT_URL)"$'\n'
|
||||||
fi
|
|
||||||
|
|
||||||
INDEX=$(($i % 2))
|
|
||||||
if [[ $INDEX -eq 0 ]]; then
|
|
||||||
TABLE="${TABLE}<tr>"
|
|
||||||
fi
|
|
||||||
TABLE="${TABLE} <td> <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
|
||||||
if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then
|
|
||||||
TABLE="${TABLE}</tr>"
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
TABLE="${TABLE}</table></details>"
|
{
|
||||||
|
echo "COMMENT<<EOF"
|
||||||
echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT"
|
echo "$COMMENT"
|
||||||
|
echo "EOF"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Saving proposed ui
|
- name: Saving proposed ui
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
working-directory: ${{ github.workspace }}/master_ui
|
working-directory: ${{ github.workspace }}/master_mici
|
||||||
run: |
|
run: |
|
||||||
git config user.name "GitHub Actions Bot"
|
git config user.name "GitHub Actions Bot"
|
||||||
git config user.email "<>"
|
git config user.email "<>"
|
||||||
@@ -159,17 +147,29 @@ jobs:
|
|||||||
git rm -rf *
|
git rm -rf *
|
||||||
mv ${{ github.workspace }}/pr_ui/* .
|
mv ${{ github.workspace }}/pr_ui/* .
|
||||||
git add .
|
git add .
|
||||||
git commit -m "screenshots for PR #${{ github.event.number }}"
|
git commit -m "ui videos for PR #${{ github.event.number }}"
|
||||||
git push origin ${{ env.BRANCH_NAME }} --force
|
git push origin ${{ env.BRANCH_NAME }} --force
|
||||||
|
|
||||||
- name: Comment Screenshots on PR
|
# Append diff reports to report files branch
|
||||||
|
git fetch origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||||
|
git checkout ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||||
|
for variant in $VARIANTS; do
|
||||||
|
IFS=':' read -r name _ _ <<< "$variant"
|
||||||
|
diff_name="${name}_diff"
|
||||||
|
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${diff_name}_pr_${{ github.event.number }}.html"
|
||||||
|
git add "${diff_name}_pr_${{ github.event.number }}.html"
|
||||||
|
done
|
||||||
|
git commit -m "ui diff reports for PR #${{ github.event.number }}" || echo "No changes to commit"
|
||||||
|
git push origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||||
|
|
||||||
|
- name: Comment on PR
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
uses: thollander/actions-comment-pull-request@v2
|
uses: thollander/actions-comment-pull-request@v2
|
||||||
with:
|
with:
|
||||||
message: |
|
message: |
|
||||||
<!-- _(run_id_screenshots **${{ github.run_id }}**)_ -->
|
<!-- _(run_id_ui_preview **${{ github.run_id }}**)_ -->
|
||||||
## UI Preview
|
## UI Preview
|
||||||
${{ steps.find_diff.outputs.DIFF }}
|
${{ steps.find_diff.outputs.COMMENT }}
|
||||||
comment_tag: run_id_screenshots
|
comment_tag: run_id_ui_preview
|
||||||
pr_number: ${{ github.event.number }}
|
pr_number: ${{ github.event.number }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
name: 'Wait for Tests'
|
|
||||||
description: 'Action to wait for workflow tests to start and complete'
|
|
||||||
inputs:
|
|
||||||
workflow:
|
|
||||||
description: 'The workflow file name to monitor'
|
|
||||||
required: true
|
|
||||||
default: 'selfdrive_tests.yaml'
|
|
||||||
branch:
|
|
||||||
description: 'The branch to monitor (defaults to current branch)'
|
|
||||||
required: false
|
|
||||||
default: ''
|
|
||||||
github-token:
|
|
||||||
description: 'GitHub token for API access'
|
|
||||||
required: true
|
|
||||||
wait-time:
|
|
||||||
description: 'Initial sleep time in seconds before monitoring starts'
|
|
||||||
required: false
|
|
||||||
default: '30'
|
|
||||||
should-wait-for-start:
|
|
||||||
description: 'Whether to wait for tests to start'
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: 'composite'
|
|
||||||
steps:
|
|
||||||
- name: Wait for tests to start
|
|
||||||
if: inputs.should-wait-for-start == 'true'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "Sleeping for ${{ inputs.wait-time }} seconds to give some time for the action to start and then we'll wait"
|
|
||||||
sleep ${{ inputs.wait-time }}
|
|
||||||
|
|
||||||
- name: Wait for tests to finish
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
BRANCH="${{ inputs.branch || github.head_ref || github.ref_name }}"
|
|
||||||
|
|
||||||
echo "Looking for workflow runs of ${{ inputs.workflow }} on branch $BRANCH"
|
|
||||||
RUN_ID=$(gh run list --workflow=${{ inputs.workflow }} --branch="$BRANCH" --limit=1 --json databaseId --jq '.[0].databaseId')
|
|
||||||
echo "Watching run ID: $RUN_ID"
|
|
||||||
gh run watch "$RUN_ID"
|
|
||||||
|
|
||||||
CONCLUSION=$(gh run view "$RUN_ID" --json conclusion --jq '.conclusion')
|
|
||||||
echo "Run concluded with: $CONCLUSION"
|
|
||||||
|
|
||||||
if [[ "$CONCLUSION" != "success" ]]; then
|
|
||||||
echo "❌ Workflow run failed with conclusion: $CONCLUSION"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ inputs.github-token }}
|
|
||||||
+20
-42
@@ -10,15 +10,16 @@ venv/
|
|||||||
.overlay_init
|
.overlay_init
|
||||||
.overlay_consistent
|
.overlay_consistent
|
||||||
.sconsign.dblite
|
.sconsign.dblite
|
||||||
model2.png
|
|
||||||
a.out
|
a.out
|
||||||
.hypothesis
|
.hypothesis
|
||||||
|
.cache/
|
||||||
|
bin/
|
||||||
|
|
||||||
/docs_site/
|
*.mp4
|
||||||
|
|
||||||
*.dylib
|
*.dylib
|
||||||
*.DSYM
|
*.DSYM
|
||||||
*.d
|
*.d
|
||||||
|
*.pem
|
||||||
*.pyc
|
*.pyc
|
||||||
*.pyo
|
*.pyo
|
||||||
.*.swp
|
.*.swp
|
||||||
@@ -35,76 +36,53 @@ a.out
|
|||||||
*.class
|
*.class
|
||||||
*.pyxbldc
|
*.pyxbldc
|
||||||
*.vcd
|
*.vcd
|
||||||
*.qm
|
*.mo
|
||||||
*_pyx.cpp
|
*_pyx.cpp
|
||||||
|
*.stats
|
||||||
|
*.pkl
|
||||||
|
*.pkl*
|
||||||
config.json
|
config.json
|
||||||
clcache
|
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
compare_runtime*.html
|
compare_runtime*.html
|
||||||
|
|
||||||
persist
|
# build artifacts
|
||||||
selfdrive/pandad/pandad
|
selfdrive/pandad/pandad
|
||||||
cereal/services.h
|
cereal/services.h
|
||||||
cereal/gen
|
cereal/gen
|
||||||
cereal/messaging/bridge
|
cereal/messaging/bridge
|
||||||
selfdrive/mapd/default_speeds_by_region.json
|
|
||||||
system/proclogd/proclogd
|
|
||||||
selfdrive/ui/translations/tmp
|
selfdrive/ui/translations/tmp
|
||||||
selfdrive/test/longitudinal_maneuvers/out
|
|
||||||
selfdrive/car/tests/cars_dump
|
selfdrive/car/tests/cars_dump
|
||||||
system/camerad/camerad
|
system/camerad/camerad
|
||||||
system/camerad/test/ae_gray_test
|
system/camerad/test/ae_gray_test
|
||||||
|
|
||||||
notebooks
|
|
||||||
hyperthneed
|
|
||||||
provisioning
|
|
||||||
|
|
||||||
.coverage*
|
.coverage*
|
||||||
coverage.xml
|
coverage.xml
|
||||||
htmlcov
|
htmlcov
|
||||||
pandaextra
|
|
||||||
|
|
||||||
.mypy_cache/
|
|
||||||
flycheck_*
|
|
||||||
|
|
||||||
cppcheck_report.txt
|
|
||||||
comma*.sh
|
|
||||||
|
|
||||||
selfdrive/modeld/models/*.pkl
|
|
||||||
sunnypilot/modeld*/thneed/compile
|
|
||||||
sunnypilot/modeld*/models/*.thneed
|
|
||||||
sunnypilot/modeld*/models/*.pkl
|
|
||||||
|
|
||||||
|
# openpilot log files
|
||||||
*.bz2
|
*.bz2
|
||||||
*.zst
|
*.zst
|
||||||
|
*.rlog
|
||||||
|
|
||||||
build/
|
build/
|
||||||
|
|
||||||
!**/.gitkeep
|
!**/.gitkeep
|
||||||
|
|
||||||
poetry.toml
|
|
||||||
Pipfile
|
|
||||||
|
|
||||||
### VisualStudioCode ###
|
### VisualStudioCode ###
|
||||||
|
*.vsix
|
||||||
|
.history
|
||||||
|
.ionide
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
.history/
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
!.vscode/*.code-snippets
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
# Local History for Visual Studio Code
|
# agents
|
||||||
.history/
|
.claude/
|
||||||
|
.context/
|
||||||
# Built Visual Studio Code Extensions
|
PLAN.md
|
||||||
*.vsix
|
TASK.md
|
||||||
|
|
||||||
### VisualStudioCode Patch ###
|
|
||||||
# Ignore all local history of files
|
|
||||||
.history
|
|
||||||
.ionide
|
|
||||||
|
|
||||||
### JetBrains ###
|
|
||||||
!.idea/customTargets.xml
|
|
||||||
!.idea/tools/*
|
|
||||||
!.run/*
|
|
||||||
|
|||||||
+5
-8
@@ -1,21 +1,18 @@
|
|||||||
[submodule "panda"]
|
[submodule "panda"]
|
||||||
path = panda
|
path = panda
|
||||||
url = https://github.com/sunnyhaibin/panda.git
|
url = ../../commaai/panda.git
|
||||||
[submodule "opendbc"]
|
[submodule "opendbc"]
|
||||||
path = opendbc_repo
|
path = opendbc_repo
|
||||||
url = https://github.com/sunnypilot/opendbc.git
|
url = ../../commaai/opendbc.git
|
||||||
[submodule "msgq"]
|
[submodule "msgq"]
|
||||||
path = msgq_repo
|
path = msgq_repo
|
||||||
url = https://github.com/sunnypilot/msgq.git
|
url = ../../commaai/msgq.git
|
||||||
[submodule "rednose_repo"]
|
[submodule "rednose_repo"]
|
||||||
path = rednose_repo
|
path = rednose_repo
|
||||||
url = https://github.com/commaai/rednose.git
|
url = ../../commaai/rednose.git
|
||||||
[submodule "teleoprtc_repo"]
|
[submodule "teleoprtc_repo"]
|
||||||
path = teleoprtc_repo
|
path = teleoprtc_repo
|
||||||
url = https://github.com/commaai/teleoprtc
|
url = ../../commaai/teleoprtc
|
||||||
[submodule "tinygrad"]
|
[submodule "tinygrad"]
|
||||||
path = tinygrad_repo
|
path = tinygrad_repo
|
||||||
url = https://github.com/tinygrad/tinygrad.git
|
url = https://github.com/tinygrad/tinygrad.git
|
||||||
[submodule "sunnypilot/neural_network_data"]
|
|
||||||
path = sunnypilot/neural_network_data
|
|
||||||
url = https://github.com/sunnypilot/neural-network-data.git
|
|
||||||
|
|||||||
Generated
-25
@@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CLionExternalBuildManager">
|
|
||||||
<target id="a62f99e8-5ec4-434c-8122-49efed5af108" name="uv Scons Build Debug" defaultType="TOOL">
|
|
||||||
<configuration id="b93ec964-16e5-4962-a12e-3ed360ce8f02" name="uv Scons Build Debug">
|
|
||||||
<build type="TOOL">
|
|
||||||
<tool actionId="Tool_External Tools_uv Scons Build Debug" />
|
|
||||||
</build>
|
|
||||||
<clean type="TOOL">
|
|
||||||
<tool actionId="Tool_External Tools_uv Scons Clean" />
|
|
||||||
</clean>
|
|
||||||
</configuration>
|
|
||||||
</target>
|
|
||||||
<target id="edd8ad9d-183b-467c-a355-0d9a0ecab026" name="uv Scons Build Release" defaultType="TOOL">
|
|
||||||
<configuration id="09523339-5ce3-4223-ab9e-904f38ad7752" name="uv Scons Build Release">
|
|
||||||
<build type="TOOL">
|
|
||||||
<tool actionId="Tool_External Tools_uv Scons Build Release" />
|
|
||||||
</build>
|
|
||||||
<clean type="TOOL">
|
|
||||||
<tool actionId="Tool_External Tools_uv Scons Clean" />
|
|
||||||
</clean>
|
|
||||||
</configuration>
|
|
||||||
</target>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-23
@@ -1,23 +0,0 @@
|
|||||||
<toolSet name="External Tools">
|
|
||||||
<tool name="uv Scons Build Debug" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
|
||||||
<exec>
|
|
||||||
<option name="COMMAND" value="bash" />
|
|
||||||
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -u -j$(nproc) --ccflags=\"-fno-inline\""" />
|
|
||||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
|
||||||
</exec>
|
|
||||||
</tool>
|
|
||||||
<tool name="uv Scons Clean" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
|
||||||
<exec>
|
|
||||||
<option name="COMMAND" value="bash" />
|
|
||||||
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -c" " />
|
|
||||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
|
||||||
</exec>
|
|
||||||
</tool>
|
|
||||||
<tool name="uv Scons Build Release" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
|
||||||
<exec>
|
|
||||||
<option name="COMMAND" value="bash" />
|
|
||||||
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -u -j$(nproc)" " />
|
|
||||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
|
||||||
</exec>
|
|
||||||
</tool>
|
|
||||||
</toolSet>
|
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
[lfs]
|
[lfs]
|
||||||
url = https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs
|
url = https://gitlab.com/commaai/openpilot-lfs.git/info/lfs
|
||||||
pushurl = ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git
|
pushurl = ssh://git@gitlab.com/commaai/openpilot-lfs.git
|
||||||
locksverify = false
|
locksverify = false
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
[lfs]
|
|
||||||
url = https://gitlab.com/commaai/openpilot-lfs.git/info/lfs
|
|
||||||
pushurl = ssh://git@gitlab.com/commaai/openpilot-lfs.git
|
|
||||||
locksverify = false
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.12.13
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Build Debug" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/selfdrive/ui" PASS_PARENT_ENVS_2="true" PROJECT_NAME="sunnypilot" TARGET_NAME="uv Scons Build Debug" CONFIG_NAME="uv Scons Build Debug" RUN_PATH="ui">
|
|
||||||
<envs>
|
|
||||||
<env name="QT_DBL_CLICK_DIST" value="150" />
|
|
||||||
</envs>
|
|
||||||
<method v="2">
|
|
||||||
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Build Release" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/selfdrive/ui" PASS_PARENT_ENVS_2="true" PROJECT_NAME="sunnypilot" TARGET_NAME="uv Scons Build Release" CONFIG_NAME="uv Scons Build Release" RUN_PATH="ui">
|
|
||||||
<envs>
|
|
||||||
<env name="QT_DBL_CLICK_DIST" value="150" />
|
|
||||||
</envs>
|
|
||||||
<method v="2">
|
|
||||||
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
Vendored
+43
-1
@@ -23,6 +23,11 @@
|
|||||||
"id": "args",
|
"id": "args",
|
||||||
"description": "Arguments to pass to the process",
|
"description": "Arguments to pass to the process",
|
||||||
"type": "promptString"
|
"type": "promptString"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "replayArg",
|
||||||
|
"type": "promptString",
|
||||||
|
"description": "Enter route or segment to replay."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configurations": [
|
"configurations": [
|
||||||
@@ -40,7 +45,44 @@
|
|||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/${input:cpp_process}",
|
"program": "${workspaceFolder}/${input:cpp_process}",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach LLDB to Replay drive",
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "attach",
|
||||||
|
"pid": "${command:pickMyProcess}",
|
||||||
|
"sourceMap": {
|
||||||
|
".": "${workspaceFolder}/opendbc/safety"
|
||||||
|
},
|
||||||
|
"initCommands": [
|
||||||
|
"script import time; time.sleep(3)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Replay drive",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/opendbc/safety/tests/safety_replay/replay_drive.py",
|
||||||
|
"args": [
|
||||||
|
"${input:replayArg}"
|
||||||
|
],
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"justMyCode": false,
|
||||||
|
"env": {
|
||||||
|
"PYTHONPATH": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"subProcess": true,
|
||||||
|
"stopOnEntry": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compounds": [
|
||||||
|
{
|
||||||
|
"name": "Replay drive + Safety LLDB",
|
||||||
|
"configurations": [
|
||||||
|
"Replay drive",
|
||||||
|
"Attach LLDB to Replay drive"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+30
-4
@@ -1,12 +1,38 @@
|
|||||||
FROM ghcr.io/commaai/openpilot-base:latest
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
ENV OPENPILOT_PATH=/home/batman/openpilot
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends sudo tzdata locales && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
||||||
|
ENV LANG=en_US.UTF-8
|
||||||
|
ENV LANGUAGE=en_US:en
|
||||||
|
ENV LC_ALL=en_US.UTF-8
|
||||||
|
|
||||||
|
ENV NVIDIA_VISIBLE_DEVICES=all
|
||||||
|
ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
|
||||||
|
|
||||||
|
ARG USER=batman
|
||||||
|
ARG USER_UID=1001
|
||||||
|
RUN useradd -m -s /bin/bash -u $USER_UID $USER
|
||||||
|
RUN usermod -aG sudo $USER
|
||||||
|
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
||||||
|
USER $USER
|
||||||
|
|
||||||
|
ENV OPENPILOT_PATH=/home/$USER/openpilot
|
||||||
RUN mkdir -p ${OPENPILOT_PATH}
|
RUN mkdir -p ${OPENPILOT_PATH}
|
||||||
WORKDIR ${OPENPILOT_PATH}
|
WORKDIR ${OPENPILOT_PATH}
|
||||||
|
|
||||||
COPY . ${OPENPILOT_PATH}/
|
COPY --chown=$USER . ${OPENPILOT_PATH}/
|
||||||
|
|
||||||
RUN scons --cache-readonly -j$(nproc)
|
ENV UV_BIN="/home/$USER/.local/bin/"
|
||||||
|
ENV VIRTUAL_ENV=${OPENPILOT_PATH}/.venv
|
||||||
|
ENV PATH="$UV_BIN:$VIRTUAL_ENV/bin:$PATH"
|
||||||
|
RUN tools/setup_dependencies.sh && \
|
||||||
|
sudo rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
USER root
|
||||||
|
RUN git config --global --add safe.directory '*'
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
FROM ubuntu:24.04
|
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends sudo tzdata locales ssh pulseaudio xvfb x11-xserver-utils gnome-screenshot python3-tk python3-dev && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
|
||||||
ENV LANG=en_US.UTF-8
|
|
||||||
ENV LANGUAGE=en_US:en
|
|
||||||
ENV LC_ALL=en_US.UTF-8
|
|
||||||
|
|
||||||
COPY tools/install_ubuntu_dependencies.sh /tmp/tools/
|
|
||||||
RUN /tmp/tools/install_ubuntu_dependencies.sh && \
|
|
||||||
rm -rf /var/lib/apt/lists/* /tmp/* && \
|
|
||||||
cd /usr/lib/gcc/arm-none-eabi/* && \
|
|
||||||
rm -rf arm/ thumb/nofp thumb/v6* thumb/v8* thumb/v7+fp thumb/v7-r+fp.sp
|
|
||||||
|
|
||||||
# Add OpenCL
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
apt-utils \
|
|
||||||
alien \
|
|
||||||
unzip \
|
|
||||||
tar \
|
|
||||||
curl \
|
|
||||||
xz-utils \
|
|
||||||
dbus \
|
|
||||||
gcc-arm-none-eabi \
|
|
||||||
tmux \
|
|
||||||
vim \
|
|
||||||
libx11-6 \
|
|
||||||
wget \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN mkdir -p /tmp/opencl-driver-intel && \
|
|
||||||
cd /tmp/opencl-driver-intel && \
|
|
||||||
wget https://github.com/intel/llvm/releases/download/2024-WW14/oclcpuexp-2024.17.3.0.09_rel.tar.gz && \
|
|
||||||
wget https://github.com/oneapi-src/oneTBB/releases/download/v2021.12.0/oneapi-tbb-2021.12.0-lin.tgz && \
|
|
||||||
mkdir -p /opt/intel/oclcpuexp_2024.17.3.0.09_rel && \
|
|
||||||
cd /opt/intel/oclcpuexp_2024.17.3.0.09_rel && \
|
|
||||||
tar -zxvf /tmp/opencl-driver-intel/oclcpuexp-2024.17.3.0.09_rel.tar.gz && \
|
|
||||||
mkdir -p /etc/OpenCL/vendors && \
|
|
||||||
echo /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64/libintelocl.so > /etc/OpenCL/vendors/intel_expcpu.icd && \
|
|
||||||
cd /opt/intel && \
|
|
||||||
tar -zxvf /tmp/opencl-driver-intel/oneapi-tbb-2021.12.0-lin.tgz && \
|
|
||||||
ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbb.so /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \
|
|
||||||
ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbbmalloc.so /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \
|
|
||||||
ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbb.so.12 /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \
|
|
||||||
ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbbmalloc.so.2 /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \
|
|
||||||
mkdir -p /etc/ld.so.conf.d && \
|
|
||||||
echo /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 > /etc/ld.so.conf.d/libintelopenclexp.conf && \
|
|
||||||
ldconfig -f /etc/ld.so.conf.d/libintelopenclexp.conf && \
|
|
||||||
cd / && \
|
|
||||||
rm -rf /tmp/opencl-driver-intel
|
|
||||||
|
|
||||||
ENV NVIDIA_VISIBLE_DEVICES=all
|
|
||||||
ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
|
|
||||||
ENV QTWEBENGINE_DISABLE_SANDBOX=1
|
|
||||||
|
|
||||||
RUN dbus-uuidgen > /etc/machine-id
|
|
||||||
RUN apt-get update && apt-get install -y fonts-noto-cjk fonts-noto-color-emoji
|
|
||||||
|
|
||||||
ARG USER=batman
|
|
||||||
ARG USER_UID=1001
|
|
||||||
RUN useradd -m -s /bin/bash -u $USER_UID $USER
|
|
||||||
RUN usermod -aG sudo $USER
|
|
||||||
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
|
||||||
USER $USER
|
|
||||||
|
|
||||||
COPY --chown=$USER pyproject.toml uv.lock /home/$USER
|
|
||||||
COPY --chown=$USER tools/install_python_dependencies.sh /home/$USER/tools/
|
|
||||||
|
|
||||||
ENV VIRTUAL_ENV=/home/$USER/.venv
|
|
||||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
|
||||||
RUN cd /home/$USER && \
|
|
||||||
tools/install_python_dependencies.sh && \
|
|
||||||
rm -rf tools/ pyproject.toml uv.lock .cache
|
|
||||||
|
|
||||||
USER root
|
|
||||||
RUN sudo git config --global --add safe.directory /tmp/openpilot
|
|
||||||
Vendored
+16
-39
@@ -22,7 +22,7 @@ shopt -s huponexit # kill all child processes when the shell exits
|
|||||||
|
|
||||||
export CI=1
|
export CI=1
|
||||||
export PYTHONWARNINGS=error
|
export PYTHONWARNINGS=error
|
||||||
export LOGPRINT=debug
|
#export LOGPRINT=debug # this has gotten too spammy...
|
||||||
export TEST_DIR=${env.TEST_DIR}
|
export TEST_DIR=${env.TEST_DIR}
|
||||||
export SOURCE_DIR=${env.SOURCE_DIR}
|
export SOURCE_DIR=${env.SOURCE_DIR}
|
||||||
export GIT_BRANCH=${env.GIT_BRANCH}
|
export GIT_BRANCH=${env.GIT_BRANCH}
|
||||||
@@ -167,7 +167,7 @@ node {
|
|||||||
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
||||||
|
|
||||||
def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging',
|
def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging',
|
||||||
'testing-closet*', 'hotfix-*']
|
'release-tici', 'release-tizi', 'release-tizi-staging', 'release-mici-staging', 'testing-closet*', 'hotfix-*']
|
||||||
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
|
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
|
||||||
|
|
||||||
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
|
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
|
||||||
@@ -178,20 +178,20 @@ node {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (env.BRANCH_NAME == 'devel-staging') {
|
if (env.BRANCH_NAME == 'devel-staging') {
|
||||||
deviceStage("build release3-staging", "tici-needs-can", [], [
|
deviceStage("build release-tizi-staging", "tizi-needs-can", [], [
|
||||||
step("build release3-staging", "RELEASE_BRANCH=release3-staging $SOURCE_DIR/release/build_release.sh"),
|
step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging $SOURCE_DIR/release/build_release.sh && git push -f origin release-tizi-staging:release-mici-staging"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env.BRANCH_NAME == '__nightly') {
|
if (env.BRANCH_NAME == '__nightly') {
|
||||||
parallel (
|
parallel (
|
||||||
'nightly': {
|
'nightly': {
|
||||||
deviceStage("build nightly", "tici-needs-can", [], [
|
deviceStage("build nightly", "tizi-needs-can", [], [
|
||||||
step("build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"),
|
step("build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'nightly-dev': {
|
'nightly-dev': {
|
||||||
deviceStage("build nightly-dev", "tici-needs-can", [], [
|
deviceStage("build nightly-dev", "tizi-needs-can", [], [
|
||||||
step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/release/build_release.sh"),
|
step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/release/build_release.sh"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
@@ -200,63 +200,43 @@ node {
|
|||||||
|
|
||||||
if (!env.BRANCH_NAME.matches(excludeRegex)) {
|
if (!env.BRANCH_NAME.matches(excludeRegex)) {
|
||||||
parallel (
|
parallel (
|
||||||
// tici tests
|
|
||||||
'onroad tests': {
|
'onroad tests': {
|
||||||
deviceStage("onroad", "tici-needs-can", ["UNSAFE=1"], [
|
deviceStage("onroad", "tizi-needs-can", ["UNSAFE=1"], [
|
||||||
step("build openpilot", "cd system/manager && ./build.py"),
|
step("build openpilot", "cd system/manager && ./build.py"),
|
||||||
step("check dirty", "release/check-dirty.sh"),
|
step("check dirty", "release/check-dirty.sh"),
|
||||||
step("onroad tests", "pytest selfdrive/test/test_onroad.py -s", [timeout: 60]),
|
step("onroad tests", "pytest selfdrive/test/test_onroad.py -s", [timeout: 60]),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'HW + Unit Tests': {
|
'HW + Unit Tests': {
|
||||||
deviceStage("tici-hardware", "tici-common", ["UNSAFE=1"], [
|
deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py"),
|
step("build", "cd system/manager && ./build.py"),
|
||||||
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
|
|
||||||
step("test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"),
|
step("test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"),
|
||||||
step("test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py", [diffPaths: ["system/loggerd/"]]),
|
step("test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py", [diffPaths: ["system/loggerd/"]]),
|
||||||
step("test pigeond", "pytest system/ubloxd/tests/test_pigeond.py", [diffPaths: ["system/ubloxd/"]]),
|
|
||||||
step("test manager", "pytest system/manager/test/test_manager.py"),
|
step("test manager", "pytest system/manager/test/test_manager.py"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'loopback': {
|
|
||||||
deviceStage("loopback", "tici-loopback", ["UNSAFE=1"], [
|
|
||||||
step("build openpilot", "cd system/manager && ./build.py"),
|
|
||||||
step("test pandad loopback", "pytest selfdrive/pandad/tests/test_pandad_loopback.py"),
|
|
||||||
])
|
|
||||||
},
|
|
||||||
'camerad AR0231': {
|
|
||||||
deviceStage("AR0231", "tici-ar0231", ["UNSAFE=1"], [
|
|
||||||
step("build", "cd system/manager && ./build.py"),
|
|
||||||
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]),
|
|
||||||
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
|
||||||
])
|
|
||||||
},
|
|
||||||
'camerad OX03C10': {
|
'camerad OX03C10': {
|
||||||
deviceStage("OX03C10", "tici-ox03c10", ["UNSAFE=1"], [
|
deviceStage("OX03C10", "tizi-ox03c10", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py"),
|
step("build", "cd system/manager && ./build.py"),
|
||||||
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]),
|
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py"),
|
||||||
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'camerad OS04C10': {
|
'camerad OS04C10': {
|
||||||
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
|
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py"),
|
step("build", "cd system/manager && ./build.py"),
|
||||||
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]),
|
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py"),
|
||||||
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'sensord': {
|
'sensord': {
|
||||||
deviceStage("LSM + MMC", "tici-lsmc", ["UNSAFE=1"], [
|
deviceStage("LSM + MMC", "tizi-lsmc", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py"),
|
|
||||||
step("test sensord", "pytest system/sensord/tests/test_sensord.py"),
|
|
||||||
])
|
|
||||||
deviceStage("BMX + LSM", "tici-bmx-lsm", ["UNSAFE=1"], [
|
|
||||||
step("build", "cd system/manager && ./build.py"),
|
step("build", "cd system/manager && ./build.py"),
|
||||||
step("test sensord", "pytest system/sensord/tests/test_sensord.py"),
|
step("test sensord", "pytest system/sensord/tests/test_sensord.py"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'replay': {
|
'replay': {
|
||||||
deviceStage("model-replay", "tici-replay", ["UNSAFE=1"], [
|
deviceStage("model-replay", "tizi-replay", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]),
|
step("build", "cd system/manager && ./build.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]),
|
||||||
step("model replay", "selfdrive/test/process_replay/model_replay.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]),
|
step("model replay", "selfdrive/test/process_replay/model_replay.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]),
|
||||||
])
|
])
|
||||||
@@ -264,12 +244,9 @@ node {
|
|||||||
'tizi': {
|
'tizi': {
|
||||||
deviceStage("tizi", "tizi", ["UNSAFE=1"], [
|
deviceStage("tizi", "tizi", ["UNSAFE=1"], [
|
||||||
step("build openpilot", "cd system/manager && ./build.py"),
|
step("build openpilot", "cd system/manager && ./build.py"),
|
||||||
step("test pandad loopback", "SINGLE_PANDA=1 pytest selfdrive/pandad/tests/test_pandad_loopback.py"),
|
step("test pandad loopback", "pytest selfdrive/pandad/tests/test_pandad_loopback.py"),
|
||||||
step("test pandad spi", "pytest selfdrive/pandad/tests/test_pandad_spi.py"),
|
step("test pandad spi", "pytest selfdrive/pandad/tests/test_pandad_spi.py"),
|
||||||
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
|
|
||||||
step("test amp", "pytest system/hardware/tici/tests/test_amplifier.py"),
|
step("test amp", "pytest system/hardware/tici/tests/test_amplifier.py"),
|
||||||
// TODO: enable once new AGNOS is available
|
|
||||||
// step("test esim", "pytest system/hardware/tici/tests/test_esim.py"),
|
|
||||||
step("test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", [diffPaths: ["system/qcomgpsd/"]]),
|
step("test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", [diffPaths: ["system/qcomgpsd/"]]),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
|||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
# Custom MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024, Haibin Wen, SUNNYPILOT LLC
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to view and modify the Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1. **Permission Required**: Permission Required for Commercial, For-Profit, or Closed Source Use: Use of the Software, in whole or in part, for any commercial purposes, for-profit projects, or in closed source projects requires explicit written permission from the original author(s).
|
|
||||||
|
|
||||||
2. **Redistribution**: Any redistribution of the Software, modified or unmodified, must retain this license notice and the following acknowledgment:
|
|
||||||
"This software is licensed under a custom license requiring permission for use."
|
|
||||||
|
|
||||||
3. **Visibility**: Any project that uses the Software must visibly mention the following acknowledgment:
|
|
||||||
"This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use."
|
|
||||||
|
|
||||||
4. **No Warranty**: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
Contact sunnypilot Support <support@sunnypilot.ai> for permission requests.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Haibin Wen, SUNNYPILOT LLC
|
|
||||||
@@ -1,119 +1,111 @@
|
|||||||

|
<div align="center" style="text-align: center;">
|
||||||
|
|
||||||
## 🌞 What is sunnypilot?
|
<h1>openpilot</h1>
|
||||||
[sunnypilot](https://github.com/sunnyhaibin/sunnypilot) is a fork of comma.ai's openpilot, an open source driver assistance system. sunnypilot offers the user a unique driving experience for over 300+ supported car makes and models with modified behaviors of driving assist engagements. sunnypilot complies with comma.ai's safety rules as accurately as possible.
|
|
||||||
|
|
||||||
## 💭 Join our Discord
|
<p>
|
||||||
Join the official sunnypilot Discord server to stay up to date with all the latest features and be a part of shaping the future of sunnypilot!
|
<b>openpilot is an operating system for robotics.</b>
|
||||||
* https://discord.gg/sunnypilot
|
<br>
|
||||||
|
Currently, it upgrades the driver assistance system in 300+ supported cars.
|
||||||
|
</p>
|
||||||
|
|
||||||
 
|
<h3>
|
||||||
|
<a href="https://docs.comma.ai">Docs</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://docs.comma.ai/contributing/roadmap/">Roadmap</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md">Contribute</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://discord.comma.ai">Community</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://comma.ai/shop">Try it on a comma four</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
## Documentation
|
Quick start: `bash <(curl -fsSL openpilot.comma.ai)`
|
||||||
https://docs.sunnypilot.ai/ is your one stop shop for everything from features to installation to FAQ about the sunnypilot
|
|
||||||
|
|
||||||
## 🚘 Running on a dedicated device in a car
|
[](https://github.com/commaai/openpilot/actions/workflows/tests.yaml)
|
||||||
* A supported device to run this software
|
[](LICENSE)
|
||||||
* a [comma three](https://comma.ai/shop/products/three) or a [C3X](https://comma.ai/shop/comma-3x)
|
[](https://x.com/comma_ai)
|
||||||
* This software
|
[](https://discord.comma.ai)
|
||||||
* One of [the 300+ supported cars](https://github.com/commaai/openpilot/blob/master/docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, Ford and more. If your car is not supported but has adaptive cruise control and lane-keeping assist, it's likely able to run sunnypilot.
|
|
||||||
* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car
|
|
||||||
|
|
||||||
Detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
|
</div>
|
||||||
|
|
||||||
## Installation
|
<table>
|
||||||
Please refer to [Recommended Branches](#-recommended-branches) to find your preferred/supported branch. This guide will assume you want to install the latest `release-c3` branch.
|
<tr>
|
||||||
|
<td><a href="https://youtu.be/NmBfgOanCyk" title="Video By Greer Viau"><img src="https://github.com/commaai/openpilot/assets/8762862/2f7112ae-f748-4f39-b617-fabd689c3772"></a></td>
|
||||||
* sunnypilot not installed or you installed a version before 0.8.17?
|
<td><a href="https://youtu.be/VHKyqZ7t8Gw" title="Video By Logan LeGrand"><img src="https://github.com/commaai/openpilot/assets/8762862/92351544-2833-40d7-9e0b-7ef7ae37ec4c"></a></td>
|
||||||
1. [Factory reset/uninstall](https://github.com/commaai/openpilot/wiki/FAQ#how-can-i-reset-the-device) the previous software if you have another software/fork installed.
|
<td><a href="https://youtu.be/SUIZYzxtMQs" title="A drive to Taco Bell"><img src="https://github.com/commaai/openpilot/assets/8762862/05ceefc5-2628-439c-a9b2-89ce77dc6f63"></a></td>
|
||||||
2. After factory reset/uninstall and upon reboot, select `Custom Software` when given the option.
|
</tr>
|
||||||
3. Input the installation URL per [Recommended Branches](#-recommended-branches). Example: ```release-c3.sunnypilot.ai```.
|
</table>
|
||||||
4. Complete the rest of the installation following the onscreen instructions.
|
|
||||||
|
|
||||||
* sunnypilot already installed and you installed a version after 0.8.17?
|
|
||||||
1. On the comma three, go to `Settings` ▶️ `Software`.
|
|
||||||
2. At the `Download` option, press `CHECK`. This will fetch the list of latest branches from sunnypilot.
|
|
||||||
3. At the `Target Branch` option, press `SELECT` to open the Target Branch selector.
|
|
||||||
4. Scroll to select the desired branch per Recommended Branches (see below). Example: `release-c3`
|
|
||||||
|
|
||||||
| Branch | Installation URL |
|
|
||||||
|:------------:|:--------------------------------:|
|
|
||||||
| `release-c3` | https://release-c3.sunnypilot.ai |
|
|
||||||
| `staging-c3` | https://staging-c3.sunnypilot.ai |
|
|
||||||
| `dev-c3` | https://dev-c3.sunnypilot.ai |
|
|
||||||
|
|
||||||
### If you want to use our newest branches (our rewrite)
|
|
||||||
> [!TIP]
|
|
||||||
>You can see the rewrite state on our [rewrite project board](https://github.com/orgs/sunnypilot/projects/2), and to install the new branches, you can use the following links
|
|
||||||
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
Using openpilot in a car
|
||||||
> It is recommended to [re-flash AGNOS](https://flash.comma.ai/) if you intend to downgrade from the new branches.
|
------
|
||||||
> You can still restore the latest sunnylink backup made on the old branches.
|
|
||||||
|
|
||||||
| Branch | Installation URL |
|
To use openpilot in a car, you need four things:
|
||||||
|:----------------:|:---------------------------------------------:|
|
1. **Supported Device:** a comma four, available at [comma.ai/shop/comma-four](https://www.comma.ai/shop/comma-four).
|
||||||
| `staging-c3-new` | `https://staging-c3-new.sunnypilot.ai` |
|
2. **Software:** The setup procedure for the comma four allows users to enter a URL for custom software. Use the URL `openpilot.comma.ai` to install the release version.
|
||||||
| `dev-c3-new` | `https://dev-c3-new.sunnypilot.ai` |
|
3. **Supported Car:** Ensure that you have one of [the 275+ supported cars](docs/CARS.md).
|
||||||
| `custom-branch` | `https://install.sunnypilot.ai/{branch_name}` |
|
4. **Car Harness:** You will also need a [car harness](https://comma.ai/shop/car-harness) to connect your comma four to your car.
|
||||||
| `release-c3-new` | **Not yet available**. |
|
|
||||||
|
|
||||||
> [!TIP]
|
We have detailed instructions for [how to install the harness and device in a car](https://comma.ai/setup). Note that it's possible to run openpilot on [other hardware](https://blog.comma.ai/self-driving-car-for-free/), although it's not plug-and-play.
|
||||||
> Do you require further assistance with software installation? Join the [sunnypilot Discord server](https://discord.sunnypilot.com) and message us in the `#installation-help` channel.
|
|
||||||
|
|
||||||
## 🎆 Pull Requests
|
|
||||||
We welcome both pull requests and issues on GitHub. Bug fixes are encouraged.
|
|
||||||
|
|
||||||
Pull requests should be against the most current `master-new` branch.
|
### Branches
|
||||||
|
|
||||||
## 📊 User Data
|
Running `master` and other branches directly is supported, but it's recommended to run one of the following prebuilt branches:
|
||||||
|
|
||||||
By default, sunnypilot uploads the driving data to comma servers. You can also access your data through [comma connect](https://connect.comma.ai/).
|
| comma four branch | comma 3X branch | URL | description |
|
||||||
|
|------------------------|------------------------|----------------------------------------|-------------------------------------------------------------------------------------|
|
||||||
|
| `release-mici` | `release-tizi` | openpilot.comma.ai | This is openpilot's release branch. |
|
||||||
|
| `release-mici-staging` | `release-tizi-staging` | openpilot-test.comma.ai | This is the staging branch for releases. Use it to get new releases slightly early. |
|
||||||
|
| `nightly` | `nightly` | openpilot-nightly.comma.ai | This is the bleeding edge development branch. Do not expect this to be stable. |
|
||||||
|
| `nightly-dev` | `nightly-dev` | installer.comma.ai/commaai/nightly-dev | Same as nightly, but includes experimental development features for some cars. |
|
||||||
|
|
||||||
sunnypilot is open source software. The user is free to disable data collection if they wish to do so.
|
To start developing openpilot
|
||||||
|
------
|
||||||
|
|
||||||
sunnypilot logs the road-facing camera, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
|
openpilot is developed by [comma](https://comma.ai/) and by users like you. We welcome both pull requests and issues on [GitHub](http://github.com/commaai/openpilot).
|
||||||
|
|
||||||
|
* Join the [community Discord](https://discord.comma.ai)
|
||||||
|
* Check out [the contributing docs](docs/CONTRIBUTING.md)
|
||||||
|
* Check out the [openpilot tools](tools/)
|
||||||
|
* Code documentation lives at https://docs.comma.ai
|
||||||
|
* Information about running openpilot lives on the [community wiki](https://github.com/commaai/openpilot/wiki)
|
||||||
|
|
||||||
|
Want to get paid to work on openpilot? [comma is hiring](https://comma.ai/jobs#open-positions) and offers lots of [bounties](https://comma.ai/bounties) for external contributors.
|
||||||
|
|
||||||
|
Safety and Testing
|
||||||
|
----
|
||||||
|
|
||||||
|
* openpilot observes [ISO26262](https://en.wikipedia.org/wiki/ISO_26262) guidelines, see [SAFETY.md](docs/SAFETY.md) for more details.
|
||||||
|
* openpilot has software-in-the-loop [tests](.github/workflows/tests.yaml) that run on every commit.
|
||||||
|
* The code enforcing the safety model lives in panda and is written in C, see [code rigor](https://github.com/commaai/panda#code-rigor) for more details.
|
||||||
|
* panda has software-in-the-loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety).
|
||||||
|
* Internally, we have a hardware-in-the-loop Jenkins test suite that builds and unit tests the various processes.
|
||||||
|
* panda has additional hardware-in-the-loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile).
|
||||||
|
* We run the latest openpilot in a testing closet containing 10 comma devices continuously replaying routes.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>MIT Licensed</summary>
|
||||||
|
|
||||||
|
openpilot is released under the MIT license. Some parts of the software are released under other licenses as specified.
|
||||||
|
|
||||||
|
Any user of this software shall indemnify and hold harmless Comma.ai, Inc. and its directors, officers, employees, agents, stockholders, affiliates, subcontractors and customers from and against all allegations, claims, actions, suits, demands, damages, liabilities, obligations, losses, settlements, judgments, costs and expenses (including without limitation attorneys’ fees and costs) which arise out of, relate to or result from any use of this software by user.
|
||||||
|
|
||||||
|
**THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT.
|
||||||
|
YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS.
|
||||||
|
NO WARRANTY EXPRESSED OR IMPLIED.**
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>User Data and comma Account</summary>
|
||||||
|
|
||||||
|
By default, openpilot uploads the driving data to our servers. You can also access your data through [comma connect](https://connect.comma.ai/). We use your data to train better models and improve openpilot for everyone.
|
||||||
|
|
||||||
|
openpilot is open source software: the user is free to disable data collection if they wish to do so.
|
||||||
|
|
||||||
|
openpilot logs the road-facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
|
||||||
The driver-facing camera and microphone are only logged if you explicitly opt-in in settings.
|
The driver-facing camera and microphone are only logged if you explicitly opt-in in settings.
|
||||||
|
|
||||||
By using this software, you understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data.
|
By using openpilot, you agree to [our Privacy Policy](https://comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data.
|
||||||
|
</details>
|
||||||
## Licensing
|
|
||||||
|
|
||||||
sunnypilot is released under the [MIT License](LICENSE). This repository includes original work as well as significant portions of code derived from [openpilot by comma.ai](https://github.com/commaai/openpilot), which is also released under the MIT license with additional disclaimers.
|
|
||||||
|
|
||||||
The original openpilot license notice, including comma.ai’s indemnification and alpha software disclaimer, is reproduced below as required:
|
|
||||||
|
|
||||||
> openpilot is released under the MIT license. Some parts of the software are released under other licenses as specified.
|
|
||||||
>
|
|
||||||
> Any user of this software shall indemnify and hold harmless Comma.ai, Inc. and its directors, officers, employees, agents, stockholders, affiliates, subcontractors and customers from and against all allegations, claims, actions, suits, demands, damages, liabilities, obligations, losses, settlements, judgments, costs and expenses (including without limitation attorneys’ fees and costs) which arise out of, relate to or result from any use of this software by user.
|
|
||||||
>
|
|
||||||
> **THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT.
|
|
||||||
> YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS.
|
|
||||||
> NO WARRANTY EXPRESSED OR IMPLIED.**
|
|
||||||
|
|
||||||
For full license terms, please see the [`LICENSE`](LICENSE) file.
|
|
||||||
|
|
||||||
## 💰 Support sunnypilot
|
|
||||||
If you find any of the features useful, consider becoming a [sponsor on GitHub](https://github.com/sponsors/sunnyhaibin) to support future feature development and improvements.
|
|
||||||
|
|
||||||
|
|
||||||
By becoming a sponsor, you will gain access to exclusive content, early access to new features, and the opportunity to directly influence the project's development.
|
|
||||||
|
|
||||||
|
|
||||||
<h3>GitHub Sponsor</h3>
|
|
||||||
|
|
||||||
<a href="https://github.com/sponsors/sunnyhaibin">
|
|
||||||
<img src="https://user-images.githubusercontent.com/47793918/244135584-9800acbd-69fd-4b2b-bec9-e5fa2d85c817.png" alt="Become a Sponsor" width="300" style="max-width: 100%; height: auto;">
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<h3>PayPal</h3>
|
|
||||||
|
|
||||||
<a href="https://paypal.me/sunnyhaibin0850" target="_blank">
|
|
||||||
<img src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" alt="PayPal this" title="PayPal - The safer, easier way to pay online!" border="0" />
|
|
||||||
</a>
|
|
||||||
<br></br>
|
|
||||||
|
|
||||||
Your continuous love and support are greatly appreciated! Enjoy 🥰
|
|
||||||
|
|
||||||
<span>-</span> Jason, Founder of sunnypilot
|
|
||||||
|
|||||||
+53
-4
@@ -1,11 +1,60 @@
|
|||||||
Version 0.10.0 (2025-07-07)
|
Version 0.11.1 (2026-04-22)
|
||||||
|
========================
|
||||||
|
* New driver monitoring model
|
||||||
|
* Improved image processing pipeline for driver camera
|
||||||
|
* Rivian R1S and R1T 2025 support thanks to lukasloetkolben!
|
||||||
|
|
||||||
|
Version 0.11.0 (2026-03-17)
|
||||||
|
========================
|
||||||
|
* New driving model #36798
|
||||||
|
* Fully trained using a learned simulator
|
||||||
|
* Improved longitudinal performance in Experimental mode
|
||||||
|
* Reduce comma four standby power usage by 77% to 52 mW
|
||||||
|
* Kia K7 2017 support thanks to royjr!
|
||||||
|
* Lexus LS 2018 support thanks to Hacheoy!
|
||||||
|
|
||||||
|
Version 0.10.3 (2025-12-17)
|
||||||
|
========================
|
||||||
|
* New driving model #36249
|
||||||
|
* New temporal policy architecture
|
||||||
|
* New on-policy training physics noise model
|
||||||
|
* New driver monitoring model #36409
|
||||||
|
* Trained on a new dataset, including comma four data
|
||||||
|
* Improved inter-process communication memory efficiency
|
||||||
|
|
||||||
|
Version 0.10.2 (2025-11-19)
|
||||||
|
========================
|
||||||
|
* comma four support
|
||||||
|
|
||||||
|
Version 0.10.1 (2025-09-08)
|
||||||
|
========================
|
||||||
|
* New driving model #36276
|
||||||
|
* World Model: removed global localization inputs
|
||||||
|
* World Model: 2x the number of parameters
|
||||||
|
* World Model: trained on 4x the number of segments
|
||||||
|
* VAE Compression Model: new architecture and training objective
|
||||||
|
* Driving Vision Model: trained on 4x the number of segments
|
||||||
|
* New Driver Monitoring model #36198
|
||||||
|
* Acura TLX 2021 support thanks to MVL!
|
||||||
|
* Honda City 2023 support thanks to vanillagorillaa and drFritz!
|
||||||
|
* Honda N-Box 2018 support thanks to miettal!
|
||||||
|
* Honda Odyssey 2021-25 support thanks to csouers and MVL!
|
||||||
|
* Honda Passport 2026 support thanks to vanillagorillaa and MVL!
|
||||||
|
|
||||||
|
Version 0.10.0 (2025-08-05)
|
||||||
========================
|
========================
|
||||||
* New driving model
|
* New driving model
|
||||||
* Lead car ground-truth fixes
|
* New training architecture
|
||||||
* Ported over VAE from the MLSIM stack
|
* Described in our CVPR paper: "Learning to Drive from a World Model"
|
||||||
* New training architecture described in CVPR paper
|
* Longitudinal MPC replaced by E2E planning from World Model in Experimental Mode
|
||||||
|
* Action from lateral MPC as training objective replaced by E2E planning from World Model
|
||||||
|
* Low-speed lead car ground-truth fixes
|
||||||
* Enable live-learned steering actuation delay
|
* Enable live-learned steering actuation delay
|
||||||
* Opt-in audio recording for dashcam video
|
* Opt-in audio recording for dashcam video
|
||||||
|
* Acura MDX 2025 support thanks to vanillagorillaa and MVL!
|
||||||
|
* Honda Accord 2023-25 support thanks to vanillagorillaa and MVL!
|
||||||
|
* Honda CR-V 2023-25 support thanks to vanillagorillaa and MVL!
|
||||||
|
* Honda Pilot 2023-25 support thanks to vanillagorillaa and MVL!
|
||||||
|
|
||||||
Version 0.9.9 (2025-05-23)
|
Version 0.9.9 (2025-05-23)
|
||||||
========================
|
========================
|
||||||
|
|||||||
+146
-267
@@ -3,345 +3,222 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
import platform
|
import platform
|
||||||
|
import shlex
|
||||||
|
import importlib
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import SCons.Errors
|
import SCons.Errors
|
||||||
|
from SCons.Defaults import _stripixes
|
||||||
|
|
||||||
SCons.Warnings.warningAsException(True)
|
SCons.Warnings.warningAsException(True)
|
||||||
|
|
||||||
# pending upstream fix - https://github.com/SCons/scons/issues/4461
|
|
||||||
#SetOption('warn', 'all')
|
|
||||||
|
|
||||||
TICI = os.path.isfile('/TICI')
|
|
||||||
AGNOS = TICI
|
|
||||||
|
|
||||||
Decider('MD5-timestamp')
|
Decider('MD5-timestamp')
|
||||||
|
|
||||||
SetOption('num_jobs', int(os.cpu_count()/2))
|
SetOption('num_jobs', max(1, int(os.cpu_count()/2)))
|
||||||
|
|
||||||
AddOption('--kaitai',
|
|
||||||
action='store_true',
|
|
||||||
help='Regenerate kaitai struct parsers')
|
|
||||||
|
|
||||||
AddOption('--asan',
|
|
||||||
action='store_true',
|
|
||||||
help='turn on ASAN')
|
|
||||||
|
|
||||||
AddOption('--ubsan',
|
|
||||||
action='store_true',
|
|
||||||
help='turn on UBSan')
|
|
||||||
|
|
||||||
AddOption('--coverage',
|
|
||||||
action='store_true',
|
|
||||||
help='build with test coverage options')
|
|
||||||
|
|
||||||
AddOption('--clazy',
|
|
||||||
action='store_true',
|
|
||||||
help='build with clazy')
|
|
||||||
|
|
||||||
AddOption('--ccflags',
|
|
||||||
action='store',
|
|
||||||
type='string',
|
|
||||||
default='',
|
|
||||||
help='pass arbitrary flags over the command line')
|
|
||||||
|
|
||||||
AddOption('--external-sconscript',
|
|
||||||
action='store',
|
|
||||||
metavar='FILE',
|
|
||||||
dest='external_sconscript',
|
|
||||||
help='add an external SConscript to the build')
|
|
||||||
|
|
||||||
AddOption('--mutation',
|
|
||||||
action='store_true',
|
|
||||||
help='generate mutation-ready code')
|
|
||||||
|
|
||||||
|
AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line')
|
||||||
|
AddOption('--verbose', action='store_true', default=False, help='show full build commands')
|
||||||
AddOption('--minimal',
|
AddOption('--minimal',
|
||||||
action='store_false',
|
action='store_false',
|
||||||
dest='extras',
|
dest='extras',
|
||||||
default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS)
|
default=os.path.exists(File('#.gitattributes').abspath), # minimal by default on release branch (where there's no LFS)
|
||||||
help='the minimum build to run openpilot. no tests, tools, etc.')
|
help='the minimum build to run openpilot. no tests, tools, etc.')
|
||||||
|
|
||||||
AddOption('--stock-ui',
|
# Detect platform
|
||||||
action='store_true',
|
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
||||||
dest='stock_ui',
|
|
||||||
default=False,
|
|
||||||
help='Build stock openpilot UI instead of sunnypilot UI')
|
|
||||||
|
|
||||||
## Architecture name breakdown (arch)
|
|
||||||
## - larch64: linux tici aarch64
|
|
||||||
## - aarch64: linux pc aarch64
|
|
||||||
## - x86_64: linux pc x64
|
|
||||||
## - Darwin: mac x64 or arm64
|
|
||||||
real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
arch = "Darwin"
|
arch = "Darwin"
|
||||||
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
elif arch == "aarch64" and os.path.isfile('/TICI'):
|
||||||
elif arch == "aarch64" and AGNOS:
|
|
||||||
arch = "larch64"
|
arch = "larch64"
|
||||||
assert arch in ["larch64", "aarch64", "x86_64", "Darwin"]
|
assert arch in [
|
||||||
|
"larch64", # linux tici arm64
|
||||||
|
"aarch64", # linux pc arm64
|
||||||
|
"x86_64", # linux pc x64
|
||||||
|
"Darwin", # macOS arm64 (x86 not supported)
|
||||||
|
]
|
||||||
|
|
||||||
lenv = {
|
pkg_names = ['bzip2', 'capnproto', 'eigen', 'ffmpeg', 'libjpeg', 'libyuv', 'ncurses', 'zeromq', 'zstd']
|
||||||
|
pkgs = [importlib.import_module(name) for name in pkg_names]
|
||||||
|
|
||||||
|
|
||||||
|
# ***** enforce a whitelist of system libraries *****
|
||||||
|
# this prevents silently relying on a 3rd party package,
|
||||||
|
# e.g. apt-installed libusb. all libraries should either
|
||||||
|
# be distributed with all Linux distros and macOS, or
|
||||||
|
# vendored in commaai/dependencies.
|
||||||
|
allowed_system_libs = {
|
||||||
|
"EGL", "GLESv2", "GL",
|
||||||
|
"Qt5Charts", "Qt5Core", "Qt5Gui", "Qt5Widgets",
|
||||||
|
"dl", "drm", "gbm", "m", "pthread",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _resolve_lib(env, name):
|
||||||
|
for d in env.Flatten(env.get('LIBPATH', [])):
|
||||||
|
p = Dir(str(d)).abspath
|
||||||
|
for ext in ('.a', '.so', '.dylib'):
|
||||||
|
f = File(os.path.join(p, f'lib{name}{ext}'))
|
||||||
|
if f.exists() or f.has_builder():
|
||||||
|
return name
|
||||||
|
if name in allowed_system_libs:
|
||||||
|
return name
|
||||||
|
raise SCons.Errors.UserError(f"Unexpected non-vendored library '{name}'")
|
||||||
|
|
||||||
|
def _libflags(target, source, env, for_signature):
|
||||||
|
libs = []
|
||||||
|
lp = env.subst('$LIBLITERALPREFIX')
|
||||||
|
for lib in env.Flatten(env.get('LIBS', [])):
|
||||||
|
if isinstance(lib, str):
|
||||||
|
if os.sep in lib or lib.startswith('#'):
|
||||||
|
libs.append(File(lib))
|
||||||
|
elif lib.startswith('-') or (lp and lib.startswith(lp)):
|
||||||
|
libs.append(lib)
|
||||||
|
else:
|
||||||
|
libs.append(_resolve_lib(env, lib))
|
||||||
|
else:
|
||||||
|
libs.append(lib)
|
||||||
|
return _stripixes(env['LIBLINKPREFIX'], libs, env['LIBLINKSUFFIX'],
|
||||||
|
env['LIBPREFIXES'], env['LIBSUFFIXES'], env, env['LIBLITERALPREFIX'])
|
||||||
|
|
||||||
|
env = Environment(
|
||||||
|
ENV={
|
||||||
"PATH": os.environ['PATH'],
|
"PATH": os.environ['PATH'],
|
||||||
"LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath],
|
|
||||||
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
|
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
|
||||||
|
|
||||||
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
|
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
|
||||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
||||||
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
||||||
}
|
},
|
||||||
|
|
||||||
rpath = lenv["LD_LIBRARY_PATH"].copy()
|
|
||||||
|
|
||||||
if arch == "larch64":
|
|
||||||
cpppath = [
|
|
||||||
"#third_party/opencl/include",
|
|
||||||
]
|
|
||||||
|
|
||||||
libpath = [
|
|
||||||
"/usr/local/lib",
|
|
||||||
"/system/vendor/lib64",
|
|
||||||
f"#third_party/acados/{arch}/lib",
|
|
||||||
]
|
|
||||||
|
|
||||||
libpath += [
|
|
||||||
"#third_party/snpe/larch64",
|
|
||||||
"#third_party/libyuv/larch64/lib",
|
|
||||||
"/usr/lib/aarch64-linux-gnu"
|
|
||||||
]
|
|
||||||
cflags = ["-DQCOM2", "-mcpu=cortex-a57"]
|
|
||||||
cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"]
|
|
||||||
rpath += ["/usr/local/lib"]
|
|
||||||
else:
|
|
||||||
cflags = []
|
|
||||||
cxxflags = []
|
|
||||||
cpppath = []
|
|
||||||
rpath += []
|
|
||||||
|
|
||||||
# MacOS
|
|
||||||
if arch == "Darwin":
|
|
||||||
libpath = [
|
|
||||||
f"#third_party/libyuv/{arch}/lib",
|
|
||||||
f"#third_party/acados/{arch}/lib",
|
|
||||||
f"{brew_prefix}/lib",
|
|
||||||
f"{brew_prefix}/opt/openssl@3.0/lib",
|
|
||||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
|
||||||
]
|
|
||||||
|
|
||||||
cflags += ["-DGL_SILENCE_DEPRECATION"]
|
|
||||||
cxxflags += ["-DGL_SILENCE_DEPRECATION"]
|
|
||||||
cpppath += [
|
|
||||||
f"{brew_prefix}/include",
|
|
||||||
f"{brew_prefix}/opt/openssl@3.0/include",
|
|
||||||
]
|
|
||||||
lenv["DYLD_LIBRARY_PATH"] = lenv["LD_LIBRARY_PATH"]
|
|
||||||
# Linux
|
|
||||||
else:
|
|
||||||
libpath = [
|
|
||||||
f"#third_party/acados/{arch}/lib",
|
|
||||||
f"#third_party/libyuv/{arch}/lib",
|
|
||||||
"/usr/lib",
|
|
||||||
"/usr/local/lib",
|
|
||||||
]
|
|
||||||
|
|
||||||
if arch == "x86_64":
|
|
||||||
libpath += [
|
|
||||||
f"#third_party/snpe/{arch}"
|
|
||||||
]
|
|
||||||
rpath += [
|
|
||||||
Dir(f"#third_party/snpe/{arch}").abspath,
|
|
||||||
]
|
|
||||||
|
|
||||||
if GetOption('asan'):
|
|
||||||
ccflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
|
|
||||||
ldflags = ["-fsanitize=address"]
|
|
||||||
elif GetOption('ubsan'):
|
|
||||||
ccflags = ["-fsanitize=undefined"]
|
|
||||||
ldflags = ["-fsanitize=undefined"]
|
|
||||||
else:
|
|
||||||
ccflags = []
|
|
||||||
ldflags = []
|
|
||||||
|
|
||||||
# no --as-needed on mac linker
|
|
||||||
if arch != "Darwin":
|
|
||||||
ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"]
|
|
||||||
|
|
||||||
if not GetOption('stock_ui'):
|
|
||||||
cflags += ["-DSUNNYPILOT"]
|
|
||||||
cxxflags += ["-DSUNNYPILOT"]
|
|
||||||
|
|
||||||
ccflags_option = GetOption('ccflags')
|
|
||||||
if ccflags_option:
|
|
||||||
ccflags += ccflags_option.split(' ')
|
|
||||||
|
|
||||||
env = Environment(
|
|
||||||
ENV=lenv,
|
|
||||||
CCFLAGS=[
|
CCFLAGS=[
|
||||||
"-g",
|
"-g",
|
||||||
"-fPIC",
|
"-fPIC",
|
||||||
"-O2",
|
"-O2",
|
||||||
"-Wunused",
|
"-Wunused",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-Wshadow",
|
"-Wshadow" if arch in ("Darwin", "larch64") else "-Wshadow=local",
|
||||||
"-Wno-unknown-warning-option",
|
"-Wno-unknown-warning-option",
|
||||||
"-Wno-inconsistent-missing-override",
|
"-Wno-inconsistent-missing-override",
|
||||||
"-Wno-c99-designator",
|
"-Wno-c99-designator",
|
||||||
"-Wno-reorder-init-list",
|
"-Wno-reorder-init-list",
|
||||||
"-Wno-vla-cxx-extension",
|
"-Wno-vla-cxx-extension",
|
||||||
] + cflags + ccflags,
|
],
|
||||||
|
CFLAGS=["-std=gnu11"],
|
||||||
CPPPATH=cpppath + [
|
CXXFLAGS=["-std=c++1z"],
|
||||||
|
CPPPATH=[
|
||||||
"#",
|
"#",
|
||||||
|
"#msgq",
|
||||||
|
"#third_party",
|
||||||
|
"#third_party/json11",
|
||||||
|
"#third_party/linux/include",
|
||||||
"#third_party/acados/include",
|
"#third_party/acados/include",
|
||||||
"#third_party/acados/include/blasfeo/include",
|
"#third_party/acados/include/blasfeo/include",
|
||||||
"#third_party/acados/include/hpipm/include",
|
"#third_party/acados/include/hpipm/include",
|
||||||
"#third_party/catch2/include",
|
"#third_party/catch2/include",
|
||||||
"#third_party/libyuv/include",
|
[x.INCLUDE_DIR for x in pkgs],
|
||||||
"#third_party/json11",
|
|
||||||
"#third_party/linux/include",
|
|
||||||
"#third_party/snpe/include",
|
|
||||||
"#third_party",
|
|
||||||
"#msgq",
|
|
||||||
],
|
],
|
||||||
|
LIBPATH=[
|
||||||
CC='clang',
|
"#common",
|
||||||
CXX='clang++',
|
|
||||||
LINKFLAGS=ldflags,
|
|
||||||
|
|
||||||
RPATH=rpath,
|
|
||||||
|
|
||||||
CFLAGS=["-std=gnu11"] + cflags,
|
|
||||||
CXXFLAGS=["-std=c++1z"] + cxxflags,
|
|
||||||
LIBPATH=libpath + [
|
|
||||||
"#msgq_repo",
|
"#msgq_repo",
|
||||||
"#third_party",
|
"#third_party",
|
||||||
"#selfdrive/pandad",
|
"#selfdrive/pandad",
|
||||||
"#common",
|
|
||||||
"#rednose/helpers",
|
"#rednose/helpers",
|
||||||
|
f"#third_party/acados/{arch}/lib",
|
||||||
|
[x.LIB_DIR for x in pkgs],
|
||||||
],
|
],
|
||||||
|
RPATH=[],
|
||||||
CYTHONCFILESUFFIX=".cpp",
|
CYTHONCFILESUFFIX=".cpp",
|
||||||
COMPILATIONDB_USE_ABSPATH=True,
|
COMPILATIONDB_USE_ABSPATH=True,
|
||||||
REDNOSE_ROOT="#",
|
REDNOSE_ROOT="#",
|
||||||
tools=["default", "cython", "compilation_db", "rednose_filter"],
|
tools=["default", "cython", "compilation_db", "rednose_filter"],
|
||||||
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
|
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
|
||||||
)
|
)
|
||||||
|
if arch != "larch64":
|
||||||
|
env['_LIBFLAGS'] = _libflags
|
||||||
|
|
||||||
if arch == "Darwin":
|
# Arch-specific flags and paths
|
||||||
# RPATH is not supported on macOS, instead use the linker flags
|
if arch == "larch64":
|
||||||
darwin_rpath_link_flags = [f"-Wl,-rpath,{path}" for path in env["RPATH"]]
|
env["CC"] = "clang"
|
||||||
env["LINKFLAGS"] += darwin_rpath_link_flags
|
env["CXX"] = "clang++"
|
||||||
|
env.Append(LIBPATH=[
|
||||||
|
"/usr/lib/aarch64-linux-gnu",
|
||||||
|
])
|
||||||
|
arch_flags = ["-D__TICI__", "-mcpu=cortex-a57"]
|
||||||
|
env.Append(CCFLAGS=arch_flags)
|
||||||
|
env.Append(CXXFLAGS=arch_flags)
|
||||||
|
elif arch == "Darwin":
|
||||||
|
env.Append(LIBPATH=[
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
||||||
|
])
|
||||||
|
env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||||
|
env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||||
|
|
||||||
env.CompilationDatabase('compile_commands.json')
|
_extra_cc = shlex.split(GetOption('ccflags') or '')
|
||||||
|
if _extra_cc:
|
||||||
|
env.Append(CCFLAGS=_extra_cc)
|
||||||
|
|
||||||
# Setup cache dir
|
# no --as-needed on mac linker
|
||||||
default_cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache'
|
if arch != "Darwin":
|
||||||
cache_dir = ARGUMENTS.get('cache_dir', default_cache_dir)
|
env.Append(LINKFLAGS=["-Wl,--as-needed", "-Wl,--no-undefined"])
|
||||||
CacheDir(cache_dir)
|
|
||||||
Clean(["."], cache_dir)
|
|
||||||
|
|
||||||
|
# Shorter build output: show brief descriptions instead of full commands.
|
||||||
|
# Full command lines are still printed on failure by scons.
|
||||||
|
if not GetOption('verbose'):
|
||||||
|
for action, short in (
|
||||||
|
("CC", "CC"),
|
||||||
|
("CXX", "CXX"),
|
||||||
|
("LINK", "LINK"),
|
||||||
|
("SHCC", "CC"),
|
||||||
|
("SHCXX", "CXX"),
|
||||||
|
("SHLINK", "LINK"),
|
||||||
|
("AR", "AR"),
|
||||||
|
("RANLIB", "RANLIB"),
|
||||||
|
("AS", "AS"),
|
||||||
|
):
|
||||||
|
env[f"{action}COMSTR"] = f" [{short}] $TARGET"
|
||||||
|
|
||||||
|
# progress output
|
||||||
node_interval = 5
|
node_interval = 5
|
||||||
node_count = 0
|
node_count = 0
|
||||||
def progress_function(node):
|
def progress_function(node):
|
||||||
global node_count
|
global node_count
|
||||||
node_count += node_interval
|
node_count += node_interval
|
||||||
sys.stderr.write("progress: %d\n" % node_count)
|
sys.stderr.write("progress: %d\n" % node_count)
|
||||||
|
|
||||||
if os.environ.get('SCONS_PROGRESS'):
|
if os.environ.get('SCONS_PROGRESS'):
|
||||||
Progress(progress_function, interval=node_interval)
|
Progress(progress_function, interval=node_interval)
|
||||||
|
|
||||||
# Cython build environment
|
# ********** Cython build environment **********
|
||||||
py_include = sysconfig.get_paths()['include']
|
|
||||||
envCython = env.Clone()
|
envCython = env.Clone()
|
||||||
envCython["CPPPATH"] += [py_include, np.get_include()]
|
envCython["CPPPATH"] += [sysconfig.get_paths()['include'], np.get_include()]
|
||||||
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-shadow", "-Wno-deprecated-declarations"]
|
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-cpp", "-Wno-shadow", "-Wno-deprecated-declarations"]
|
||||||
envCython["CCFLAGS"].remove("-Werror")
|
envCython["CCFLAGS"].remove("-Werror")
|
||||||
|
|
||||||
envCython["LIBS"] = []
|
envCython["LIBS"] = []
|
||||||
if arch == "Darwin":
|
if arch == "Darwin":
|
||||||
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"] + darwin_rpath_link_flags
|
envCython["LINKFLAGS"] = env["LINKFLAGS"] + ["-bundle", "-undefined", "dynamic_lookup"]
|
||||||
else:
|
else:
|
||||||
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
||||||
|
|
||||||
np_version = SCons.Script.Value(np.__version__)
|
np_version = SCons.Script.Value(np.__version__)
|
||||||
Export('envCython', 'np_version')
|
Export('envCython', 'np_version')
|
||||||
|
|
||||||
# Qt build environment
|
Export('env', 'arch')
|
||||||
qt_env = env.Clone()
|
|
||||||
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "DBus", "Xml"]
|
|
||||||
|
|
||||||
qt_libs = []
|
# Setup cache dir
|
||||||
if arch == "Darwin":
|
cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache'
|
||||||
qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5"
|
CacheDir(cache_dir)
|
||||||
qt_dirs = [
|
Clean(["."], cache_dir)
|
||||||
os.path.join(qt_env['QTDIR'], "include"),
|
|
||||||
]
|
|
||||||
qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules]
|
|
||||||
qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")]
|
|
||||||
qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"]
|
|
||||||
qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin"))
|
|
||||||
else:
|
|
||||||
qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip()
|
|
||||||
qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip()
|
|
||||||
|
|
||||||
qt_env['QTDIR'] = qt_install_prefix
|
# ********** start building stuff **********
|
||||||
qt_dirs = [
|
|
||||||
f"{qt_install_headers}",
|
|
||||||
]
|
|
||||||
|
|
||||||
qt_gui_path = os.path.join(qt_install_headers, "QtGui")
|
|
||||||
qt_gui_dirs = [d for d in os.listdir(qt_gui_path) if os.path.isdir(os.path.join(qt_gui_path, d))]
|
|
||||||
qt_dirs += [f"{qt_install_headers}/QtGui/{qt_gui_dirs[0]}/QtGui", ] if qt_gui_dirs else []
|
|
||||||
qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules]
|
|
||||||
|
|
||||||
qt_libs = [f"Qt5{m}" for m in qt_modules]
|
|
||||||
if arch == "larch64":
|
|
||||||
qt_libs += ["GLESv2", "wayland-client"]
|
|
||||||
qt_env.PrependENVPath('PATH', Dir("#third_party/qt5/larch64/bin/").abspath)
|
|
||||||
elif arch != "Darwin":
|
|
||||||
qt_libs += ["GL"]
|
|
||||||
qt_env['QT3DIR'] = qt_env['QTDIR']
|
|
||||||
qt_env.Tool('qt3')
|
|
||||||
|
|
||||||
qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"]
|
|
||||||
qt_flags = [
|
|
||||||
"-D_REENTRANT",
|
|
||||||
"-DQT_NO_DEBUG",
|
|
||||||
"-DQT_WIDGETS_LIB",
|
|
||||||
"-DQT_GUI_LIB",
|
|
||||||
"-DQT_CORE_LIB",
|
|
||||||
"-DQT_MESSAGELOGCONTEXT",
|
|
||||||
]
|
|
||||||
qt_env['CXXFLAGS'] += qt_flags
|
|
||||||
qt_env['LIBPATH'] += ['#selfdrive/ui', ]
|
|
||||||
qt_env['LIBS'] = qt_libs
|
|
||||||
|
|
||||||
if GetOption("clazy"):
|
|
||||||
checks = [
|
|
||||||
"level0",
|
|
||||||
"level1",
|
|
||||||
"no-range-loop",
|
|
||||||
"no-non-pod-global-static",
|
|
||||||
]
|
|
||||||
qt_env['CXX'] = 'clazy'
|
|
||||||
qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0]
|
|
||||||
qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks)
|
|
||||||
|
|
||||||
Export('env', 'qt_env', 'arch', 'real_arch')
|
|
||||||
|
|
||||||
# Build common module
|
# Build common module
|
||||||
SConscript(['common/SConscript'])
|
SConscript(['common/SConscript'])
|
||||||
Import('_common', '_gpucommon')
|
Import('_common')
|
||||||
|
|
||||||
common = [_common, 'json11', 'zmq']
|
common = [_common, 'json11', 'zmq']
|
||||||
gpucommon = [_gpucommon]
|
Export('common')
|
||||||
|
|
||||||
Export('common', 'gpucommon')
|
|
||||||
|
|
||||||
# Build messaging (cereal + msgq + socketmaster + their dependencies)
|
# Build messaging (cereal + msgq + socketmaster + their dependencies)
|
||||||
# Enable swaglog include in submodules
|
# Enable swaglog include in submodules
|
||||||
env_swaglog = env.Clone()
|
env_swaglog = env.Clone()
|
||||||
env_swaglog['CXXFLAGS'].append('-DSWAGLOG="\\"common/swaglog.h\\""')
|
env_swaglog['CXXFLAGS'].append('-DSWAGLOG="\\"common/swaglog.h\\""')
|
||||||
SConscript(['msgq_repo/SConscript'], exports={'env': env_swaglog})
|
SConscript(['msgq_repo/SConscript'], exports={'env': env_swaglog})
|
||||||
SConscript(['opendbc_repo/SConscript'], exports={'env': env_swaglog})
|
|
||||||
|
|
||||||
SConscript(['cereal/SConscript'])
|
SConscript(['cereal/SConscript'])
|
||||||
|
|
||||||
@@ -358,14 +235,8 @@ SConscript(['rednose/SConscript'])
|
|||||||
|
|
||||||
# Build system services
|
# Build system services
|
||||||
SConscript([
|
SConscript([
|
||||||
'system/ubloxd/SConscript',
|
|
||||||
'system/loggerd/SConscript',
|
'system/loggerd/SConscript',
|
||||||
])
|
])
|
||||||
if arch != "Darwin":
|
|
||||||
SConscript([
|
|
||||||
'system/logcatd/SConscript',
|
|
||||||
'system/proclogd/SConscript',
|
|
||||||
])
|
|
||||||
|
|
||||||
if arch == "larch64":
|
if arch == "larch64":
|
||||||
SConscript(['system/camerad/SConscript'])
|
SConscript(['system/camerad/SConscript'])
|
||||||
@@ -373,15 +244,23 @@ if arch == "larch64":
|
|||||||
# Build openpilot
|
# Build openpilot
|
||||||
SConscript(['third_party/SConscript'])
|
SConscript(['third_party/SConscript'])
|
||||||
|
|
||||||
SConscript(['selfdrive/SConscript'])
|
# Build selfdrive
|
||||||
|
SConscript([
|
||||||
|
'selfdrive/pandad/SConscript',
|
||||||
|
'selfdrive/controls/lib/lateral_mpc_lib/SConscript',
|
||||||
|
'selfdrive/controls/lib/longitudinal_mpc_lib/SConscript',
|
||||||
|
'selfdrive/locationd/SConscript',
|
||||||
|
'selfdrive/modeld/SConscript',
|
||||||
|
'selfdrive/ui/SConscript',
|
||||||
|
])
|
||||||
|
|
||||||
SConscript(['sunnypilot/SConscript'])
|
# Build tools
|
||||||
|
|
||||||
if Dir('#tools/cabana/').exists() and GetOption('extras'):
|
|
||||||
SConscript(['tools/replay/SConscript'])
|
|
||||||
if arch != "larch64":
|
if arch != "larch64":
|
||||||
SConscript(['tools/cabana/SConscript'])
|
SConscript([
|
||||||
|
'tools/replay/SConscript',
|
||||||
|
'tools/cabana/SConscript',
|
||||||
|
'tools/jotpluggler/SConscript',
|
||||||
|
])
|
||||||
|
|
||||||
external_sconscript = GetOption('external_sconscript')
|
|
||||||
if external_sconscript:
|
env.CompilationDatabase('compile_commands.json')
|
||||||
SConscript([external_sconscript])
|
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ cereal = env.Library('cereal', [f'gen/cpp/{s}.c++' for s in schema_files])
|
|||||||
|
|
||||||
# Build messaging
|
# Build messaging
|
||||||
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
|
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
|
||||||
env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc'], LIBS=[msgq, common, 'pthread'])
|
env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc', 'messaging/bridge_zmq.cc'], LIBS=[msgq, common, 'pthread'])
|
||||||
|
|
||||||
socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc'])
|
socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc'])
|
||||||
|
|
||||||
|
|||||||
+9
-207
@@ -10,229 +10,31 @@ $Cxx.namespace("cereal");
|
|||||||
# DO rename the structs
|
# DO rename the structs
|
||||||
# DON'T change the identifier (e.g. @0x81c2f05a394cf4af)
|
# DON'T change the identifier (e.g. @0x81c2f05a394cf4af)
|
||||||
|
|
||||||
struct ModularAssistiveDrivingSystem {
|
struct CustomReserved0 @0x81c2f05a394cf4af {
|
||||||
state @0 :ModularAssistiveDrivingSystemState;
|
|
||||||
enabled @1 :Bool;
|
|
||||||
active @2 :Bool;
|
|
||||||
available @3 :Bool;
|
|
||||||
|
|
||||||
enum ModularAssistiveDrivingSystemState {
|
|
||||||
disabled @0;
|
|
||||||
paused @1;
|
|
||||||
enabled @2;
|
|
||||||
softDisabling @3;
|
|
||||||
overriding @4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SelfdriveStateSP @0x81c2f05a394cf4af {
|
struct CustomReserved1 @0xaedffd8f31e7b55d {
|
||||||
mads @0 :ModularAssistiveDrivingSystem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ModelManagerSP @0xaedffd8f31e7b55d {
|
struct CustomReserved2 @0xf35cc4560bbf6ec2 {
|
||||||
activeBundle @0 :ModelBundle;
|
|
||||||
selectedBundle @1 :ModelBundle;
|
|
||||||
availableBundles @2 :List(ModelBundle);
|
|
||||||
|
|
||||||
struct DownloadUri {
|
|
||||||
uri @0 :Text;
|
|
||||||
sha256 @1 :Text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DownloadStatus {
|
struct CustomReserved3 @0xda96579883444c35 {
|
||||||
notDownloading @0;
|
|
||||||
downloading @1;
|
|
||||||
downloaded @2;
|
|
||||||
cached @3;
|
|
||||||
failed @4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DownloadProgress {
|
struct CustomReserved4 @0x80ae746ee2596b11 {
|
||||||
status @0 :DownloadStatus;
|
|
||||||
progress @1 :Float32;
|
|
||||||
eta @2 :UInt32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Artifact {
|
struct CustomReserved5 @0xa5cd762cd951a455 {
|
||||||
fileName @0 :Text;
|
|
||||||
downloadUri @1 :DownloadUri;
|
|
||||||
downloadProgress @2 :DownloadProgress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Model {
|
struct CustomReserved6 @0xf98d843bfd7004a3 {
|
||||||
type @0 :Type;
|
|
||||||
artifact @1 :Artifact; # Main artifact
|
|
||||||
metadata @2 :Artifact; # Metadata artifact
|
|
||||||
|
|
||||||
enum Type {
|
|
||||||
supercombo @0;
|
|
||||||
navigation @1;
|
|
||||||
vision @2;
|
|
||||||
policy @3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Runner {
|
struct CustomReserved7 @0xb86e6369214c01c8 {
|
||||||
snpe @0;
|
|
||||||
tinygrad @1;
|
|
||||||
stock @2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Override {
|
struct CustomReserved8 @0xf416ec09499d9d19 {
|
||||||
key @0 :Text;
|
|
||||||
value @1 :Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ModelBundle {
|
|
||||||
index @0 :UInt32;
|
|
||||||
internalName @1 :Text;
|
|
||||||
displayName @2 :Text;
|
|
||||||
models @3 :List(Model);
|
|
||||||
status @4 :DownloadStatus;
|
|
||||||
generation @5 :UInt32;
|
|
||||||
environment @6 :Text;
|
|
||||||
runner @7 :Runner;
|
|
||||||
is20hz @8 :Bool;
|
|
||||||
ref @9 :Text;
|
|
||||||
minimumSelectorVersion @10 :UInt32;
|
|
||||||
overrides @11 :List(Override);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
|
|
||||||
dec @0 :DynamicExperimentalControl;
|
|
||||||
|
|
||||||
struct DynamicExperimentalControl {
|
|
||||||
state @0 :DynamicExperimentalControlState;
|
|
||||||
enabled @1 :Bool;
|
|
||||||
active @2 :Bool;
|
|
||||||
|
|
||||||
enum DynamicExperimentalControlState {
|
|
||||||
acc @0;
|
|
||||||
blended @1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OnroadEventSP @0xda96579883444c35 {
|
|
||||||
events @0 :List(Event);
|
|
||||||
|
|
||||||
struct Event {
|
|
||||||
name @0 :EventName;
|
|
||||||
|
|
||||||
# event types
|
|
||||||
enable @1 :Bool;
|
|
||||||
noEntry @2 :Bool;
|
|
||||||
warning @3 :Bool; # alerts presented only when enabled or soft disabling
|
|
||||||
userDisable @4 :Bool;
|
|
||||||
softDisable @5 :Bool;
|
|
||||||
immediateDisable @6 :Bool;
|
|
||||||
preEnable @7 :Bool;
|
|
||||||
permanent @8 :Bool; # alerts presented regardless of openpilot state
|
|
||||||
overrideLateral @10 :Bool;
|
|
||||||
overrideLongitudinal @9 :Bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum EventName {
|
|
||||||
lkasEnable @0;
|
|
||||||
lkasDisable @1;
|
|
||||||
manualSteeringRequired @2;
|
|
||||||
manualLongitudinalRequired @3;
|
|
||||||
silentLkasEnable @4;
|
|
||||||
silentLkasDisable @5;
|
|
||||||
silentBrakeHold @6;
|
|
||||||
silentWrongGear @7;
|
|
||||||
silentReverseGear @8;
|
|
||||||
silentDoorOpen @9;
|
|
||||||
silentSeatbeltNotLatched @10;
|
|
||||||
silentParkBrake @11;
|
|
||||||
controlsMismatchLateral @12;
|
|
||||||
hyundaiRadarTracksConfirmed @13;
|
|
||||||
experimentalModeSwitched @14;
|
|
||||||
wrongCarModeAlertOnly @15;
|
|
||||||
pedalPressedAlertOnly @16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CarParamsSP @0x80ae746ee2596b11 {
|
|
||||||
flags @0 :UInt32; # flags for car specific quirks in sunnypilot
|
|
||||||
safetyParam @1 : Int16; # flags for sunnypilot's custom safety flags
|
|
||||||
|
|
||||||
neuralNetworkLateralControl @2 :NeuralNetworkLateralControl;
|
|
||||||
|
|
||||||
struct NeuralNetworkLateralControl {
|
|
||||||
model @0 :Model;
|
|
||||||
fuzzyFingerprint @1 :Bool;
|
|
||||||
|
|
||||||
struct Model {
|
|
||||||
path @0 :Text;
|
|
||||||
name @1 :Text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CarControlSP @0xa5cd762cd951a455 {
|
|
||||||
mads @0 :ModularAssistiveDrivingSystem;
|
|
||||||
params @1 :List(Param);
|
|
||||||
|
|
||||||
struct Param {
|
|
||||||
key @0 :Text;
|
|
||||||
value @1 :Text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BackupManagerSP @0xf98d843bfd7004a3 {
|
|
||||||
backupStatus @0 :Status;
|
|
||||||
restoreStatus @1 :Status;
|
|
||||||
backupProgress @2 :Float32;
|
|
||||||
restoreProgress @3 :Float32;
|
|
||||||
lastError @4 :Text;
|
|
||||||
currentBackup @5 :BackupInfo;
|
|
||||||
backupHistory @6 :List(BackupInfo);
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
idle @0;
|
|
||||||
inProgress @1;
|
|
||||||
completed @2;
|
|
||||||
failed @3;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Version {
|
|
||||||
major @0 :UInt16;
|
|
||||||
minor @1 :UInt16;
|
|
||||||
patch @2 :UInt16;
|
|
||||||
build @3 :UInt16;
|
|
||||||
branch @4 :Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MetadataEntry {
|
|
||||||
key @0 :Text;
|
|
||||||
value @1 :Text;
|
|
||||||
tags @2 :List(Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BackupInfo {
|
|
||||||
deviceId @0 :Text;
|
|
||||||
version @1 :UInt32;
|
|
||||||
config @2 :Text;
|
|
||||||
isEncrypted @3 :Bool;
|
|
||||||
createdAt @4 :Text; # ISO timestamp
|
|
||||||
updatedAt @5 :Text; # ISO timestamp
|
|
||||||
sunnypilotVersion @6 :Version;
|
|
||||||
backupMetadata @7 :List(MetadataEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CarStateSP @0xb86e6369214c01c8 {
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LiveMapDataSP @0xf416ec09499d9d19 {
|
|
||||||
speedLimitValid @0 :Bool;
|
|
||||||
speedLimit @1 :Float32;
|
|
||||||
speedLimitAheadValid @2 :Bool;
|
|
||||||
speedLimitAhead @3 :Float32;
|
|
||||||
speedLimitAheadDistance @4 :Float32;
|
|
||||||
roadName @5 :Text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CustomReserved9 @0xa1680744031fdb2d {
|
struct CustomReserved9 @0xa1680744031fdb2d {
|
||||||
|
|||||||
+71
-30
@@ -87,6 +87,8 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
|||||||
laneChange @50;
|
laneChange @50;
|
||||||
lowMemory @51;
|
lowMemory @51;
|
||||||
stockAeb @52;
|
stockAeb @52;
|
||||||
|
stockLkas @98;
|
||||||
|
lateralManeuver @99;
|
||||||
ldw @53;
|
ldw @53;
|
||||||
carUnrecognized @54;
|
carUnrecognized @54;
|
||||||
invalidLkasSetting @55;
|
invalidLkasSetting @55;
|
||||||
@@ -127,7 +129,9 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
|||||||
espActive @90;
|
espActive @90;
|
||||||
personalityChanged @91;
|
personalityChanged @91;
|
||||||
aeb @92;
|
aeb @92;
|
||||||
userFlag @95;
|
userBookmark @95;
|
||||||
|
excessiveActuation @96;
|
||||||
|
audioFeedback @97;
|
||||||
|
|
||||||
soundsUnavailableDEPRECATED @47;
|
soundsUnavailableDEPRECATED @47;
|
||||||
}
|
}
|
||||||
@@ -492,12 +496,12 @@ struct DeviceState @0xa4d8b5af2aa492eb {
|
|||||||
gpuTempC @27 :List(Float32);
|
gpuTempC @27 :List(Float32);
|
||||||
dspTempC @49 :Float32;
|
dspTempC @49 :Float32;
|
||||||
memoryTempC @28 :Float32;
|
memoryTempC @28 :Float32;
|
||||||
nvmeTempC @35 :List(Float32);
|
|
||||||
modemTempC @36 :List(Float32);
|
modemTempC @36 :List(Float32);
|
||||||
pmicTempC @39 :List(Float32);
|
pmicTempC @39 :List(Float32);
|
||||||
intakeTempC @46 :Float32;
|
intakeTempC @46 :Float32;
|
||||||
exhaustTempC @47 :Float32;
|
exhaustTempC @47 :Float32;
|
||||||
caseTempC @48 :Float32;
|
gnssTempC @48 :Float32;
|
||||||
|
bottomSocTempC @50 :Float32;
|
||||||
maxTempC @44 :Float32; # max of other temps, used to control fan
|
maxTempC @44 :Float32; # max of other temps, used to control fan
|
||||||
thermalZones @38 :List(ThermalZone);
|
thermalZones @38 :List(ThermalZone);
|
||||||
thermalStatus @14 :ThermalStatus;
|
thermalStatus @14 :ThermalStatus;
|
||||||
@@ -568,6 +572,7 @@ struct DeviceState @0xa4d8b5af2aa492eb {
|
|||||||
chargingDisabledDEPRECATED @18 :Bool;
|
chargingDisabledDEPRECATED @18 :Bool;
|
||||||
usbOnlineDEPRECATED @12 :Bool;
|
usbOnlineDEPRECATED @12 :Bool;
|
||||||
ambientTempCDEPRECATED @30 :Float32;
|
ambientTempCDEPRECATED @30 :Float32;
|
||||||
|
nvmeTempCDEPRECATED @35 :List(Float32);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PandaState @0xa7649e2575e4591e {
|
struct PandaState @0xa7649e2575e4591e {
|
||||||
@@ -583,13 +588,13 @@ struct PandaState @0xa7649e2575e4591e {
|
|||||||
heartbeatLost @22 :Bool;
|
heartbeatLost @22 :Bool;
|
||||||
interruptLoad @25 :Float32;
|
interruptLoad @25 :Float32;
|
||||||
fanPower @28 :UInt8;
|
fanPower @28 :UInt8;
|
||||||
fanStallCount @34 :UInt8;
|
|
||||||
|
|
||||||
spiChecksumErrorCount @33 :UInt16;
|
spiErrorCount @33 :UInt16;
|
||||||
|
|
||||||
harnessStatus @21 :HarnessStatus;
|
harnessStatus @21 :HarnessStatus;
|
||||||
sbu1Voltage @35 :Float32;
|
sbu1Voltage @35 :Float32;
|
||||||
sbu2Voltage @36 :Float32;
|
sbu2Voltage @36 :Float32;
|
||||||
|
soundOutputLevel @37 :UInt16;
|
||||||
|
|
||||||
# can health
|
# can health
|
||||||
canState0 @29 :PandaCanState;
|
canState0 @29 :PandaCanState;
|
||||||
@@ -608,6 +613,11 @@ struct PandaState @0xa7649e2575e4591e {
|
|||||||
voltage @0 :UInt32;
|
voltage @0 :UInt32;
|
||||||
current @1 :UInt32;
|
current @1 :UInt32;
|
||||||
|
|
||||||
|
# these fields are not used by openpilot, but they're
|
||||||
|
# reserved for forks building alternate experiences.
|
||||||
|
controlsAllowedRESERVED1 @38 :Bool;
|
||||||
|
controlsAllowedRESERVED2 @39 :Bool;
|
||||||
|
|
||||||
enum FaultStatus {
|
enum FaultStatus {
|
||||||
none @0;
|
none @0;
|
||||||
faultTemp @1;
|
faultTemp @1;
|
||||||
@@ -712,6 +722,7 @@ struct PandaState @0xa7649e2575e4591e {
|
|||||||
usbPowerModeDEPRECATED @12 :PeripheralState.UsbPowerModeDEPRECATED;
|
usbPowerModeDEPRECATED @12 :PeripheralState.UsbPowerModeDEPRECATED;
|
||||||
safetyParamDEPRECATED @20 :Int16;
|
safetyParamDEPRECATED @20 :Int16;
|
||||||
safetyParam2DEPRECATED @26 :UInt32;
|
safetyParam2DEPRECATED @26 :UInt32;
|
||||||
|
fanStallCountDEPRECATED @34 :UInt8;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PeripheralState {
|
struct PeripheralState {
|
||||||
@@ -916,6 +927,8 @@ struct ControlsState @0x97ff69c53601abf1 {
|
|||||||
saturated @7 :Bool;
|
saturated @7 :Bool;
|
||||||
actualLateralAccel @9 :Float32;
|
actualLateralAccel @9 :Float32;
|
||||||
desiredLateralAccel @10 :Float32;
|
desiredLateralAccel @10 :Float32;
|
||||||
|
desiredLateralJerk @11 :Float32;
|
||||||
|
version @12 :Int32;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LateralLQRState {
|
struct LateralLQRState {
|
||||||
@@ -1234,6 +1247,10 @@ struct DriverAssistance {
|
|||||||
# FCW, AEB, etc. will go here
|
# FCW, AEB, etc. will go here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LateralManeuverPlan {
|
||||||
|
desiredCurvature @0 :Float32; # 1/m
|
||||||
|
}
|
||||||
|
|
||||||
struct LongitudinalPlan @0xe00b5b3eba12876c {
|
struct LongitudinalPlan @0xe00b5b3eba12876c {
|
||||||
modelMonoTime @9 :UInt64;
|
modelMonoTime @9 :UInt64;
|
||||||
hasLead @7 :Bool;
|
hasLead @7 :Bool;
|
||||||
@@ -1419,6 +1436,8 @@ struct LivePose {
|
|||||||
posenetOK @5 :Bool = false;
|
posenetOK @5 :Bool = false;
|
||||||
sensorsOK @6 :Bool = false;
|
sensorsOK @6 :Bool = false;
|
||||||
|
|
||||||
|
timestamp @8 :UInt64;
|
||||||
|
|
||||||
debugFilterState @7 :FilterState;
|
debugFilterState @7 :FilterState;
|
||||||
|
|
||||||
struct XYZMeasurement {
|
struct XYZMeasurement {
|
||||||
@@ -1473,6 +1492,11 @@ struct ProcLog {
|
|||||||
|
|
||||||
cmdline @15 :List(Text);
|
cmdline @15 :List(Text);
|
||||||
exe @16 :Text;
|
exe @16 :Text;
|
||||||
|
|
||||||
|
# from /proc/<pid>/smaps_rollup (proportional/private memory)
|
||||||
|
memPss @17 :UInt64; # Pss — shared pages split by mapper count
|
||||||
|
memPssAnon @18 :UInt64; # Pss_Anon — private anonymous (heap, stack)
|
||||||
|
memPssShmem @19 :UInt64; # Pss_Shmem — proportional MSGQ/tmpfs share
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CPUTimes {
|
struct CPUTimes {
|
||||||
@@ -2144,13 +2168,10 @@ struct Joystick {
|
|||||||
struct DriverStateV2 {
|
struct DriverStateV2 {
|
||||||
frameId @0 :UInt32;
|
frameId @0 :UInt32;
|
||||||
modelExecutionTime @1 :Float32;
|
modelExecutionTime @1 :Float32;
|
||||||
dspExecutionTimeDEPRECATED @2 :Float32;
|
|
||||||
gpuExecutionTime @8 :Float32;
|
gpuExecutionTime @8 :Float32;
|
||||||
rawPredictions @3 :Data;
|
rawPredictions @3 :Data;
|
||||||
|
|
||||||
poorVisionProb @4 :Float32;
|
|
||||||
wheelOnRightProb @5 :Float32;
|
wheelOnRightProb @5 :Float32;
|
||||||
|
|
||||||
leftDriverData @6 :DriverData;
|
leftDriverData @6 :DriverData;
|
||||||
rightDriverData @7 :DriverData;
|
rightDriverData @7 :DriverData;
|
||||||
|
|
||||||
@@ -2160,15 +2181,21 @@ struct DriverStateV2 {
|
|||||||
facePosition @2 :List(Float32);
|
facePosition @2 :List(Float32);
|
||||||
facePositionStd @3 :List(Float32);
|
facePositionStd @3 :List(Float32);
|
||||||
faceProb @4 :Float32;
|
faceProb @4 :Float32;
|
||||||
leftEyeProb @5 :Float32;
|
eyesVisibleProb @14 :Float32;
|
||||||
rightEyeProb @6 :Float32;
|
eyesClosedProb @15 :Float32;
|
||||||
leftBlinkProb @7 :Float32;
|
phoneProb @13 :Float32;
|
||||||
rightBlinkProb @8 :Float32;
|
leftEyeProbDEPRECATED @5 :Float32;
|
||||||
sunglassesProb @9 :Float32;
|
rightEyeProbDEPRECATED @6 :Float32;
|
||||||
occludedProb @10 :Float32;
|
leftBlinkProbDEPRECATED @7 :Float32;
|
||||||
readyProb @11 :List(Float32);
|
rightBlinkProbDEPRECATED @8 :Float32;
|
||||||
notReadyProb @12 :List(Float32);
|
sunglassesProbDEPRECATED @9 :Float32;
|
||||||
|
notReadyProbDEPRECATED @12 :List(Float32);
|
||||||
|
occludedProbDEPRECATED @10 :Float32;
|
||||||
|
readyProbDEPRECATED @11 :List(Float32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dspExecutionTimeDEPRECATED @2 :Float32;
|
||||||
|
poorVisionProbDEPRECATED @4 :Float32;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
|
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
|
||||||
@@ -2220,7 +2247,10 @@ struct DriverMonitoringState @0xb83cda094a1da284 {
|
|||||||
hiStdCount @14 :UInt32;
|
hiStdCount @14 :UInt32;
|
||||||
isActiveMode @16 :Bool;
|
isActiveMode @16 :Bool;
|
||||||
isRHD @4 :Bool;
|
isRHD @4 :Bool;
|
||||||
|
uncertainCount @19 :UInt32;
|
||||||
|
|
||||||
|
phoneProbOffsetDEPRECATED @20 :Float32;
|
||||||
|
phoneProbValidCountDEPRECATED @21 :UInt32;
|
||||||
isPreviewDEPRECATED @15 :Bool;
|
isPreviewDEPRECATED @15 :Bool;
|
||||||
rhdCheckedDEPRECATED @5 :Bool;
|
rhdCheckedDEPRECATED @5 :Bool;
|
||||||
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
|
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
|
||||||
@@ -2467,7 +2497,7 @@ struct DebugAlert {
|
|||||||
alertText2 @1 :Text;
|
alertText2 @1 :Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UserFlag {
|
struct UserBookmark @0xfe346a9de48d9b50 {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SoundPressure @0xdc24138990726023 {
|
struct SoundPressure @0xdc24138990726023 {
|
||||||
@@ -2485,6 +2515,11 @@ struct AudioData {
|
|||||||
sampleRate @1 :UInt32;
|
sampleRate @1 :UInt32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AudioFeedback {
|
||||||
|
audio @0 :AudioData;
|
||||||
|
blockNum @1 :UInt16;
|
||||||
|
}
|
||||||
|
|
||||||
struct Touch {
|
struct Touch {
|
||||||
sec @0 :Int64;
|
sec @0 :Int64;
|
||||||
usec @1 :Int64;
|
usec @1 :Int64;
|
||||||
@@ -2511,13 +2546,10 @@ struct Event {
|
|||||||
controlsState @7 :ControlsState;
|
controlsState @7 :ControlsState;
|
||||||
selfdriveState @130 :SelfdriveState;
|
selfdriveState @130 :SelfdriveState;
|
||||||
gyroscope @99 :SensorEventData;
|
gyroscope @99 :SensorEventData;
|
||||||
gyroscope2 @100 :SensorEventData;
|
|
||||||
accelerometer @98 :SensorEventData;
|
accelerometer @98 :SensorEventData;
|
||||||
accelerometer2 @101 :SensorEventData;
|
|
||||||
magnetometer @95 :SensorEventData;
|
magnetometer @95 :SensorEventData;
|
||||||
lightSensor @96 :SensorEventData;
|
lightSensor @96 :SensorEventData;
|
||||||
temperatureSensor @97 :SensorEventData;
|
temperatureSensor @97 :SensorEventData;
|
||||||
temperatureSensor2 @123 :SensorEventData;
|
|
||||||
pandaStates @81 :List(PandaState);
|
pandaStates @81 :List(PandaState);
|
||||||
peripheralState @80 :PeripheralState;
|
peripheralState @80 :PeripheralState;
|
||||||
radarState @13 :RadarState;
|
radarState @13 :RadarState;
|
||||||
@@ -2585,9 +2617,15 @@ struct Event {
|
|||||||
mapRenderState @105: MapRenderState;
|
mapRenderState @105: MapRenderState;
|
||||||
|
|
||||||
# UI services
|
# UI services
|
||||||
userFlag @93 :UserFlag;
|
|
||||||
uiDebug @102 :UIDebug;
|
uiDebug @102 :UIDebug;
|
||||||
|
|
||||||
|
# driving feedback
|
||||||
|
userBookmark @93 :UserBookmark;
|
||||||
|
bookmarkButton @148 :UserBookmark;
|
||||||
|
audioFeedback @149 :AudioFeedback;
|
||||||
|
|
||||||
|
lateralManeuverPlan @150 :LateralManeuverPlan;
|
||||||
|
|
||||||
# *********** debug ***********
|
# *********** debug ***********
|
||||||
testJoystick @52 :Joystick;
|
testJoystick @52 :Joystick;
|
||||||
roadEncodeData @86 :EncodeData;
|
roadEncodeData @86 :EncodeData;
|
||||||
@@ -2611,15 +2649,15 @@ struct Event {
|
|||||||
# DO change the name of the field and struct
|
# DO change the name of the field and struct
|
||||||
# DON'T change the ID (e.g. @107)
|
# DON'T change the ID (e.g. @107)
|
||||||
# DON'T change which struct it points to
|
# DON'T change which struct it points to
|
||||||
selfdriveStateSP @107 :Custom.SelfdriveStateSP;
|
customReserved0 @107 :Custom.CustomReserved0;
|
||||||
modelManagerSP @108 :Custom.ModelManagerSP;
|
customReserved1 @108 :Custom.CustomReserved1;
|
||||||
longitudinalPlanSP @109 :Custom.LongitudinalPlanSP;
|
customReserved2 @109 :Custom.CustomReserved2;
|
||||||
onroadEventsSP @110 :Custom.OnroadEventSP;
|
customReserved3 @110 :Custom.CustomReserved3;
|
||||||
carParamsSP @111 :Custom.CarParamsSP;
|
customReserved4 @111 :Custom.CustomReserved4;
|
||||||
carControlSP @112 :Custom.CarControlSP;
|
customReserved5 @112 :Custom.CustomReserved5;
|
||||||
backupManagerSP @113 :Custom.BackupManagerSP;
|
customReserved6 @113 :Custom.CustomReserved6;
|
||||||
carStateSP @114 :Custom.CarStateSP;
|
customReserved7 @114 :Custom.CustomReserved7;
|
||||||
liveMapDataSP @115 :Custom.LiveMapDataSP;
|
customReserved8 @115 :Custom.CustomReserved8;
|
||||||
customReserved9 @116 :Custom.CustomReserved9;
|
customReserved9 @116 :Custom.CustomReserved9;
|
||||||
customReserved10 @136 :Custom.CustomReserved10;
|
customReserved10 @136 :Custom.CustomReserved10;
|
||||||
customReserved11 @137 :Custom.CustomReserved11;
|
customReserved11 @137 :Custom.CustomReserved11;
|
||||||
@@ -2676,5 +2714,8 @@ struct Event {
|
|||||||
liveLocationKalmanDEPRECATED @72 :LiveLocationKalman;
|
liveLocationKalmanDEPRECATED @72 :LiveLocationKalman;
|
||||||
liveTracksDEPRECATED @16 :List(LiveTracksDEPRECATED);
|
liveTracksDEPRECATED @16 :List(LiveTracksDEPRECATED);
|
||||||
onroadEventsDEPRECATED @68: List(Car.OnroadEventDEPRECATED);
|
onroadEventsDEPRECATED @68: List(Car.OnroadEventDEPRECATED);
|
||||||
|
gyroscope2DEPRECATED @100 :SensorEventData;
|
||||||
|
accelerometer2DEPRECATED @101 :SensorEventData;
|
||||||
|
temperatureSensor2DEPRECATED @123 :SensorEventData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
# must be built with scons
|
# must be built with scons
|
||||||
from msgq.ipc_pyx import Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
|
from msgq import fake_event_handle, drain_sock_raw, MultiplePublishersError, IpcError, \
|
||||||
|
Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
|
||||||
set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event
|
set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event
|
||||||
from msgq.ipc_pyx import MultiplePublishersError, IpcError
|
|
||||||
from msgq import fake_event_handle, pub_sock, sub_sock, drain_sock_raw
|
|
||||||
import msgq
|
import msgq
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import capnp
|
import capnp
|
||||||
import time
|
import time
|
||||||
@@ -13,11 +11,25 @@ from typing import Optional, List, Union, Dict
|
|||||||
|
|
||||||
from cereal import log
|
from cereal import log
|
||||||
from cereal.services import SERVICE_LIST
|
from cereal.services import SERVICE_LIST
|
||||||
from openpilot.common.util import MovingAverage
|
from openpilot.common.utils import MovingAverage
|
||||||
|
|
||||||
NO_TRAVERSAL_LIMIT = 2**64-1
|
NO_TRAVERSAL_LIMIT = 2**64-1
|
||||||
|
|
||||||
|
|
||||||
|
def pub_sock(endpoint: str) -> PubSocket:
|
||||||
|
service = SERVICE_LIST.get(endpoint)
|
||||||
|
segment_size = service.queue_size if service else 0
|
||||||
|
return msgq.pub_sock(endpoint, segment_size)
|
||||||
|
|
||||||
|
|
||||||
|
def sub_sock(endpoint: str, poller: Optional[Poller] = None, addr: str = "127.0.0.1",
|
||||||
|
conflate: bool = False, timeout: Optional[int] = None) -> SubSocket:
|
||||||
|
service = SERVICE_LIST.get(endpoint)
|
||||||
|
segment_size = service.queue_size if service else 0
|
||||||
|
return msgq.sub_sock(endpoint, poller=poller, addr=addr, conflate=conflate,
|
||||||
|
timeout=timeout, segment_size=segment_size)
|
||||||
|
|
||||||
|
|
||||||
def reset_context():
|
def reset_context():
|
||||||
msgq.context = Context()
|
msgq.context = Context()
|
||||||
|
|
||||||
|
|||||||
@@ -25,15 +25,16 @@ void msgq_to_zmq(const std::vector<std::string> &endpoints, const std::string &i
|
|||||||
}
|
}
|
||||||
|
|
||||||
void zmq_to_msgq(const std::vector<std::string> &endpoints, const std::string &ip) {
|
void zmq_to_msgq(const std::vector<std::string> &endpoints, const std::string &ip) {
|
||||||
auto poller = std::make_unique<ZMQPoller>();
|
auto poller = std::make_unique<BridgeZmqPoller>();
|
||||||
auto pub_context = std::make_unique<MSGQContext>();
|
auto pub_context = std::make_unique<Context>();
|
||||||
auto sub_context = std::make_unique<ZMQContext>();
|
auto sub_context = std::make_unique<BridgeZmqContext>();
|
||||||
std::map<SubSocket *, PubSocket *> sub2pub;
|
std::map<BridgeZmqSubSocket *, PubSocket *> sub2pub;
|
||||||
|
|
||||||
for (auto endpoint : endpoints) {
|
for (auto endpoint : endpoints) {
|
||||||
auto pub_sock = new MSGQPubSocket();
|
auto pub_sock = new PubSocket();
|
||||||
auto sub_sock = new ZMQSubSocket();
|
auto sub_sock = new BridgeZmqSubSocket();
|
||||||
pub_sock->connect(pub_context.get(), endpoint);
|
size_t queue_size = services.at(endpoint).queue_size;
|
||||||
|
pub_sock->connect(pub_context.get(), endpoint, true, queue_size);
|
||||||
sub_sock->connect(sub_context.get(), endpoint, ip, false);
|
sub_sock->connect(sub_context.get(), endpoint, ip, false);
|
||||||
|
|
||||||
poller->registerSocket(sub_sock);
|
poller->registerSocket(sub_sock);
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
#include "cereal/messaging/bridge_zmq.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static size_t fnv1a_hash(const std::string &str) {
|
||||||
|
const size_t fnv_prime = 0x100000001b3;
|
||||||
|
size_t hash_value = 0xcbf29ce484222325;
|
||||||
|
for (char c : str) {
|
||||||
|
hash_value ^= (unsigned char)c;
|
||||||
|
hash_value *= fnv_prime;
|
||||||
|
}
|
||||||
|
return hash_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This is a hack to get the port number from the socket name, might have collisions.
|
||||||
|
static int get_port(std::string endpoint) {
|
||||||
|
size_t hash_value = fnv1a_hash(endpoint);
|
||||||
|
int start_port = 8023;
|
||||||
|
int max_port = 65535;
|
||||||
|
return start_port + (hash_value % (max_port - start_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqContext::BridgeZmqContext() {
|
||||||
|
context = zmq_ctx_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqContext::~BridgeZmqContext() {
|
||||||
|
if (context != nullptr) {
|
||||||
|
zmq_ctx_term(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqMessage::init(size_t sz) {
|
||||||
|
size = sz;
|
||||||
|
data = new char[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqMessage::init(char *d, size_t sz) {
|
||||||
|
size = sz;
|
||||||
|
data = new char[size];
|
||||||
|
memcpy(data, d, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqMessage::close() {
|
||||||
|
if (size > 0) {
|
||||||
|
delete[] data;
|
||||||
|
}
|
||||||
|
data = nullptr;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqMessage::~BridgeZmqMessage() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
int BridgeZmqSubSocket::connect(BridgeZmqContext *context, std::string endpoint, std::string address, bool conflate, bool check_endpoint) {
|
||||||
|
sock = zmq_socket(context->getRawContext(), ZMQ_SUB);
|
||||||
|
if (sock == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
zmq_setsockopt(sock, ZMQ_SUBSCRIBE, "", 0);
|
||||||
|
|
||||||
|
if (conflate) {
|
||||||
|
int arg = 1;
|
||||||
|
zmq_setsockopt(sock, ZMQ_CONFLATE, &arg, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
int reconnect_ivl = 500;
|
||||||
|
zmq_setsockopt(sock, ZMQ_RECONNECT_IVL_MAX, &reconnect_ivl, sizeof(reconnect_ivl));
|
||||||
|
|
||||||
|
full_endpoint = "tcp://" + address + ":";
|
||||||
|
if (check_endpoint) {
|
||||||
|
full_endpoint += std::to_string(get_port(endpoint));
|
||||||
|
} else {
|
||||||
|
full_endpoint += endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
return zmq_connect(sock, full_endpoint.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqSubSocket::setTimeout(int timeout) {
|
||||||
|
zmq_setsockopt(sock, ZMQ_RCVTIMEO, &timeout, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
Message *BridgeZmqSubSocket::receive(bool non_blocking) {
|
||||||
|
zmq_msg_t msg;
|
||||||
|
assert(zmq_msg_init(&msg) == 0);
|
||||||
|
|
||||||
|
int flags = non_blocking ? ZMQ_DONTWAIT : 0;
|
||||||
|
int rc = zmq_msg_recv(&msg, sock, flags);
|
||||||
|
|
||||||
|
Message *ret = nullptr;
|
||||||
|
if (rc >= 0) {
|
||||||
|
ret = new BridgeZmqMessage;
|
||||||
|
ret->init((char *)zmq_msg_data(&msg), zmq_msg_size(&msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
zmq_msg_close(&msg);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqSubSocket::~BridgeZmqSubSocket() {
|
||||||
|
if (sock != nullptr) {
|
||||||
|
zmq_close(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BridgeZmqPubSocket::connect(BridgeZmqContext *context, std::string endpoint, bool check_endpoint) {
|
||||||
|
sock = zmq_socket(context->getRawContext(), ZMQ_PUB);
|
||||||
|
if (sock == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
full_endpoint = "tcp://*:";
|
||||||
|
if (check_endpoint) {
|
||||||
|
full_endpoint += std::to_string(get_port(endpoint));
|
||||||
|
} else {
|
||||||
|
full_endpoint += endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZMQ pub sockets cannot be shared between processes, so we need to ensure pid stays the same.
|
||||||
|
pid = getpid();
|
||||||
|
|
||||||
|
return zmq_bind(sock, full_endpoint.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int BridgeZmqPubSocket::sendMessage(Message *message) {
|
||||||
|
assert(pid == getpid());
|
||||||
|
return zmq_send(sock, message->getData(), message->getSize(), ZMQ_DONTWAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BridgeZmqPubSocket::send(char *data, size_t size) {
|
||||||
|
assert(pid == getpid());
|
||||||
|
return zmq_send(sock, data, size, ZMQ_DONTWAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqPubSocket::~BridgeZmqPubSocket() {
|
||||||
|
if (sock != nullptr) {
|
||||||
|
zmq_close(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqPoller::registerSocket(BridgeZmqSubSocket *socket) {
|
||||||
|
assert(num_polls + 1 < (sizeof(polls) / sizeof(polls[0])));
|
||||||
|
polls[num_polls].socket = socket->getRawSocket();
|
||||||
|
polls[num_polls].events = ZMQ_POLLIN;
|
||||||
|
|
||||||
|
sockets.push_back(socket);
|
||||||
|
num_polls++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BridgeZmqSubSocket *> BridgeZmqPoller::poll(int timeout) {
|
||||||
|
std::vector<BridgeZmqSubSocket *> ret;
|
||||||
|
|
||||||
|
int rc = zmq_poll(polls, num_polls, timeout);
|
||||||
|
if (rc < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_polls; i++) {
|
||||||
|
if (polls[i].revents) {
|
||||||
|
ret.push_back(sockets[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <zmq.h>
|
||||||
|
|
||||||
|
#include "msgq/ipc.h"
|
||||||
|
|
||||||
|
class BridgeZmqContext {
|
||||||
|
public:
|
||||||
|
BridgeZmqContext();
|
||||||
|
void *getRawContext() { return context; }
|
||||||
|
~BridgeZmqContext();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *context = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BridgeZmqMessage : public Message {
|
||||||
|
public:
|
||||||
|
void init(size_t size);
|
||||||
|
void init(char *data, size_t size);
|
||||||
|
void close();
|
||||||
|
size_t getSize() { return size; }
|
||||||
|
char *getData() { return data; }
|
||||||
|
~BridgeZmqMessage();
|
||||||
|
|
||||||
|
private:
|
||||||
|
char *data = nullptr;
|
||||||
|
size_t size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BridgeZmqSubSocket {
|
||||||
|
public:
|
||||||
|
int connect(BridgeZmqContext *context, std::string endpoint, std::string address, bool conflate = false, bool check_endpoint = true);
|
||||||
|
void setTimeout(int timeout);
|
||||||
|
Message *receive(bool non_blocking = false);
|
||||||
|
void *getRawSocket() { return sock; }
|
||||||
|
~BridgeZmqSubSocket();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *sock = nullptr;
|
||||||
|
std::string full_endpoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BridgeZmqPubSocket {
|
||||||
|
public:
|
||||||
|
int connect(BridgeZmqContext *context, std::string endpoint, bool check_endpoint = true);
|
||||||
|
int sendMessage(Message *message);
|
||||||
|
int send(char *data, size_t size);
|
||||||
|
void *getRawSocket() { return sock; }
|
||||||
|
~BridgeZmqPubSocket();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *sock = nullptr;
|
||||||
|
std::string full_endpoint;
|
||||||
|
int pid = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BridgeZmqPoller {
|
||||||
|
public:
|
||||||
|
void registerSocket(BridgeZmqSubSocket *socket);
|
||||||
|
std::vector<BridgeZmqSubSocket *> poll(int timeout);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr size_t MAX_BRIDGE_ZMQ_POLLERS = 128;
|
||||||
|
std::vector<BridgeZmqSubSocket *> sockets;
|
||||||
|
zmq_pollitem_t polls[MAX_BRIDGE_ZMQ_POLLERS] = {};
|
||||||
|
size_t num_polls = 0;
|
||||||
|
};
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "cereal/services.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
|
||||||
extern ExitHandler do_exit;
|
extern ExitHandler do_exit;
|
||||||
@@ -21,14 +22,14 @@ static std::string recv_zmq_msg(void *sock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MsgqToZmq::run(const std::vector<std::string> &endpoints, const std::string &ip) {
|
void MsgqToZmq::run(const std::vector<std::string> &endpoints, const std::string &ip) {
|
||||||
zmq_context = std::make_unique<ZMQContext>();
|
zmq_context = std::make_unique<BridgeZmqContext>();
|
||||||
msgq_context = std::make_unique<MSGQContext>();
|
msgq_context = std::make_unique<Context>();
|
||||||
|
|
||||||
// Create ZMQPubSockets for each endpoint
|
// Create ZMQPubSockets for each endpoint
|
||||||
for (const auto &endpoint : endpoints) {
|
for (const auto &endpoint : endpoints) {
|
||||||
auto &socket_pair = socket_pairs.emplace_back();
|
auto &socket_pair = socket_pairs.emplace_back();
|
||||||
socket_pair.endpoint = endpoint;
|
socket_pair.endpoint = endpoint;
|
||||||
socket_pair.pub_sock = std::make_unique<ZMQPubSocket>();
|
socket_pair.pub_sock = std::make_unique<BridgeZmqPubSocket>();
|
||||||
int ret = socket_pair.pub_sock->connect(zmq_context.get(), endpoint);
|
int ret = socket_pair.pub_sock->connect(zmq_context.get(), endpoint);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
printf("Failed to create ZMQ publisher for [%s]: %s\n", endpoint.c_str(), zmq_strerror(zmq_errno()));
|
printf("Failed to create ZMQ publisher for [%s]: %s\n", endpoint.c_str(), zmq_strerror(zmq_errno()));
|
||||||
@@ -48,7 +49,7 @@ void MsgqToZmq::run(const std::vector<std::string> &endpoints, const std::string
|
|||||||
|
|
||||||
for (auto sub_sock : msgq_poller->poll(100)) {
|
for (auto sub_sock : msgq_poller->poll(100)) {
|
||||||
// Process messages for each socket
|
// Process messages for each socket
|
||||||
ZMQPubSocket *pub_sock = sub2pub.at(sub_sock);
|
BridgeZmqPubSocket *pub_sock = sub2pub.at(sub_sock);
|
||||||
for (int i = 0; i < MAX_MESSAGES_PER_SOCKET; ++i) {
|
for (int i = 0; i < MAX_MESSAGES_PER_SOCKET; ++i) {
|
||||||
auto msg = std::unique_ptr<Message>(sub_sock->receive(true));
|
auto msg = std::unique_ptr<Message>(sub_sock->receive(true));
|
||||||
if (!msg) break;
|
if (!msg) break;
|
||||||
@@ -71,7 +72,7 @@ void MsgqToZmq::zmqMonitorThread() {
|
|||||||
// Set up ZMQ monitor for each pub socket
|
// Set up ZMQ monitor for each pub socket
|
||||||
for (int i = 0; i < socket_pairs.size(); ++i) {
|
for (int i = 0; i < socket_pairs.size(); ++i) {
|
||||||
std::string addr = "inproc://op-bridge-monitor-" + std::to_string(i);
|
std::string addr = "inproc://op-bridge-monitor-" + std::to_string(i);
|
||||||
zmq_socket_monitor(socket_pairs[i].pub_sock->sock, addr.c_str(), ZMQ_EVENT_ACCEPTED | ZMQ_EVENT_DISCONNECTED);
|
zmq_socket_monitor(socket_pairs[i].pub_sock->getRawSocket(), addr.c_str(), ZMQ_EVENT_ACCEPTED | ZMQ_EVENT_DISCONNECTED);
|
||||||
|
|
||||||
void *monitor_socket = zmq_socket(zmq_context->getRawContext(), ZMQ_PAIR);
|
void *monitor_socket = zmq_socket(zmq_context->getRawContext(), ZMQ_PAIR);
|
||||||
zmq_connect(monitor_socket, addr.c_str());
|
zmq_connect(monitor_socket, addr.c_str());
|
||||||
@@ -108,7 +109,8 @@ void MsgqToZmq::zmqMonitorThread() {
|
|||||||
if (++pair.connected_clients == 1) {
|
if (++pair.connected_clients == 1) {
|
||||||
// Create new MSGQ subscriber socket and map to ZMQ publisher
|
// Create new MSGQ subscriber socket and map to ZMQ publisher
|
||||||
pair.sub_sock = std::make_unique<MSGQSubSocket>();
|
pair.sub_sock = std::make_unique<MSGQSubSocket>();
|
||||||
pair.sub_sock->connect(msgq_context.get(), pair.endpoint, "127.0.0.1");
|
size_t queue_size = services.at(pair.endpoint).queue_size;
|
||||||
|
pair.sub_sock->connect(msgq_context.get(), pair.endpoint, "127.0.0.1", false, true, queue_size);
|
||||||
sub2pub[pair.sub_sock.get()] = pair.pub_sock.get();
|
sub2pub[pair.sub_sock.get()] = pair.pub_sock.get();
|
||||||
registerSockets();
|
registerSockets();
|
||||||
}
|
}
|
||||||
@@ -128,7 +130,7 @@ void MsgqToZmq::zmqMonitorThread() {
|
|||||||
|
|
||||||
// Clean up monitor sockets
|
// Clean up monitor sockets
|
||||||
for (int i = 0; i < pollitems.size(); ++i) {
|
for (int i = 0; i < pollitems.size(); ++i) {
|
||||||
zmq_socket_monitor(socket_pairs[i].pub_sock->sock, nullptr, 0);
|
zmq_socket_monitor(socket_pairs[i].pub_sock->getRawSocket(), nullptr, 0);
|
||||||
zmq_close(pollitems[i].socket);
|
zmq_close(pollitems[i].socket);
|
||||||
}
|
}
|
||||||
cv.notify_one();
|
cv.notify_one();
|
||||||
|
|||||||
@@ -7,9 +7,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define private public
|
|
||||||
#include "msgq/impl_msgq.h"
|
#include "msgq/impl_msgq.h"
|
||||||
#include "msgq/impl_zmq.h"
|
#include "cereal/messaging/bridge_zmq.h"
|
||||||
|
|
||||||
class MsgqToZmq {
|
class MsgqToZmq {
|
||||||
public:
|
public:
|
||||||
@@ -22,16 +21,16 @@ protected:
|
|||||||
|
|
||||||
struct SocketPair {
|
struct SocketPair {
|
||||||
std::string endpoint;
|
std::string endpoint;
|
||||||
std::unique_ptr<ZMQPubSocket> pub_sock;
|
std::unique_ptr<BridgeZmqPubSocket> pub_sock;
|
||||||
std::unique_ptr<MSGQSubSocket> sub_sock;
|
std::unique_ptr<MSGQSubSocket> sub_sock;
|
||||||
int connected_clients = 0;
|
int connected_clients = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<MSGQContext> msgq_context;
|
std::unique_ptr<Context> msgq_context;
|
||||||
std::unique_ptr<ZMQContext> zmq_context;
|
std::unique_ptr<BridgeZmqContext> zmq_context;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::unique_ptr<MSGQPoller> msgq_poller;
|
std::unique_ptr<MSGQPoller> msgq_poller;
|
||||||
std::map<SubSocket *, ZMQPubSocket *> sub2pub;
|
std::map<SubSocket *, BridgeZmqPubSocket *> sub2pub;
|
||||||
std::vector<SocketPair> socket_pairs;
|
std::vector<SocketPair> socket_pairs;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ MessageContext message_context;
|
|||||||
struct SubMaster::SubMessage {
|
struct SubMaster::SubMessage {
|
||||||
std::string name;
|
std::string name;
|
||||||
SubSocket *socket = nullptr;
|
SubSocket *socket = nullptr;
|
||||||
int freq = 0;
|
float freq = 0.0f;
|
||||||
bool updated = false, alive = false, valid = false, ignore_alive;
|
bool updated = false, alive = false, valid = false, ignore_alive;
|
||||||
uint64_t rcv_time = 0, rcv_frame = 0;
|
uint64_t rcv_time = 0, rcv_frame = 0;
|
||||||
void *allocated_msg_reader = nullptr;
|
void *allocated_msg_reader = nullptr;
|
||||||
@@ -50,7 +50,7 @@ SubMaster::SubMaster(const std::vector<const char *> &service_list, const std::v
|
|||||||
assert(services.count(std::string(name)) > 0);
|
assert(services.count(std::string(name)) > 0);
|
||||||
|
|
||||||
service serv = services.at(std::string(name));
|
service serv = services.at(std::string(name));
|
||||||
SubSocket *socket = SubSocket::create(message_context.context(), name, address ? address : "127.0.0.1", true);
|
SubSocket *socket = SubSocket::create(message_context.context(), name, address ? address : "127.0.0.1", true, true, serv.queue_size);
|
||||||
assert(socket != 0);
|
assert(socket != 0);
|
||||||
bool is_polled = inList(poll, name) || poll.empty();
|
bool is_polled = inList(poll, name) || poll.empty();
|
||||||
if (is_polled) poller_->registerSocket(socket);
|
if (is_polled) poller_->registerSocket(socket);
|
||||||
@@ -187,7 +187,8 @@ SubMaster::~SubMaster() {
|
|||||||
PubMaster::PubMaster(const std::vector<const char *> &service_list) {
|
PubMaster::PubMaster(const std::vector<const char *> &service_list) {
|
||||||
for (auto name : service_list) {
|
for (auto name : service_list) {
|
||||||
assert(services.count(name) > 0);
|
assert(services.count(name) > 0);
|
||||||
PubSocket *socket = PubSocket::create(message_context.context(), name);
|
service serv = services.at(std::string(name));
|
||||||
|
PubSocket *socket = PubSocket::create(message_context.context(), name, true, serv.queue_size);
|
||||||
assert(socket);
|
assert(socket);
|
||||||
sockets_[name] = socket;
|
sockets_[name] = socket;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import numbers
|
|||||||
import random
|
import random
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from parameterized import parameterized
|
from openpilot.common.parameterized import parameterized
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from cereal import log, car
|
from cereal import log, car
|
||||||
@@ -30,7 +30,7 @@ def zmq_sleep(t=1):
|
|||||||
|
|
||||||
# TODO: this should take any capnp struct and returrn a msg with random populated data
|
# TODO: this should take any capnp struct and returrn a msg with random populated data
|
||||||
def random_carstate():
|
def random_carstate():
|
||||||
fields = ["vEgo", "aEgo", "gas", "steeringAngleDeg"]
|
fields = ["vEgo", "aEgo", "brake", "steeringAngleDeg"]
|
||||||
msg = messaging.new_message("carState")
|
msg = messaging.new_message("carState")
|
||||||
cs = msg.carState
|
cs = msg.carState
|
||||||
for f in fields:
|
for f in fields:
|
||||||
@@ -177,8 +177,8 @@ class TestMessaging:
|
|||||||
|
|
||||||
# wait 5 socket timeouts before sending
|
# wait 5 socket timeouts before sending
|
||||||
msg = random_carstate()
|
msg = random_carstate()
|
||||||
delayed_send(sock_timeout*5, pub_sock, msg.to_bytes())
|
|
||||||
start_time = time.monotonic()
|
start_time = time.monotonic()
|
||||||
|
delayed_send(sock_timeout*5, pub_sock, msg.to_bytes())
|
||||||
recvd = messaging.recv_one_retry(sub_sock)
|
recvd = messaging.recv_one_retry(sub_sock)
|
||||||
assert (time.monotonic() - start_time) >= sock_timeout*5
|
assert (time.monotonic() - start_time) >= sock_timeout*5
|
||||||
assert isinstance(recvd, capnp._DynamicStructReader)
|
assert isinstance(recvd, capnp._DynamicStructReader)
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class TestSubMaster:
|
|||||||
"cameraOdometry": (20, 10),
|
"cameraOdometry": (20, 10),
|
||||||
"liveCalibration": (4, 4),
|
"liveCalibration": (4, 4),
|
||||||
"carParams": (None, None),
|
"carParams": (None, None),
|
||||||
"userFlag": (None, None),
|
"userBookmark": (None, None),
|
||||||
}
|
}
|
||||||
|
|
||||||
for service, (max_freq, min_freq) in checks.items():
|
for service, (max_freq, min_freq) in checks.items():
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from parameterized import parameterized
|
from openpilot.common.parameterized import parameterized
|
||||||
|
|
||||||
import cereal.services as services
|
import cereal.services as services
|
||||||
from cereal.services import SERVICE_LIST
|
from cereal.services import SERVICE_LIST
|
||||||
|
|||||||
@@ -1,222 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import argparse
|
|
||||||
import sys
|
|
||||||
from typing import Any, List, Tuple
|
|
||||||
|
|
||||||
DEBUG = False
|
|
||||||
|
|
||||||
|
|
||||||
def print_debug(string: str) -> None:
|
|
||||||
if DEBUG:
|
|
||||||
print(string)
|
|
||||||
|
|
||||||
|
|
||||||
def create_schema_instance(struct: Any, prop: Tuple[str, Any]) -> Any:
|
|
||||||
"""
|
|
||||||
Create a new instance of a schema type, handling different field types.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
struct: The Cap'n Proto schema structure
|
|
||||||
prop: A tuple containing the field name and field metadata
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A new initialized schema instance
|
|
||||||
"""
|
|
||||||
struct_instance = struct.new_message()
|
|
||||||
field_name, field_metadata = prop
|
|
||||||
|
|
||||||
try:
|
|
||||||
field_type = field_metadata.proto.slot.type.which()
|
|
||||||
|
|
||||||
# Initialize different types of fields
|
|
||||||
if field_type in ('list', 'text', 'data'):
|
|
||||||
struct_instance.init(field_name, 1)
|
|
||||||
print_debug(f"Initialized list/text/data field: {field_name}")
|
|
||||||
elif field_type in ('struct', 'object'):
|
|
||||||
struct_instance.init(field_name)
|
|
||||||
print_debug(f"Initialized struct/object field: {field_name}")
|
|
||||||
|
|
||||||
return struct_instance
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error creating instance for {field_name}: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_schema_fields(schema_struct: Any) -> List[Tuple[str, Any]]:
|
|
||||||
"""
|
|
||||||
Retrieve all fields from a given schema structure.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
schema_struct: The Cap'n Proto schema structure
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A list of field names and their metadata
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# Get all fields from the schema
|
|
||||||
schema_fields = list(schema_struct.schema.fields.items())
|
|
||||||
|
|
||||||
print_debug("Discovered schema fields:")
|
|
||||||
for field_name, field_metadata in schema_fields:
|
|
||||||
print_debug(f"- {field_name}")
|
|
||||||
|
|
||||||
return schema_fields
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error retrieving schema fields: {e}")
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def generate_schema_instances(schema_struct: Any) -> List[Any]:
|
|
||||||
"""
|
|
||||||
Generate instances for all fields in a given schema.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
schema_struct: The Cap'n Proto schema structure
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A list of schema instances
|
|
||||||
"""
|
|
||||||
schema_fields = get_schema_fields(schema_struct)
|
|
||||||
instances = []
|
|
||||||
|
|
||||||
for field_prop in schema_fields:
|
|
||||||
try:
|
|
||||||
instance = create_schema_instance(schema_struct, field_prop)
|
|
||||||
if instance is not None:
|
|
||||||
instances.append(instance)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Skipping field due to error: {e}")
|
|
||||||
|
|
||||||
print(f"Generated {len(instances)} schema instances")
|
|
||||||
return instances
|
|
||||||
|
|
||||||
|
|
||||||
def persist_instances(instances: List[Any], filename: str) -> None:
|
|
||||||
"""
|
|
||||||
Write schema instances to a binary file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
instances: List of schema instances
|
|
||||||
filename: Output file path
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
with open(filename, 'wb') as f:
|
|
||||||
for instance in instances:
|
|
||||||
f.write(instance.to_bytes())
|
|
||||||
|
|
||||||
print(f"Successfully wrote {len(instances)} instances to {filename}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error persisting instances: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def read_instances(filename: str, schema_type: Any) -> List[Any]:
|
|
||||||
"""
|
|
||||||
Read schema instances from a binary file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
filename: Input file path
|
|
||||||
schema_type: The schema type to use for reading
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A list of read schema instances
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
with open(filename, 'rb') as f:
|
|
||||||
data = f.read()
|
|
||||||
|
|
||||||
instances = list(schema_type.read_multiple_bytes(data))
|
|
||||||
|
|
||||||
print(f"Read {len(instances)} instances from {filename}")
|
|
||||||
return instances
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error reading instances: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def compare_schemas(original_instances: List[Any], read_instances: List[Any]) -> bool:
|
|
||||||
"""
|
|
||||||
Compare original and read-back instances to detect potential breaking changes.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
original_instances: List of originally generated instances
|
|
||||||
read_instances: List of instances read back from file
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Boolean indicating whether schemas appear compatible
|
|
||||||
"""
|
|
||||||
if len(original_instances) != len(read_instances):
|
|
||||||
print("❌ Schema Compatibility Warning: Instance count mismatch")
|
|
||||||
return False
|
|
||||||
|
|
||||||
compatible = True
|
|
||||||
for struct in read_instances:
|
|
||||||
try:
|
|
||||||
getattr(struct, struct.which()) # Attempting to access the field to validate readability
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Structural change detected: {struct.which()} is not readable.\nFull error: {e}")
|
|
||||||
compatible = False
|
|
||||||
|
|
||||||
return compatible
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
CLI entry point for schema compatibility testing.
|
|
||||||
"""
|
|
||||||
# Setup argument parser
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description='Cap\'n Proto Schema Compatibility Testing Tool',
|
|
||||||
epilog='Test schema compatibility by generating and reading back instances.'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add mutually exclusive group for generation or reading mode
|
|
||||||
mode_group = parser.add_mutually_exclusive_group(required=True)
|
|
||||||
mode_group.add_argument('-g', '--generate', action='store_true',
|
|
||||||
help='Generate schema instances')
|
|
||||||
mode_group.add_argument('-r', '--read', action='store_true',
|
|
||||||
help='Read and validate schema instances')
|
|
||||||
|
|
||||||
# Common arguments
|
|
||||||
parser.add_argument('-f', '--file',
|
|
||||||
default='schema_instances.bin',
|
|
||||||
help='Output/input binary file (default: schema_instances.bin)')
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# Import the schema dynamically
|
|
||||||
try:
|
|
||||||
from cereal import log
|
|
||||||
schema_type = log.Event
|
|
||||||
except ImportError:
|
|
||||||
print("Error: Unable to import schema. Ensure 'cereal' is installed.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Execute based on mode
|
|
||||||
if args.generate:
|
|
||||||
print("🔧 Generating Schema Instances")
|
|
||||||
instances = generate_schema_instances(schema_type)
|
|
||||||
persist_instances(instances, args.file)
|
|
||||||
print("✅ Instance generation complete")
|
|
||||||
|
|
||||||
elif args.read:
|
|
||||||
print("🔍 Reading and Validating Schema Instances")
|
|
||||||
generated_instances = generate_schema_instances(schema_type)
|
|
||||||
read_back_instances = read_instances(args.file, schema_type)
|
|
||||||
|
|
||||||
# Compare schemas
|
|
||||||
if compare_schemas(generated_instances, read_back_instances):
|
|
||||||
print("✅ Schema Compatibility: No breaking changes detected")
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
print("❌ Potential Schema Breaking Changes Detected")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
+30
-31
@@ -1,37 +1,44 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
from enum import IntEnum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: this should be automatically determined using the capnp schema
|
||||||
|
class QueueSize(IntEnum):
|
||||||
|
BIG = 10 * 1024 * 1024 # 10MB - video frames, large AI outputs
|
||||||
|
MEDIUM = 2 * 1024 * 1024 # 2MB - high freq (CAN), livestream
|
||||||
|
SMALL = 250 * 1024 # 250KB - most services
|
||||||
|
|
||||||
|
|
||||||
class Service:
|
class Service:
|
||||||
def __init__(self, should_log: bool, frequency: float, decimation: Optional[int] = None):
|
def __init__(self, should_log: bool, frequency: float, decimation: Optional[int] = None,
|
||||||
|
queue_size: QueueSize = QueueSize.SMALL):
|
||||||
self.should_log = should_log
|
self.should_log = should_log
|
||||||
self.frequency = frequency
|
self.frequency = frequency
|
||||||
self.decimation = decimation
|
self.decimation = decimation
|
||||||
|
self.queue_size = queue_size
|
||||||
|
|
||||||
|
|
||||||
_services: dict[str, tuple] = {
|
_services: dict[str, tuple] = {
|
||||||
# service: (should_log, frequency, qlog decimation (optional))
|
# service: (should_log, frequency, qlog decimation (optional))
|
||||||
# note: the "EncodeIdx" packets will still be in the log
|
# note: the "EncodeIdx" packets will still be in the log
|
||||||
"gyroscope": (True, 104., 104),
|
"gyroscope": (True, 104., 104),
|
||||||
"gyroscope2": (True, 100., 100),
|
|
||||||
"accelerometer": (True, 104., 104),
|
"accelerometer": (True, 104., 104),
|
||||||
"accelerometer2": (True, 100., 100),
|
|
||||||
"magnetometer": (True, 25.),
|
"magnetometer": (True, 25.),
|
||||||
"lightSensor": (True, 100., 100),
|
"lightSensor": (True, 100., 100),
|
||||||
"temperatureSensor": (True, 2., 200),
|
"temperatureSensor": (True, 2., 200),
|
||||||
"temperatureSensor2": (True, 2., 200),
|
|
||||||
"gpsNMEA": (True, 9.),
|
"gpsNMEA": (True, 9.),
|
||||||
"deviceState": (True, 2., 1),
|
"deviceState": (True, 2., 1),
|
||||||
"touch": (True, 20., 1),
|
"touch": (True, 20., 1),
|
||||||
"can": (True, 100., 2053), # decimation gives ~3 msgs in a full segment
|
"can": (True, 100., 2053, QueueSize.BIG), # decimation gives ~3 msgs in a full segment
|
||||||
"controlsState": (True, 100., 10),
|
"controlsState": (True, 100., 10, QueueSize.MEDIUM),
|
||||||
"selfdriveState": (True, 100., 10),
|
"selfdriveState": (True, 100., 10),
|
||||||
"pandaStates": (True, 10., 1),
|
"pandaStates": (True, 10., 1),
|
||||||
"peripheralState": (True, 2., 1),
|
"peripheralState": (True, 2., 1),
|
||||||
"radarState": (True, 20., 5),
|
"radarState": (True, 20., 5),
|
||||||
"roadEncodeIdx": (False, 20., 1),
|
"roadEncodeIdx": (False, 20., 1),
|
||||||
"liveTracks": (True, 20.),
|
"liveTracks": (True, 20.),
|
||||||
"sendcan": (True, 100., 139),
|
"sendcan": (True, 100., 139, QueueSize.MEDIUM),
|
||||||
"logMessage": (True, 0.),
|
"logMessage": (True, 0.),
|
||||||
"errorLogMessage": (True, 0., 1),
|
"errorLogMessage": (True, 0., 1),
|
||||||
"liveCalibration": (True, 4., 4),
|
"liveCalibration": (True, 4., 4),
|
||||||
@@ -42,8 +49,9 @@ _services: dict[str, tuple] = {
|
|||||||
"carControl": (True, 100., 10),
|
"carControl": (True, 100., 10),
|
||||||
"carOutput": (True, 100., 10),
|
"carOutput": (True, 100., 10),
|
||||||
"longitudinalPlan": (True, 20., 10),
|
"longitudinalPlan": (True, 20., 10),
|
||||||
|
"lateralManeuverPlan": (True, 20.),
|
||||||
"driverAssistance": (True, 20., 20),
|
"driverAssistance": (True, 20., 20),
|
||||||
"procLog": (True, 0.5, 15),
|
"procLog": (True, 0.5, 15, QueueSize.BIG),
|
||||||
"gpsLocationExternal": (True, 10., 10),
|
"gpsLocationExternal": (True, 10., 10),
|
||||||
"gpsLocation": (True, 1., 1),
|
"gpsLocation": (True, 1., 1),
|
||||||
"ubloxGnss": (True, 10.),
|
"ubloxGnss": (True, 10.),
|
||||||
@@ -65,42 +73,33 @@ _services: dict[str, tuple] = {
|
|||||||
"wideRoadEncodeIdx": (False, 20., 1),
|
"wideRoadEncodeIdx": (False, 20., 1),
|
||||||
"wideRoadCameraState": (True, 20., 20),
|
"wideRoadCameraState": (True, 20., 20),
|
||||||
"drivingModelData": (True, 20., 10),
|
"drivingModelData": (True, 20., 10),
|
||||||
"modelV2": (True, 20.),
|
"modelV2": (True, 20., None, QueueSize.BIG),
|
||||||
"managerState": (True, 2., 1),
|
"managerState": (True, 2., 1),
|
||||||
"uploaderState": (True, 0., 1),
|
"uploaderState": (True, 0., 1),
|
||||||
"navInstruction": (True, 1., 10),
|
"navInstruction": (True, 1., 10),
|
||||||
"navRoute": (True, 0.),
|
"navRoute": (True, 0.),
|
||||||
"navThumbnail": (True, 0.),
|
"navThumbnail": (True, 0.),
|
||||||
"qRoadEncodeIdx": (False, 20.),
|
"qRoadEncodeIdx": (False, 20.),
|
||||||
"userFlag": (True, 0., 1),
|
"userBookmark": (True, 0., 1),
|
||||||
"soundPressure": (True, 10., 10),
|
"soundPressure": (True, 10., 10),
|
||||||
"rawAudioData": (False, 20.),
|
"rawAudioData": (False, 20.),
|
||||||
|
"bookmarkButton": (True, 0., 1),
|
||||||
# sunnypilot
|
"audioFeedback": (True, 0., 1),
|
||||||
"modelManagerSP": (False, 1., 1),
|
"roadEncodeData": (False, 20., None, QueueSize.BIG),
|
||||||
"backupManagerSP": (False, 1., 1),
|
"driverEncodeData": (False, 20., None, QueueSize.BIG),
|
||||||
"selfdriveStateSP": (True, 100., 10),
|
"wideRoadEncodeData": (False, 20., None, QueueSize.BIG),
|
||||||
"longitudinalPlanSP": (True, 20., 10),
|
"qRoadEncodeData": (False, 20., None, QueueSize.BIG),
|
||||||
"onroadEventsSP": (True, 1., 1),
|
|
||||||
"carParamsSP": (True, 0.02, 1),
|
|
||||||
"carControlSP": (True, 100., 10),
|
|
||||||
"carStateSP": (True, 100., 10),
|
|
||||||
"liveMapDataSP": (True, 1., 1),
|
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
"uiDebug": (True, 0., 1),
|
"uiDebug": (True, 0., 1),
|
||||||
"testJoystick": (True, 0.),
|
"testJoystick": (True, 0.),
|
||||||
"alertDebug": (True, 20., 5),
|
"alertDebug": (True, 20., 5),
|
||||||
"roadEncodeData": (False, 20.),
|
|
||||||
"driverEncodeData": (False, 20.),
|
|
||||||
"wideRoadEncodeData": (False, 20.),
|
|
||||||
"qRoadEncodeData": (False, 20.),
|
|
||||||
"livestreamWideRoadEncodeIdx": (False, 20.),
|
"livestreamWideRoadEncodeIdx": (False, 20.),
|
||||||
"livestreamRoadEncodeIdx": (False, 20.),
|
"livestreamRoadEncodeIdx": (False, 20.),
|
||||||
"livestreamDriverEncodeIdx": (False, 20.),
|
"livestreamDriverEncodeIdx": (False, 20.),
|
||||||
"livestreamWideRoadEncodeData": (False, 20.),
|
"livestreamWideRoadEncodeData": (False, 20., None, QueueSize.MEDIUM),
|
||||||
"livestreamRoadEncodeData": (False, 20.),
|
"livestreamRoadEncodeData": (False, 20., None, QueueSize.MEDIUM),
|
||||||
"livestreamDriverEncodeData": (False, 20.),
|
"livestreamDriverEncodeData": (False, 20., None, QueueSize.MEDIUM),
|
||||||
"customReservedRawData0": (True, 0.),
|
"customReservedRawData0": (True, 0.),
|
||||||
"customReservedRawData1": (True, 0.),
|
"customReservedRawData1": (True, 0.),
|
||||||
"customReservedRawData2": (True, 0.),
|
"customReservedRawData2": (True, 0.),
|
||||||
@@ -118,13 +117,13 @@ def build_header():
|
|||||||
h += "#include <map>\n"
|
h += "#include <map>\n"
|
||||||
h += "#include <string>\n"
|
h += "#include <string>\n"
|
||||||
|
|
||||||
h += "struct service { std::string name; bool should_log; int frequency; int decimation; };\n"
|
h += "struct service { std::string name; bool should_log; float frequency; int decimation; size_t queue_size; };\n"
|
||||||
h += "static std::map<std::string, service> services = {\n"
|
h += "static std::map<std::string, service> services = {\n"
|
||||||
for k, v in SERVICE_LIST.items():
|
for k, v in SERVICE_LIST.items():
|
||||||
should_log = "true" if v.should_log else "false"
|
should_log = "true" if v.should_log else "false"
|
||||||
decimation = -1 if v.decimation is None else v.decimation
|
decimation = -1 if v.decimation is None else v.decimation
|
||||||
h += ' { "%s", {"%s", %s, %d, %d}},\n' % \
|
h += ' { "%s", {"%s", %s, %f, %d, %d}},\n' % \
|
||||||
(k, k, should_log, v.frequency, decimation)
|
(k, k, should_log, v.frequency, decimation, v.queue_size)
|
||||||
h += "};\n"
|
h += "};\n"
|
||||||
|
|
||||||
h += "#endif\n"
|
h += "#endif\n"
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
*.cpp
|
|
||||||
+4
-16
@@ -1,21 +1,14 @@
|
|||||||
Import('env', 'envCython', 'arch')
|
Import('env', 'envCython')
|
||||||
|
|
||||||
common_libs = [
|
common_libs = [
|
||||||
'params.cc',
|
'params.cc',
|
||||||
'swaglog.cc',
|
'swaglog.cc',
|
||||||
'util.cc',
|
'util.cc',
|
||||||
'watchdog.cc',
|
'ratekeeper.cc',
|
||||||
'ratekeeper.cc'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
_common = env.Library('common', common_libs, LIBS="json11")
|
_common = env.Library('common', common_libs, LIBS="json11")
|
||||||
|
Export('_common')
|
||||||
files = [
|
|
||||||
'clutil.cc',
|
|
||||||
]
|
|
||||||
|
|
||||||
_gpucommon = env.Library('gpucommon', files)
|
|
||||||
Export('_common', '_gpucommon')
|
|
||||||
|
|
||||||
if GetOption('extras'):
|
if GetOption('extras'):
|
||||||
env.Program('tests/test_common',
|
env.Program('tests/test_common',
|
||||||
@@ -25,11 +18,6 @@ if GetOption('extras'):
|
|||||||
# Cython bindings
|
# Cython bindings
|
||||||
params_python = envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
|
params_python = envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
|
||||||
|
|
||||||
SConscript([
|
common_python = [params_python]
|
||||||
'transformations/SConscript',
|
|
||||||
])
|
|
||||||
|
|
||||||
Import('transformations_python')
|
|
||||||
common_python = [params_python, transformations_python]
|
|
||||||
|
|
||||||
Export('common_python')
|
Export('common_python')
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import jwt
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
from datetime import datetime, timedelta, UTC
|
||||||
|
from openpilot.system.hardware.hw import Paths
|
||||||
|
from openpilot.system.version import get_version
|
||||||
|
|
||||||
|
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
|
||||||
|
|
||||||
|
# name: jwt signature algorithm
|
||||||
|
KEYS = {"id_rsa": "RS256",
|
||||||
|
"id_ecdsa": "ES256"}
|
||||||
|
|
||||||
|
|
||||||
|
class Api:
|
||||||
|
def __init__(self, dongle_id):
|
||||||
|
self.dongle_id = dongle_id
|
||||||
|
self.jwt_algorithm, self.private_key, _ = get_key_pair()
|
||||||
|
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
return self.request('GET', *args, **kwargs)
|
||||||
|
|
||||||
|
def post(self, *args, **kwargs):
|
||||||
|
return self.request('POST', *args, **kwargs)
|
||||||
|
|
||||||
|
def request(self, method, endpoint, timeout=None, access_token=None, **params):
|
||||||
|
return api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
|
||||||
|
|
||||||
|
def get_token(self, payload_extra=None, expiry_hours=1):
|
||||||
|
now = datetime.now(UTC).replace(tzinfo=None)
|
||||||
|
payload = {
|
||||||
|
'identity': self.dongle_id,
|
||||||
|
'nbf': now,
|
||||||
|
'iat': now,
|
||||||
|
'exp': now + timedelta(hours=expiry_hours)
|
||||||
|
}
|
||||||
|
if payload_extra is not None:
|
||||||
|
payload.update(payload_extra)
|
||||||
|
token = jwt.encode(payload, self.private_key, algorithm=self.jwt_algorithm)
|
||||||
|
if isinstance(token, bytes):
|
||||||
|
token = token.decode('utf8')
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
|
def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params):
|
||||||
|
headers = {}
|
||||||
|
if access_token is not None:
|
||||||
|
headers['Authorization'] = "JWT " + access_token
|
||||||
|
|
||||||
|
headers['User-Agent'] = "openpilot-" + get_version()
|
||||||
|
|
||||||
|
# TODO: add session to Api
|
||||||
|
req = requests if session is None else session
|
||||||
|
return req.request(method, API_HOST + "/" + endpoint, timeout=timeout, headers=headers, params=params)
|
||||||
|
|
||||||
|
|
||||||
|
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||||
|
for key in KEYS:
|
||||||
|
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
|
||||||
|
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
|
||||||
|
return KEYS[key], private.read(), public.read()
|
||||||
|
return None, None, None
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
from openpilot.common.api.comma_connect import CommaConnectApi
|
|
||||||
|
|
||||||
|
|
||||||
class Api:
|
|
||||||
def __init__(self, dongle_id):
|
|
||||||
self.service = CommaConnectApi(dongle_id)
|
|
||||||
|
|
||||||
def request(self, method, endpoint, **params):
|
|
||||||
return self.service.request(method, endpoint, **params)
|
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
return self.service.get(*args, **kwargs)
|
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
|
||||||
return self.service.post(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_token(self, expiry_hours=1):
|
|
||||||
return self.service.get_token(expiry_hours)
|
|
||||||
|
|
||||||
|
|
||||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
|
|
||||||
return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import jwt
|
|
||||||
import requests
|
|
||||||
import unicodedata
|
|
||||||
from datetime import datetime, timedelta, UTC
|
|
||||||
from openpilot.system.hardware.hw import Paths
|
|
||||||
from openpilot.system.version import get_version
|
|
||||||
|
|
||||||
|
|
||||||
class BaseApi:
|
|
||||||
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
|
|
||||||
self.dongle_id = dongle_id
|
|
||||||
self.api_host = api_host
|
|
||||||
self.user_agent = user_agent
|
|
||||||
with open(f'{Paths.persist_root()}/comma/id_rsa') as f:
|
|
||||||
self.private_key = f.read()
|
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
return self.request('GET', *args, **kwargs)
|
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
|
||||||
return self.request('POST', *args, **kwargs)
|
|
||||||
|
|
||||||
def request(self, method, endpoint, timeout=None, access_token=None, **params):
|
|
||||||
return self.api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
|
|
||||||
|
|
||||||
def _get_token(self, expiry_hours=1, **extra_payload):
|
|
||||||
now = datetime.now(UTC).replace(tzinfo=None)
|
|
||||||
payload = {
|
|
||||||
'identity': self.dongle_id,
|
|
||||||
'nbf': now,
|
|
||||||
'iat': now,
|
|
||||||
'exp': now + timedelta(hours=expiry_hours),
|
|
||||||
**extra_payload
|
|
||||||
}
|
|
||||||
token = jwt.encode(payload, self.private_key, algorithm='RS256')
|
|
||||||
if isinstance(token, bytes):
|
|
||||||
token = token.decode('utf8')
|
|
||||||
return token
|
|
||||||
|
|
||||||
def get_token(self, expiry_hours=1):
|
|
||||||
return self._get_token(expiry_hours)
|
|
||||||
|
|
||||||
def remove_non_ascii_chars(self, text):
|
|
||||||
normalized_text = unicodedata.normalize('NFD', text)
|
|
||||||
ascii_encoded_text = normalized_text.encode('ascii', 'ignore')
|
|
||||||
return ascii_encoded_text.decode()
|
|
||||||
|
|
||||||
def api_get(self, endpoint, method='GET', timeout=None, access_token=None, json=None, **params):
|
|
||||||
headers = {}
|
|
||||||
if access_token is not None:
|
|
||||||
headers['Authorization'] = "JWT " + access_token
|
|
||||||
|
|
||||||
version = self.remove_non_ascii_chars(get_version())
|
|
||||||
headers['User-Agent'] = self.user_agent + version
|
|
||||||
|
|
||||||
return requests.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from openpilot.common.api.base import BaseApi
|
|
||||||
|
|
||||||
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
|
|
||||||
|
|
||||||
|
|
||||||
class CommaConnectApi(BaseApi):
|
|
||||||
def __init__(self, dongle_id):
|
|
||||||
super().__init__(dongle_id, API_HOST)
|
|
||||||
self.user_agent = "openpilot-"
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
#include "common/clutil.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "common/util.h"
|
|
||||||
#include "common/swaglog.h"
|
|
||||||
|
|
||||||
namespace { // helper functions
|
|
||||||
|
|
||||||
template <typename Func, typename Id, typename Name>
|
|
||||||
std::string get_info(Func get_info_func, Id id, Name param_name) {
|
|
||||||
size_t size = 0;
|
|
||||||
CL_CHECK(get_info_func(id, param_name, 0, NULL, &size));
|
|
||||||
std::string info(size, '\0');
|
|
||||||
CL_CHECK(get_info_func(id, param_name, size, info.data(), NULL));
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
inline std::string get_platform_info(cl_platform_id id, cl_platform_info name) { return get_info(&clGetPlatformInfo, id, name); }
|
|
||||||
inline std::string get_device_info(cl_device_id id, cl_device_info name) { return get_info(&clGetDeviceInfo, id, name); }
|
|
||||||
|
|
||||||
void cl_print_info(cl_platform_id platform, cl_device_id device) {
|
|
||||||
size_t work_group_size = 0;
|
|
||||||
cl_device_type device_type = 0;
|
|
||||||
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(work_group_size), &work_group_size, NULL);
|
|
||||||
clGetDeviceInfo(device, CL_DEVICE_TYPE, sizeof(device_type), &device_type, NULL);
|
|
||||||
const char *type_str = "Other...";
|
|
||||||
switch (device_type) {
|
|
||||||
case CL_DEVICE_TYPE_CPU: type_str ="CL_DEVICE_TYPE_CPU"; break;
|
|
||||||
case CL_DEVICE_TYPE_GPU: type_str = "CL_DEVICE_TYPE_GPU"; break;
|
|
||||||
case CL_DEVICE_TYPE_ACCELERATOR: type_str = "CL_DEVICE_TYPE_ACCELERATOR"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD("vendor: %s", get_platform_info(platform, CL_PLATFORM_VENDOR).c_str());
|
|
||||||
LOGD("platform version: %s", get_platform_info(platform, CL_PLATFORM_VERSION).c_str());
|
|
||||||
LOGD("profile: %s", get_platform_info(platform, CL_PLATFORM_PROFILE).c_str());
|
|
||||||
LOGD("extensions: %s", get_platform_info(platform, CL_PLATFORM_EXTENSIONS).c_str());
|
|
||||||
LOGD("name: %s", get_device_info(device, CL_DEVICE_NAME).c_str());
|
|
||||||
LOGD("device version: %s", get_device_info(device, CL_DEVICE_VERSION).c_str());
|
|
||||||
LOGD("max work group size: %zu", work_group_size);
|
|
||||||
LOGD("type = %d, %s", (int)device_type, type_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cl_print_build_errors(cl_program program, cl_device_id device) {
|
|
||||||
cl_build_status status;
|
|
||||||
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_STATUS, sizeof(status), &status, NULL);
|
|
||||||
size_t log_size;
|
|
||||||
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);
|
|
||||||
std::string log(log_size, '\0');
|
|
||||||
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, &log[0], NULL);
|
|
||||||
|
|
||||||
LOGE("build failed; status=%d, log: %s", status, log.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
cl_device_id cl_get_device_id(cl_device_type device_type) {
|
|
||||||
cl_uint num_platforms = 0;
|
|
||||||
CL_CHECK(clGetPlatformIDs(0, NULL, &num_platforms));
|
|
||||||
std::unique_ptr<cl_platform_id[]> platform_ids = std::make_unique<cl_platform_id[]>(num_platforms);
|
|
||||||
CL_CHECK(clGetPlatformIDs(num_platforms, &platform_ids[0], NULL));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_platforms; ++i) {
|
|
||||||
LOGD("platform[%zu] CL_PLATFORM_NAME: %s", i, get_platform_info(platform_ids[i], CL_PLATFORM_NAME).c_str());
|
|
||||||
|
|
||||||
// Get first device
|
|
||||||
if (cl_device_id device_id = NULL; clGetDeviceIDs(platform_ids[i], device_type, 1, &device_id, NULL) == 0 && device_id) {
|
|
||||||
cl_print_info(platform_ids[i], device_id);
|
|
||||||
return device_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOGE("No valid openCL platform found");
|
|
||||||
assert(0);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
cl_context cl_create_context(cl_device_id device_id) {
|
|
||||||
return CL_CHECK_ERR(clCreateContext(NULL, 1, &device_id, NULL, NULL, &err));
|
|
||||||
}
|
|
||||||
|
|
||||||
void cl_release_context(cl_context context) {
|
|
||||||
clReleaseContext(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args) {
|
|
||||||
return cl_program_from_source(ctx, device_id, util::read_file(path), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args) {
|
|
||||||
const char *csrc = src.c_str();
|
|
||||||
cl_program prg = CL_CHECK_ERR(clCreateProgramWithSource(ctx, 1, &csrc, NULL, &err));
|
|
||||||
if (int err = clBuildProgram(prg, 1, &device_id, args, NULL, NULL); err != 0) {
|
|
||||||
cl_print_build_errors(prg, device_id);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
return prg;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <OpenCL/cl.h>
|
|
||||||
#else
|
|
||||||
#include <CL/cl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#define CL_CHECK(_expr) \
|
|
||||||
do { \
|
|
||||||
assert(CL_SUCCESS == (_expr)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define CL_CHECK_ERR(_expr) \
|
|
||||||
({ \
|
|
||||||
cl_int err = CL_INVALID_VALUE; \
|
|
||||||
__typeof__(_expr) _ret = _expr; \
|
|
||||||
assert(_ret&& err == CL_SUCCESS); \
|
|
||||||
_ret; \
|
|
||||||
})
|
|
||||||
|
|
||||||
cl_device_id cl_get_device_id(cl_device_type device_type);
|
|
||||||
cl_context cl_create_context(cl_device_id device_id);
|
|
||||||
void cl_release_context(cl_context context);
|
|
||||||
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args = nullptr);
|
|
||||||
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args);
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
class Conversions:
|
# conversions
|
||||||
|
class CV:
|
||||||
# Speed
|
# Speed
|
||||||
MPH_TO_KPH = 1.609344
|
MPH_TO_KPH = 1.609344
|
||||||
KPH_TO_MPH = 1. / MPH_TO_KPH
|
KPH_TO_MPH = 1. / MPH_TO_KPH
|
||||||
@@ -17,3 +18,6 @@ class Conversions:
|
|||||||
|
|
||||||
# Mass
|
# Mass
|
||||||
LB_TO_KG = 0.453592
|
LB_TO_KG = 0.453592
|
||||||
|
|
||||||
|
|
||||||
|
ACCELERATION_DUE_TO_GRAVITY = 9.81 # m/s^2
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# remove all keys that end in DEPRECATED
|
|
||||||
def strip_deprecated_keys(d):
|
|
||||||
for k in list(d.keys()):
|
|
||||||
if isinstance(k, str):
|
|
||||||
if k.endswith('DEPRECATED'):
|
|
||||||
d.pop(k)
|
|
||||||
elif isinstance(d[k], dict):
|
|
||||||
strip_deprecated_keys(d[k])
|
|
||||||
return d
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import math
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
CHUNK_SIZE = 45 * 1024 * 1024 # 45MB, under GitHub's 50MB limit
|
||||||
|
|
||||||
|
def get_chunk_name(name, idx, num_chunks):
|
||||||
|
return f"{name}.chunk{idx+1:02d}of{num_chunks:02d}"
|
||||||
|
|
||||||
|
def get_manifest_path(name):
|
||||||
|
return f"{name}.chunkmanifest"
|
||||||
|
|
||||||
|
def get_chunk_paths(path, file_size):
|
||||||
|
num_chunks = math.ceil(file_size / CHUNK_SIZE)
|
||||||
|
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
|
||||||
|
|
||||||
|
def chunk_file(path, targets):
|
||||||
|
manifest_path, *chunk_paths = targets
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
actual_num_chunks = max(1, math.ceil(len(data) / CHUNK_SIZE))
|
||||||
|
assert len(chunk_paths) >= actual_num_chunks, f"Allowed {len(chunk_paths)} chunks but needs at least {actual_num_chunks}, for path {path}"
|
||||||
|
for i, chunk_path in enumerate(chunk_paths):
|
||||||
|
with open(chunk_path, 'wb') as f:
|
||||||
|
f.write(data[i * CHUNK_SIZE:(i + 1) * CHUNK_SIZE])
|
||||||
|
Path(manifest_path).write_text(str(len(chunk_paths)))
|
||||||
|
os.remove(path)
|
||||||
|
|
||||||
|
|
||||||
|
def read_file_chunked(path):
|
||||||
|
manifest_path = get_manifest_path(path)
|
||||||
|
if os.path.isfile(manifest_path):
|
||||||
|
num_chunks = int(Path(manifest_path).read_text().strip())
|
||||||
|
return b''.join(Path(get_chunk_name(path, i, num_chunks)).read_bytes() for i in range(num_chunks))
|
||||||
|
if os.path.isfile(path):
|
||||||
|
return Path(path).read_bytes()
|
||||||
|
raise FileNotFoundError(path)
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import io
|
|
||||||
import os
|
|
||||||
import tempfile
|
|
||||||
import contextlib
|
|
||||||
import zstandard as zstd
|
|
||||||
|
|
||||||
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
|
|
||||||
|
|
||||||
|
|
||||||
class CallbackReader:
|
|
||||||
"""Wraps a file, but overrides the read method to also
|
|
||||||
call a callback function with the number of bytes read so far."""
|
|
||||||
def __init__(self, f, callback, *args):
|
|
||||||
self.f = f
|
|
||||||
self.callback = callback
|
|
||||||
self.cb_args = args
|
|
||||||
self.total_read = 0
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
return getattr(self.f, attr)
|
|
||||||
|
|
||||||
def read(self, *args, **kwargs):
|
|
||||||
chunk = self.f.read(*args, **kwargs)
|
|
||||||
self.total_read += len(chunk)
|
|
||||||
self.callback(*self.cb_args, self.total_read)
|
|
||||||
return chunk
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str = None, newline: str = None,
|
|
||||||
overwrite: bool = False):
|
|
||||||
"""Write to a file atomically using a temporary file in the same directory as the destination file."""
|
|
||||||
dir_name = os.path.dirname(path)
|
|
||||||
|
|
||||||
if not overwrite and os.path.exists(path):
|
|
||||||
raise FileExistsError(f"File '{path}' already exists. To overwrite it, set 'overwrite' to True.")
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode=mode, buffering=buffering, encoding=encoding, newline=newline, dir=dir_name, delete=False) as tmp_file:
|
|
||||||
yield tmp_file
|
|
||||||
tmp_file_name = tmp_file.name
|
|
||||||
os.replace(tmp_file_name, path)
|
|
||||||
|
|
||||||
|
|
||||||
def get_upload_stream(filepath: str, should_compress: bool) -> tuple[io.BufferedIOBase, int]:
|
|
||||||
if not should_compress:
|
|
||||||
file_size = os.path.getsize(filepath)
|
|
||||||
file_stream = open(filepath, "rb")
|
|
||||||
return file_stream, file_size
|
|
||||||
|
|
||||||
# Compress the file on the fly
|
|
||||||
compressed_stream = io.BytesIO()
|
|
||||||
compressor = zstd.ZstdCompressor(level=LOG_COMPRESSION_LEVEL)
|
|
||||||
|
|
||||||
with open(filepath, "rb") as f:
|
|
||||||
compressor.copy_stream(f, compressed_stream)
|
|
||||||
compressed_size = compressed_stream.tell()
|
|
||||||
compressed_stream.seek(0)
|
|
||||||
return compressed_stream, compressed_size
|
|
||||||
@@ -15,3 +15,20 @@ class FirstOrderFilter:
|
|||||||
self.initialized = True
|
self.initialized = True
|
||||||
self.x = x
|
self.x = x
|
||||||
return self.x
|
return self.x
|
||||||
|
|
||||||
|
|
||||||
|
class BounceFilter(FirstOrderFilter):
|
||||||
|
def __init__(self, x0, rc, dt, initialized=True, bounce=2):
|
||||||
|
self.velocity = FirstOrderFilter(0.0, 0.15, dt)
|
||||||
|
self.bounce = bounce
|
||||||
|
super().__init__(x0, rc, dt, initialized)
|
||||||
|
|
||||||
|
def update(self, x):
|
||||||
|
super().update(x)
|
||||||
|
scale = self.dt / (1.0 / 60.0) # tuned at 60 fps
|
||||||
|
self.velocity.x += (x - self.x) * self.bounce * scale * self.dt
|
||||||
|
self.velocity.update(0.0)
|
||||||
|
if abs(self.velocity.x) < 1e-3:
|
||||||
|
self.velocity.x = 0.0
|
||||||
|
self.x += self.velocity.x
|
||||||
|
return self.x
|
||||||
|
|||||||
+7
-7
@@ -1,30 +1,30 @@
|
|||||||
from functools import cache
|
from functools import cache
|
||||||
import subprocess
|
import subprocess
|
||||||
from openpilot.common.run import run_cmd, run_cmd_default
|
from openpilot.common.utils import run_cmd, run_cmd_default
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_commit(cwd: str = None, branch: str = "HEAD") -> str:
|
def get_commit(cwd: str | None = None, branch: str = "HEAD") -> str:
|
||||||
return run_cmd_default(["git", "rev-parse", branch], cwd=cwd)
|
return run_cmd_default(["git", "rev-parse", branch], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_commit_date(cwd: str = None, commit: str = "HEAD") -> str:
|
def get_commit_date(cwd: str | None = None, commit: str = "HEAD") -> str:
|
||||||
return run_cmd_default(["git", "show", "--no-patch", "--format='%ct %ci'", commit], cwd=cwd)
|
return run_cmd_default(["git", "show", "--no-patch", "--format='%ct %ci'", commit], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_short_branch(cwd: str = None) -> str:
|
def get_short_branch(cwd: str | None = None) -> str:
|
||||||
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=cwd)
|
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_branch(cwd: str = None) -> str:
|
def get_branch(cwd: str | None = None) -> str:
|
||||||
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], cwd=cwd)
|
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_origin(cwd: str = None) -> str:
|
def get_origin(cwd: str | None = None) -> str:
|
||||||
try:
|
try:
|
||||||
local_branch = run_cmd(["git", "name-rev", "--name-only", "HEAD"], cwd=cwd)
|
local_branch = run_cmd(["git", "name-rev", "--name-only", "HEAD"], cwd=cwd)
|
||||||
tracking_remote = run_cmd(["git", "config", "branch." + local_branch + ".remote"], cwd=cwd)
|
tracking_remote = run_cmd(["git", "config", "branch." + local_branch + ".remote"], cwd=cwd)
|
||||||
@@ -34,7 +34,7 @@ def get_origin(cwd: str = None) -> str:
|
|||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_normalized_origin(cwd: str = None) -> str:
|
def get_normalized_origin(cwd: str | None = None) -> str:
|
||||||
return get_origin(cwd) \
|
return get_origin(cwd) \
|
||||||
.replace("git@", "", 1) \
|
.replace("git@", "", 1) \
|
||||||
.replace(".git", "", 1) \
|
.replace(".git", "", 1) \
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import os
|
||||||
|
import fcntl
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
# I2C constants from /usr/include/linux/i2c-dev.h
|
||||||
|
I2C_SLAVE = 0x0703
|
||||||
|
I2C_SLAVE_FORCE = 0x0706
|
||||||
|
I2C_SMBUS = 0x0720
|
||||||
|
|
||||||
|
# SMBus transfer types
|
||||||
|
I2C_SMBUS_READ = 1
|
||||||
|
I2C_SMBUS_WRITE = 0
|
||||||
|
I2C_SMBUS_BYTE_DATA = 2
|
||||||
|
I2C_SMBUS_I2C_BLOCK_DATA = 8
|
||||||
|
|
||||||
|
I2C_SMBUS_BLOCK_MAX = 32
|
||||||
|
|
||||||
|
|
||||||
|
class _I2cSmbusData(ctypes.Union):
|
||||||
|
_fields_ = [
|
||||||
|
("byte", ctypes.c_uint8),
|
||||||
|
("word", ctypes.c_uint16),
|
||||||
|
("block", ctypes.c_uint8 * (I2C_SMBUS_BLOCK_MAX + 2)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class _I2cSmbusIoctlData(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("read_write", ctypes.c_uint8),
|
||||||
|
("command", ctypes.c_uint8),
|
||||||
|
("size", ctypes.c_uint32),
|
||||||
|
("data", ctypes.POINTER(_I2cSmbusData)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SMBus:
|
||||||
|
def __init__(self, bus: int):
|
||||||
|
self._fd = os.open(f'/dev/i2c-{bus}', os.O_RDWR)
|
||||||
|
|
||||||
|
def __enter__(self) -> 'SMBus':
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args) -> None:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
if hasattr(self, '_fd') and self._fd >= 0:
|
||||||
|
os.close(self._fd)
|
||||||
|
self._fd = -1
|
||||||
|
|
||||||
|
def _set_address(self, addr: int, force: bool = False) -> None:
|
||||||
|
ioctl_arg = I2C_SLAVE_FORCE if force else I2C_SLAVE
|
||||||
|
fcntl.ioctl(self._fd, ioctl_arg, addr)
|
||||||
|
|
||||||
|
def _smbus_access(self, read_write: int, command: int, size: int, data: _I2cSmbusData) -> None:
|
||||||
|
ioctl_data = _I2cSmbusIoctlData(read_write, command, size, ctypes.pointer(data))
|
||||||
|
fcntl.ioctl(self._fd, I2C_SMBUS, ioctl_data)
|
||||||
|
|
||||||
|
def read_byte_data(self, addr: int, register: int, force: bool = False) -> int:
|
||||||
|
self._set_address(addr, force)
|
||||||
|
data = _I2cSmbusData()
|
||||||
|
self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_BYTE_DATA, data)
|
||||||
|
return int(data.byte)
|
||||||
|
|
||||||
|
def write_byte_data(self, addr: int, register: int, value: int, force: bool = False) -> None:
|
||||||
|
self._set_address(addr, force)
|
||||||
|
data = _I2cSmbusData()
|
||||||
|
data.byte = value & 0xFF
|
||||||
|
self._smbus_access(I2C_SMBUS_WRITE, register, I2C_SMBUS_BYTE_DATA, data)
|
||||||
|
|
||||||
|
def read_i2c_block_data(self, addr: int, register: int, length: int, force: bool = False) -> list[int]:
|
||||||
|
self._set_address(addr, force)
|
||||||
|
if not (0 <= length <= I2C_SMBUS_BLOCK_MAX):
|
||||||
|
raise ValueError(f"length must be 0..{I2C_SMBUS_BLOCK_MAX}")
|
||||||
|
|
||||||
|
data = _I2cSmbusData()
|
||||||
|
data.block[0] = length
|
||||||
|
self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_I2C_BLOCK_DATA, data)
|
||||||
|
read_len = int(data.block[0]) or length
|
||||||
|
read_len = min(read_len, length)
|
||||||
|
return [int(b) for b in data.block[1 : read_len + 1]]
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
typedef struct vec3 {
|
|
||||||
float v[3];
|
|
||||||
} vec3;
|
|
||||||
|
|
||||||
typedef struct vec4 {
|
|
||||||
float v[4];
|
|
||||||
} vec4;
|
|
||||||
|
|
||||||
typedef struct mat3 {
|
|
||||||
float v[3*3];
|
|
||||||
} mat3;
|
|
||||||
|
|
||||||
typedef struct mat4 {
|
|
||||||
float v[4*4];
|
|
||||||
} mat4;
|
|
||||||
|
|
||||||
static inline mat3 matmul3(const mat3 &a, const mat3 &b) {
|
|
||||||
mat3 ret = {{0.0}};
|
|
||||||
for (int r=0; r<3; r++) {
|
|
||||||
for (int c=0; c<3; c++) {
|
|
||||||
float v = 0.0;
|
|
||||||
for (int k=0; k<3; k++) {
|
|
||||||
v += a.v[r*3+k] * b.v[k*3+c];
|
|
||||||
}
|
|
||||||
ret.v[r*3+c] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline vec3 matvecmul3(const mat3 &a, const vec3 &b) {
|
|
||||||
vec3 ret = {{0.0}};
|
|
||||||
for (int r=0; r<3; r++) {
|
|
||||||
for (int c=0; c<3; c++) {
|
|
||||||
ret.v[r] += a.v[r*3+c] * b.v[c];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline mat4 matmul(const mat4 &a, const mat4 &b) {
|
|
||||||
mat4 ret = {{0.0}};
|
|
||||||
for (int r=0; r<4; r++) {
|
|
||||||
for (int c=0; c<4; c++) {
|
|
||||||
float v = 0.0;
|
|
||||||
for (int k=0; k<4; k++) {
|
|
||||||
v += a.v[r*4+k] * b.v[k*4+c];
|
|
||||||
}
|
|
||||||
ret.v[r*4+c] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline vec4 matvecmul(const mat4 &a, const vec4 &b) {
|
|
||||||
vec4 ret = {{0.0}};
|
|
||||||
for (int r=0; r<4; r++) {
|
|
||||||
for (int c=0; c<4; c++) {
|
|
||||||
ret.v[r] += a.v[r*4+c] * b.v[c];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// scales the input and output space of a transformation matrix
|
|
||||||
// that assumes pixel-center origin.
|
|
||||||
static inline mat3 transform_scale_buffer(const mat3 &in, float s) {
|
|
||||||
// in_pt = ( transform(out_pt/s + 0.5) - 0.5) * s
|
|
||||||
|
|
||||||
mat3 transform_out = {{
|
|
||||||
1.0f/s, 0.0f, 0.5f,
|
|
||||||
0.0f, 1.0f/s, 0.5f,
|
|
||||||
0.0f, 0.0f, 1.0f,
|
|
||||||
}};
|
|
||||||
|
|
||||||
mat3 transform_in = {{
|
|
||||||
s, 0.0f, -0.5f*s,
|
|
||||||
0.0f, s, -0.5f*s,
|
|
||||||
0.0f, 0.0f, 1.0f,
|
|
||||||
}};
|
|
||||||
|
|
||||||
return matmul3(transform_in, matmul3(in, transform_out));
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#define DEFAULT_MODEL "Tomb Raider 14 (Default)"
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
|
class parameterized:
|
||||||
|
@staticmethod
|
||||||
|
def expand(cases):
|
||||||
|
cases = list(cases)
|
||||||
|
|
||||||
|
if not cases:
|
||||||
|
return lambda func: pytest.mark.skip("no parameterized cases")(func)
|
||||||
|
|
||||||
|
def decorator(func):
|
||||||
|
params = [p for p in inspect.signature(func).parameters if p != 'self']
|
||||||
|
normalized = [c if isinstance(c, tuple) else (c,) for c in cases]
|
||||||
|
# Infer arg count from first case so extra params (e.g. from @given) are left untouched
|
||||||
|
expand_params = params[: len(normalized[0])]
|
||||||
|
if len(expand_params) == 1:
|
||||||
|
return pytest.mark.parametrize(expand_params[0], [c[0] for c in normalized])(func)
|
||||||
|
return pytest.mark.parametrize(', '.join(expand_params), normalized)(func)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def parameterized_class(attrs, input_list=None):
|
||||||
|
if isinstance(attrs, list) and (not attrs or isinstance(attrs[0], dict)):
|
||||||
|
params_list = attrs
|
||||||
|
else:
|
||||||
|
assert input_list is not None
|
||||||
|
attr_names = (attrs,) if isinstance(attrs, str) else tuple(attrs)
|
||||||
|
params_list = [dict(zip(attr_names, v if isinstance(v, (tuple, list)) else (v,), strict=False)) for v in input_list]
|
||||||
|
|
||||||
|
def decorator(cls):
|
||||||
|
globs = sys._getframe(1).f_globals
|
||||||
|
for i, params in enumerate(params_list):
|
||||||
|
name = f"{cls.__name__}_{i}"
|
||||||
|
new_cls = type(name, (cls,), dict(params))
|
||||||
|
new_cls.__module__ = cls.__module__
|
||||||
|
new_cls.__test__ = True # override inherited False so pytest collects this subclass
|
||||||
|
globs[name] = new_cls
|
||||||
|
# Don't collect the un-parametrised base, but return it so outer decorators
|
||||||
|
# (e.g. @pytest.mark.skip) land on it and propagate to subclasses via MRO.
|
||||||
|
cls.__test__ = False
|
||||||
|
return cls
|
||||||
|
|
||||||
|
return decorator
|
||||||
+13
-7
@@ -103,13 +103,11 @@ Params::~Params() {
|
|||||||
assert(queue.empty());
|
assert(queue.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Params::allKeys(ParamKeyType type) const {
|
std::vector<std::string> Params::allKeys() const {
|
||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
for (auto &p : keys) {
|
for (auto &p : keys) {
|
||||||
if (type == ALL || (p.second & type)) {
|
|
||||||
ret.push_back(p.first);
|
ret.push_back(p.first);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,8 +115,16 @@ bool Params::checkKey(const std::string &key) {
|
|||||||
return keys.find(key) != keys.end();
|
return keys.find(key) != keys.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParamKeyFlag Params::getKeyFlag(const std::string &key) {
|
||||||
|
return static_cast<ParamKeyFlag>(keys[key].flags);
|
||||||
|
}
|
||||||
|
|
||||||
ParamKeyType Params::getKeyType(const std::string &key) {
|
ParamKeyType Params::getKeyType(const std::string &key) {
|
||||||
return static_cast<ParamKeyType>(keys[key]);
|
return keys[key].type;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Params::getKeyDefaultValue(const std::string &key) {
|
||||||
|
return keys[key].default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Params::put(const char* key, const char* value, size_t value_size) {
|
int Params::put(const char* key, const char* value, size_t value_size) {
|
||||||
@@ -197,17 +203,17 @@ std::map<std::string, std::string> Params::readAll() {
|
|||||||
return util::read_files_in_dir(getParamPath());
|
return util::read_files_in_dir(getParamPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Params::clearAll(ParamKeyType key_type) {
|
void Params::clearAll(ParamKeyFlag key_flag) {
|
||||||
FileLock file_lock(params_path + "/.lock");
|
FileLock file_lock(params_path + "/.lock");
|
||||||
|
|
||||||
// 1) delete params of key_type
|
// 1) delete params of key_flag
|
||||||
// 2) delete files that are not defined in the keys.
|
// 2) delete files that are not defined in the keys.
|
||||||
if (DIR *d = opendir(getParamPath().c_str())) {
|
if (DIR *d = opendir(getParamPath().c_str())) {
|
||||||
struct dirent *de = NULL;
|
struct dirent *de = NULL;
|
||||||
while ((de = readdir(d))) {
|
while ((de = readdir(d))) {
|
||||||
if (de->d_type != DT_DIR) {
|
if (de->d_type != DT_DIR) {
|
||||||
auto it = keys.find(de->d_name);
|
auto it = keys.find(de->d_name);
|
||||||
if (it == keys.end() || (it->second & key_type)) {
|
if (it == keys.end() || (it->second.flags & key_flag)) {
|
||||||
unlink(getParamPath(de->d_name).c_str());
|
unlink(getParamPath(de->d_name).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-4
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -9,17 +10,33 @@
|
|||||||
|
|
||||||
#include "common/queue.h"
|
#include "common/queue.h"
|
||||||
|
|
||||||
enum ParamKeyType {
|
enum ParamKeyFlag {
|
||||||
PERSISTENT = 0x02,
|
PERSISTENT = 0x02,
|
||||||
CLEAR_ON_MANAGER_START = 0x04,
|
CLEAR_ON_MANAGER_START = 0x04,
|
||||||
CLEAR_ON_ONROAD_TRANSITION = 0x08,
|
CLEAR_ON_ONROAD_TRANSITION = 0x08,
|
||||||
CLEAR_ON_OFFROAD_TRANSITION = 0x10,
|
CLEAR_ON_OFFROAD_TRANSITION = 0x10,
|
||||||
DONT_LOG = 0x20,
|
DONT_LOG = 0x20,
|
||||||
DEVELOPMENT_ONLY = 0x40,
|
DEVELOPMENT_ONLY = 0x40,
|
||||||
BACKUP = 0x80,
|
CLEAR_ON_IGNITION_ON = 0x80,
|
||||||
ALL = 0xFFFFFFFF
|
ALL = 0xFFFFFFFF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ParamKeyType {
|
||||||
|
STRING = 0, // must be utf-8 decodable
|
||||||
|
BOOL = 1,
|
||||||
|
INT = 2,
|
||||||
|
FLOAT = 3,
|
||||||
|
TIME = 4, // ISO 8601
|
||||||
|
JSON = 5,
|
||||||
|
BYTES = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParamKeyAttributes {
|
||||||
|
uint32_t flags;
|
||||||
|
ParamKeyType type;
|
||||||
|
std::optional<std::string> default_value = std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
class Params {
|
class Params {
|
||||||
public:
|
public:
|
||||||
explicit Params(const std::string &path = {});
|
explicit Params(const std::string &path = {});
|
||||||
@@ -28,16 +45,18 @@ public:
|
|||||||
Params(const Params&) = delete;
|
Params(const Params&) = delete;
|
||||||
Params& operator=(const Params&) = delete;
|
Params& operator=(const Params&) = delete;
|
||||||
|
|
||||||
std::vector<std::string> allKeys(ParamKeyType type = ALL) const;
|
std::vector<std::string> allKeys() const;
|
||||||
bool checkKey(const std::string &key);
|
bool checkKey(const std::string &key);
|
||||||
|
ParamKeyFlag getKeyFlag(const std::string &key);
|
||||||
ParamKeyType getKeyType(const std::string &key);
|
ParamKeyType getKeyType(const std::string &key);
|
||||||
|
std::optional<std::string> getKeyDefaultValue(const std::string &key);
|
||||||
inline std::string getParamPath(const std::string &key = {}) {
|
inline std::string getParamPath(const std::string &key = {}) {
|
||||||
return params_path + params_prefix + (key.empty() ? "" : "/" + key);
|
return params_path + params_prefix + (key.empty() ? "" : "/" + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a value
|
// Delete a value
|
||||||
int remove(const std::string &key);
|
int remove(const std::string &key);
|
||||||
void clearAll(ParamKeyType type);
|
void clearAll(ParamKeyFlag flag);
|
||||||
|
|
||||||
// helpers for reading values
|
// helpers for reading values
|
||||||
std::string get(const std::string &key, bool block = false);
|
std::string get(const std::string &key, bool block = false);
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
from openpilot.common.params_pyx import Params, ParamKeyType, UnknownKeyName
|
from openpilot.common.params_pyx import Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
|
||||||
assert Params
|
assert Params
|
||||||
|
assert ParamKeyFlag
|
||||||
assert ParamKeyType
|
assert ParamKeyType
|
||||||
assert UnknownKeyName
|
assert UnknownKeyName
|
||||||
|
|
||||||
|
|||||||
+126
-203
@@ -3,208 +3,131 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
inline static std::unordered_map<std::string, uint32_t> keys = {
|
#include "cereal/gen/cpp/log.capnp.h"
|
||||||
{"AccessToken", CLEAR_ON_MANAGER_START | DONT_LOG},
|
|
||||||
{"AdbEnabled", PERSISTENT | BACKUP},
|
|
||||||
{"AlwaysOnDM", PERSISTENT | BACKUP},
|
|
||||||
{"ApiCache_Device", PERSISTENT},
|
|
||||||
{"ApiCache_FirehoseStats", PERSISTENT},
|
|
||||||
{"AssistNowToken", PERSISTENT},
|
|
||||||
{"AthenadPid", PERSISTENT},
|
|
||||||
{"AthenadUploadQueue", PERSISTENT},
|
|
||||||
{"AthenadRecentlyViewedRoutes", PERSISTENT},
|
|
||||||
{"BootCount", PERSISTENT},
|
|
||||||
{"CalibrationParams", PERSISTENT},
|
|
||||||
{"CameraDebugExpGain", CLEAR_ON_MANAGER_START},
|
|
||||||
{"CameraDebugExpTime", CLEAR_ON_MANAGER_START},
|
|
||||||
{"CarBatteryCapacity", PERSISTENT},
|
|
||||||
{"CarParams", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"CarParamsCache", CLEAR_ON_MANAGER_START},
|
|
||||||
{"CarParamsPersistent", PERSISTENT},
|
|
||||||
{"CarParamsPrevRoute", PERSISTENT},
|
|
||||||
{"CompletedTrainingVersion", PERSISTENT},
|
|
||||||
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"CurrentBootlog", PERSISTENT},
|
|
||||||
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"DisablePowerDown", PERSISTENT | BACKUP},
|
|
||||||
{"DisableUpdates", PERSISTENT | BACKUP},
|
|
||||||
{"DisengageOnAccelerator", PERSISTENT | BACKUP},
|
|
||||||
{"DongleId", PERSISTENT},
|
|
||||||
{"DoReboot", CLEAR_ON_MANAGER_START},
|
|
||||||
{"DoShutdown", CLEAR_ON_MANAGER_START},
|
|
||||||
{"DoUninstall", CLEAR_ON_MANAGER_START},
|
|
||||||
{"AlphaLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY | BACKUP},
|
|
||||||
{"ExperimentalMode", PERSISTENT | BACKUP},
|
|
||||||
{"ExperimentalModeConfirmed", PERSISTENT | BACKUP},
|
|
||||||
{"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"ForcePowerDown", PERSISTENT},
|
|
||||||
{"GitBranch", PERSISTENT},
|
|
||||||
{"GitCommit", PERSISTENT},
|
|
||||||
{"GitCommitDate", PERSISTENT},
|
|
||||||
{"GitDiff", PERSISTENT},
|
|
||||||
{"GithubSshKeys", PERSISTENT | BACKUP},
|
|
||||||
{"GithubUsername", PERSISTENT | BACKUP},
|
|
||||||
{"GitRemote", PERSISTENT},
|
|
||||||
{"GsmApn", PERSISTENT | BACKUP},
|
|
||||||
{"GsmMetered", PERSISTENT | BACKUP},
|
|
||||||
{"GsmRoaming", PERSISTENT | BACKUP},
|
|
||||||
{"HardwareSerial", PERSISTENT},
|
|
||||||
{"HasAcceptedTerms", PERSISTENT},
|
|
||||||
{"InstallDate", PERSISTENT},
|
|
||||||
{"IsDriverViewEnabled", CLEAR_ON_MANAGER_START},
|
|
||||||
{"IsEngaged", PERSISTENT},
|
|
||||||
{"IsLdwEnabled", PERSISTENT | BACKUP},
|
|
||||||
{"IsMetric", PERSISTENT | BACKUP},
|
|
||||||
{"IsOffroad", CLEAR_ON_MANAGER_START},
|
|
||||||
{"IsOnroad", PERSISTENT},
|
|
||||||
{"IsRhdDetected", PERSISTENT},
|
|
||||||
{"IsReleaseBranch", CLEAR_ON_MANAGER_START},
|
|
||||||
{"IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
|
||||||
{"IsTestedBranch", CLEAR_ON_MANAGER_START},
|
|
||||||
{"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
|
||||||
{"LanguageSetting", PERSISTENT | BACKUP},
|
|
||||||
{"LastAthenaPingTime", CLEAR_ON_MANAGER_START},
|
|
||||||
{"LastGPSPosition", PERSISTENT},
|
|
||||||
{"LastManagerExitReason", CLEAR_ON_MANAGER_START},
|
|
||||||
{"LastOffroadStatusPacket", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
|
||||||
{"LastPowerDropDetected", CLEAR_ON_MANAGER_START},
|
|
||||||
{"LastUpdateException", CLEAR_ON_MANAGER_START},
|
|
||||||
{"LastUpdateTime", PERSISTENT},
|
|
||||||
{"LiveDelay", PERSISTENT | BACKUP},
|
|
||||||
{"LiveParameters", PERSISTENT},
|
|
||||||
{"LiveParametersV2", PERSISTENT},
|
|
||||||
{"LiveTorqueParameters", PERSISTENT | DONT_LOG},
|
|
||||||
{"LocationFilterInitialState", PERSISTENT},
|
|
||||||
{"LongitudinalManeuverMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
|
||||||
{"LongitudinalPersonality", PERSISTENT | BACKUP},
|
|
||||||
{"NetworkMetered", PERSISTENT | BACKUP},
|
|
||||||
{"ObdMultiplexingChanged", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"ObdMultiplexingEnabled", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"Offroad_BadNvme", CLEAR_ON_MANAGER_START},
|
|
||||||
{"Offroad_CarUnrecognized", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"Offroad_ConnectivityNeeded", CLEAR_ON_MANAGER_START},
|
|
||||||
{"Offroad_ConnectivityNeededPrompt", CLEAR_ON_MANAGER_START},
|
|
||||||
{"Offroad_IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
|
||||||
{"Offroad_NeosUpdate", CLEAR_ON_MANAGER_START},
|
|
||||||
{"Offroad_NoFirmware", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"Offroad_Recalibration", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"Offroad_StorageMissing", CLEAR_ON_MANAGER_START},
|
|
||||||
{"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START},
|
|
||||||
{"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START},
|
|
||||||
{"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START},
|
|
||||||
{"OnroadCycleRequested", CLEAR_ON_MANAGER_START},
|
|
||||||
{"OpenpilotEnabledToggle", PERSISTENT | BACKUP},
|
|
||||||
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
|
||||||
{"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
|
||||||
{"PandaSignatures", CLEAR_ON_MANAGER_START},
|
|
||||||
{"PrimeType", PERSISTENT},
|
|
||||||
{"RecordAudio", PERSISTENT | BACKUP},
|
|
||||||
{"RecordFront", PERSISTENT | BACKUP},
|
|
||||||
{"RecordFrontLock", PERSISTENT}, // for the internal fleet
|
|
||||||
{"SecOCKey", PERSISTENT | DONT_LOG | BACKUP},
|
|
||||||
{"RouteCount", PERSISTENT},
|
|
||||||
{"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
|
||||||
{"SshEnabled", PERSISTENT | BACKUP},
|
|
||||||
{"TermsVersion", PERSISTENT},
|
|
||||||
{"TrainingVersion", PERSISTENT},
|
|
||||||
{"UbloxAvailable", PERSISTENT},
|
|
||||||
{"UpdateAvailable", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
|
||||||
{"UpdateFailedCount", CLEAR_ON_MANAGER_START},
|
|
||||||
{"UpdaterAvailableBranches", PERSISTENT},
|
|
||||||
{"UpdaterCurrentDescription", CLEAR_ON_MANAGER_START},
|
|
||||||
{"UpdaterCurrentReleaseNotes", CLEAR_ON_MANAGER_START},
|
|
||||||
{"UpdaterFetchAvailable", CLEAR_ON_MANAGER_START},
|
|
||||||
{"UpdaterNewDescription", CLEAR_ON_MANAGER_START},
|
|
||||||
{"UpdaterNewReleaseNotes", CLEAR_ON_MANAGER_START},
|
|
||||||
{"UpdaterState", CLEAR_ON_MANAGER_START},
|
|
||||||
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
|
|
||||||
{"UpdaterLastFetchTime", PERSISTENT},
|
|
||||||
{"Version", PERSISTENT},
|
|
||||||
|
|
||||||
// --- sunnypilot params --- //
|
inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||||
{"ApiCache_DriveStats", PERSISTENT},
|
{"AccessToken", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
|
||||||
{"AutoLaneChangeBsmDelay", PERSISTENT | BACKUP},
|
{"AdbEnabled", {PERSISTENT, BOOL}},
|
||||||
{"AutoLaneChangeTimer", PERSISTENT | BACKUP},
|
{"AlwaysOnDM", {PERSISTENT, BOOL}},
|
||||||
{"BlinkerMinLateralControlSpeed", PERSISTENT | BACKUP},
|
{"ApiCache_Device", {PERSISTENT, STRING}},
|
||||||
{"BlinkerPauseLateralControl", PERSISTENT | BACKUP},
|
{"ApiCache_FirehoseStats", {PERSISTENT, JSON}},
|
||||||
{"Brightness", PERSISTENT | BACKUP},
|
{"AssistNowToken", {PERSISTENT, STRING}},
|
||||||
{"CarParamsSP", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
{"AthenadPid", {PERSISTENT, INT}},
|
||||||
{"CarParamsSPCache", CLEAR_ON_MANAGER_START},
|
{"AthenadUploadQueue", {PERSISTENT, JSON}},
|
||||||
{"CarParamsSPPersistent", PERSISTENT},
|
{"AthenadRecentlyViewedRoutes", {PERSISTENT, STRING}},
|
||||||
{"CarPlatformBundle", PERSISTENT | BACKUP},
|
{"BootCount", {PERSISTENT, INT}},
|
||||||
{"ChevronInfo", PERSISTENT | BACKUP},
|
{"CalibrationParams", {PERSISTENT, BYTES}},
|
||||||
{"CustomAccIncrementsEnabled", PERSISTENT | BACKUP},
|
{"CameraDebugExpGain", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
{"CustomAccLongPressIncrement", PERSISTENT | BACKUP},
|
{"CameraDebugExpTime", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
{"CustomAccShortPressIncrement", PERSISTENT | BACKUP},
|
{"CarBatteryCapacity", {PERSISTENT, INT}},
|
||||||
{"DeviceBootMode", PERSISTENT | BACKUP},
|
{"CarParams", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES}},
|
||||||
{"EnableGithubRunner", PERSISTENT | BACKUP},
|
{"CarParamsCache", {CLEAR_ON_MANAGER_START, BYTES}},
|
||||||
{"InteractivityTimeout", PERSISTENT | BACKUP},
|
{"CarParamsPersistent", {PERSISTENT, BYTES}},
|
||||||
{"IsDevelopmentBranch", CLEAR_ON_MANAGER_START},
|
{"CarParamsPrevRoute", {PERSISTENT, BYTES}},
|
||||||
{"MaxTimeOffroad", PERSISTENT | BACKUP},
|
{"CompletedTrainingVersion", {PERSISTENT, STRING, "0"}},
|
||||||
{"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION},
|
{"ControlsReady", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||||
{"OffroadMode", CLEAR_ON_MANAGER_START},
|
{"CurrentBootlog", {PERSISTENT, STRING}},
|
||||||
{"QuickBootToggle", PERSISTENT | BACKUP},
|
{"CurrentRoute", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, STRING}},
|
||||||
{"QuietMode", PERSISTENT | BACKUP},
|
{"DisableLogging", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||||
{"ShowAdvancedControls", PERSISTENT | BACKUP},
|
{"DisablePowerDown", {PERSISTENT, BOOL}},
|
||||||
|
{"DisableUpdates", {PERSISTENT, BOOL}},
|
||||||
// MADS params
|
{"DisengageOnAccelerator", {PERSISTENT, BOOL, "0"}},
|
||||||
{"Mads", PERSISTENT | BACKUP},
|
{"DongleId", {PERSISTENT, STRING}},
|
||||||
{"MadsMainCruiseAllowed", PERSISTENT | BACKUP},
|
{"DoReboot", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MadsSteeringMode", PERSISTENT | BACKUP},
|
{"DoShutdown", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MadsUnifiedEngagementMode", PERSISTENT | BACKUP},
|
{"DoUninstall", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
|
{"DriverTooDistracted", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||||
// Model Manager params
|
{"AlphaLongitudinalEnabled", {PERSISTENT | DEVELOPMENT_ONLY, BOOL}},
|
||||||
{"ModelManager_ActiveBundle", PERSISTENT},
|
{"ExperimentalMode", {PERSISTENT, BOOL}},
|
||||||
{"ModelManager_ClearCache", CLEAR_ON_MANAGER_START},
|
{"ExperimentalModeConfirmed", {PERSISTENT, BOOL}},
|
||||||
{"ModelManager_DownloadIndex", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
{"FirmwareQueryDone", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||||
{"ModelManager_LastSyncTime", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
{"ForcePowerDown", {PERSISTENT, BOOL}},
|
||||||
{"ModelManager_ModelsCache", PERSISTENT | BACKUP},
|
{"GitBranch", {PERSISTENT, STRING}},
|
||||||
|
{"GitCommit", {PERSISTENT, STRING}},
|
||||||
// Neural Network Lateral Control
|
{"GitCommitDate", {PERSISTENT, STRING}},
|
||||||
{"NeuralNetworkLateralControl", PERSISTENT | BACKUP},
|
{"GitDiff", {PERSISTENT, STRING}},
|
||||||
|
{"GithubSshKeys", {PERSISTENT, STRING}},
|
||||||
// sunnylink params
|
{"GithubUsername", {PERSISTENT, STRING}},
|
||||||
{"EnableSunnylinkUploader", PERSISTENT | BACKUP},
|
{"GitRemote", {PERSISTENT, STRING}},
|
||||||
{"LastSunnylinkPingTime", CLEAR_ON_MANAGER_START},
|
{"GsmApn", {PERSISTENT, STRING}},
|
||||||
{"SunnylinkCache_Roles", PERSISTENT},
|
{"GsmMetered", {PERSISTENT, BOOL, "1"}},
|
||||||
{"SunnylinkCache_Users", PERSISTENT},
|
{"GsmRoaming", {PERSISTENT, BOOL}},
|
||||||
{"SunnylinkDongleId", PERSISTENT},
|
{"HardwareSerial", {PERSISTENT, STRING}},
|
||||||
{"SunnylinkdPid", PERSISTENT},
|
{"HasAcceptedTerms", {PERSISTENT, STRING, "0"}},
|
||||||
{"SunnylinkEnabled", PERSISTENT},
|
{"InstallDate", {PERSISTENT, TIME}},
|
||||||
|
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
// Backup Manager params
|
{"IsEngaged", {PERSISTENT, BOOL}},
|
||||||
{"BackupManager_CreateBackup", PERSISTENT},
|
{"IsLdwEnabled", {PERSISTENT, BOOL}},
|
||||||
{"BackupManager_RestoreVersion", PERSISTENT},
|
{"IsMetric", {PERSISTENT, BOOL}},
|
||||||
|
{"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
// sunnypilot car specific params
|
{"IsOnroad", {PERSISTENT, BOOL}},
|
||||||
{"HyundaiLongitudinalTuning", PERSISTENT | BACKUP},
|
{"IsRhdDetected", {PERSISTENT, BOOL}},
|
||||||
|
{"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"DynamicExperimentalControl", PERSISTENT | BACKUP},
|
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"BlindSpot", PERSISTENT | BACKUP},
|
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
|
{"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
// model panel params
|
{"LanguageSetting", {PERSISTENT, STRING, "en"}},
|
||||||
{"LagdToggle", PERSISTENT | BACKUP},
|
{"LastAthenaPingTime", {CLEAR_ON_MANAGER_START, INT}},
|
||||||
{"LagdToggleDesc", PERSISTENT},
|
{"LastGPSPosition", {PERSISTENT, STRING}},
|
||||||
{"LagdToggledelay", PERSISTENT | BACKUP},
|
{"LastManagerExitReason", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
|
{"LastOffroadStatusPacket", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON}},
|
||||||
// mapd
|
{"LastAgnosPowerMonitorShutdown", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
{"MapAdvisorySpeedLimit", CLEAR_ON_ONROAD_TRANSITION},
|
{"LastPowerDropDetected", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
{"MapdVersion", PERSISTENT},
|
{"LastUpdateException", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
{"MapSpeedLimit", CLEAR_ON_ONROAD_TRANSITION},
|
{"LastUpdateRouteCount", {PERSISTENT, INT, "0"}},
|
||||||
{"NextMapSpeedLimit", CLEAR_ON_ONROAD_TRANSITION},
|
{"LastUpdateTime", {PERSISTENT, TIME}},
|
||||||
{"Offroad_OSMUpdateRequired", CLEAR_ON_MANAGER_START},
|
{"LastUpdateUptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||||
{"OsmDbUpdatesCheck", CLEAR_ON_MANAGER_START}, // mapd database update happens with device ON, reset on boot
|
{"LiveDelay", {PERSISTENT, BYTES}},
|
||||||
{"OSMDownloadBounds", PERSISTENT},
|
{"LiveParameters", {PERSISTENT, JSON}},
|
||||||
{"OsmDownloadedDate", PERSISTENT},
|
{"LiveParametersV2", {PERSISTENT, BYTES}},
|
||||||
{"OSMDownloadLocations", PERSISTENT},
|
{"LiveTorqueParameters", {PERSISTENT | DONT_LOG, BYTES}},
|
||||||
{"OSMDownloadProgress", CLEAR_ON_MANAGER_START},
|
{"LocationFilterInitialState", {PERSISTENT, BYTES}},
|
||||||
{"OsmLocal", PERSISTENT},
|
{"LateralManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
{"OsmLocationName", PERSISTENT},
|
{"LongitudinalManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
{"OsmLocationTitle", PERSISTENT},
|
{"LongitudinalPersonality", {PERSISTENT, INT, std::to_string(static_cast<int>(cereal::LongitudinalPersonality::STANDARD))}},
|
||||||
{"OsmLocationUrl", PERSISTENT},
|
{"NetworkMetered", {PERSISTENT, BOOL}},
|
||||||
{"OsmStateName", PERSISTENT},
|
{"ObdMultiplexingChanged", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||||
{"OsmStateTitle", PERSISTENT},
|
{"ObdMultiplexingEnabled", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||||
{"OsmWayTest", PERSISTENT},
|
{"Offroad_CarUnrecognized", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||||
{"RoadName", CLEAR_ON_ONROAD_TRANSITION},
|
{"Offroad_ConnectivityNeeded", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
|
{"Offroad_ConnectivityNeededPrompt", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
|
{"Offroad_ExcessiveActuation", {PERSISTENT, JSON}},
|
||||||
|
{"Offroad_IsTakingSnapshot", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
|
{"Offroad_NeosUpdate", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
|
{"Offroad_NoFirmware", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||||
|
{"Offroad_Recalibration", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||||
|
{"Offroad_TemperatureTooHigh", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
|
{"Offroad_UnregisteredHardware", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
|
{"Offroad_UpdateFailed", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
|
{"Offroad_DriverMonitoringUncertain", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||||
|
{"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
|
{"OpenpilotEnabledToggle", {PERSISTENT, BOOL, "1"}},
|
||||||
|
{"PandaHeartbeatLost", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
|
{"PandaSomResetTriggered", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
|
{"PandaSignatures", {CLEAR_ON_MANAGER_START, BYTES}},
|
||||||
|
{"PrimeType", {PERSISTENT, INT}},
|
||||||
|
{"RecordAudio", {PERSISTENT, BOOL}},
|
||||||
|
{"RecordAudioFeedback", {PERSISTENT, BOOL, "0"}},
|
||||||
|
{"RecordFront", {PERSISTENT, BOOL}},
|
||||||
|
{"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet
|
||||||
|
{"SecOCKey", {PERSISTENT | DONT_LOG, STRING}},
|
||||||
|
{"ShowDebugInfo", {PERSISTENT, BOOL}},
|
||||||
|
{"RouteCount", {PERSISTENT, INT, "0"}},
|
||||||
|
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
|
{"SshEnabled", {PERSISTENT, BOOL}},
|
||||||
|
{"UbloxAvailable", {PERSISTENT, BOOL}},
|
||||||
|
{"UpdateAvailable", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||||
|
{"UpdateFailedCount", {CLEAR_ON_MANAGER_START, INT}},
|
||||||
|
{"UpdaterAvailableBranches", {PERSISTENT, STRING}},
|
||||||
|
{"UpdaterCurrentDescription", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
|
{"UpdaterCurrentReleaseNotes", {CLEAR_ON_MANAGER_START, BYTES}},
|
||||||
|
{"UpdaterFetchAvailable", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
|
{"UpdaterNewDescription", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
|
{"UpdaterNewReleaseNotes", {CLEAR_ON_MANAGER_START, BYTES}},
|
||||||
|
{"UpdaterState", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
|
{"UpdaterTargetBranch", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
|
{"UpdaterLastFetchTime", {PERSISTENT, TIME}},
|
||||||
|
{"UptimeOffroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||||
|
{"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||||
|
{"Version", {PERSISTENT, STRING}},
|
||||||
};
|
};
|
||||||
|
|||||||
+86
-14
@@ -1,19 +1,34 @@
|
|||||||
# distutils: language = c++
|
# distutils: language = c++
|
||||||
# cython: language_level = 3
|
# cython: language_level = 3
|
||||||
|
import builtins
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
from libcpp cimport bool
|
from libcpp cimport bool
|
||||||
from libcpp.string cimport string
|
from libcpp.string cimport string
|
||||||
from libcpp.vector cimport vector
|
from libcpp.vector cimport vector
|
||||||
|
from libcpp.optional cimport optional
|
||||||
|
|
||||||
|
from openpilot.common.swaglog import cloudlog
|
||||||
|
|
||||||
cdef extern from "common/params.h":
|
cdef extern from "common/params.h":
|
||||||
cpdef enum ParamKeyType:
|
cpdef enum ParamKeyFlag:
|
||||||
PERSISTENT
|
PERSISTENT
|
||||||
CLEAR_ON_MANAGER_START
|
CLEAR_ON_MANAGER_START
|
||||||
CLEAR_ON_ONROAD_TRANSITION
|
CLEAR_ON_ONROAD_TRANSITION
|
||||||
CLEAR_ON_OFFROAD_TRANSITION
|
CLEAR_ON_OFFROAD_TRANSITION
|
||||||
DEVELOPMENT_ONLY
|
DEVELOPMENT_ONLY
|
||||||
BACKUP
|
CLEAR_ON_IGNITION_ON
|
||||||
ALL
|
ALL
|
||||||
|
|
||||||
|
cpdef enum ParamKeyType:
|
||||||
|
STRING
|
||||||
|
BOOL
|
||||||
|
INT
|
||||||
|
FLOAT
|
||||||
|
TIME
|
||||||
|
JSON
|
||||||
|
BYTES
|
||||||
|
|
||||||
cdef cppclass c_Params "Params":
|
cdef cppclass c_Params "Params":
|
||||||
c_Params(string) except + nogil
|
c_Params(string) except + nogil
|
||||||
string get(string, bool) nogil
|
string get(string, bool) nogil
|
||||||
@@ -24,10 +39,31 @@ cdef extern from "common/params.h":
|
|||||||
void putBoolNonBlocking(string, bool) nogil
|
void putBoolNonBlocking(string, bool) nogil
|
||||||
int putBool(string, bool) nogil
|
int putBool(string, bool) nogil
|
||||||
bool checkKey(string) nogil
|
bool checkKey(string) nogil
|
||||||
|
ParamKeyType getKeyType(string) nogil
|
||||||
|
optional[string] getKeyDefaultValue(string) nogil
|
||||||
string getParamPath(string) nogil
|
string getParamPath(string) nogil
|
||||||
void clearAll(ParamKeyType)
|
void clearAll(ParamKeyFlag)
|
||||||
vector[string] allKeys(ParamKeyType)
|
vector[string] allKeys()
|
||||||
|
|
||||||
|
PYTHON_2_CPP = {
|
||||||
|
(str, STRING): lambda v: v,
|
||||||
|
(builtins.bool, BOOL): lambda v: "1" if v else "0",
|
||||||
|
(int, INT): str,
|
||||||
|
(float, FLOAT): str,
|
||||||
|
(datetime.datetime, TIME): lambda v: v.isoformat(),
|
||||||
|
(dict, JSON): json.dumps,
|
||||||
|
(list, JSON): json.dumps,
|
||||||
|
(bytes, BYTES): lambda v: v,
|
||||||
|
}
|
||||||
|
CPP_2_PYTHON = {
|
||||||
|
STRING: lambda v: v.decode("utf-8"),
|
||||||
|
BOOL: lambda v: v == b"1",
|
||||||
|
INT: int,
|
||||||
|
FLOAT: float,
|
||||||
|
TIME: lambda v: datetime.datetime.fromisoformat(v.decode("utf-8")),
|
||||||
|
JSON: json.loads,
|
||||||
|
BYTES: lambda v: v,
|
||||||
|
}
|
||||||
|
|
||||||
def ensure_bytes(v):
|
def ensure_bytes(v):
|
||||||
return v.encode() if isinstance(v, str) else v
|
return v.encode() if isinstance(v, str) else v
|
||||||
@@ -51,8 +87,8 @@ cdef class Params:
|
|||||||
def __dealloc__(self):
|
def __dealloc__(self):
|
||||||
del self.p
|
del self.p
|
||||||
|
|
||||||
def clear_all(self, tx_type=ParamKeyType.ALL):
|
def clear_all(self, tx_flag=ParamKeyFlag.ALL):
|
||||||
self.p.clearAll(tx_type)
|
self.p.clearAll(tx_flag)
|
||||||
|
|
||||||
def check_key(self, key):
|
def check_key(self, key):
|
||||||
key = ensure_bytes(key)
|
key = ensure_bytes(key)
|
||||||
@@ -60,21 +96,38 @@ cdef class Params:
|
|||||||
raise UnknownKeyName(key)
|
raise UnknownKeyName(key)
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def get(self, key, bool block=False, encoding=None):
|
def python2cpp(self, proposed_type, expected_type, value, key):
|
||||||
|
cast = PYTHON_2_CPP.get((proposed_type, expected_type))
|
||||||
|
if cast:
|
||||||
|
return cast(value)
|
||||||
|
raise TypeError(f"Type mismatch while writing param {key}: {proposed_type=} {expected_type=} {value=}")
|
||||||
|
|
||||||
|
def _cpp2python(self, t, value, default, key):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return CPP_2_PYTHON[t](value)
|
||||||
|
except (KeyError, TypeError, ValueError):
|
||||||
|
cloudlog.warning(f"Failed to cast param {key} with {value=} from type {t=}")
|
||||||
|
return self._cpp2python(t, default, None, key)
|
||||||
|
|
||||||
|
def get(self, key, bool block=False, bool return_default=False):
|
||||||
cdef string k = self.check_key(key)
|
cdef string k = self.check_key(key)
|
||||||
|
cdef ParamKeyType t = self.p.getKeyType(k)
|
||||||
|
cdef optional[string] default = self.p.getKeyDefaultValue(k)
|
||||||
cdef string val
|
cdef string val
|
||||||
with nogil:
|
with nogil:
|
||||||
val = self.p.get(k, block)
|
val = self.p.get(k, block)
|
||||||
|
|
||||||
|
default_val = (default.value() if default.has_value() else None) if return_default else None
|
||||||
if val == b"":
|
if val == b"":
|
||||||
if block:
|
if block:
|
||||||
# If we got no value while running in blocked mode
|
# If we got no value while running in blocked mode
|
||||||
# it means we got an interrupt while waiting
|
# it means we got an interrupt while waiting
|
||||||
raise KeyboardInterrupt
|
raise KeyboardInterrupt
|
||||||
else:
|
else:
|
||||||
return None
|
return self._cpp2python(t, default_val, None, key)
|
||||||
|
return self._cpp2python(t, val, default_val, key)
|
||||||
return val if encoding is None else val.decode(encoding)
|
|
||||||
|
|
||||||
def get_bool(self, key, bool block=False):
|
def get_bool(self, key, bool block=False):
|
||||||
cdef string k = self.check_key(key)
|
cdef string k = self.check_key(key)
|
||||||
@@ -83,6 +136,11 @@ cdef class Params:
|
|||||||
r = self.p.getBool(k, block)
|
r = self.p.getBool(k, block)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
def _put_cast(self, key, dat):
|
||||||
|
cdef string k = self.check_key(key)
|
||||||
|
cdef ParamKeyType t = self.p.getKeyType(k)
|
||||||
|
return ensure_bytes(self.python2cpp(type(dat), t, dat, key))
|
||||||
|
|
||||||
def put(self, key, dat):
|
def put(self, key, dat):
|
||||||
"""
|
"""
|
||||||
Warning: This function blocks until the param is written to disk!
|
Warning: This function blocks until the param is written to disk!
|
||||||
@@ -91,7 +149,7 @@ cdef class Params:
|
|||||||
in general try to avoid writing params as much as possible.
|
in general try to avoid writing params as much as possible.
|
||||||
"""
|
"""
|
||||||
cdef string k = self.check_key(key)
|
cdef string k = self.check_key(key)
|
||||||
cdef string dat_bytes = ensure_bytes(dat)
|
cdef string dat_bytes = self._put_cast(key, dat)
|
||||||
with nogil:
|
with nogil:
|
||||||
self.p.put(k, dat_bytes)
|
self.p.put(k, dat_bytes)
|
||||||
|
|
||||||
@@ -102,7 +160,7 @@ cdef class Params:
|
|||||||
|
|
||||||
def put_nonblocking(self, key, dat):
|
def put_nonblocking(self, key, dat):
|
||||||
cdef string k = self.check_key(key)
|
cdef string k = self.check_key(key)
|
||||||
cdef string dat_bytes = ensure_bytes(dat)
|
cdef string dat_bytes = self._put_cast(key, dat)
|
||||||
with nogil:
|
with nogil:
|
||||||
self.p.putNonBlocking(k, dat_bytes)
|
self.p.putNonBlocking(k, dat_bytes)
|
||||||
|
|
||||||
@@ -120,5 +178,19 @@ cdef class Params:
|
|||||||
cdef string key_bytes = ensure_bytes(key)
|
cdef string key_bytes = ensure_bytes(key)
|
||||||
return self.p.getParamPath(key_bytes).decode("utf-8")
|
return self.p.getParamPath(key_bytes).decode("utf-8")
|
||||||
|
|
||||||
def all_keys(self, type=ParamKeyType.ALL):
|
def get_type(self, key):
|
||||||
return self.p.allKeys(type)
|
return self.p.getKeyType(self.check_key(key))
|
||||||
|
|
||||||
|
def all_keys(self):
|
||||||
|
return self.p.allKeys()
|
||||||
|
|
||||||
|
def get_default_value(self, key):
|
||||||
|
cdef string k = self.check_key(key)
|
||||||
|
cdef ParamKeyType t = self.p.getKeyType(k)
|
||||||
|
cdef optional[string] default = self.p.getKeyDefaultValue(k)
|
||||||
|
return self._cpp2python(t, default.value(), None, key) if default.has_value() else None
|
||||||
|
|
||||||
|
def cpp2python(self, key, value):
|
||||||
|
cdef string k = self.check_key(key)
|
||||||
|
cdef ParamKeyType t = self.p.getKeyType(k)
|
||||||
|
return self._cpp2python(t, value, None, key)
|
||||||
|
|||||||
+20
-33
@@ -2,23 +2,14 @@ import numpy as np
|
|||||||
from numbers import Number
|
from numbers import Number
|
||||||
|
|
||||||
class PIDController:
|
class PIDController:
|
||||||
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
|
def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
|
||||||
self._k_p = k_p
|
self._k_p: list[list[float]] = [[0], [k_p]] if isinstance(k_p, Number) else k_p
|
||||||
self._k_i = k_i
|
self._k_i: list[list[float]] = [[0], [k_i]] if isinstance(k_i, Number) else k_i
|
||||||
self._k_d = k_d
|
self._k_d: list[list[float]] = [[0], [k_d]] if isinstance(k_d, Number) else k_d
|
||||||
self.k_f = k_f # feedforward gain
|
|
||||||
if isinstance(self._k_p, Number):
|
|
||||||
self._k_p = [[0], [self._k_p]]
|
|
||||||
if isinstance(self._k_i, Number):
|
|
||||||
self._k_i = [[0], [self._k_i]]
|
|
||||||
if isinstance(self._k_d, Number):
|
|
||||||
self._k_d = [[0], [self._k_d]]
|
|
||||||
|
|
||||||
self.pos_limit = pos_limit
|
self.set_limits(pos_limit, neg_limit)
|
||||||
self.neg_limit = neg_limit
|
|
||||||
|
|
||||||
self.i_unwind_rate = 0.3 / rate
|
self.i_dt = 1.0 / rate
|
||||||
self.i_rate = 1.0 / rate
|
|
||||||
self.speed = 0.0
|
self.speed = 0.0
|
||||||
|
|
||||||
self.reset()
|
self.reset()
|
||||||
@@ -35,10 +26,6 @@ class PIDController:
|
|||||||
def k_d(self):
|
def k_d(self):
|
||||||
return np.interp(self.speed, self._k_d[0], self._k_d[1])
|
return np.interp(self.speed, self._k_d[0], self._k_d[1])
|
||||||
|
|
||||||
@property
|
|
||||||
def error_integral(self):
|
|
||||||
return self.i/self.k_i
|
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.p = 0.0
|
self.p = 0.0
|
||||||
self.i = 0.0
|
self.i = 0.0
|
||||||
@@ -46,25 +33,25 @@ class PIDController:
|
|||||||
self.f = 0.0
|
self.f = 0.0
|
||||||
self.control = 0
|
self.control = 0
|
||||||
|
|
||||||
def update(self, error, error_rate=0.0, speed=0.0, override=False, feedforward=0., freeze_integrator=False):
|
def set_limits(self, pos_limit, neg_limit):
|
||||||
|
self.pos_limit = pos_limit
|
||||||
|
self.neg_limit = neg_limit
|
||||||
|
|
||||||
|
def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False):
|
||||||
self.speed = speed
|
self.speed = speed
|
||||||
|
self.p = self.k_p * float(error)
|
||||||
|
self.d = self.k_d * error_rate
|
||||||
|
self.f = feedforward
|
||||||
|
|
||||||
self.p = float(error) * self.k_p
|
|
||||||
self.f = feedforward * self.k_f
|
|
||||||
self.d = error_rate * self.k_d
|
|
||||||
|
|
||||||
if override:
|
|
||||||
self.i -= self.i_unwind_rate * float(np.sign(self.i))
|
|
||||||
else:
|
|
||||||
if not freeze_integrator:
|
if not freeze_integrator:
|
||||||
self.i = self.i + error * self.k_i * self.i_rate
|
i = self.i + self.k_i * self.i_dt * error
|
||||||
|
|
||||||
# Clip i to prevent exceeding control limits
|
# Don't allow windup if already clipping
|
||||||
control_no_i = self.p + self.d + self.f
|
test_control = self.p + i + self.d + self.f
|
||||||
control_no_i = np.clip(control_no_i, self.neg_limit, self.pos_limit)
|
i_upperbound = self.i if test_control > self.pos_limit else self.pos_limit
|
||||||
self.i = np.clip(self.i, self.neg_limit - control_no_i, self.pos_limit - control_no_i)
|
i_lowerbound = self.i if test_control < self.neg_limit else self.neg_limit
|
||||||
|
self.i = np.clip(i, i_lowerbound, i_upperbound)
|
||||||
|
|
||||||
control = self.p + self.i + self.d + self.f
|
control = self.p + self.i + self.d + self.f
|
||||||
|
|
||||||
self.control = np.clip(control, self.neg_limit, self.pos_limit)
|
self.control = np.clip(control, self.neg_limit, self.pos_limit)
|
||||||
return self.control
|
return self.control
|
||||||
|
|||||||
+9
-5
@@ -13,7 +13,11 @@ public:
|
|||||||
if (prefix.empty()) {
|
if (prefix.empty()) {
|
||||||
prefix = util::random_string(15);
|
prefix = util::random_string(15);
|
||||||
}
|
}
|
||||||
msgq_path = Path::shm_path() + "/" + prefix;
|
#ifdef __APPLE__
|
||||||
|
msgq_path = "/tmp/msgq_" + prefix;
|
||||||
|
#else
|
||||||
|
msgq_path = "/dev/shm/msgq_" + prefix;
|
||||||
|
#endif
|
||||||
bool ret = util::create_directories(msgq_path, 0777);
|
bool ret = util::create_directories(msgq_path, 0777);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
setenv("OPENPILOT_PREFIX", prefix.c_str(), 1);
|
setenv("OPENPILOT_PREFIX", prefix.c_str(), 1);
|
||||||
@@ -23,14 +27,14 @@ public:
|
|||||||
auto param_path = Params().getParamPath();
|
auto param_path = Params().getParamPath();
|
||||||
if (util::file_exists(param_path)) {
|
if (util::file_exists(param_path)) {
|
||||||
std::string real_path = util::readlink(param_path);
|
std::string real_path = util::readlink(param_path);
|
||||||
system(util::string_format("rm %s -rf", real_path.c_str()).c_str());
|
util::check_system(util::string_format("rm %s -rf", real_path.c_str()));
|
||||||
unlink(param_path.c_str());
|
unlink(param_path.c_str());
|
||||||
}
|
}
|
||||||
if (getenv("COMMA_CACHE") == nullptr) {
|
if (getenv("COMMA_CACHE") == nullptr) {
|
||||||
system(util::string_format("rm %s -rf", Path::download_cache_root().c_str()).c_str());
|
util::check_system(util::string_format("rm %s -rf", Path::download_cache_root().c_str()));
|
||||||
}
|
}
|
||||||
system(util::string_format("rm %s -rf", Path::comma_home().c_str()).c_str());
|
util::check_system(util::string_format("rm %s -rf", Path::comma_home().c_str()));
|
||||||
system(util::string_format("rm %s -rf", msgq_path.c_str()).c_str());
|
util::check_system(util::string_format("rm %s -rf", msgq_path.c_str()));
|
||||||
unsetenv("OPENPILOT_PREFIX");
|
unsetenv("OPENPILOT_PREFIX");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-7
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import shutil
|
import shutil
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@@ -9,20 +10,20 @@ from openpilot.system.hardware.hw import Paths
|
|||||||
from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
|
from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
|
||||||
|
|
||||||
class OpenpilotPrefix:
|
class OpenpilotPrefix:
|
||||||
def __init__(self, prefix: str = None, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False):
|
def __init__(self, prefix: str | None = None, create_dirs_on_enter: bool = True, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False):
|
||||||
self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15])
|
self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15])
|
||||||
self.msgq_path = os.path.join(Paths.shm_path(), self.prefix)
|
shm_path = "/tmp" if platform.system() == "Darwin" else "/dev/shm"
|
||||||
|
self.msgq_path = os.path.join(shm_path, "msgq_" + self.prefix)
|
||||||
|
self.create_dirs_on_enter = create_dirs_on_enter
|
||||||
self.clean_dirs_on_exit = clean_dirs_on_exit
|
self.clean_dirs_on_exit = clean_dirs_on_exit
|
||||||
self.shared_download_cache = shared_download_cache
|
self.shared_download_cache = shared_download_cache
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.original_prefix = os.environ.get('OPENPILOT_PREFIX', None)
|
self.original_prefix = os.environ.get('OPENPILOT_PREFIX', None)
|
||||||
os.environ['OPENPILOT_PREFIX'] = self.prefix
|
os.environ['OPENPILOT_PREFIX'] = self.prefix
|
||||||
try:
|
|
||||||
os.mkdir(self.msgq_path)
|
if self.create_dirs_on_enter:
|
||||||
except FileExistsError:
|
self.create_dirs()
|
||||||
pass
|
|
||||||
os.makedirs(Paths.log_root(), exist_ok=True)
|
|
||||||
|
|
||||||
if self.shared_download_cache:
|
if self.shared_download_cache:
|
||||||
os.environ["COMMA_CACHE"] = DEFAULT_DOWNLOAD_CACHE_ROOT
|
os.environ["COMMA_CACHE"] = DEFAULT_DOWNLOAD_CACHE_ROOT
|
||||||
@@ -40,6 +41,13 @@ class OpenpilotPrefix:
|
|||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def create_dirs(self):
|
||||||
|
try:
|
||||||
|
os.mkdir(self.msgq_path)
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
os.makedirs(Paths.log_root(), exist_ok=True)
|
||||||
|
|
||||||
def clean_dirs(self):
|
def clean_dirs(self):
|
||||||
symlink_path = Params().get_param_path()
|
symlink_path = Params().get_param_path()
|
||||||
if os.path.exists(symlink_path):
|
if os.path.exists(symlink_path):
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
#include "common/timing.h"
|
#include "common/timing.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
|
||||||
RateKeeper::RateKeeper(const std::string &name, float rate, float print_delay_threshold)
|
RateKeeper::RateKeeper(const std::string &name_, float rate, float print_delay_threshold_)
|
||||||
: name(name),
|
: name(name_),
|
||||||
print_delay_threshold(std::max(0.f, print_delay_threshold)) {
|
print_delay_threshold(std::max(0.f, print_delay_threshold_)) {
|
||||||
interval = 1 / rate;
|
interval = 1 / rate;
|
||||||
last_monitor_time = seconds_since_boot();
|
last_monitor_time = seconds_since_boot();
|
||||||
next_frame_time = last_monitor_time + interval;
|
next_frame_time = last_monitor_time + interval;
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import time
|
|||||||
|
|
||||||
from setproctitle import getproctitle
|
from setproctitle import getproctitle
|
||||||
|
|
||||||
from openpilot.common.util import MovingAverage
|
from openpilot.common.utils import MovingAverage
|
||||||
from openpilot.system.hardware import PC
|
from openpilot.system.hardware import PC
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import time
|
|
||||||
import functools
|
|
||||||
|
|
||||||
from openpilot.common.swaglog import cloudlog
|
|
||||||
|
|
||||||
|
|
||||||
def retry(attempts=3, delay=1.0, ignore_failure=False):
|
|
||||||
def decorator(func):
|
|
||||||
@functools.wraps(func)
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
for _ in range(attempts):
|
|
||||||
try:
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
except Exception:
|
|
||||||
cloudlog.exception(f"{func.__name__} failed, trying again")
|
|
||||||
time.sleep(delay)
|
|
||||||
|
|
||||||
if ignore_failure:
|
|
||||||
cloudlog.error(f"{func.__name__} failed after retry")
|
|
||||||
else:
|
|
||||||
raise Exception(f"{func.__name__} failed after retry")
|
|
||||||
return wrapper
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
@retry(attempts=10)
|
|
||||||
def abc():
|
|
||||||
raise ValueError("abc failed :(")
|
|
||||||
abc()
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
|
|
||||||
|
|
||||||
def run_cmd(cmd: list[str], cwd=None, env=None) -> str:
|
|
||||||
return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip()
|
|
||||||
|
|
||||||
|
|
||||||
def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str:
|
|
||||||
try:
|
|
||||||
return run_cmd(cmd, cwd=cwd, env=env)
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
return default
|
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from openpilot.common.file_helpers import atomic_write_in_dir
|
from openpilot.common.utils import atomic_write
|
||||||
|
|
||||||
|
|
||||||
class TestFileHelpers:
|
class TestFileHelpers:
|
||||||
@@ -15,5 +15,5 @@ class TestFileHelpers:
|
|||||||
assert f.read() == "test"
|
assert f.read() == "test"
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
|
|
||||||
def test_atomic_write_in_dir(self):
|
def test_atomic_write(self):
|
||||||
self.run_atomic_write_func(atomic_write_in_dir)
|
self.run_atomic_write_func(atomic_write)
|
||||||
|
|||||||
+44
-12
@@ -1,10 +1,11 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
import datetime
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from openpilot.common.params import Params, ParamKeyType, UnknownKeyName
|
from openpilot.common.params import Params, ParamKeyFlag, UnknownKeyName
|
||||||
|
|
||||||
class TestParams:
|
class TestParams:
|
||||||
def setup_method(self):
|
def setup_method(self):
|
||||||
@@ -12,7 +13,7 @@ class TestParams:
|
|||||||
|
|
||||||
def test_params_put_and_get(self):
|
def test_params_put_and_get(self):
|
||||||
self.params.put("DongleId", "cb38263377b873ee")
|
self.params.put("DongleId", "cb38263377b873ee")
|
||||||
assert self.params.get("DongleId") == b"cb38263377b873ee"
|
assert self.params.get("DongleId") == "cb38263377b873ee"
|
||||||
|
|
||||||
def test_params_non_ascii(self):
|
def test_params_non_ascii(self):
|
||||||
st = b"\xe1\x90\xff"
|
st = b"\xe1\x90\xff"
|
||||||
@@ -20,7 +21,7 @@ class TestParams:
|
|||||||
assert self.params.get("CarParams") == st
|
assert self.params.get("CarParams") == st
|
||||||
|
|
||||||
def test_params_get_cleared_manager_start(self):
|
def test_params_get_cleared_manager_start(self):
|
||||||
self.params.put("CarParams", "test")
|
self.params.put("CarParams", b"test")
|
||||||
self.params.put("DongleId", "cb38263377b873ee")
|
self.params.put("DongleId", "cb38263377b873ee")
|
||||||
assert self.params.get("CarParams") == b"test"
|
assert self.params.get("CarParams") == b"test"
|
||||||
|
|
||||||
@@ -29,24 +30,24 @@ class TestParams:
|
|||||||
f.write("test")
|
f.write("test")
|
||||||
assert os.path.isfile(undefined_param)
|
assert os.path.isfile(undefined_param)
|
||||||
|
|
||||||
self.params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)
|
self.params.clear_all(ParamKeyFlag.CLEAR_ON_MANAGER_START)
|
||||||
assert self.params.get("CarParams") is None
|
assert self.params.get("CarParams") is None
|
||||||
assert self.params.get("DongleId") is not None
|
assert self.params.get("DongleId") is not None
|
||||||
assert not os.path.isfile(undefined_param)
|
assert not os.path.isfile(undefined_param)
|
||||||
|
|
||||||
def test_params_two_things(self):
|
def test_params_two_things(self):
|
||||||
self.params.put("DongleId", "bob")
|
self.params.put("DongleId", "bob")
|
||||||
self.params.put("AthenadPid", "123")
|
self.params.put("AthenadPid", 123)
|
||||||
assert self.params.get("DongleId") == b"bob"
|
assert self.params.get("DongleId") == "bob"
|
||||||
assert self.params.get("AthenadPid") == b"123"
|
assert self.params.get("AthenadPid") == 123
|
||||||
|
|
||||||
def test_params_get_block(self):
|
def test_params_get_block(self):
|
||||||
def _delayed_writer():
|
def _delayed_writer():
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
self.params.put("CarParams", "test")
|
self.params.put("CarParams", b"test")
|
||||||
threading.Thread(target=_delayed_writer).start()
|
threading.Thread(target=_delayed_writer).start()
|
||||||
assert self.params.get("CarParams") is None
|
assert self.params.get("CarParams") is None
|
||||||
assert self.params.get("CarParams", True) == b"test"
|
assert self.params.get("CarParams", block=True) == b"test"
|
||||||
|
|
||||||
def test_params_unknown_key_fails(self):
|
def test_params_unknown_key_fails(self):
|
||||||
with pytest.raises(UnknownKeyName):
|
with pytest.raises(UnknownKeyName):
|
||||||
@@ -76,17 +77,17 @@ class TestParams:
|
|||||||
self.params.put_bool("IsMetric", False)
|
self.params.put_bool("IsMetric", False)
|
||||||
assert not self.params.get_bool("IsMetric")
|
assert not self.params.get_bool("IsMetric")
|
||||||
|
|
||||||
self.params.put("IsMetric", "1")
|
self.params.put("IsMetric", True)
|
||||||
assert self.params.get_bool("IsMetric")
|
assert self.params.get_bool("IsMetric")
|
||||||
|
|
||||||
self.params.put("IsMetric", "0")
|
self.params.put("IsMetric", False)
|
||||||
assert not self.params.get_bool("IsMetric")
|
assert not self.params.get_bool("IsMetric")
|
||||||
|
|
||||||
def test_put_non_blocking_with_get_block(self):
|
def test_put_non_blocking_with_get_block(self):
|
||||||
q = Params()
|
q = Params()
|
||||||
def _delayed_writer():
|
def _delayed_writer():
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
Params().put_nonblocking("CarParams", "test")
|
Params().put_nonblocking("CarParams", b"test")
|
||||||
threading.Thread(target=_delayed_writer).start()
|
threading.Thread(target=_delayed_writer).start()
|
||||||
assert q.get("CarParams") is None
|
assert q.get("CarParams") is None
|
||||||
assert q.get("CarParams", True) == b"test"
|
assert q.get("CarParams", True) == b"test"
|
||||||
@@ -107,3 +108,34 @@ class TestParams:
|
|||||||
assert len(keys) > 20
|
assert len(keys) > 20
|
||||||
assert len(keys) == len(set(keys))
|
assert len(keys) == len(set(keys))
|
||||||
assert b"CarParams" in keys
|
assert b"CarParams" in keys
|
||||||
|
|
||||||
|
def test_params_default_value(self):
|
||||||
|
self.params.remove("LanguageSetting")
|
||||||
|
self.params.remove("LongitudinalPersonality")
|
||||||
|
self.params.remove("LiveParameters")
|
||||||
|
|
||||||
|
assert self.params.get("LanguageSetting") is None
|
||||||
|
assert self.params.get("LanguageSetting", return_default=False) is None
|
||||||
|
assert isinstance(self.params.get("LanguageSetting", return_default=True), str)
|
||||||
|
assert isinstance(self.params.get("LongitudinalPersonality", return_default=True), int)
|
||||||
|
assert self.params.get("LiveParameters") is None
|
||||||
|
assert self.params.get("LiveParameters", return_default=True) is None
|
||||||
|
|
||||||
|
def test_params_get_type(self):
|
||||||
|
# json
|
||||||
|
self.params.put("ApiCache_FirehoseStats", {"a": 0})
|
||||||
|
assert self.params.get("ApiCache_FirehoseStats") == {"a": 0}
|
||||||
|
|
||||||
|
# int
|
||||||
|
self.params.put("BootCount", 1441)
|
||||||
|
assert self.params.get("BootCount") == 1441
|
||||||
|
|
||||||
|
# bool
|
||||||
|
self.params.put("AdbEnabled", True)
|
||||||
|
assert self.params.get("AdbEnabled")
|
||||||
|
assert isinstance(self.params.get("AdbEnabled"), bool)
|
||||||
|
|
||||||
|
# time
|
||||||
|
now = datetime.datetime.now(datetime.UTC)
|
||||||
|
self.params.put("InstallDate", now)
|
||||||
|
assert self.params.get("InstallDate") == now
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ TEST_CASE("util::read_file") {
|
|||||||
REQUIRE(util::read_file(filename).empty());
|
REQUIRE(util::read_file(filename).empty());
|
||||||
|
|
||||||
std::string content = random_bytes(64 * 1024);
|
std::string content = random_bytes(64 * 1024);
|
||||||
write(fd, content.c_str(), content.size());
|
REQUIRE(write(fd, content.c_str(), content.size()) == (ssize_t)content.size());
|
||||||
std::string ret = util::read_file(filename);
|
std::string ret = util::read_file(filename);
|
||||||
bool equal = (ret == content);
|
bool equal = (ret == content);
|
||||||
REQUIRE(equal);
|
REQUIRE(equal);
|
||||||
@@ -114,12 +114,12 @@ TEST_CASE("util::safe_fwrite") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("util::create_directories") {
|
TEST_CASE("util::create_directories") {
|
||||||
system("rm /tmp/test_create_directories -rf");
|
REQUIRE(system("rm /tmp/test_create_directories -rf") == 0);
|
||||||
std::string dir = "/tmp/test_create_directories/a/b/c/d/e/f";
|
std::string dir = "/tmp/test_create_directories/a/b/c/d/e/f";
|
||||||
|
|
||||||
auto check_dir_permissions = [](const std::string &dir, mode_t mode) -> bool {
|
auto check_dir_permissions = [](const std::string &path, mode_t mode) -> bool {
|
||||||
struct stat st = {};
|
struct stat st = {};
|
||||||
return stat(dir.c_str(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR && (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == mode;
|
return stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR && (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
SECTION("create_directories") {
|
SECTION("create_directories") {
|
||||||
@@ -132,7 +132,7 @@ TEST_CASE("util::create_directories") {
|
|||||||
}
|
}
|
||||||
SECTION("a file exists with the same name") {
|
SECTION("a file exists with the same name") {
|
||||||
REQUIRE(util::create_directories(dir, 0755));
|
REQUIRE(util::create_directories(dir, 0755));
|
||||||
int f = open((dir + "/file").c_str(), O_RDWR | O_CREAT);
|
int f = open((dir + "/file").c_str(), O_RDWR | O_CREAT, 0644);
|
||||||
REQUIRE(f != -1);
|
REQUIRE(f != -1);
|
||||||
close(f);
|
close(f);
|
||||||
REQUIRE(util::create_directories(dir + "/file", 0755) == false);
|
REQUIRE(util::create_directories(dir + "/file", 0755) == false);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user