mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-05 03:21:42 +00:00
Compare commits
1715 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf1a411acf | ||
|
|
1e05b21ab5 | ||
|
|
e5a6197633 | ||
|
|
039edcc23f | ||
|
|
f7f19207e0 | ||
|
|
befd12911c | ||
|
|
34519de60a | ||
|
|
dc4355c031 | ||
|
|
53d8d33bca | ||
|
|
530ae40614 | ||
|
|
79f565191e | ||
|
|
2cd9be413f | ||
|
|
babb0c1fcf | ||
|
|
330ba45f9c | ||
|
|
51272ef6b3 | ||
|
|
0d105ab771 | ||
|
|
cf4235ea36 | ||
|
|
2d4b7b9147 | ||
|
|
aec6f3d506 | ||
|
|
bfe346c76a | ||
|
|
83f1860047 | ||
|
|
9872e676d8 | ||
|
|
25db20e49d | ||
|
|
b0c6724eed | ||
|
|
f0fa8205ac | ||
|
|
42fc4cb6bc | ||
|
|
cc166c98d2 | ||
|
|
3f51f10ad3 | ||
|
|
1562eae74a | ||
|
|
b632b288a3 | ||
|
|
c11bd2720f | ||
|
|
f1151d375f | ||
|
|
fe1b62647f | ||
|
|
c49a45abbd | ||
|
|
bc3d01a721 | ||
|
|
bc473240ae | ||
|
|
2121bd5fb8 | ||
|
|
835f8470d6 | ||
|
|
6cfe5de00d | ||
|
|
2ac41f3edc | ||
|
|
26bdbf3d41 | ||
|
|
92149efa11 | ||
|
|
176fddeb4c | ||
|
|
87a34af367 | ||
|
|
4534e75787 | ||
|
|
1cbebaa2f7 | ||
|
|
6efb9ee405 | ||
|
|
1e7fcd5637 | ||
|
|
1f940e2b60 | ||
|
|
a6d127aedf | ||
|
|
b893b3d6d3 | ||
|
|
1696a490fa | ||
|
|
40a22d69bc | ||
|
|
e84dbfede0 | ||
|
|
8aa9dbfa90 | ||
|
|
eda2fa8a17 | ||
|
|
d20594db0d | ||
|
|
dd8ecfdd54 | ||
|
|
17ceebfff4 | ||
|
|
8b74ab389d | ||
|
|
1aad3489c2 | ||
|
|
2744991771 | ||
|
|
ffbf6a1fa2 | ||
|
|
fbabd0ef15 | ||
|
|
7713f68772 | ||
|
|
701995d6cc | ||
|
|
bf7068ac27 | ||
|
|
aae0f52ca6 | ||
|
|
ee02fb7ba7 | ||
|
|
518916ba02 | ||
|
|
3997c6635b | ||
|
|
cc8675a4e5 | ||
|
|
fb2c170b6e | ||
|
|
7ba8a9ee1f | ||
|
|
c569d8e523 | ||
|
|
2a5e89fa97 | ||
|
|
cc003c6c38 | ||
|
|
5522916123 | ||
|
|
967b30de3a | ||
|
|
3270d4fc86 | ||
|
|
be39678447 | ||
|
|
61c75deb2a | ||
|
|
a865f48e96 | ||
|
|
f66a667321 | ||
|
|
f8d4e9866e | ||
|
|
7e84ea891f | ||
|
|
da3ec1be10 | ||
|
|
944dd7265d | ||
|
|
6948cea67a | ||
|
|
a31459bce6 | ||
|
|
4a0ad6b48c | ||
|
|
e6552d272e | ||
|
|
bde383f763 | ||
|
|
5a52b51443 | ||
|
|
69a66ec5ec | ||
|
|
989c9fb29a | ||
|
|
0f5b08ec69 | ||
|
|
fba191099c | ||
|
|
b390cad095 | ||
|
|
b9772214d9 | ||
|
|
342c375a71 | ||
|
|
b0e4053087 | ||
|
|
f3e666b7bb | ||
|
|
b300518bd1 | ||
|
|
be27171236 | ||
|
|
4bbdbdfb48 | ||
|
|
f18fd41ac3 | ||
|
|
2d0faecf4f | ||
|
|
348bd107fc | ||
|
|
3149dc64b8 | ||
|
|
8618dd4160 | ||
|
|
72e21a1ed1 | ||
|
|
eab0d929e6 | ||
|
|
6789869663 | ||
|
|
c9dea2968d | ||
|
|
8f402645f5 | ||
|
|
f24ad1d715 | ||
|
|
ff88756864 | ||
|
|
f58873db8e | ||
|
|
37e969b41a | ||
|
|
13cdc29382 | ||
|
|
6e23985ae6 | ||
|
|
66bb0ffb2c | ||
|
|
74cc86c4c5 | ||
|
|
e22d8cc343 | ||
|
|
68dba92630 | ||
|
|
23bfc2d9ab | ||
|
|
1f1461e254 | ||
|
|
eae68fc165 | ||
|
|
4c4545fb4b | ||
|
|
16ffaa754d | ||
|
|
5e74ff26d8 | ||
|
|
53875419a1 | ||
|
|
aa6499e920 | ||
|
|
df70351107 | ||
|
|
e9bd50ff9b | ||
|
|
9b319fd56b | ||
|
|
18d28ec5e3 | ||
|
|
bdfb625211 | ||
|
|
21003e34eb | ||
|
|
70080457d5 | ||
|
|
e82cd5147b | ||
|
|
ec124bb662 | ||
|
|
2b2aa8eef7 | ||
|
|
cb38bacfe8 | ||
|
|
15561338d5 | ||
|
|
ca35a2e097 | ||
|
|
20dbae0cee | ||
|
|
1a59737f40 | ||
|
|
42b6d4e3f7 | ||
|
|
135c13958f | ||
|
|
ffbfc61532 | ||
|
|
8958b2a4da | ||
|
|
e4ac09077c | ||
|
|
f40de0c120 | ||
|
|
51fa3e851f | ||
|
|
1da84b2255 | ||
|
|
e43e2fbc84 | ||
|
|
5804d8fa84 | ||
|
|
169ef5fabf | ||
|
|
148759ef54 | ||
|
|
58ed112b51 | ||
|
|
dd1da77d20 | ||
|
|
7cda85df20 | ||
|
|
7ed9b13277 | ||
|
|
6b4f26225d | ||
|
|
b2d2924b72 | ||
|
|
34ec89c041 | ||
|
|
e15200068d | ||
|
|
d5200db6cd | ||
|
|
2ee3d86de4 | ||
|
|
9f0a8b930f | ||
|
|
2bca43779e | ||
|
|
4307d0ee8b | ||
|
|
3fe8d355a1 | ||
|
|
b44034dadc | ||
|
|
52d2c53888 | ||
|
|
76e918f71e | ||
|
|
0bee875aff | ||
|
|
98e922313b | ||
|
|
9a36373b8f | ||
|
|
cf8faa9e67 | ||
|
|
5ec067c1f8 | ||
|
|
e962fd2916 | ||
|
|
88bd67e7de | ||
|
|
902e8686d3 | ||
|
|
e2d49181da | ||
|
|
149bac55b1 | ||
|
|
88f7a3ccb9 | ||
|
|
8acce443f0 | ||
|
|
295a1f8f3b | ||
|
|
388e7a4265 | ||
|
|
13aceea8dc | ||
|
|
c203f3f0a9 | ||
|
|
f54d495c90 | ||
|
|
e6392a1570 | ||
|
|
53904e7cf4 | ||
|
|
2e88a496c2 | ||
|
|
ce4c45df13 | ||
|
|
0401597d3b | ||
|
|
2e5f9e45bb | ||
|
|
e4b5795fc7 | ||
|
|
03d0ea188c | ||
|
|
3082bd236b | ||
|
|
b7ca860417 | ||
|
|
64838e6367 | ||
|
|
1269d2b901 | ||
|
|
14d8506b72 | ||
|
|
d1d458db2b | ||
|
|
f656e99245 | ||
|
|
6dd937cef7 | ||
|
|
49047c85b9 | ||
|
|
d07267fed1 | ||
|
|
b53ce1d3f0 | ||
|
|
5a320c326b | ||
|
|
c4e526d315 | ||
|
|
d122e4254f | ||
|
|
5a1e7ea036 | ||
|
|
179f569113 | ||
|
|
b0f6dc199d | ||
|
|
7836f661cd | ||
|
|
dbcc1de37f | ||
|
|
93890c528b | ||
|
|
3d8d5936f9 | ||
|
|
2b04159dec | ||
|
|
2764004fad | ||
|
|
85f1bb8f2b | ||
|
|
231ae2c353 | ||
|
|
e92b6dd5f9 | ||
|
|
2a8e0e1cc8 | ||
|
|
7d06e517e9 | ||
|
|
323524fed6 | ||
|
|
d426873ed1 | ||
|
|
5be5869b2f | ||
|
|
b1b4c1e9e7 | ||
|
|
a4054d702f | ||
|
|
0190301e09 | ||
|
|
9d1ce6a6d9 | ||
|
|
5005e2ca04 | ||
|
|
fa44a07938 | ||
|
|
4ba16db645 | ||
|
|
837415abfd | ||
|
|
2c20fd0d09 | ||
|
|
64a7136e08 | ||
|
|
b2b473b24a | ||
|
|
7aab8fa93a | ||
|
|
12c2851856 | ||
|
|
0da169dd84 | ||
|
|
2416827c25 | ||
|
|
1177a3522e | ||
|
|
102344e27a | ||
|
|
1831ef3e19 | ||
|
|
a9606ce870 | ||
|
|
6c80d5eab3 | ||
|
|
b114006543 | ||
|
|
32fbfb7da6 | ||
|
|
02465920fb | ||
|
|
3a5a376465 | ||
|
|
1c3c86e9f1 | ||
|
|
dcda09f90a | ||
|
|
66157397c1 | ||
|
|
9e22ffbebf | ||
|
|
648ab6115c | ||
|
|
8bc3b04f5b | ||
|
|
cfb84a6083 | ||
|
|
02c47726e1 | ||
|
|
b2a0093294 | ||
|
|
2a98d6b5d7 | ||
|
|
9f36301dc8 | ||
|
|
901fc555f4 | ||
|
|
4170ec6107 | ||
|
|
fe400f68c5 | ||
|
|
794669b346 | ||
|
|
dcfa85a5d5 | ||
|
|
15ad855f1d | ||
|
|
11244a49d9 | ||
|
|
1d2e8eb153 | ||
|
|
ad53fb19b4 | ||
|
|
ba850bac3b | ||
|
|
023a5989f8 | ||
|
|
c970011ccc | ||
|
|
07a43c3d9a | ||
|
|
a05b212b04 | ||
|
|
09faf31b67 | ||
|
|
9e1f9c1133 | ||
|
|
f19d2b9b84 | ||
|
|
a28f93863c | ||
|
|
c9f61669b8 | ||
|
|
dcce5ad3b3 | ||
|
|
6836e5923d | ||
|
|
335188c652 | ||
|
|
60a29dcb99 | ||
|
|
b55d5b3034 | ||
|
|
10ed4b3969 | ||
|
|
4a401b89d7 | ||
|
|
c195b4fc46 | ||
|
|
8f2e34c6a3 | ||
|
|
150d692df7 | ||
|
|
3e5bfff1b5 | ||
|
|
9c7e66a27d | ||
|
|
0ca274866b | ||
|
|
dc037f8d41 | ||
|
|
16cdc741cf | ||
|
|
9d5055176d | ||
|
|
d1e66e1296 | ||
|
|
1fc098e696 | ||
|
|
878cc8defb | ||
|
|
8153911160 | ||
|
|
fbdc810887 | ||
|
|
396143004c | ||
|
|
1f45732700 | ||
|
|
574cb41c18 | ||
|
|
d9d6c425e7 | ||
|
|
58b6484dbe | ||
|
|
ca43fe2798 | ||
|
|
87a64ccedc | ||
|
|
89a3d00297 | ||
|
|
1497665f96 | ||
|
|
27b173374e | ||
|
|
2a13dba8ac | ||
|
|
77301b126c | ||
|
|
90cfa00115 | ||
|
|
5cf961edb9 | ||
|
|
b2276e47de | ||
|
|
893b46139a | ||
|
|
60e29627c0 | ||
|
|
3b81cf6c35 | ||
|
|
5c067d30a0 | ||
|
|
ceaf493811 | ||
|
|
10e04e2b13 | ||
|
|
726f67c64b | ||
|
|
c7b7624c1c | ||
|
|
d600529ec0 | ||
|
|
b53b3526a2 | ||
|
|
38bb23eb18 | ||
|
|
3937ff8221 | ||
|
|
abbfe244b5 | ||
|
|
4ddb8aa0dd | ||
|
|
a791470de7 | ||
|
|
17f504f548 | ||
|
|
773198537c | ||
|
|
5ac658c8f0 | ||
|
|
8767e4a941 | ||
|
|
8c4af073f4 | ||
|
|
c79f38584a | ||
|
|
36c08dd97c | ||
|
|
69b7b3dd7d | ||
|
|
738c75fed8 | ||
|
|
4eb1d03fb3 | ||
|
|
ba4ec6c967 | ||
|
|
97836f0e55 | ||
|
|
fdfb85f695 | ||
|
|
ab1baf4832 | ||
|
|
9730032866 | ||
|
|
5b656eecf6 | ||
|
|
9ae38eaa7c | ||
|
|
cb33bd71df | ||
|
|
d9b4e7b8bf | ||
|
|
0389bf5214 | ||
|
|
4267fa08d1 | ||
|
|
65c8fbd452 | ||
|
|
f36162fddc | ||
|
|
5149f7d894 | ||
|
|
20b5bed1cb | ||
|
|
f18dd1905d | ||
|
|
aa9d7f1cdc | ||
|
|
2742662254 | ||
|
|
a5df391166 | ||
|
|
59e6706b75 | ||
|
|
8461bb1e03 | ||
|
|
b873e208b4 | ||
|
|
873e72df8c | ||
|
|
c8bd6fc5b4 | ||
|
|
fed68b83b4 | ||
|
|
0ef8a4e1df | ||
|
|
c393b3b367 | ||
|
|
b5a17f762c | ||
|
|
fafc81ed1a | ||
|
|
cc56bdc787 | ||
|
|
4254438d8d | ||
|
|
97c15af238 | ||
|
|
d22ff8a158 | ||
|
|
fdb5a2791f | ||
|
|
c3a93fb995 | ||
|
|
f2a8d38d2a | ||
|
|
9e24c6eac0 | ||
|
|
fe4d12ce22 | ||
|
|
eb08486039 | ||
|
|
ccf83c634a | ||
|
|
3fd69749e7 | ||
|
|
594df5fc08 | ||
|
|
539070820d | ||
|
|
564a56d99b | ||
|
|
5adf50d93c | ||
|
|
d80e8039d7 | ||
|
|
0e6d67b23b | ||
|
|
be5270697a | ||
|
|
8d28851263 | ||
|
|
3d2115c93e | ||
|
|
91002ae3cc | ||
|
|
148c18e658 | ||
|
|
a2a5c926b6 | ||
|
|
ea6b1d8449 | ||
|
|
ac9be78e27 | ||
|
|
151dabb2af | ||
|
|
340465c929 | ||
|
|
d45bcddd15 | ||
|
|
0e0786331a | ||
|
|
c5db23f296 | ||
|
|
44abc8dfa6 | ||
|
|
3fdb2f767d | ||
|
|
0485f05da9 | ||
|
|
0a5eb65231 | ||
|
|
19ffebaf3e | ||
|
|
ce1a90d639 | ||
|
|
d25af48797 | ||
|
|
ebb836dacb | ||
|
|
d83e202f00 | ||
|
|
3ccf806064 | ||
|
|
6f1e01f8bd | ||
|
|
1023c34b1c | ||
|
|
faa29d596c | ||
|
|
add00a96ed | ||
|
|
82fac41244 | ||
|
|
5eb44e22a9 | ||
|
|
2e09fa7325 | ||
|
|
fe3c24b1ee | ||
|
|
aa221597bc | ||
|
|
579a8ee229 | ||
|
|
5105c5eab6 | ||
|
|
787fe6e7a5 | ||
|
|
6671f8d099 | ||
|
|
9ac9b69aa2 | ||
|
|
e61028cb18 | ||
|
|
661d23eaf5 | ||
|
|
666040e3e5 | ||
|
|
aebc272449 | ||
|
|
fd884581e4 | ||
|
|
9b1bf5c7f1 | ||
|
|
c9e620a920 | ||
|
|
41d40dd62f | ||
|
|
30f5b68264 | ||
|
|
f7b3f4b90e | ||
|
|
a99c11c14c | ||
|
|
45b7fc445b | ||
|
|
16a4888c52 | ||
|
|
17752f1337 | ||
|
|
abb45a68db | ||
|
|
1280a54ef3 | ||
|
|
f2d243fa68 | ||
|
|
a4787130f4 | ||
|
|
af7985e46c | ||
|
|
e9d1b5c2d0 | ||
|
|
45b598d236 | ||
|
|
fc37265da5 | ||
|
|
a4ec13eb0e | ||
|
|
2fa52007af | ||
|
|
d9f9cd1140 | ||
|
|
8b6df88783 | ||
|
|
345cff08c0 | ||
|
|
57428112ac | ||
|
|
a18d4ff154 | ||
|
|
d1cd07b9f3 | ||
|
|
e67f8e917a | ||
|
|
be2fedfe50 | ||
|
|
7ad2be172e | ||
|
|
abc605c9c9 | ||
|
|
3e94805220 | ||
|
|
db2e1d170e | ||
|
|
96ebdcaf16 | ||
|
|
553b2a3b12 | ||
|
|
3e13ef42eb | ||
|
|
d651a1fcec | ||
|
|
b193b318c1 | ||
|
|
ef3714223b | ||
|
|
3d8dbbbac3 | ||
|
|
013efdde25 | ||
|
|
816aa4e465 | ||
|
|
046c2c8972 | ||
|
|
d80e9cdf64 | ||
|
|
7576136b4a | ||
|
|
c3b223ce60 | ||
|
|
5aa67f56e6 | ||
|
|
fff4d1f44e | ||
|
|
0d9956273c | ||
|
|
3fada4e0b4 | ||
|
|
65b23ac45e | ||
|
|
4ac34c0141 | ||
|
|
8bd614bb1e | ||
|
|
4253d6d5f0 | ||
|
|
6a4752dcdc | ||
|
|
5876b40f08 | ||
|
|
5983434a70 | ||
|
|
a3d44a1e69 | ||
|
|
d364bbd5a7 | ||
|
|
f341e1b2be | ||
|
|
9af389b200 | ||
|
|
2ae4adf2d7 | ||
|
|
178b2a1e88 | ||
|
|
18db343cdc | ||
|
|
f0c821282a | ||
|
|
d673ead481 | ||
|
|
b33715db15 | ||
|
|
99424ad562 | ||
|
|
dc6f641fd2 | ||
|
|
f20a20f3f1 | ||
|
|
708a4bc3bc | ||
|
|
ef7ed21a9d | ||
|
|
b1abf455c1 | ||
|
|
d5456cf278 | ||
|
|
99343d40ba | ||
|
|
ee03a7ad3b | ||
|
|
ce1a7d698a | ||
|
|
87bf70fa8e | ||
|
|
ebd2a303bf | ||
|
|
e28776d361 | ||
|
|
dac1429aa9 | ||
|
|
e767605e94 | ||
|
|
97c493241f | ||
|
|
8ea90d8bc9 | ||
|
|
ae7b1851ec | ||
|
|
e3d62c22d3 | ||
|
|
7200c4951d | ||
|
|
84056c9347 | ||
|
|
09cf6eeecb | ||
|
|
03230fc842 | ||
|
|
63cf3aaa3f | ||
|
|
18ff694f02 | ||
|
|
4f79ceedd9 | ||
|
|
f7ca72fb41 | ||
|
|
a06b3f0307 | ||
|
|
d926dd1610 | ||
|
|
51bc893bc5 | ||
|
|
fbe761f7f6 | ||
|
|
5ebe911933 | ||
|
|
3919250da2 | ||
|
|
b3aee28388 | ||
|
|
9d20ab3024 | ||
|
|
aaa69f6717 | ||
|
|
355a11a414 | ||
|
|
ffc69d406c | ||
|
|
922d50079a | ||
|
|
dd163b62ae | ||
|
|
bd80e220b9 | ||
|
|
aef4b16d4c | ||
|
|
975171609e | ||
|
|
5bf46a9093 | ||
|
|
f27a1f9bfb | ||
|
|
1b26e2d5da | ||
|
|
88222daa3d | ||
|
|
81c5b41ce1 | ||
|
|
9650e6733e | ||
|
|
c8905ec29a | ||
|
|
b4620f01f9 | ||
|
|
2e462a19d3 | ||
|
|
069f932e59 | ||
|
|
126e3de91a | ||
|
|
ba0dccaae4 | ||
|
|
e1b6e5f212 | ||
|
|
8d79dc8738 | ||
|
|
78108c2aba | ||
|
|
cdafc723fb | ||
|
|
0d70884dce | ||
|
|
765efa325e | ||
|
|
89ffcbbe41 | ||
|
|
95ae23b0e7 | ||
|
|
d5cb6fed67 | ||
|
|
d0fef18378 | ||
|
|
d640c0f41f | ||
|
|
a2fa5e3ff7 | ||
|
|
a3eea9958e | ||
|
|
db27331d7b | ||
|
|
fdfb31f164 | ||
|
|
f93c3331b3 | ||
|
|
ab7ac4fbb9 | ||
|
|
bc39a1a293 | ||
|
|
2668130e70 | ||
|
|
d27ed3722b | ||
|
|
dae18308c9 | ||
|
|
d66555e42f | ||
|
|
9f52d8a3b1 | ||
|
|
757ea91932 | ||
|
|
02a804f1c5 | ||
|
|
9b500df0d9 | ||
|
|
5a89575b3a | ||
|
|
b8c4d7527b | ||
|
|
ac5d46cfa7 | ||
|
|
bc9e96e86f | ||
|
|
f88c435dd0 | ||
|
|
e4b91005cf | ||
|
|
a260bfd83b | ||
|
|
18e262a100 | ||
|
|
4bd1f526ab | ||
|
|
27847d7eb2 | ||
|
|
b2a8d3f0f3 | ||
|
|
49adac9564 | ||
|
|
a19d1133b1 | ||
|
|
dde91717e4 | ||
|
|
e9050afd67 | ||
|
|
165d2837cf | ||
|
|
ac7549edca | ||
|
|
4d96bc72e0 | ||
|
|
3411eee20f | ||
|
|
74de97eeca | ||
|
|
4e3cc25012 | ||
|
|
90c1db393e | ||
|
|
2f43274aa4 | ||
|
|
aeca09db09 | ||
|
|
c107f22c67 | ||
|
|
68fe51e8da | ||
|
|
8d08d67cf1 | ||
|
|
4f1782f66e | ||
|
|
3f77725cd3 | ||
|
|
5635f33a32 | ||
|
|
bca4b7111b | ||
|
|
6a6366b0d6 | ||
|
|
16bf2c70c5 | ||
|
|
4b3edb742c | ||
|
|
fcf23fc9e9 | ||
|
|
af5ef510c5 | ||
|
|
05401e2b81 | ||
|
|
9fde0110b6 | ||
|
|
b03f8ddb2e | ||
|
|
a26df2a022 | ||
|
|
d68d6674e8 | ||
|
|
a8f0f6bb90 | ||
|
|
dd9c92d5bf | ||
|
|
84df14dd70 | ||
|
|
560094ad92 | ||
|
|
7ea1be9c01 | ||
|
|
700f13bffd | ||
|
|
b6aa7c1aa9 | ||
|
|
eb4d183e48 | ||
|
|
77e4e81e1e | ||
|
|
88f5cb6eb0 | ||
|
|
efae552f3e | ||
|
|
46b277421a | ||
|
|
42908126b9 | ||
|
|
a467392cbd | ||
|
|
78d0bcf49d | ||
|
|
02a0ced9b0 | ||
|
|
4ccfe1c9f2 | ||
|
|
830c0c5c2f | ||
|
|
5548a37465 | ||
|
|
2f9a600de2 | ||
|
|
559db11a20 | ||
|
|
76c78e295b | ||
|
|
debbd5ff4b | ||
|
|
841174f302 | ||
|
|
8c55844f91 | ||
|
|
0b990bf0f5 | ||
|
|
104d7e2abc | ||
|
|
5ba69e1af1 | ||
|
|
f3a0b5c7d7 | ||
|
|
7f4265facc | ||
|
|
63d1fb3bed | ||
|
|
36af6d25a5 | ||
|
|
8f387f7255 | ||
|
|
792e735e08 | ||
|
|
3b76de83eb | ||
|
|
ad9f738cfa | ||
|
|
49455199e5 | ||
|
|
64426502ea | ||
|
|
8cc112d196 | ||
|
|
e78cd49861 | ||
|
|
cfb4f756d6 | ||
|
|
ba0908216c | ||
|
|
a3a0334fad | ||
|
|
cf7460c3a8 | ||
|
|
db0fbb6448 | ||
|
|
8ac45f20bb | ||
|
|
b3707cca0b | ||
|
|
a69ee3ece9 | ||
|
|
750d9a30c9 | ||
|
|
41c5b2b5ac | ||
|
|
6cf3f9b28e | ||
|
|
269cf03472 | ||
|
|
5ccfc4adbd | ||
|
|
b6f44f98be | ||
|
|
0dca57e9ec | ||
|
|
22c4da152e | ||
|
|
05d66f862d | ||
|
|
b09f5b2cf2 | ||
|
|
a2510aaa0e | ||
|
|
947f589916 | ||
|
|
bbd8980a20 | ||
|
|
831052f118 | ||
|
|
c137569f91 | ||
|
|
dae61226fa | ||
|
|
3ad4594a88 | ||
|
|
112acb9a62 | ||
|
|
ecee224e81 | ||
|
|
a3645dea34 | ||
|
|
7c09f44898 | ||
|
|
f401ea9aae | ||
|
|
bf8517f1e6 | ||
|
|
528087e308 | ||
|
|
1f59ff72f9 | ||
|
|
315be873c4 | ||
|
|
b8d019cdb4 | ||
|
|
102f23bbdb | ||
|
|
b85c24a79c | ||
|
|
eee29daaf9 | ||
|
|
aa9dfabde2 | ||
|
|
5999a26fba | ||
|
|
1e82595ffb | ||
|
|
e373fa476b | ||
|
|
dceb5364f4 | ||
|
|
baacbc8e24 | ||
|
|
84b15f0883 | ||
|
|
c0da9d2d35 | ||
|
|
0fe0be2733 | ||
|
|
eafa947338 | ||
|
|
9d13ae8d01 | ||
|
|
2a64e4bae5 | ||
|
|
c3fd20db59 | ||
|
|
6f91f37cd0 | ||
|
|
5158730b81 | ||
|
|
2ad2d2ca96 | ||
|
|
b5fd29774f | ||
|
|
9f8f330e96 | ||
|
|
1ad3b1b333 | ||
|
|
dfc24425c3 | ||
|
|
c02577bb51 | ||
|
|
c9e6a96a61 | ||
|
|
9778626f34 | ||
|
|
34b2d0dae1 | ||
|
|
59a7528fd6 | ||
|
|
11d9859ef1 | ||
|
|
e44ed752ad | ||
|
|
52b2dd5b89 | ||
|
|
c46636f218 | ||
|
|
dc531462e1 | ||
|
|
92ba98621c | ||
|
|
d41d051f1b | ||
|
|
c5ae0effac | ||
|
|
b3974c6ed3 | ||
|
|
589a00e379 | ||
|
|
8d9836ca02 | ||
|
|
8b5bf8632e | ||
|
|
1846114491 | ||
|
|
2c461c794e | ||
|
|
109363daa4 | ||
|
|
41d26bee8d | ||
|
|
7269a170fb | ||
|
|
441f2ed9b0 | ||
|
|
15275433d4 | ||
|
|
1ac02d2f58 | ||
|
|
8ee6257e92 | ||
|
|
d81ef2bc5c | ||
|
|
acb64dff97 | ||
|
|
3f6cf76a8c | ||
|
|
3ef2dab465 | ||
|
|
a5a56dd067 | ||
|
|
04957a6d30 | ||
|
|
5da54888f8 | ||
|
|
6c93365b0f | ||
|
|
1c4a8bfb66 | ||
|
|
d51a1d3b3d | ||
|
|
aced2e7da6 | ||
|
|
fa1ceca83a | ||
|
|
0ea05d34e6 | ||
|
|
09db4f672b | ||
|
|
4ba80a3a8c | ||
|
|
ae208d2b17 | ||
|
|
6f51cb3b98 | ||
|
|
f6159ee1a2 | ||
|
|
7f401ae895 | ||
|
|
0587b7043a | ||
|
|
0bbf51d566 | ||
|
|
92c6eae6b0 | ||
|
|
141354cd79 | ||
|
|
f6e9fc7d71 | ||
|
|
d8418be7d1 | ||
|
|
82066946e5 | ||
|
|
4b1defc8d8 | ||
|
|
da90c1cd2c | ||
|
|
3aa54fa027 | ||
|
|
b156db400d | ||
|
|
9aba2b7583 | ||
|
|
a9931223f0 | ||
|
|
aacf9442e4 | ||
|
|
7db10ecf00 | ||
|
|
a0a5b2aea5 | ||
|
|
eb835598a4 | ||
|
|
f0ab6df5b6 | ||
|
|
075144faa3 | ||
|
|
32468a40cb | ||
|
|
38611e9dd7 | ||
|
|
4d78def31e | ||
|
|
3be0c1217a | ||
|
|
42d3a50aa2 | ||
|
|
52f1464029 | ||
|
|
68ce62e2e9 | ||
|
|
3dd41c0d37 | ||
|
|
7699a1f827 | ||
|
|
e473a00f97 | ||
|
|
52f2d580eb | ||
|
|
d7b1e3576f | ||
|
|
93799fbe9d | ||
|
|
b84644740d | ||
|
|
2dc35c1399 | ||
|
|
2cb171f6f5 | ||
|
|
a939cd9c89 | ||
|
|
d5270d113b | ||
|
|
b20903960b | ||
|
|
c0cb545b41 | ||
|
|
35c9f66a75 | ||
|
|
796d03d105 | ||
|
|
8723caf78d | ||
|
|
187f50a344 | ||
|
|
8d04606c4d | ||
|
|
b82d4f6e58 | ||
|
|
87e34967ef | ||
|
|
e32499c546 | ||
|
|
aa0b49228d | ||
|
|
57301a7f9c | ||
|
|
d1842ea8fb | ||
|
|
558151061e | ||
|
|
181723e573 | ||
|
|
6bc1db44bc | ||
|
|
737b57ed5f | ||
|
|
4d287d1ae2 | ||
|
|
f26eb5ada4 | ||
|
|
94ed275ba6 | ||
|
|
ac2f44da33 | ||
|
|
75fa1fbfbf | ||
|
|
5e689faea8 | ||
|
|
649a50b401 | ||
|
|
e94397a97d | ||
|
|
4ec58724d7 | ||
|
|
8cb294aa4a | ||
|
|
2b3a9a52b3 | ||
|
|
138cbb94b2 | ||
|
|
e56d6dea81 | ||
|
|
43f30e44e4 | ||
|
|
9e6db17ef4 | ||
|
|
0e631ad2f2 | ||
|
|
6229d8d2ff | ||
|
|
4647d09b50 | ||
|
|
4ad3a54026 | ||
|
|
05e5feac41 | ||
|
|
67df469c58 | ||
|
|
689e9ccfb5 | ||
|
|
9cd4bc407a | ||
|
|
61996c2bbf | ||
|
|
6d3dd99eb2 | ||
|
|
f0b484892c | ||
|
|
8352cbb7a3 | ||
|
|
6da08b71dc | ||
|
|
5d99059bf9 | ||
|
|
fa41b106ab | ||
|
|
4e3fd7d420 | ||
|
|
791d8354da | ||
|
|
10f19e6fc3 | ||
|
|
4c61a13057 | ||
|
|
7b3f62244f | ||
|
|
d347928e4d | ||
|
|
cc31079a11 | ||
|
|
4676648b78 | ||
|
|
c7594c9111 | ||
|
|
173cb7359d | ||
|
|
af295879fb | ||
|
|
95dd20a151 | ||
|
|
b65fce86bf | ||
|
|
3b8d3bbcbf | ||
|
|
f9e976467f | ||
|
|
118a27be2f | ||
|
|
59c89428e5 | ||
|
|
202ca5e782 | ||
|
|
d6f185d8e5 | ||
|
|
54ca474dba | ||
|
|
1ca383b23b | ||
|
|
179fde3a7b | ||
|
|
50198ffcf1 | ||
|
|
a446c25598 | ||
|
|
a7fb831035 | ||
|
|
4f5e0c9f85 | ||
|
|
8164b91dc4 | ||
|
|
4d5fca27ef | ||
|
|
fe59e4b9fe | ||
|
|
5c88685495 | ||
|
|
9359431bad | ||
|
|
72716f51ce | ||
|
|
382deef750 | ||
|
|
161e211734 | ||
|
|
263a248119 | ||
|
|
725251d294 | ||
|
|
4dff873013 | ||
|
|
60ede67abd | ||
|
|
906bfc8464 | ||
|
|
0d4f08ad9c | ||
|
|
a52bf87b7d | ||
|
|
40c7645d6e | ||
|
|
5b886a23fd | ||
|
|
65797caa7b | ||
|
|
66abb39880 | ||
|
|
95a12e0ee8 | ||
|
|
27951442b8 | ||
|
|
c42e053ae9 | ||
|
|
e7048c8c8b | ||
|
|
d8e0b6c977 | ||
|
|
5e6c34c61c | ||
|
|
828f073ebd | ||
|
|
2dd5701ac0 | ||
|
|
531439198e | ||
|
|
3d9a4d585e | ||
|
|
54eda59286 | ||
|
|
5a4f75c9f8 | ||
|
|
7810f665f1 | ||
|
|
697e2dd330 | ||
|
|
936c64ff51 | ||
|
|
1ea6549548 | ||
|
|
32d9949e4d | ||
|
|
01ba56a8a6 | ||
|
|
e109c42515 | ||
|
|
e4c7091647 | ||
|
|
960b6e5897 | ||
|
|
82eef8d5ee | ||
|
|
aa3848b2c8 | ||
|
|
3b5d0d141a | ||
|
|
c8fc715fe2 | ||
|
|
e5fe3a8e16 | ||
|
|
e0c6cd3dcc | ||
|
|
2edd510ab7 | ||
|
|
2b3e2ebc11 | ||
|
|
ab4cb59326 | ||
|
|
57c8615af3 | ||
|
|
b903173211 | ||
|
|
10cc8b89c4 | ||
|
|
5d9ae3cdb4 | ||
|
|
192d251ee8 | ||
|
|
031962608b | ||
|
|
25f6f478a5 | ||
|
|
e80f09f849 | ||
|
|
c159f91f06 | ||
|
|
eb790a74d2 | ||
|
|
e47451f5a0 | ||
|
|
252b8c83bf | ||
|
|
09666ff294 | ||
|
|
baf2e501dc | ||
|
|
5ac60f0a20 | ||
|
|
fb2ee8ec34 | ||
|
|
94e0db7963 | ||
|
|
b253246959 | ||
|
|
1ac73a247e | ||
|
|
2014115bca | ||
|
|
4a944dc397 | ||
|
|
3838cc4ec4 | ||
|
|
85748f2e6f | ||
|
|
d7b8aa56dc | ||
|
|
16e096cf86 | ||
|
|
50af74c38f | ||
|
|
bfc209b120 | ||
|
|
e7f9331e4b | ||
|
|
ab8e8aa2a1 | ||
|
|
1ef826d1da | ||
|
|
52bddaa97b | ||
|
|
e4dc3908bb | ||
|
|
1daadb812f | ||
|
|
9c2723391b | ||
|
|
7a8796414c | ||
|
|
f8f13c536f | ||
|
|
1adb69c0fd | ||
|
|
5f5830b960 | ||
|
|
9fb4f35bb6 | ||
|
|
c002e6f926 | ||
|
|
c154e5262e | ||
|
|
6ddef1cca6 | ||
|
|
9d68d95825 | ||
|
|
a4233e1a16 | ||
|
|
39373c6d2d | ||
|
|
d29acc90a2 | ||
|
|
a371235b0d | ||
|
|
53b4891a5e | ||
|
|
646ded9123 | ||
|
|
ea0b8f48e6 | ||
|
|
2067d7e300 | ||
|
|
866571ab41 | ||
|
|
4e1fa03087 | ||
|
|
b45572167d | ||
|
|
57a160f1b3 | ||
|
|
29ae8640d9 | ||
|
|
ee6377e54b | ||
|
|
b0f1ae7ea3 | ||
|
|
7aae2b72dc | ||
|
|
73fcbbd748 | ||
|
|
e7bedde835 | ||
|
|
7253edb1e1 | ||
|
|
efa28125c6 | ||
|
|
9832fcfe14 | ||
|
|
49c71f9f2d | ||
|
|
791770cf6e | ||
|
|
9ce9188ff6 | ||
|
|
362066a9b7 | ||
|
|
26ff9371d9 | ||
|
|
4a36f86c81 | ||
|
|
85144e47ff | ||
|
|
b417d961ec | ||
|
|
ef4ea84cb5 | ||
|
|
b422665a3c | ||
|
|
186dadf14d | ||
|
|
04e64db945 | ||
|
|
f60994ad24 | ||
|
|
cfa1ca96f2 | ||
|
|
4d9ff76433 | ||
|
|
9e1b7e0464 | ||
|
|
a77c6ff3a2 | ||
|
|
099fde97d5 | ||
|
|
f8fc2463ee | ||
|
|
e246b78846 | ||
|
|
c1e128bd5a | ||
|
|
8284075ee4 | ||
|
|
f7db09a128 | ||
|
|
f9c3d54bd1 | ||
|
|
0773f4033e | ||
|
|
d068fb321f | ||
|
|
8d054c940c | ||
|
|
80f1d79218 | ||
|
|
64f5413253 | ||
|
|
2abbbd9e70 | ||
|
|
a5c471af7e | ||
|
|
edd2f0232c | ||
|
|
b2c3b2dd8e | ||
|
|
4f1cecb8e7 | ||
|
|
b336eac343 | ||
|
|
2a09f6fa45 | ||
|
|
0e702eb799 | ||
|
|
dea841103d | ||
|
|
82856cf816 | ||
|
|
8aecda03ed | ||
|
|
5c817082a0 | ||
|
|
15c521a7fe | ||
|
|
989de4a1bf | ||
|
|
d9eeb45b03 | ||
|
|
fe570e000c | ||
|
|
01dc0249de | ||
|
|
93523d572e | ||
|
|
4f7a4464df | ||
|
|
e09293a15e | ||
|
|
827b17a52b | ||
|
|
a610379965 | ||
|
|
4e2c3051be | ||
|
|
50915cb7ed | ||
|
|
f4e2d26d58 | ||
|
|
cb0051ae60 | ||
|
|
a238cec12b | ||
|
|
393bf23c0c | ||
|
|
e7d9ba135c | ||
|
|
0a0003c110 | ||
|
|
7e1b27554c | ||
|
|
fb2a56e2d6 | ||
|
|
84218bfb45 | ||
|
|
dd78243406 | ||
|
|
484cf9c0e8 | ||
|
|
a04d1b4f97 | ||
|
|
831c10bdfc | ||
|
|
87603e400d | ||
|
|
4426216725 | ||
|
|
bc15b953b4 | ||
|
|
6d20b12043 | ||
|
|
2385c2a430 | ||
|
|
87384ea684 | ||
|
|
04dae799a2 | ||
|
|
0768c39036 | ||
|
|
8dd8d4d2d4 | ||
|
|
49b06beedf | ||
|
|
4c562a4457 | ||
|
|
fdbbf11887 | ||
|
|
f1e64b349a | ||
|
|
224f3a05cf | ||
|
|
ff56d6861d | ||
|
|
1873fbefba | ||
|
|
80b479e502 | ||
|
|
1f7c9adeeb | ||
|
|
9ebc3b058d | ||
|
|
e21b443a45 | ||
|
|
da590120c4 | ||
|
|
561d53885e | ||
|
|
44ad67ca8c | ||
|
|
3fe8fc75aa | ||
|
|
3cc2da3328 | ||
|
|
a3cd90dd71 | ||
|
|
d6cfdd3bae | ||
|
|
ba08baabb9 | ||
|
|
497b712f67 | ||
|
|
f00dfed7ac | ||
|
|
3c08461685 | ||
|
|
5ef2b4fdc0 | ||
|
|
23d2326d1d | ||
|
|
e338f9f190 | ||
|
|
c8ff626fe2 | ||
|
|
4e424d51f4 | ||
|
|
6b50323b07 | ||
|
|
65c148536d | ||
|
|
f99f62bee4 | ||
|
|
365f5449ca | ||
|
|
ff52f207cc | ||
|
|
ee0eed143a | ||
|
|
5127380727 | ||
|
|
ced654880c | ||
|
|
936d3b9f83 | ||
|
|
6e31230adc | ||
|
|
05ebe93180 | ||
|
|
aaf24e2552 | ||
|
|
8d961dfd25 | ||
|
|
659b37ef9d | ||
|
|
0abf6c9045 | ||
|
|
4630191fa4 | ||
|
|
b7b178f49c | ||
|
|
687066af3d | ||
|
|
6b168ab0c8 | ||
|
|
948625de9d | ||
|
|
3848eb8b16 | ||
|
|
3abe0b9d49 | ||
|
|
d7fabe81fe | ||
|
|
1dc81669c2 | ||
|
|
01d0e12603 | ||
|
|
397e569aaf | ||
|
|
79267f9e60 | ||
|
|
48ebe5736a | ||
|
|
c2064c78b2 | ||
|
|
36608a283b | ||
|
|
a1c87e1e15 | ||
|
|
fa7240a501 | ||
|
|
ffb4d38e24 | ||
|
|
f57c188f2e | ||
|
|
8471c4fb26 | ||
|
|
c97acd18e7 | ||
|
|
ffb590bfcc | ||
|
|
6137f98eb5 | ||
|
|
a6f6539691 | ||
|
|
10c038d9bf | ||
|
|
2d2eaa3eff | ||
|
|
4d28b1f0cd | ||
|
|
3de800a607 | ||
|
|
eff5923778 | ||
|
|
a79faace1b | ||
|
|
9550777b9d | ||
|
|
c8521a3c33 | ||
|
|
d71eb2cf39 | ||
|
|
160fb740f4 | ||
|
|
48e9f39513 | ||
|
|
8afa596fdf | ||
|
|
d3e45c297c | ||
|
|
2c98939c18 | ||
|
|
a18a7409fb | ||
|
|
2f19cf4524 | ||
|
|
da96707dca | ||
|
|
c3a8bdc240 | ||
|
|
21624ef273 | ||
|
|
1032c9b917 | ||
|
|
2e07902d76 | ||
|
|
6e23e1840a | ||
|
|
3e22e45066 | ||
|
|
79d1bd6c8f | ||
|
|
fe341a16f5 | ||
|
|
62abf46b3f | ||
|
|
a95a5e0d9b | ||
|
|
d6f6ceaba5 | ||
|
|
0922f8af59 | ||
|
|
6812302ff9 | ||
|
|
f3f86b8e78 | ||
|
|
1a2680b9e5 | ||
|
|
435bbb6a8c | ||
|
|
3cf44e0a53 | ||
|
|
40b367513f | ||
|
|
9f563d584a | ||
|
|
af4adf5379 | ||
|
|
2560146da3 | ||
|
|
eae3a6397a | ||
|
|
959611420b | ||
|
|
9522f2e8c7 | ||
|
|
e42a0b6cf0 | ||
|
|
d1c7032dec | ||
|
|
d241bc81ae | ||
|
|
cb7f39afa1 | ||
|
|
99b5ad9ebb | ||
|
|
c14b237757 | ||
|
|
d447c7dc32 | ||
|
|
247420c9c1 | ||
|
|
04b112e004 | ||
|
|
be0ce99007 | ||
|
|
69e8d0b65d | ||
|
|
94e685e1bd | ||
|
|
5899f3dd28 | ||
|
|
f82aa1d564 | ||
|
|
fe5c2cf4b7 | ||
|
|
43d40c6e9e | ||
|
|
3d44d8f79c | ||
|
|
ba6fe8ff2e | ||
|
|
5337258888 | ||
|
|
29bf9d6ef1 | ||
|
|
483beb6361 | ||
|
|
fdaec77594 | ||
|
|
8494a5761b | ||
|
|
45590eea22 | ||
|
|
834a64920b | ||
|
|
2c2ccb3618 | ||
|
|
fb5c8c39ae | ||
|
|
f7d39193a4 | ||
|
|
aec796b212 | ||
|
|
fb13bb2393 | ||
|
|
c517ec6779 | ||
|
|
e8cbf2a717 | ||
|
|
e9c86df468 | ||
|
|
6ba8c289c5 | ||
|
|
3f1672e89f | ||
|
|
15be5cbf1f | ||
|
|
4cf0e5e6db | ||
|
|
6b1898f12e | ||
|
|
b3426e9c0d | ||
|
|
38c1a1f3e0 | ||
|
|
4488e25f16 | ||
|
|
cfdb3058ee | ||
|
|
64adff5fea | ||
|
|
6efc58e3db | ||
|
|
f393989a5b | ||
|
|
d6ed20c1e4 | ||
|
|
7c524014c8 | ||
|
|
406d055f07 | ||
|
|
04a90b5dd1 | ||
|
|
8c5bfa0132 | ||
|
|
bc80c55353 | ||
|
|
9c3b51ca0f | ||
|
|
26584ff145 | ||
|
|
42c3d52863 | ||
|
|
64e72f7103 | ||
|
|
e955037980 | ||
|
|
fb99910c23 | ||
|
|
e620bd4d3f | ||
|
|
a44e761d9e | ||
|
|
ea1974d576 | ||
|
|
85a0bd43c2 | ||
|
|
278e0f126e | ||
|
|
d8652cfd4f | ||
|
|
377b7065aa | ||
|
|
1e9c79c587 | ||
|
|
78147aa342 | ||
|
|
cd4a2265b9 | ||
|
|
767ac1de10 | ||
|
|
d762215d13 | ||
|
|
1fd09593cf | ||
|
|
e45a0f6ce2 | ||
|
|
94f8525721 | ||
|
|
d73cf64e54 | ||
|
|
f945462bab | ||
|
|
b05946175b | ||
|
|
62f0f8190d | ||
|
|
bf8c0da4be | ||
|
|
dfdd6b3891 | ||
|
|
f5e0fc3e9a | ||
|
|
d741101fe3 | ||
|
|
e1620799c7 | ||
|
|
749c7adb13 | ||
|
|
921a733911 | ||
|
|
26d0004fe1 | ||
|
|
948af6d5b5 | ||
|
|
670a187a3c | ||
|
|
be9f725023 | ||
|
|
daf3f4cb1a | ||
|
|
5acda4cc71 | ||
|
|
8452af606b | ||
|
|
630d449520 | ||
|
|
7372cf7d99 | ||
|
|
b7e46c558f | ||
|
|
bf8f8710ea | ||
|
|
6ffe5b775d | ||
|
|
be0805b85b | ||
|
|
65b2359b27 | ||
|
|
8651aa73e8 | ||
|
|
78b542737a | ||
|
|
fccf86532f | ||
|
|
1857417601 | ||
|
|
ee7114a58c | ||
|
|
00fa91d0d6 | ||
|
|
b7a25bfc33 | ||
|
|
a2e46b9a1b | ||
|
|
a751fa22d2 | ||
|
|
e563a0b7db | ||
|
|
49085ca943 | ||
|
|
560a850a2b | ||
|
|
66782e2317 | ||
|
|
b60eb1544b | ||
|
|
cbe59fa3bf | ||
|
|
c97c6dc065 | ||
|
|
32fa97d68d | ||
|
|
aee4603269 | ||
|
|
29c7da5f1a | ||
|
|
6131f4e32b | ||
|
|
67e03e625d | ||
|
|
b124f9101b | ||
|
|
d11a3a6380 | ||
|
|
4cc0706b06 | ||
|
|
885f5a9781 | ||
|
|
60b5964577 | ||
|
|
6e1919414e | ||
|
|
8c8ec5e63e | ||
|
|
b8413ddd5b | ||
|
|
5d976b6e18 | ||
|
|
2b4317452b | ||
|
|
1c3011ba4b | ||
|
|
574a42f3b4 | ||
|
|
9005cd59e5 | ||
|
|
dd494d4ab7 | ||
|
|
7cca469a12 | ||
|
|
2aed432b4b | ||
|
|
0291a3ff56 | ||
|
|
5d7315280a | ||
|
|
254931b9a8 | ||
|
|
aa89744c95 | ||
|
|
1cda3f5ad7 | ||
|
|
8f1f64ffb6 | ||
|
|
2bc0a7795c | ||
|
|
4204da6ad4 | ||
|
|
7ac39b10f7 | ||
|
|
6b567e0066 | ||
|
|
df299d6edd | ||
|
|
4e34f05238 | ||
|
|
9962f6fd79 | ||
|
|
f9d492f4b1 | ||
|
|
a8bb2ef1c3 | ||
|
|
240c629cda | ||
|
|
952567dd3c | ||
|
|
10b396b4c2 | ||
|
|
699bbe21a4 | ||
|
|
27b67deca6 | ||
|
|
581b7ec553 | ||
|
|
acfbb1a44a | ||
|
|
d85a6d8fe4 | ||
|
|
20fbb5c63b | ||
|
|
1051a06a76 | ||
|
|
98dfc26208 | ||
|
|
1ba0a2cedd | ||
|
|
4afb13f98b | ||
|
|
b27d33675d | ||
|
|
00b9ae77f9 | ||
|
|
65219f3093 | ||
|
|
f78d1d4340 | ||
|
|
941edca597 | ||
|
|
a699435ede | ||
|
|
66d6417189 | ||
|
|
558e37c412 | ||
|
|
4f40d991ea | ||
|
|
549af99007 | ||
|
|
b336930093 | ||
|
|
c9a0edfb8b | ||
|
|
2c5668af46 | ||
|
|
751dea32ae | ||
|
|
cd8f4bfb1f | ||
|
|
a6cfcc6866 | ||
|
|
b8c4f603db | ||
|
|
0075e4b391 | ||
|
|
125af556ce | ||
|
|
963ad5c255 | ||
|
|
387f56cb7b | ||
|
|
b3deda38c9 | ||
|
|
2a5ca77454 | ||
|
|
727940d847 | ||
|
|
8cd42478e1 | ||
|
|
c11d34b26c | ||
|
|
339d7445b3 | ||
|
|
d16f05f2c8 | ||
|
|
5427b32a40 | ||
|
|
f73701239d | ||
|
|
f5a6fc0703 | ||
|
|
bdf5ba91a4 | ||
|
|
bc8f338771 | ||
|
|
3413bfc06a | ||
|
|
2056f0ce09 | ||
|
|
5eb8cfd691 | ||
|
|
e6a4254488 | ||
|
|
d478ad00d0 | ||
|
|
53eda861de | ||
|
|
cc1d86ba63 | ||
|
|
f0528f00e7 | ||
|
|
5cd2ad124d | ||
|
|
6c00e7a902 | ||
|
|
53170bbb58 | ||
|
|
467258e050 | ||
|
|
129be23c9e | ||
|
|
8e9290e86e | ||
|
|
7cf5bebf8e | ||
|
|
f7f9087fb5 | ||
|
|
256e7904fd | ||
|
|
c8ea1b6c38 | ||
|
|
2816b06c05 | ||
|
|
4c695ea088 | ||
|
|
a14d51806d | ||
|
|
0170977914 | ||
|
|
f61726b5ae | ||
|
|
fc27fdac57 | ||
|
|
47c59859ee | ||
|
|
6e76ab9c09 | ||
|
|
5f78b76702 | ||
|
|
5e0808c848 | ||
|
|
508f05a47e | ||
|
|
66b57f0323 | ||
|
|
cfac22b4c0 | ||
|
|
2ef67f20a7 | ||
|
|
7a73946ce1 | ||
|
|
6d22b5a3c6 | ||
|
|
50050a0e53 | ||
|
|
de17422793 | ||
|
|
6b5e83ce1d | ||
|
|
17d75d8a3b | ||
|
|
616405f7ae | ||
|
|
5c2dc043cd | ||
|
|
24a3c12f31 | ||
|
|
3e544282ff | ||
|
|
3c85a40648 | ||
|
|
ac3c336b98 | ||
|
|
f24f282442 | ||
|
|
0dfee4745c | ||
|
|
eb80f7a787 | ||
|
|
68fed2a1a6 | ||
|
|
10d118a8d0 | ||
|
|
e021c33191 | ||
|
|
7b7548196a | ||
|
|
e019c735e1 | ||
|
|
c253f2c7e2 | ||
|
|
0ce9133b55 | ||
|
|
6ac9683a32 | ||
|
|
1d7739d8da | ||
|
|
1de2a6d85c | ||
|
|
0a723a50dc | ||
|
|
34b91318f4 | ||
|
|
b11a7114a5 | ||
|
|
2ca5e1c8d6 | ||
|
|
7ae8c7b247 | ||
|
|
1e17e40868 | ||
|
|
1069019fd2 | ||
|
|
4422904a2e | ||
|
|
5d085b78bd | ||
|
|
0927c73b13 | ||
|
|
5fe8f9017f | ||
|
|
5cdffeaba1 | ||
|
|
6e7c7374fd | ||
|
|
fb7b41793b | ||
|
|
abbf9f01ab | ||
|
|
17698af5e3 | ||
|
|
b66fcdb3f7 | ||
|
|
7fc48b75dd | ||
|
|
07ac5370d8 | ||
|
|
7c8f1cf7af | ||
|
|
57a97e3fb3 | ||
|
|
061432147d | ||
|
|
8f3e5794b3 | ||
|
|
a112b5e231 | ||
|
|
67859aea44 | ||
|
|
37643ab0b2 | ||
|
|
b0ee11f7cc | ||
|
|
7647e29b21 | ||
|
|
a3e672f863 | ||
|
|
9058fe803d | ||
|
|
32f429a819 | ||
|
|
bf26656547 | ||
|
|
96e26247d7 | ||
|
|
8e538aa82f | ||
|
|
fa809b2c4b | ||
|
|
9a63cd36cd | ||
|
|
21d949daa8 | ||
|
|
330b90e046 | ||
|
|
693daafe29 | ||
|
|
c3b3fb8b74 | ||
|
|
62731bea20 | ||
|
|
c55fb583b8 | ||
|
|
9725aff83b | ||
|
|
1320691eb4 | ||
|
|
451f67c63d | ||
|
|
43b1b128b1 | ||
|
|
fc991cb59c | ||
|
|
17d3d2f754 | ||
|
|
73950b9857 | ||
|
|
61ee331f11 | ||
|
|
651319589c | ||
|
|
5479948bd4 | ||
|
|
1ec145ec87 | ||
|
|
8ab47cb075 | ||
|
|
b7b3dcf3ea | ||
|
|
f7da0b4525 | ||
|
|
f9a3380184 | ||
|
|
b61f08d3fa | ||
|
|
0503225928 | ||
|
|
d4d0b5501b | ||
|
|
6c5726c96f | ||
|
|
56972447b2 | ||
|
|
67e1796ef6 | ||
|
|
1ae8da3b3f | ||
|
|
3ed97f45fb | ||
|
|
ae2809cad4 | ||
|
|
b0a296477e | ||
|
|
629a4574db | ||
|
|
4be809499d | ||
|
|
b10063da9a | ||
|
|
5e54e13140 | ||
|
|
8f87c56312 | ||
|
|
1b18560e90 | ||
|
|
f86a5f9b09 | ||
|
|
f1b1bdb121 | ||
|
|
0365557b25 | ||
|
|
883d962838 | ||
|
|
35790a8990 | ||
|
|
8edc17dac9 | ||
|
|
502f5c8b5f | ||
|
|
bc28fb187c | ||
|
|
de0277713b | ||
|
|
1593eb4d47 | ||
|
|
70ba92f917 | ||
|
|
47cc71ea01 | ||
|
|
9453d67273 | ||
|
|
f758af826d | ||
|
|
6a1e5385e5 | ||
|
|
63487dca76 | ||
|
|
7d6f09f5c7 | ||
|
|
148eed3cdc | ||
|
|
05701317b4 | ||
|
|
7548fdae5d | ||
|
|
3e41d92c18 | ||
|
|
ad6c8b882e | ||
|
|
947ff655c5 | ||
|
|
183b7c85a1 | ||
|
|
4cf6947af7 | ||
|
|
b83709b269 | ||
|
|
c699d55175 | ||
|
|
781afaa852 | ||
|
|
007b03a2c2 | ||
|
|
ed18693345 | ||
|
|
7c99224bb7 | ||
|
|
c2c4090902 | ||
|
|
416288061a | ||
|
|
83376bc231 | ||
|
|
c0de98f9f2 | ||
|
|
70db0db221 | ||
|
|
526f26685d | ||
|
|
5d1220e629 | ||
|
|
b80fde4388 | ||
|
|
148f43b4a5 | ||
|
|
0d88213de5 | ||
|
|
36b8d74faa | ||
|
|
5021f28159 | ||
|
|
ee6a814576 | ||
|
|
807de9a0a9 | ||
|
|
9398b6b55b | ||
|
|
932bfcf374 | ||
|
|
e127bac7fd | ||
|
|
bccf13bf67 | ||
|
|
950bbd6dc2 | ||
|
|
e680ba85fa | ||
|
|
b23ede7d50 | ||
|
|
73047d8b35 | ||
|
|
ef5e0c2e75 | ||
|
|
dc9e560f9b | ||
|
|
ae5b07ba01 | ||
|
|
3384c23a56 | ||
|
|
c2259d42a8 | ||
|
|
370e9d089f | ||
|
|
cab8b18c68 | ||
|
|
3b283ab9aa | ||
|
|
e5b84e2f87 | ||
|
|
f86417d791 | ||
|
|
8dc3d23831 | ||
|
|
42993b15c6 | ||
|
|
ad817d4f23 | ||
|
|
77954bb3dd | ||
|
|
52f7a62e1e | ||
|
|
1b8ceb36fc | ||
|
|
ceea1f9d44 | ||
|
|
2f81f2b78a | ||
|
|
aba035eb3d | ||
|
|
5ca2702083 | ||
|
|
af588adce5 | ||
|
|
399684a58f | ||
|
|
5cf4c16f5b | ||
|
|
0fe2319dfc | ||
|
|
e759dad019 | ||
|
|
0c18abed33 | ||
|
|
89dad2fd84 | ||
|
|
a6a71f8c76 | ||
|
|
1fee190fd0 | ||
|
|
23ba3ca19e | ||
|
|
33d8363297 | ||
|
|
49dcf7cf59 | ||
|
|
9778445a74 | ||
|
|
c2cc90b27d | ||
|
|
b9feb81226 | ||
|
|
b06ddcdd86 | ||
|
|
16ef372b53 | ||
|
|
f0a34ea64e | ||
|
|
33f7dec5cf | ||
|
|
3005803598 | ||
|
|
eeaaf5d258 | ||
|
|
15819cc981 | ||
|
|
fe6bfb1ba2 | ||
|
|
5c8d7b2423 | ||
|
|
ff0801d783 | ||
|
|
ac4b0a3118 | ||
|
|
227084e92e | ||
|
|
a175f6e862 | ||
|
|
4043c461d7 | ||
|
|
b3b2aa6359 | ||
|
|
5bd2dca463 | ||
|
|
21740fd2c5 | ||
|
|
c11ef442fb | ||
|
|
b5a38001dd | ||
|
|
5b6f68cf72 | ||
|
|
3050e935a1 | ||
|
|
80a1fa9ece | ||
|
|
1851ba1434 | ||
|
|
c4b8a2505d | ||
|
|
a1d2d40ad3 | ||
|
|
a3881bb452 | ||
|
|
6e4ee8da2b | ||
|
|
2a0f79b90f | ||
|
|
5ccc98bc14 | ||
|
|
b1353e4d6e | ||
|
|
9f4de91554 | ||
|
|
d30d1088da | ||
|
|
c6e6346642 | ||
|
|
e08c8a1fc9 | ||
|
|
e50dbe0c43 | ||
|
|
d9c7bbd046 | ||
|
|
dac0e5b133 | ||
|
|
ce3bc91946 | ||
|
|
22c0e2813a | ||
|
|
9796987d59 | ||
|
|
4d275e4767 | ||
|
|
492d6c2826 | ||
|
|
765f009350 | ||
|
|
629e8b41f2 | ||
|
|
71c187a1ed | ||
|
|
00e991e2a0 | ||
|
|
8a80f97c06 | ||
|
|
3cbec411c7 | ||
|
|
3bcb89fb84 | ||
|
|
62b8a36ce9 | ||
|
|
27566abb06 | ||
|
|
dfb130270a | ||
|
|
1ba616f843 | ||
|
|
11fb0a4cb7 | ||
|
|
1557a4c3b0 | ||
|
|
ab28a7d65f | ||
|
|
d58a5e124a | ||
|
|
762347f005 | ||
|
|
4b76adf15b | ||
|
|
6be1b95241 | ||
|
|
38ad790612 | ||
|
|
86acb27e24 | ||
|
|
1b395fa21d | ||
|
|
92380485c8 | ||
|
|
27f9a21a2c | ||
|
|
cf7088a462 | ||
|
|
67554ef3b0 | ||
|
|
cf20b068ca | ||
|
|
0b9316d94a | ||
|
|
3011ebe547 | ||
|
|
4b77b0773e | ||
|
|
e5935a4737 | ||
|
|
a3a8472b82 | ||
|
|
212182d991 | ||
|
|
c82b8546bc | ||
|
|
fac4e3fcfc | ||
|
|
5b0122fed4 | ||
|
|
b46b5df16a | ||
|
|
cb51029335 | ||
|
|
e504b3ecbd | ||
|
|
0ad0ec0985 | ||
|
|
d1d32ada00 | ||
|
|
8058daa982 | ||
|
|
f4c5c0f5b5 | ||
|
|
2ecb8dab7d | ||
|
|
576d427f03 | ||
|
|
21aafea098 | ||
|
|
3ed2908563 | ||
|
|
2b188b54d8 | ||
|
|
306720e63e | ||
|
|
a308dd4471 | ||
|
|
8f33d21bc2 | ||
|
|
b9ee3ae030 | ||
|
|
c14b87b228 | ||
|
|
8a279aaf20 | ||
|
|
3a5e541b2d | ||
|
|
43574128b3 | ||
|
|
e4a9903844 | ||
|
|
0d30108dcb | ||
|
|
b7a79c70cc | ||
|
|
6e6f28d1ac | ||
|
|
d14978e549 | ||
|
|
1c28b729ad | ||
|
|
303194b08b | ||
|
|
2ee3bfaa25 | ||
|
|
029a94dd33 | ||
|
|
f6df9217b6 | ||
|
|
184fae4ba5 | ||
|
|
35b236651e | ||
|
|
c926770550 | ||
|
|
272eaf184f | ||
|
|
56fbb1fc33 | ||
|
|
33f6bf947e | ||
|
|
07326edb6b | ||
|
|
144610151c | ||
|
|
4228d3609e | ||
|
|
510936a2a0 | ||
|
|
5854e284ea | ||
|
|
a732606e55 | ||
|
|
84e300739c | ||
|
|
8edf9282c3 | ||
|
|
5a1acaaefc | ||
|
|
4fd7c210d8 | ||
|
|
a26a7d217d | ||
|
|
7b7f44d937 | ||
|
|
6cf89aa0f3 | ||
|
|
3be83784cd | ||
|
|
b6c163acd7 | ||
|
|
05a26b7279 |
@@ -1,26 +1,30 @@
|
|||||||
---
|
---
|
||||||
Language: Cpp
|
|
||||||
BasedOnStyle: Google
|
BasedOnStyle: Google
|
||||||
AccessModifierOffset: -1
|
AccessModifierOffset: -1
|
||||||
AlignAfterOpenBracket: Align
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveMacros: false
|
||||||
AlignConsecutiveAssignments: false
|
AlignConsecutiveAssignments: false
|
||||||
AlignConsecutiveDeclarations: false
|
AlignConsecutiveDeclarations: false
|
||||||
AlignEscapedNewlines: Left
|
AlignEscapedNewlines: Left
|
||||||
AlignOperands: true
|
AlignOperands: true
|
||||||
AlignTrailingComments: true
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
AllowAllParametersOfDeclarationOnNextLine: true
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
AllowShortBlocksOnASingleLine: false
|
AllowShortBlocksOnASingleLine: Never
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
AllowShortFunctionsOnASingleLine: All
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
AllowShortIfStatementsOnASingleLine: true
|
AllowShortLambdasOnASingleLine: All
|
||||||
AllowShortLoopsOnASingleLine: true
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
AlwaysBreakAfterDefinitionReturnType: None
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
AlwaysBreakAfterReturnType: None
|
AlwaysBreakAfterReturnType: None
|
||||||
AlwaysBreakBeforeMultilineStrings: true
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
AlwaysBreakTemplateDeclarations: true
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
BinPackArguments: true
|
BinPackArguments: true
|
||||||
BinPackParameters: true
|
BinPackParameters: true
|
||||||
BraceWrapping:
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
AfterClass: false
|
AfterClass: false
|
||||||
AfterControlStatement: false
|
AfterControlStatement: false
|
||||||
AfterEnum: false
|
AfterEnum: false
|
||||||
@@ -29,6 +33,7 @@ BraceWrapping:
|
|||||||
AfterObjCDeclaration: false
|
AfterObjCDeclaration: false
|
||||||
AfterStruct: false
|
AfterStruct: false
|
||||||
AfterUnion: false
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
BeforeCatch: false
|
BeforeCatch: false
|
||||||
BeforeElse: false
|
BeforeElse: false
|
||||||
IndentBraces: false
|
IndentBraces: false
|
||||||
@@ -38,6 +43,7 @@ BraceWrapping:
|
|||||||
BreakBeforeBinaryOperators: None
|
BreakBeforeBinaryOperators: None
|
||||||
BreakBeforeBraces: Attach
|
BreakBeforeBraces: Attach
|
||||||
BreakBeforeInheritanceComma: false
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
BreakBeforeTernaryOperators: true
|
BreakBeforeTernaryOperators: true
|
||||||
BreakConstructorInitializersBeforeComma: false
|
BreakConstructorInitializersBeforeComma: false
|
||||||
BreakConstructorInitializers: BeforeColon
|
BreakConstructorInitializers: BeforeColon
|
||||||
@@ -50,6 +56,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
|||||||
ConstructorInitializerIndentWidth: 4
|
ConstructorInitializerIndentWidth: 4
|
||||||
ContinuationIndentWidth: 4
|
ContinuationIndentWidth: 4
|
||||||
Cpp11BracedListStyle: true
|
Cpp11BracedListStyle: true
|
||||||
|
DeriveLineEnding: true
|
||||||
DerivePointerAlignment: false
|
DerivePointerAlignment: false
|
||||||
DisableFormat: false
|
DisableFormat: false
|
||||||
ExperimentalAutoDetectBinPacking: false
|
ExperimentalAutoDetectBinPacking: false
|
||||||
@@ -58,15 +65,25 @@ ForEachMacros:
|
|||||||
- foreach
|
- foreach
|
||||||
- Q_FOREACH
|
- Q_FOREACH
|
||||||
- BOOST_FOREACH
|
- BOOST_FOREACH
|
||||||
|
IncludeBlocks: Regroup
|
||||||
IncludeCategories:
|
IncludeCategories:
|
||||||
|
- Regex: '^<ext/.*\.h>'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
- Regex: '^<.*\.h>'
|
- Regex: '^<.*\.h>'
|
||||||
Priority: 1
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
- Regex: '^<.*'
|
- Regex: '^<.*'
|
||||||
Priority: 2
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
- Regex: '.*'
|
- Regex: '.*'
|
||||||
Priority: 3
|
Priority: 3
|
||||||
|
SortPriority: 0
|
||||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||||
|
IncludeIsMainSourceRegex: ''
|
||||||
IndentCaseLabels: true
|
IndentCaseLabels: true
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
IndentWidth: 2
|
IndentWidth: 2
|
||||||
IndentWrappedFunctionNames: false
|
IndentWrappedFunctionNames: false
|
||||||
JavaScriptQuotes: Leave
|
JavaScriptQuotes: Leave
|
||||||
@@ -76,32 +93,73 @@ MacroBlockBegin: ''
|
|||||||
MacroBlockEnd: ''
|
MacroBlockEnd: ''
|
||||||
MaxEmptyLinesToKeep: 1
|
MaxEmptyLinesToKeep: 1
|
||||||
NamespaceIndentation: None
|
NamespaceIndentation: None
|
||||||
|
ObjCBinPackProtocolList: Never
|
||||||
ObjCBlockIndentWidth: 2
|
ObjCBlockIndentWidth: 2
|
||||||
ObjCSpaceAfterProperty: false
|
ObjCSpaceAfterProperty: false
|
||||||
ObjCSpaceBeforeProtocolList: false
|
ObjCSpaceBeforeProtocolList: true
|
||||||
PenaltyBreakAssignment: 2
|
PenaltyBreakAssignment: 2
|
||||||
PenaltyBreakBeforeFirstCallParameter: 1
|
PenaltyBreakBeforeFirstCallParameter: 1
|
||||||
PenaltyBreakComment: 300
|
PenaltyBreakComment: 300
|
||||||
PenaltyBreakFirstLessLess: 120
|
PenaltyBreakFirstLessLess: 120
|
||||||
PenaltyBreakString: 1000
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
PenaltyExcessCharacter: 1000000
|
PenaltyExcessCharacter: 1000000
|
||||||
PenaltyReturnTypeOnItsOwnLine: 200
|
PenaltyReturnTypeOnItsOwnLine: 200
|
||||||
PointerAlignment: Left
|
PointerAlignment: Left
|
||||||
|
RawStringFormats:
|
||||||
|
- Language: Cpp
|
||||||
|
Delimiters:
|
||||||
|
- cc
|
||||||
|
- CC
|
||||||
|
- cpp
|
||||||
|
- Cpp
|
||||||
|
- CPP
|
||||||
|
- 'c++'
|
||||||
|
- 'C++'
|
||||||
|
CanonicalDelimiter: ''
|
||||||
|
BasedOnStyle: google
|
||||||
|
- Language: TextProto
|
||||||
|
Delimiters:
|
||||||
|
- pb
|
||||||
|
- PB
|
||||||
|
- proto
|
||||||
|
- PROTO
|
||||||
|
EnclosingFunctions:
|
||||||
|
- EqualsProto
|
||||||
|
- EquivToProto
|
||||||
|
- PARSE_PARTIAL_TEXT_PROTO
|
||||||
|
- PARSE_TEST_PROTO
|
||||||
|
- PARSE_TEXT_PROTO
|
||||||
|
- ParseTextOrDie
|
||||||
|
- ParseTextProtoOrDie
|
||||||
|
CanonicalDelimiter: ''
|
||||||
|
BasedOnStyle: google
|
||||||
ReflowComments: true
|
ReflowComments: true
|
||||||
SortIncludes: false
|
SortIncludes: false
|
||||||
SortUsingDeclarations: true
|
SortUsingDeclarations: true
|
||||||
SpaceAfterCStyleCast: false
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
SpaceAfterTemplateKeyword: true
|
SpaceAfterTemplateKeyword: true
|
||||||
SpaceBeforeAssignmentOperators: true
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
SpaceBeforeParens: ControlStatements
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
SpaceInEmptyParentheses: false
|
SpaceInEmptyParentheses: false
|
||||||
SpacesBeforeTrailingComments: 2
|
SpacesBeforeTrailingComments: 2
|
||||||
SpacesInAngles: false
|
SpacesInAngles: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
SpacesInContainerLiterals: true
|
SpacesInContainerLiterals: true
|
||||||
SpacesInCStyleCastParentheses: false
|
SpacesInCStyleCastParentheses: false
|
||||||
SpacesInParentheses: false
|
SpacesInParentheses: false
|
||||||
SpacesInSquareBrackets: false
|
SpacesInSquareBrackets: false
|
||||||
Standard: Auto
|
SpaceBeforeSquareBrackets: false
|
||||||
|
Standard: c++17
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
TabWidth: 8
|
TabWidth: 8
|
||||||
UseTab: Never
|
UseTab: Never
|
||||||
...
|
...
|
||||||
|
|||||||
71
.clang-tidy
Normal file
71
.clang-tidy
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
Checks:
|
||||||
|
'bugprone-assert-side-effect,
|
||||||
|
bugprone-bool-pointer-implicit-conversion,
|
||||||
|
bugprone-copy-constructor-init,
|
||||||
|
bugprone-dangling-handle,
|
||||||
|
bugprone-dynamic-static-initializers,
|
||||||
|
bugprone-forward-declaration-namespace,
|
||||||
|
bugprone-forwarding-reference-overload,
|
||||||
|
bugprone-inaccurate-erase,
|
||||||
|
bugprone-incorrect-roundings,
|
||||||
|
bugprone-integer-division,
|
||||||
|
bugprone-lambda-function-name,
|
||||||
|
bugprone-misplaced-operator-in-strlen-in-alloc,
|
||||||
|
bugprone-misplaced-widening-cast,
|
||||||
|
bugprone-move-forwarding-reference,
|
||||||
|
bugprone-multiple-statement-macro,
|
||||||
|
bugprone-parent-virtual-call,
|
||||||
|
bugprone-posix-return,
|
||||||
|
bugprone-sizeof-container,
|
||||||
|
bugprone-sizeof-expression,
|
||||||
|
bugprone-spuriously-wake-up-functions,
|
||||||
|
bugprone-string-constructor,
|
||||||
|
bugprone-string-integer-assignment,
|
||||||
|
bugprone-string-literal-with-embedded-nul,
|
||||||
|
bugprone-suspicious-enum-usage,
|
||||||
|
bugprone-suspicious-include,
|
||||||
|
bugprone-suspicious-memset-usage,
|
||||||
|
bugprone-suspicious-missing-comma,
|
||||||
|
bugprone-suspicious-semicolon,
|
||||||
|
bugprone-suspicious-string-compare,
|
||||||
|
bugprone-throw-keyword-missing,
|
||||||
|
bugprone-too-small-loop-variable,
|
||||||
|
bugprone-undefined-memory-manipulation,
|
||||||
|
bugprone-undelegated-constructor,
|
||||||
|
bugprone-unhandled-self-assignment,
|
||||||
|
bugprone-unused-raii,
|
||||||
|
bugprone-virtual-near-miss,
|
||||||
|
cert-dcl58-cpp,
|
||||||
|
cert-err52-cpp,
|
||||||
|
cert-err60-cpp,
|
||||||
|
cert-mem57-cpp,
|
||||||
|
cert-oop57-cpp,
|
||||||
|
cert-oop58-cpp,
|
||||||
|
clang-diagnostic-*,
|
||||||
|
-clang-diagnostic-deprecated-declarations,
|
||||||
|
-clang-diagnostic-#warnings,
|
||||||
|
-clang-diagnostic-pedantic,
|
||||||
|
clang-analyzer-*,
|
||||||
|
cppcoreguidelines-slicing,
|
||||||
|
google-build-namespaces,
|
||||||
|
google-explicit-constructor,
|
||||||
|
google-global-names-in-headers,
|
||||||
|
google-readability-avoid-underscore-in-googletest-name,
|
||||||
|
google-readability-casting,
|
||||||
|
google-runtime-operator,
|
||||||
|
misc-definitions-in-headers,
|
||||||
|
misc-misplaced-const,
|
||||||
|
misc-new-delete-overloads,
|
||||||
|
misc-non-copyable-objects,
|
||||||
|
modernize-avoid-bind,
|
||||||
|
modernize-concat-nested-namespaces,
|
||||||
|
modernize-make-shared,
|
||||||
|
modernize-make-unique,
|
||||||
|
modernize-pass-by-value,
|
||||||
|
modernize-use-default-member-init,
|
||||||
|
modernize-use-noexcept,
|
||||||
|
modernize-use-nullptr,
|
||||||
|
modernize-use-override,
|
||||||
|
modernize-use-using,
|
||||||
|
readability-braces-around-statements'
|
||||||
|
FormatStyle: file
|
||||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*.gradle text eol=lf
|
||||||
|
*.java text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
*.xml text eol=lf
|
||||||
38
.github/CODEOWNERS
vendored
Normal file
38
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Lines starting with '#' are comments.
|
||||||
|
# Each line is a file pattern followed by one or more owners.
|
||||||
|
|
||||||
|
# More details are here: https://help.github.com/articles/about-codeowners/
|
||||||
|
|
||||||
|
# The '*' pattern is global owners.
|
||||||
|
|
||||||
|
# Order is important. The last matching pattern has the most precedence.
|
||||||
|
|
||||||
|
# The folders are ordered as a tree: by depth then alphabetically.
|
||||||
|
# This should make it easy to add new rules without breaking existing ones.
|
||||||
|
|
||||||
|
# Global rule:
|
||||||
|
* @wpilibsuite/wpilib
|
||||||
|
|
||||||
|
/cameraserver/ @wpilibsuite/camera-server
|
||||||
|
|
||||||
|
/cscore/ @wpilibsuite/camera-server
|
||||||
|
|
||||||
|
/hal/ @wpilibsuite/hardware
|
||||||
|
/hal/src/main/java/**/sim/ @wpilibsuite/simulation
|
||||||
|
/hal/src/main/native/include/mockdata/ @wpilibsuite/simulation
|
||||||
|
/hal/src/main/native/include/simulation/ @wpilibsuite/simulation
|
||||||
|
/hal/src/main/native/sim/ @wpilibsuite/simulation
|
||||||
|
|
||||||
|
/ntcore/ @wpilibsuite/network-tables
|
||||||
|
|
||||||
|
/simulation/ @wpilibsuite/simulation
|
||||||
|
|
||||||
|
/wpilibNewCommands/ @wpilibsuite/commandbased
|
||||||
|
|
||||||
|
/wpilibOldCommands/ @wpilibsuite/commandbased
|
||||||
|
|
||||||
|
/wpilibcExamples/ @wpilibsuite/wpilib @wpilibsuite/documentation
|
||||||
|
|
||||||
|
/wpilibjExamples/ @wpilibsuite/wpilib @wpilibsuite/documentation
|
||||||
|
|
||||||
|
/wpiutil/ @PeterJohnson
|
||||||
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. ...
|
||||||
|
2. ...
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- WPILib Version: [e.g. 2021.3.1]
|
||||||
|
- OS: [e.g. Windows 11]
|
||||||
|
- Java version [e.g. 1.10.2]
|
||||||
|
- C++ version [e.g. 17]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
20
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Question
|
||||||
|
about: Ask about features or parts of this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the question you have.**
|
||||||
|
A clear and concise description of what you want clarification on.
|
||||||
|
|
||||||
|
**Describe the reason for your confusion.**
|
||||||
|
A clear and concise description of why the question requires clarification and what's confusing.
|
||||||
|
|
||||||
|
**Is your question related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the question here.
|
||||||
56
.github/workflows/cmake.yml
vendored
Normal file
56
.github/workflows/cmake.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
name: CMake
|
||||||
|
|
||||||
|
on: [pull_request, push]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
name: Linux
|
||||||
|
container: wpilib/roborio-cross-ubuntu:2023-22.04
|
||||||
|
flags: ""
|
||||||
|
- os: macOS-11
|
||||||
|
name: macOS
|
||||||
|
container: ""
|
||||||
|
flags: "-DWITH_JAVA=OFF"
|
||||||
|
|
||||||
|
name: "Build - ${{ matrix.name }}"
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
container: ${{ matrix.container }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install dependencies (Linux)
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3
|
||||||
|
|
||||||
|
- name: Install opencv (macOS)
|
||||||
|
run: brew install opencv
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
|
||||||
|
- name: Set up Python 3.8 (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
|
||||||
|
- name: Install jinja
|
||||||
|
run: python -m pip install jinja2
|
||||||
|
|
||||||
|
- name: configure
|
||||||
|
run: mkdir build && cd build && cmake ${{ matrix.flags }} ..
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
working-directory: build
|
||||||
|
run: cmake --build . --parallel $(nproc)
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
working-directory: build
|
||||||
|
run: ctest --output-on-failure
|
||||||
64
.github/workflows/comment-command.yml
vendored
Normal file
64
.github/workflows/comment-command.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
name: Comment Commands
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [ created ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
format:
|
||||||
|
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/format')
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: React Rocket
|
||||||
|
uses: actions/github-script@v4
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const {owner, repo} = context.issue
|
||||||
|
github.reactions.createForIssueComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
comment_id: context.payload.comment.id,
|
||||||
|
content: "rocket",
|
||||||
|
});
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}
|
||||||
|
- name: Fetch all history and metadata
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
git checkout -b pr
|
||||||
|
git branch -f main origin/main
|
||||||
|
- name: Checkout PR
|
||||||
|
run: |
|
||||||
|
gh pr checkout $NUMBER
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
|
||||||
|
NUMBER: ${{ github.event.issue.number }}
|
||||||
|
- name: Set up Python 3.8
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 11
|
||||||
|
- name: Install clang-format
|
||||||
|
run: |
|
||||||
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
|
sudo sh -c "echo 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-14 main' >> /etc/apt/sources.list.d/proposed-repositories.list"
|
||||||
|
sudo apt-get update -q
|
||||||
|
sudo apt-get install -y clang-format-14
|
||||||
|
- name: Install wpiformat
|
||||||
|
run: pip3 install wpiformat
|
||||||
|
- name: Run wpiformat
|
||||||
|
run: wpiformat -clang 14
|
||||||
|
- name: Run spotlessApply
|
||||||
|
run: ./gradlew spotlessApply
|
||||||
|
- name: Commit
|
||||||
|
run: |
|
||||||
|
# Set credentials
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
# Commit
|
||||||
|
git commit -am "Formatting fixes"
|
||||||
|
git push
|
||||||
64
.github/workflows/documentation.yml
vendored
Normal file
64
.github/workflows/documentation.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
name: Documentation
|
||||||
|
|
||||||
|
on: [push, workflow_dispatch]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
BASE_PATH: allwpilib/docs
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
name: "Documentation - Publish"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
if: github.repository_owner == 'wpilibsuite' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
|
concurrency: ci-docs-publish
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 13
|
||||||
|
- name: Set environment variables (Development)
|
||||||
|
run: |
|
||||||
|
echo "TARGET_FOLDER=$BASE_PATH/development" >> $GITHUB_ENV
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
- name: Set environment variables (Tag)
|
||||||
|
run: |
|
||||||
|
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||||
|
echo "TARGET_FOLDER=$BASE_PATH/beta" >> $GITHUB_ENV
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
- name: Set environment variables (Release)
|
||||||
|
run: |
|
||||||
|
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||||
|
echo "TARGET_FOLDER=$BASE_PATH/release" >> $GITHUB_ENV
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta')
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
|
||||||
|
- name: Install SSH Client 🔑
|
||||||
|
uses: webfactory/ssh-agent@v0.7.0
|
||||||
|
with:
|
||||||
|
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
|
||||||
|
- name: Deploy Java 🚀
|
||||||
|
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||||
|
with:
|
||||||
|
SSH: true
|
||||||
|
REPOSITORY_NAME: wpilibsuite/wpilibsuite.github.io
|
||||||
|
BRANCH: main
|
||||||
|
CLEAN: true
|
||||||
|
FOLDER: docs/build/docs/javadoc
|
||||||
|
TARGET_FOLDER: ${{ env.TARGET_FOLDER }}/java
|
||||||
|
- name: Deploy C++ 🚀
|
||||||
|
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||||
|
with:
|
||||||
|
SSH: true
|
||||||
|
REPOSITORY_NAME: wpilibsuite/wpilibsuite.github.io
|
||||||
|
BRANCH: main
|
||||||
|
CLEAN: true
|
||||||
|
FOLDER: docs/build/docs/doxygen/html
|
||||||
|
TARGET_FOLDER: ${{ env.TARGET_FOLDER }}/cpp
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
name: "Validate Gradle Wrapper"
|
name: "Validate Gradle Wrapper"
|
||||||
on: [push]
|
on: [pull_request, push]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validation:
|
validation:
|
||||||
name: "Validation"
|
name: "Validation"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
|||||||
194
.github/workflows/gradle.yml
vendored
Normal file
194
.github/workflows/gradle.yml
vendored
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
name: Gradle
|
||||||
|
|
||||||
|
on: [pull_request, push]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-docker:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- container: wpilib/roborio-cross-ubuntu:2023-22.04
|
||||||
|
artifact-name: Athena
|
||||||
|
build-options: "-Ponlylinuxathena"
|
||||||
|
- container: wpilib/raspbian-cross-ubuntu:bullseye-22.04
|
||||||
|
artifact-name: Arm32
|
||||||
|
build-options: "-Ponlylinuxarm32"
|
||||||
|
- container: wpilib/aarch64-cross-ubuntu:bullseye-22.04
|
||||||
|
artifact-name: Arm64
|
||||||
|
build-options: "-Ponlylinuxarm64"
|
||||||
|
- container: wpilib/ubuntu-base:22.04
|
||||||
|
artifact-name: Linux
|
||||||
|
build-options: "-Ponlylinuxx86-64"
|
||||||
|
name: "Build - ${{ matrix.artifact-name }}"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
container: ${{ matrix.container }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Set release environment variable
|
||||||
|
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
|
||||||
|
env:
|
||||||
|
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||||
|
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.artifact-name }}
|
||||||
|
path: build/allOutputs
|
||||||
|
|
||||||
|
build-host:
|
||||||
|
env:
|
||||||
|
MACOSX_DEPLOYMENT_TARGET: 10.15
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: windows-2022
|
||||||
|
artifact-name: Win64Debug
|
||||||
|
architecture: x64
|
||||||
|
task: "build"
|
||||||
|
build-options: "-PciDebugOnly --max-workers 1"
|
||||||
|
outputs: "build/allOutputs"
|
||||||
|
- os: windows-2022
|
||||||
|
artifact-name: Win64Release
|
||||||
|
architecture: x64
|
||||||
|
build-options: "-PciReleaseOnly --max-workers 1"
|
||||||
|
task: "copyAllOutputs"
|
||||||
|
outputs: "build/allOutputs"
|
||||||
|
- os: macOS-11
|
||||||
|
artifact-name: macOS
|
||||||
|
architecture: x64
|
||||||
|
task: "build"
|
||||||
|
outputs: "build/allOutputs"
|
||||||
|
- os: windows-2022
|
||||||
|
artifact-name: Win32
|
||||||
|
architecture: x86
|
||||||
|
task: ":ntcoreffi:build"
|
||||||
|
outputs: "ntcoreffi/build/outputs"
|
||||||
|
name: "Build - ${{ matrix.artifact-name }}"
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 17
|
||||||
|
architecture: ${{ matrix.architecture }}
|
||||||
|
- name: Import Developer ID Certificate
|
||||||
|
uses: wpilibsuite/import-signing-certificate@v1
|
||||||
|
with:
|
||||||
|
certificate-data: ${{ secrets.APPLE_CERTIFICATE_DATA }}
|
||||||
|
certificate-passphrase: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
|
keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
|
||||||
|
if: |
|
||||||
|
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' &&
|
||||||
|
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
||||||
|
- name: Set Keychain Lock Timeout
|
||||||
|
run: security set-keychain-settings -lut 3600
|
||||||
|
if: |
|
||||||
|
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' &&
|
||||||
|
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
||||||
|
- name: Set release environment variable
|
||||||
|
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||||
|
shell: bash
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
- name: Set Java Heap Size
|
||||||
|
run: sed -i 's/-Xmx2g/-Xmx1g/g' gradle.properties
|
||||||
|
if: matrix.artifact-name == 'Win32'
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew ${{ matrix.task }} --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
|
||||||
|
env:
|
||||||
|
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||||
|
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||||
|
- name: Sign Libraries with Developer ID
|
||||||
|
run: ./gradlew copyAllOutputs --build-cache -PbuildServer -PskipJavaFormat -PdeveloperID=${{ secrets.APPLE_DEVELOPER_ID }} ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
|
||||||
|
if: |
|
||||||
|
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' &&
|
||||||
|
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.artifact-name }}
|
||||||
|
path: ${{ matrix.outputs }}
|
||||||
|
|
||||||
|
build-documentation:
|
||||||
|
name: "Build - Documentation"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 13
|
||||||
|
- name: Set release environment variable
|
||||||
|
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew docs:zipDocs --build-cache -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
|
||||||
|
env:
|
||||||
|
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||||
|
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Documentation
|
||||||
|
path: docs/build/outputs
|
||||||
|
|
||||||
|
combine:
|
||||||
|
name: Combine
|
||||||
|
needs: [build-docker, build-host, build-documentation]
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: wpilibsuite/build-tools
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
path: combiner/products/build/allOutputs
|
||||||
|
- name: Flatten Artifacts
|
||||||
|
run: rsync -a --delete combiner/products/build/allOutputs/*/* combiner/products/build/allOutputs/
|
||||||
|
- name: Check version number exists
|
||||||
|
run: |
|
||||||
|
cat combiner/products/build/allOutputs/version.txt
|
||||||
|
test -s combiner/products/build/allOutputs/version.txt
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 11
|
||||||
|
- name: Combine
|
||||||
|
if: |
|
||||||
|
!startsWith(github.ref, 'refs/tags/v') &&
|
||||||
|
github.ref != 'refs/heads/main'
|
||||||
|
run: cd combiner && ./gradlew publish -Pallwpilib
|
||||||
|
- name: Combine (Master)
|
||||||
|
if: |
|
||||||
|
github.repository_owner == 'wpilibsuite' &&
|
||||||
|
github.ref == 'refs/heads/main'
|
||||||
|
run: cd combiner && ./gradlew publish -Pallwpilib
|
||||||
|
env:
|
||||||
|
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
|
||||||
|
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||||
|
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||||
|
- name: Combine (Release)
|
||||||
|
if: |
|
||||||
|
github.repository_owner == 'wpilibsuite' &&
|
||||||
|
startsWith(github.ref, 'refs/tags/v')
|
||||||
|
run: cd combiner && ./gradlew publish -Pallwpilib -PreleaseRepoPublish
|
||||||
|
env:
|
||||||
|
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
|
||||||
|
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||||
|
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Maven
|
||||||
|
path: ~/releases
|
||||||
118
.github/workflows/lint-format.yml
vendored
Normal file
118
.github/workflows/lint-format.yml
vendored
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
name: Lint and Format
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- main
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
wpiformat:
|
||||||
|
name: "wpiformat"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Fetch all history and metadata
|
||||||
|
run: |
|
||||||
|
git config --global --add safe.directory /__w/allwpilib/allwpilib
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
git checkout -b pr
|
||||||
|
git branch -f main origin/main
|
||||||
|
- name: Set up Python 3.8
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
- name: Install clang-format
|
||||||
|
run: |
|
||||||
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
|
sudo sh -c "echo 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-14 main' >> /etc/apt/sources.list.d/proposed-repositories.list"
|
||||||
|
sudo apt-get update -q
|
||||||
|
sudo apt-get install -y clang-format-14
|
||||||
|
- name: Install wpiformat
|
||||||
|
run: pip3 install wpiformat
|
||||||
|
- name: Run
|
||||||
|
run: wpiformat -clang 14
|
||||||
|
- name: Check output
|
||||||
|
run: git --no-pager diff --exit-code HEAD
|
||||||
|
- name: Generate diff
|
||||||
|
run: git diff HEAD > wpiformat-fixes.patch
|
||||||
|
if: ${{ failure() }}
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: wpiformat fixes
|
||||||
|
path: wpiformat-fixes.patch
|
||||||
|
if: ${{ failure() }}
|
||||||
|
- name: Write to job summary
|
||||||
|
run: |
|
||||||
|
echo '```diff' >> $GITHUB_STEP_SUMMARY
|
||||||
|
cat wpiformat-fixes.patch >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
|
if: ${{ failure() }}
|
||||||
|
|
||||||
|
tidy:
|
||||||
|
name: "clang-tidy"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
container: wpilib/roborio-cross-ubuntu:2023-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Fetch all history and metadata
|
||||||
|
run: |
|
||||||
|
git config --global --add safe.directory /__w/allwpilib/allwpilib
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
git checkout -b pr
|
||||||
|
git branch -f main origin/main
|
||||||
|
- name: Set up Python 3.8
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
- name: Install clang-tidy
|
||||||
|
run: |
|
||||||
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
|
sudo sh -c "echo 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-14 main' >> /etc/apt/sources.list.d/proposed-repositories.list"
|
||||||
|
sudo apt-get update -q
|
||||||
|
sudo apt-get install -y clang-tidy-14 clang-format-14
|
||||||
|
- name: Install wpiformat
|
||||||
|
run: pip3 install wpiformat
|
||||||
|
- name: Create compile_commands.json
|
||||||
|
run: ./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
|
||||||
|
- name: List changed files
|
||||||
|
run: wpiformat -list-changed-files
|
||||||
|
- name: Run clang-tidy
|
||||||
|
run: wpiformat -clang 14 -no-format -tidy-changed -compile-commands=build/compile_commands/linuxx86-64 -vv
|
||||||
|
javaformat:
|
||||||
|
name: "Java format"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
container: wpilib/ubuntu-base:22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Fetch all history and metadata
|
||||||
|
run: |
|
||||||
|
git config --global --add safe.directory /__w/allwpilib/allwpilib
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
git checkout -b pr
|
||||||
|
git branch -f main origin/main
|
||||||
|
- name: Run Java format
|
||||||
|
run: ./gradlew javaFormat spotbugsMain spotbugsTest spotbugsDev
|
||||||
|
- name: Check output
|
||||||
|
run: git --no-pager diff --exit-code HEAD
|
||||||
|
- name: Generate diff
|
||||||
|
run: git diff HEAD > javaformat-fixes.patch
|
||||||
|
if: ${{ failure() }}
|
||||||
|
documentation:
|
||||||
|
name: "Documentation"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 13
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew docs:zipDocs -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}
|
||||||
48
.github/workflows/sanitizers.yml
vendored
Normal file
48
.github/workflows/sanitizers.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
name: Sanitizers
|
||||||
|
|
||||||
|
on: [pull_request, push]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: asan
|
||||||
|
cmake-flags: "-DCMAKE_BUILD_TYPE=Asan"
|
||||||
|
ctest-env: ""
|
||||||
|
ctest-flags: "-E 'wpilibc'"
|
||||||
|
- name: tsan
|
||||||
|
cmake-flags: "-DCMAKE_BUILD_TYPE=Tsan"
|
||||||
|
ctest-env: "TSAN_OPTIONS=second_deadlock_stack=1"
|
||||||
|
ctest-flags: "-E 'cscore|cameraserver|wpilibc|wpilibNewCommands'"
|
||||||
|
- name: ubsan
|
||||||
|
cmake-flags: "-DCMAKE_BUILD_TYPE=Ubsan"
|
||||||
|
ctest-env: ""
|
||||||
|
ctest-flags: ""
|
||||||
|
name: "${{ matrix.name }}"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
container: wpilib/roborio-cross-ubuntu:2023-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3 clang-14
|
||||||
|
|
||||||
|
- name: Install jinja
|
||||||
|
run: python -m pip install jinja2
|
||||||
|
|
||||||
|
- name: configure
|
||||||
|
run: mkdir build && cd build && cmake -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-14 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-14 ${{ matrix.cmake-flags }} ..
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
working-directory: build
|
||||||
|
run: cmake --build . --parallel $(nproc)
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
working-directory: build
|
||||||
|
run: ${{ matrix.ctest-env }} ctest --output-on-failure ${{ matrix.ctest-flags }}
|
||||||
67
.github/workflows/upstream-utils.yml
vendored
Normal file
67
.github/workflows/upstream-utils.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
name: Upstream utils
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- main
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update:
|
||||||
|
name: "Update"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Fetch all history and metadata
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
git checkout -b pr
|
||||||
|
git branch -f main origin/main
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
- name: Configure committer identity
|
||||||
|
run: |
|
||||||
|
git config --global user.email "you@example.com"
|
||||||
|
git config --global user.name "Your Name"
|
||||||
|
- name: Run update_drake.py
|
||||||
|
run: |
|
||||||
|
cd upstream_utils
|
||||||
|
./update_drake.py
|
||||||
|
- name: Run update_eigen.py
|
||||||
|
run: |
|
||||||
|
cd upstream_utils
|
||||||
|
./update_eigen.py
|
||||||
|
- name: Run update_fmt.py
|
||||||
|
run: |
|
||||||
|
cd upstream_utils
|
||||||
|
./update_fmt.py
|
||||||
|
- name: Run update_libuv.py
|
||||||
|
run: |
|
||||||
|
cd upstream_utils
|
||||||
|
./update_libuv.py
|
||||||
|
- name: Run update_llvm.py
|
||||||
|
run: |
|
||||||
|
cd upstream_utils
|
||||||
|
./update_llvm.py
|
||||||
|
- name: Run update_mpack.py
|
||||||
|
run: |
|
||||||
|
cd upstream_utils
|
||||||
|
./update_mpack.py
|
||||||
|
- name: Run update_stack_walker.py
|
||||||
|
run: |
|
||||||
|
cd upstream_utils
|
||||||
|
./update_stack_walker.py
|
||||||
|
- name: Run update_memory.py
|
||||||
|
run: |
|
||||||
|
cd upstream_utils
|
||||||
|
./update_memory.py
|
||||||
|
- name: Add untracked files to index so they count as changes
|
||||||
|
run: git add -A
|
||||||
|
- name: Check output
|
||||||
|
run: git --no-pager diff --exit-code HEAD
|
||||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -5,6 +5,10 @@ doxygen.log
|
|||||||
build*/
|
build*/
|
||||||
!buildSrc/
|
!buildSrc/
|
||||||
|
|
||||||
|
simgui-ds.json
|
||||||
|
simgui-window.json
|
||||||
|
simgui.json
|
||||||
|
|
||||||
# Created by the jenkins test script
|
# Created by the jenkins test script
|
||||||
test-reports
|
test-reports
|
||||||
|
|
||||||
@@ -15,6 +19,9 @@ test-reports
|
|||||||
.idea/
|
.idea/
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
# Fleet
|
||||||
|
.fleet
|
||||||
|
|
||||||
# Created by http://www.gitignore.io
|
# Created by http://www.gitignore.io
|
||||||
|
|
||||||
### Linux ###
|
### Linux ###
|
||||||
@@ -222,3 +229,12 @@ compile_commands.json
|
|||||||
# clang configuration and clangd cache
|
# clang configuration and clangd cache
|
||||||
.clang
|
.clang
|
||||||
.clangd/
|
.clangd/
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
imgui.ini
|
||||||
|
|
||||||
|
# Bazel
|
||||||
|
/.ijwb/
|
||||||
|
/bazel-*
|
||||||
|
user.bazelrc
|
||||||
|
coverage_report/
|
||||||
|
|||||||
17
.styleguide
17
.styleguide
@@ -2,16 +2,23 @@ cppHeaderFileInclude {
|
|||||||
\.h$
|
\.h$
|
||||||
\.hpp$
|
\.hpp$
|
||||||
\.inc$
|
\.inc$
|
||||||
|
\.inl$
|
||||||
}
|
}
|
||||||
|
|
||||||
cppSrcFileInclude {
|
cppSrcFileInclude {
|
||||||
\.cpp$
|
\.cpp$
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modifiableFileExclude {
|
||||||
|
\.patch$
|
||||||
|
gradlew
|
||||||
|
}
|
||||||
|
|
||||||
generatedFileExclude {
|
generatedFileExclude {
|
||||||
FRCNetComm\.java$
|
FRCNetComm\.java$
|
||||||
simulation/gz_msgs/src/include/simulation/gz_msgs/msgs\.h$
|
simulation/gz_msgs/src/include/simulation/gz_msgs/msgs\.h$
|
||||||
simulation/halsim_gui/src/main/native/include/portable-file-dialogs\.h$
|
fieldImages/src/main/native/resources/
|
||||||
|
apriltag/src/test/resources/
|
||||||
}
|
}
|
||||||
|
|
||||||
repoRootNameOverride {
|
repoRootNameOverride {
|
||||||
@@ -19,16 +26,24 @@ repoRootNameOverride {
|
|||||||
}
|
}
|
||||||
|
|
||||||
includeOtherLibs {
|
includeOtherLibs {
|
||||||
|
^Eigen/
|
||||||
^cameraserver/
|
^cameraserver/
|
||||||
^cscore
|
^cscore
|
||||||
|
^drake/
|
||||||
|
^fmt/
|
||||||
^hal/
|
^hal/
|
||||||
^imgui
|
^imgui
|
||||||
|
^implot
|
||||||
^mockdata/
|
^mockdata/
|
||||||
^networktables/
|
^networktables/
|
||||||
^ntcore
|
^ntcore
|
||||||
^opencv2/
|
^opencv2/
|
||||||
^support/
|
^support/
|
||||||
^units/
|
^units/
|
||||||
|
^unsupported/
|
||||||
^vision/
|
^vision/
|
||||||
^wpi/
|
^wpi/
|
||||||
|
^wpigui
|
||||||
|
^wpimath/
|
||||||
|
^wpinet/
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) {year} FIRST. All Rights Reserved.{padding}*/
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"enableCppIntellisense": true,
|
"enableCppIntellisense": true,
|
||||||
"currentLanguage": "cpp",
|
"currentLanguage": "cpp",
|
||||||
"projectYear": "2020",
|
"projectYear": "intellisense",
|
||||||
"teamNumber": 0
|
"teamNumber": 0
|
||||||
}
|
}
|
||||||
|
|||||||
289
CMakeLists.txt
289
CMakeLists.txt
@@ -1,29 +1,33 @@
|
|||||||
# Disable in-source builds to prevent source tree corruption.
|
# Disable in-source builds to prevent source tree corruption.
|
||||||
if(" ${CMAKE_SOURCE_DIR}" STREQUAL " ${CMAKE_BINARY_DIR}")
|
if(" ${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL " ${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
message(FATAL_ERROR "
|
message(FATAL_ERROR "
|
||||||
FATAL: In-source builds are not allowed.
|
FATAL: In-source builds are not allowed.
|
||||||
You should create a separate directory for build files.
|
You should create a separate directory for build files.
|
||||||
")
|
")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
|
||||||
|
set(CMAKE_SYSTEM_VERSION 10.0.18362.0 CACHE STRING INTERNAL FORCE)
|
||||||
|
set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION 10.0.18362.0 CACHE STRING INTERNAL FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
project(allwpilib)
|
project(allwpilib)
|
||||||
cmake_minimum_required(VERSION 3.3.0)
|
cmake_minimum_required(VERSION 3.3.0)
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||||
|
|
||||||
|
message(STATUS "Platform version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
|
||||||
|
|
||||||
|
set(WPILIB_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
INCLUDE(CPack)
|
INCLUDE(CPack)
|
||||||
|
|
||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
|
||||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND MSVC)
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${WPILIB_BINARY_DIR}/lib)
|
||||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Default install dir on windows" FORCE)
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${WPILIB_BINARY_DIR}/lib)
|
||||||
endif()
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${WPILIB_BINARY_DIR}/bin)
|
||||||
|
set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${WPILIB_BINARY_DIR}/jar)
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
|
||||||
set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${CMAKE_BINARY_DIR}/jar)
|
|
||||||
|
|
||||||
# use, i.e. don't skip the full RPATH for the build tree
|
# use, i.e. don't skip the full RPATH for the build tree
|
||||||
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
|
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||||
@@ -32,40 +36,73 @@ SET(CMAKE_SKIP_BUILD_RPATH FALSE)
|
|||||||
# (but later on when installing)
|
# (but later on when installing)
|
||||||
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||||
|
|
||||||
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/wpilib/lib")
|
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
||||||
|
|
||||||
# add the automatically determined parts of the RPATH
|
# add the automatically determined parts of the RPATH
|
||||||
# which point to directories outside the build tree to the install RPATH
|
# which point to directories outside the build tree to the install RPATH
|
||||||
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
|
|
||||||
# the RPATH to be used when installing, but only if it's not a system directory
|
# the RPATH to be used when installing, but only if it's not a system directory
|
||||||
LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/wpilib/lib" isSystemDir)
|
LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
|
||||||
IF("${isSystemDir}" STREQUAL "-1")
|
IF("${isSystemDir}" STREQUAL "-1")
|
||||||
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/wpilib/lib")
|
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
||||||
ENDIF("${isSystemDir}" STREQUAL "-1")
|
ENDIF("${isSystemDir}" STREQUAL "-1")
|
||||||
|
|
||||||
option(WITHOUT_JAVA "don't include java and JNI in the build" OFF)
|
# Options for building certain parts of the repo. Everything is built by default.
|
||||||
option(BUILD_SHARED_LIBS "build with shared libs (needed for JNI)" ON)
|
option(BUILD_SHARED_LIBS "Build with shared libs (needed for JNI)" ON)
|
||||||
option(WITHOUT_CSCORE "Don't build cscore (removes OpenCV requirement)" OFF)
|
option(WITH_JAVA "Include java and JNI in the build" ON)
|
||||||
option(WITHOUT_ALLWPILIB "Don't build allwpilib (removes OpenCV requirement)" ON)
|
option(WITH_CSCORE "Build cscore (needs OpenCV)" ON)
|
||||||
option(WITH_TESTS "build unit tests (requires internet connection)" OFF)
|
option(WITH_NTCORE "Build ntcore" ON)
|
||||||
option(USE_EXTERNAL_HAL "Use a separately built HAL" OFF)
|
option(WITH_WPIMATH "Build wpimath" ON)
|
||||||
|
option(WITH_WPILIB "Build hal, wpilibc/j, and myRobot (needs OpenCV)" ON)
|
||||||
|
option(WITH_EXAMPLES "Build examples" OFF)
|
||||||
|
option(WITH_TESTS "Build unit tests (requires internet connection)" ON)
|
||||||
|
option(WITH_GUI "Build GUI items" ON)
|
||||||
|
option(WITH_SIMULATION_MODULES "Build simulation modules" ON)
|
||||||
|
|
||||||
|
# Options for external HAL.
|
||||||
|
option(WITH_EXTERNAL_HAL "Use a separately built HAL" OFF)
|
||||||
set(EXTERNAL_HAL_FILE "" CACHE FILEPATH "Location to look for an external HAL CMake File")
|
set(EXTERNAL_HAL_FILE "" CACHE FILEPATH "Location to look for an external HAL CMake File")
|
||||||
option(USE_VCPKG_LIBUV "Use vcpkg libuv" OFF)
|
|
||||||
option(USE_VCPKG_EIGEN "Use vcpkg eigen" OFF)
|
# Options for using a package manager (e.g., vcpkg) for certain dependencies.
|
||||||
option(FLAT_INSTALL_WPILIB "Use a flat install directory" OFF)
|
option(USE_SYSTEM_FMTLIB "Use system fmtlib" OFF)
|
||||||
option(WITH_SIMULATION_MODULES "build simulation modules" OFF)
|
option(USE_SYSTEM_LIBUV "Use system libuv" OFF)
|
||||||
|
option(USE_SYSTEM_EIGEN "Use system eigen" OFF)
|
||||||
|
|
||||||
|
# Options for installation.
|
||||||
|
option(WITH_FLAT_INSTALL "Use a flat install directory" OFF)
|
||||||
|
|
||||||
|
# Options for location of OpenCV Java.
|
||||||
set(OPENCV_JAVA_INSTALL_DIR "" CACHE PATH "Location to search for the OpenCV jar file")
|
set(OPENCV_JAVA_INSTALL_DIR "" CACHE PATH "Location to search for the OpenCV jar file")
|
||||||
|
|
||||||
if (NOT WITHOUT_JAVA AND NOT BUILD_SHARED_LIBS)
|
# Set default build type to release with debug info (i.e. release mode optimizations
|
||||||
|
# are performed, but debug info still exists).
|
||||||
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
|
set (CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# We always want flat install with MSVC.
|
||||||
|
if (MSVC)
|
||||||
|
set(WITH_FLAT_INSTALL ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WITH_JAVA AND NOT BUILD_SHARED_LIBS)
|
||||||
message(FATAL_ERROR "
|
message(FATAL_ERROR "
|
||||||
FATAL: Cannot build static libs with Java enabled.
|
FATAL: Cannot build static libs with Java enabled.
|
||||||
Static libs requires both BUILD_SHARED_LIBS=OFF and
|
Static libs requires both BUILD_SHARED_LIBS=OFF and
|
||||||
WITHOUT_JAVA=ON
|
WITH_JAVA=OFF
|
||||||
")
|
")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WITHOUT_JAVA OR WITHOUT_CSCORE)
|
if (WITH_SIMULATION_MODULES AND NOT BUILD_SHARED_LIBS)
|
||||||
|
message(FATAL_ERROR "
|
||||||
|
FATAL: Cannot build static libs with simulation modules enabled.
|
||||||
|
Static libs requires both BUILD_SHARED_LIBS=OFF and
|
||||||
|
WITH_SIMULATION_MODULES=OFF
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT WITH_JAVA OR NOT WITH_CSCORE)
|
||||||
if(NOT "${OPENCV_JAVA_INSTALL_DIR}" STREQUAL "")
|
if(NOT "${OPENCV_JAVA_INSTALL_DIR}" STREQUAL "")
|
||||||
message(WARNING "
|
message(WARNING "
|
||||||
WARNING: OpenCV Java dir set but java is not enabled!
|
WARNING: OpenCV Java dir set but java is not enabled!
|
||||||
@@ -74,45 +111,167 @@ It will be ignored.
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set( wpilib_dest wpilib)
|
if (NOT WITH_WPILIB AND WITH_SIMULATION_MODULES)
|
||||||
set( include_dest wpilib/include )
|
message(FATAL_ERROR "
|
||||||
set( main_lib_dest wpilib/lib )
|
FATAL: Cannot build simulation modules with wpilib disabled.
|
||||||
set( java_lib_dest wpilib/java )
|
Enable wpilib by setting WITH_WPILIB=ON
|
||||||
set( jni_lib_dest wpilib/jni )
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
if (MSVC OR FLAT_INSTALL_WPILIB)
|
if (NOT WITH_NTCORE AND WITH_CSCORE)
|
||||||
|
message(FATAL_ERROR "
|
||||||
|
FATAL: Cannot build cameraserver without ntcore.
|
||||||
|
Enable ntcore by setting WITH_NTCORE=ON
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT WITH_NTCORE AND WITH_GUI)
|
||||||
|
message(FATAL_ERROR "
|
||||||
|
FATAL: Cannot build GUI modules without ntcore.
|
||||||
|
Enable ntcore by setting WITH_NTCORE=ON
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT WITH_NTCORE AND WITH_SIMULATION_MODULES)
|
||||||
|
message(FATAL_ERROR "
|
||||||
|
FATAL: Cannot build simulation modules without ntcore.
|
||||||
|
Enable ntcore by setting WITH_NTCORE=ON
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT WITH_NTCORE AND WITH_WPILIB)
|
||||||
|
message(FATAL_ERROR "
|
||||||
|
FATAL: Cannot build wpilib without ntcore.
|
||||||
|
Enable ntcore by setting WITH_NTCORE=ON
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT WITH_WPIMATH AND WITH_WPILIB)
|
||||||
|
message(FATAL_ERROR "
|
||||||
|
FATAL: Cannot build wpilib without wpimath.
|
||||||
|
Enable wpimath by setting WITH_WPIMATH=ON
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set( wpilib_dest "")
|
||||||
|
set( include_dest include )
|
||||||
|
set( main_lib_dest lib )
|
||||||
|
set( java_lib_dest java )
|
||||||
|
set( jni_lib_dest jni )
|
||||||
|
|
||||||
|
if (WITH_FLAT_INSTALL)
|
||||||
set (wpilib_config_dir ${wpilib_dest})
|
set (wpilib_config_dir ${wpilib_dest})
|
||||||
else()
|
else()
|
||||||
set (wpilib_config_dir share/wpilib)
|
set (wpilib_config_dir share/wpilib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_VCPKG_LIBUV)
|
if (USE_SYSTEM_LIBUV)
|
||||||
set (LIBUV_VCPKG_REPLACE "find_package(unofficial-libuv CONFIG)")
|
set (LIBUV_SYSTEM_REPLACE "
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(libuv REQUIRED IMPORTED_TARGET libuv)
|
||||||
|
")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_VCPKG_EIGEN)
|
if (USE_SYSTEM_EIGEN)
|
||||||
set (EIGEN_VCPKG_REPLACE "find_package(Eigen3 CONFIG)")
|
set (EIGEN_SYSTEM_REPLACE "find_package(Eigen3 CONFIG)")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MSVC OR FLAT_INSTALL_WPILIB)
|
find_package(LIBSSH 0.7.1)
|
||||||
|
|
||||||
|
if (WITH_FLAT_INSTALL)
|
||||||
set(WPIUTIL_DEP_REPLACE "include($\{SELF_DIR\}/wpiutil-config.cmake)")
|
set(WPIUTIL_DEP_REPLACE "include($\{SELF_DIR\}/wpiutil-config.cmake)")
|
||||||
|
set(WPINET_DEP_REPLACE "include($\{SELF_DIR\}/wpinet-config.cmake)")
|
||||||
set(NTCORE_DEP_REPLACE "include($\{SELF_DIR\}/ntcore-config.cmake)")
|
set(NTCORE_DEP_REPLACE "include($\{SELF_DIR\}/ntcore-config.cmake)")
|
||||||
set(CSCORE_DEP_REPLACE_IMPL "include(\${SELF_DIR}/cscore-config.cmake)")
|
set(CSCORE_DEP_REPLACE_IMPL "include(\${SELF_DIR}/cscore-config.cmake)")
|
||||||
set(CAMERASERVER_DEP_REPLACE_IMPL "include(\${SELF_DIR}/cameraserver-config.cmake)")
|
set(CAMERASERVER_DEP_REPLACE_IMPL "include(\${SELF_DIR}/cameraserver-config.cmake)")
|
||||||
set(HAL_DEP_REPLACE_IMPL "include(\${SELF_DIR}/hal-config.cmake)")
|
set(HAL_DEP_REPLACE_IMPL "include(\${SELF_DIR}/hal-config.cmake)")
|
||||||
|
set(WPIMATH_DEP_REPLACE "include($\{SELF_DIR\}/wpimath-config.cmake)")
|
||||||
set(WPILIBC_DEP_REPLACE_IMPL "include(\${SELF_DIR}/wpilibc-config.cmake)")
|
set(WPILIBC_DEP_REPLACE_IMPL "include(\${SELF_DIR}/wpilibc-config.cmake)")
|
||||||
|
set(WPILIBNEWCOMMANDS_DEP_REPLACE "include(\${SELF_DIR}/wpilibNewcommands-config.cmake)")
|
||||||
else()
|
else()
|
||||||
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
|
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
|
||||||
|
set(WPINET_DEP_REPLACE "find_dependency(wpinet)")
|
||||||
set(NTCORE_DEP_REPLACE "find_dependency(ntcore)")
|
set(NTCORE_DEP_REPLACE "find_dependency(ntcore)")
|
||||||
set(CSCORE_DEP_REPLACE_IMPL "find_dependency(cscore)")
|
set(CSCORE_DEP_REPLACE_IMPL "find_dependency(cscore)")
|
||||||
set(CAMERASERVER_DEP_REPLACE_IMPL "find_dependency(cameraserver)")
|
set(CAMERASERVER_DEP_REPLACE_IMPL "find_dependency(cameraserver)")
|
||||||
set(HAL_DEP_REPLACE_IMPL "find_dependency(hal)")
|
set(HAL_DEP_REPLACE_IMPL "find_dependency(hal)")
|
||||||
|
set(WPIMATH_DEP_REPLACE "find_dependency(wpimath)")
|
||||||
set(WPILIBC_DEP_REPLACE_IMPL "find_dependency(wpilibc)")
|
set(WPILIBC_DEP_REPLACE_IMPL "find_dependency(wpilibc)")
|
||||||
|
set(WPILIBNEWCOMMANDS_DEP_REPLACE "find_dependency(wpilibNewCommands)")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)")
|
set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)")
|
||||||
set(SELF_DIR "$\{SELF_DIR\}")
|
set(SELF_DIR "$\{SELF_DIR\}")
|
||||||
|
|
||||||
|
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
|
||||||
|
if(isMultiConfig)
|
||||||
|
if(NOT "Asan" IN_LIST CMAKE_CONFIGURATION_TYPES)
|
||||||
|
list(APPEND CMAKE_CONFIGURATION_TYPES Asan)
|
||||||
|
endif()
|
||||||
|
if(NOT "Tsan" IN_LIST CMAKE_CONFIGURATION_TYPES)
|
||||||
|
list(APPEND CMAKE_CONFIGURATION_TYPES Tsan)
|
||||||
|
endif()
|
||||||
|
if(NOT "Ubsan" IN_LIST CMAKE_CONFIGURATION_TYPES)
|
||||||
|
list(APPEND CMAKE_CONFIGURATION_TYPES Ubsan)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(allowedBuildTypes Asan Tsan Ubsan Debug Release RelWithDebInfo MinSizeRel)
|
||||||
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${allowedBuildTypes}")
|
||||||
|
|
||||||
|
if(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE IN_LIST allowedBuildTypes)
|
||||||
|
message(FATAL_ERROR "Invalid build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS_ASAN
|
||||||
|
"${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer" CACHE STRING
|
||||||
|
"Flags used by the C compiler for Asan build type or configuration." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS_ASAN
|
||||||
|
"${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer" CACHE STRING
|
||||||
|
"Flags used by the C++ compiler for Asan build type or configuration." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_ASAN
|
||||||
|
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=address" CACHE STRING
|
||||||
|
"Linker flags to be used to create executables for Asan build type." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS_ASAN
|
||||||
|
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=address" CACHE STRING
|
||||||
|
"Linker lags to be used to create shared libraries for Asan build type." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS_TSAN
|
||||||
|
"${CMAKE_C_FLAGS_DEBUG} -fsanitize=thread -fno-omit-frame-pointer" CACHE STRING
|
||||||
|
"Flags used by the C compiler for Tsan build type or configuration." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS_TSAN
|
||||||
|
"${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=thread -fno-omit-frame-pointer" CACHE STRING
|
||||||
|
"Flags used by the C++ compiler for Tsan build type or configuration." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_TSAN
|
||||||
|
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=thread" CACHE STRING
|
||||||
|
"Linker flags to be used to create executables for Tsan build type." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS_TSAN
|
||||||
|
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=thread" CACHE STRING
|
||||||
|
"Linker lags to be used to create shared libraries for Tsan build type." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS_UBSAN
|
||||||
|
"${CMAKE_C_FLAGS_DEBUG} -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer" CACHE STRING
|
||||||
|
"Flags used by the C compiler for Ubsan build type or configuration." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS_UBSAN
|
||||||
|
"${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer" CACHE STRING
|
||||||
|
"Flags used by the C++ compiler for Ubsan build type or configuration." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_UBSAN
|
||||||
|
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=undefined -fno-sanitize-recover=all" CACHE STRING
|
||||||
|
"Linker flags to be used to create executables for Ubsan build type." FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS_UBSAN
|
||||||
|
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=undefined" CACHE STRING
|
||||||
|
"Linker lags to be used to create shared libraries for Ubsan build type." FORCE)
|
||||||
|
|
||||||
if (WITH_TESTS)
|
if (WITH_TESTS)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory(googletest)
|
add_subdirectory(googletest)
|
||||||
@@ -120,27 +279,55 @@ if (WITH_TESTS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(wpiutil)
|
add_subdirectory(wpiutil)
|
||||||
add_subdirectory(ntcore)
|
|
||||||
|
|
||||||
if (NOT WITHOUT_CSCORE)
|
if (WITH_NTCORE)
|
||||||
|
add_subdirectory(wpinet)
|
||||||
|
add_subdirectory(ntcore)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WITH_WPIMATH)
|
||||||
|
add_subdirectory(wpimath)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WITH_GUI)
|
||||||
|
add_subdirectory(fieldImages)
|
||||||
|
add_subdirectory(imgui)
|
||||||
|
add_subdirectory(wpigui)
|
||||||
|
add_subdirectory(glass)
|
||||||
|
add_subdirectory(outlineviewer)
|
||||||
|
if (LIBSSH_FOUND)
|
||||||
|
add_subdirectory(roborioteamnumbersetter)
|
||||||
|
add_subdirectory(datalogtool)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WITH_WPILIB OR WITH_SIMULATION_MODULES)
|
||||||
|
set(HAL_DEP_REPLACE ${HAL_DEP_REPLACE_IMPL})
|
||||||
|
add_subdirectory(hal)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WITH_CSCORE)
|
||||||
set(CSCORE_DEP_REPLACE ${CSCORE_DEP_REPLACE_IMPL})
|
set(CSCORE_DEP_REPLACE ${CSCORE_DEP_REPLACE_IMPL})
|
||||||
set(CAMERASERVER_DEP_REPLACE ${CAMERASERVER_DEP_REPLACE_IMPL})
|
set(CAMERASERVER_DEP_REPLACE ${CAMERASERVER_DEP_REPLACE_IMPL})
|
||||||
add_subdirectory(cscore)
|
add_subdirectory(cscore)
|
||||||
add_subdirectory(cameraserver)
|
add_subdirectory(cameraserver)
|
||||||
if (NOT WITHOUT_ALLWPILIB)
|
|
||||||
set(HAL_DEP_REPLACE ${HAL_DEP_REPLACE_IMPL})
|
|
||||||
set(WPILIBC_DEP_REPLACE ${WPILIBC_DEP_REPLACE_IMPL})
|
|
||||||
add_subdirectory(hal)
|
|
||||||
add_subdirectory(wpilibj)
|
|
||||||
add_subdirectory(wpilibc)
|
|
||||||
add_subdirectory(myRobot)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WITH_SIMULATION_MODULES AND NOT USE_EXTERNAL_HAL)
|
if (WITH_WPILIB)
|
||||||
add_subdirectory(imgui)
|
set(WPILIBC_DEP_REPLACE ${WPILIBC_DEP_REPLACE_IMPL})
|
||||||
|
add_subdirectory(apriltag)
|
||||||
|
add_subdirectory(wpilibj)
|
||||||
|
add_subdirectory(wpilibc)
|
||||||
|
add_subdirectory(wpilibNewCommands)
|
||||||
|
if (WITH_EXAMPLES)
|
||||||
|
add_subdirectory(wpilibcExamples)
|
||||||
|
endif()
|
||||||
|
add_subdirectory(myRobot)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WITH_SIMULATION_MODULES AND NOT WITH_EXTERNAL_HAL)
|
||||||
add_subdirectory(simulation)
|
add_subdirectory(simulation)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
configure_file(wpilib-config.cmake.in ${CMAKE_BINARY_DIR}/wpilib-config.cmake )
|
configure_file(wpilib-config.cmake.in ${WPILIB_BINARY_DIR}/wpilib-config.cmake )
|
||||||
install(FILES ${CMAKE_BINARY_DIR}/wpilib-config.cmake DESTINATION ${wpilib_config_dir})
|
install(FILES ${WPILIB_BINARY_DIR}/wpilib-config.cmake DESTINATION ${wpilib_config_dir})
|
||||||
|
|||||||
131
CODE_OF_CONDUCT.md
Normal file
131
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Exhibiting Gracious Professionalism® at all times. Gracious Professionalism
|
||||||
|
is a way of doing things that encourages high-quality work, emphasizes the
|
||||||
|
value of others, and respects individuals and the community.
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
[conduct@wpilib.org](mailto:conduct@wpilib.org).
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Contributing to WPILib
|
# Contributing to WPILib
|
||||||
|
|
||||||
So you want to contribute your changes back to WPILib. Great! We have a few contributing rules that will help you make sure your changes will be accepted into the project. Please remember to follow the rules written here, and behave with Gracious Professionalism.
|
So you want to contribute your changes back to WPILib. Great! We have a few contributing rules that will help you make sure your changes will be accepted into the project. Please remember to follow the rules in the [code of conduct](https://github.com/wpilibsuite/allwpilib/blob/main/CODE_OF_CONDUCT.md), and behave with Gracious Professionalism.
|
||||||
|
|
||||||
- [General Contribution Rules](#general-contribution-rules)
|
- [General Contribution Rules](#general-contribution-rules)
|
||||||
- [What to Contribute](#what-to-contribute)
|
- [What to Contribute](#what-to-contribute)
|
||||||
@@ -37,20 +37,21 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
|
|||||||
|
|
||||||
## Coding Guidelines
|
## Coding Guidelines
|
||||||
|
|
||||||
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system. We currently use clang-format 6.0 with wpiformat.
|
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system.
|
||||||
|
|
||||||
While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
|
While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
|
||||||
|
|
||||||
|
When writing math expressions in documentation, use https://www.unicodeit.net/ to convert LaTeX to a Unicode equivalent that's easier to read. Not all expressions will translate (e.g., superscripts of superscripts) so focus on making it readable by someone who isn't familiar with LaTeX. If content on multiple lines needs to be aligned in Doxygen/Javadoc comments (e.g., integration/summation limits, matrices packed with square brackets and superscripts for them), put them in @verbatim/@endverbatim blocks in Doxygen or `<pre>` tags in Javadoc so they render with monospace font.
|
||||||
|
|
||||||
## Submitting Changes
|
## Submitting Changes
|
||||||
|
|
||||||
### Pull Request Format
|
### Pull Request Format
|
||||||
|
|
||||||
Changes should be submitted as a Pull Request against the master branch of WPILib. For most changes, we ask that you squash your changes down to a single commit. For particularly large changes, multiple commits are ok, but assume one commit unless asked otherwise. No change will be merged unless it is up to date with the current master. We will also not merge any changes with merge commits in them; please rebase off of master before submitting a pull request. We do this to make sure that the git history isn't too cluttered.
|
Changes should be submitted as a Pull Request against the main branch of WPILib. For most changes, commits will be squashed upon merge. For particularly large changes, multiple commits are ok, but assume one commit unless asked otherwise. We may ask you to break a PR into multiple standalone PRs or commits for rebase within one PR to separate unrelated changes. No change will be merged unless it is up to date with the current main branch. We do this to make sure that the git history isn't too cluttered.
|
||||||
|
|
||||||
### Merge Process
|
### Merge Process
|
||||||
|
|
||||||
When you first submit changes, Travis-CI will attempt to run `./gradlew check` on your change. If this fails, you will need to fix any issues that it sees. Once Travis passes, we will begin the review process in more earnest. One or more WPILib team members will review your change. This will be a back-and-forth process with the WPILib team and the greater community. Once we are satisfied that your change is ready, we will allow our Jenkins instance to test it. This will run the full gamut of checks, including integration tests on actual hardware. Once all tests have passed and the team is satisfied, we will merge your change into the WPILib repository.
|
When you first submit changes, GitHub Actions will attempt to run `./gradlew check` on your change. If this fails, you will need to fix any issues that it sees. Once Actions passes, we will begin the review process in more earnest. One or more WPILib team members will review your change. This will be a back-and-forth process with the WPILib team and the greater community. Once we are satisfied that your change is ready, we will allow our hosted instance to test it. This will run the full gamut of checks, including integration tests on actual hardware. Once all tests have passed and the team is satisfied, we will merge your change into the WPILib repository.
|
||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
By contributing to WPILib, you agree that your code will be distributed with WPILib, and licensed under the license for the WPILib project. You should not contribute code that you do not have permission to relicense in this manner. This includes code that is licensed under the GPL that you do not have permission to relicense, as WPILib is not released under a copyleft license. Our license is the 3-clause BSD license, which you can find [here](LICENSE.txt).
|
By contributing to WPILib, you agree that your code will be distributed with WPILib, and licensed under the license for the WPILib project. You should not contribute code that you do not have permission to relicense in this manner. This includes code that is licensed under the GPL that you do not have permission to relicense, as WPILib is not released under a copyleft license. Our license is the 3-clause BSD license, which you can find [here](LICENSE.md).
|
||||||
|
|||||||
98
DevelopmentBuilds.md
Normal file
98
DevelopmentBuilds.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Installing Development Builds
|
||||||
|
|
||||||
|
This article contains instructions on building projects using a development build and a local WPILib build.
|
||||||
|
|
||||||
|
**Note:** This only applies to Java/C++ teams.
|
||||||
|
|
||||||
|
## Development Build
|
||||||
|
|
||||||
|
Development builds are the per-commit build hosted every time a commit is pushed to the [allwpilib](https://github.com/wpilibsuite/allwpilib/) repository. These builds are then hosted on [artifactory](https://frcmaven.wpi.edu/artifactory/webapp/#/home).
|
||||||
|
|
||||||
|
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2023 GradleRIO version, ie `2023.0.0-alpha-1`
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
wpi.maven.useLocal = false
|
||||||
|
wpi.maven.useDevelopment = true
|
||||||
|
wpi.versions.wpilibVersion = 'YEAR.+'
|
||||||
|
wpi.versions.wpimathVersion = 'YEAR.+
|
||||||
|
```
|
||||||
|
|
||||||
|
The top of your ``build.gradle`` file should now look similar to the code below. Ignore any differences in versions.
|
||||||
|
|
||||||
|
Java
|
||||||
|
```groovy
|
||||||
|
plugins {
|
||||||
|
id "java"
|
||||||
|
id "edu.wpi.first.GradleRIO" version "2023.0.0-alpha-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi.maven.useLocal = false
|
||||||
|
wpi.maven.useDevelopment = true
|
||||||
|
wpi.versions.wpilibVersion = '2023.+'
|
||||||
|
wpi.versions.wpimathVersion = '2023.+'
|
||||||
|
```
|
||||||
|
|
||||||
|
C++
|
||||||
|
```groovy
|
||||||
|
plugins {
|
||||||
|
id "cpp"
|
||||||
|
id "google-test-test-suite"
|
||||||
|
id "edu.wpi.first.GradleRIO" version "2023.0.0-alpha-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi.maven.useLocal = false
|
||||||
|
wpi.maven.useDevelopment = true
|
||||||
|
wpi.versions.wpilibVersion = '2023.+'
|
||||||
|
wpi.versions.wpimathVersion = '2023.+'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Build Documentation
|
||||||
|
|
||||||
|
* C++: https://github.wpilib.org/allwpilib/docs/development/cpp/
|
||||||
|
* Java: https://github.wpilib.org/allwpilib/docs/development/java/
|
||||||
|
|
||||||
|
## Local Build
|
||||||
|
|
||||||
|
Building with a local build is very similar to building with a development build. Ensure you have built and published WPILib by following the instructions attached [here](https://github.com/wpilibsuite/allwpilib#building-wpilib). Next, find the ``build.gradle`` file in your robot project and open it. Then, add the following code below the plugin section and replace ``YEAR`` with the year of the local version.
|
||||||
|
|
||||||
|
Java
|
||||||
|
```groovy
|
||||||
|
plugins {
|
||||||
|
id "java"
|
||||||
|
id "edu.wpi.first.GradleRIO" version "2023.0.0-alpha-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi.maven.useLocal = false
|
||||||
|
wpi.maven.useFrcMavenLocalDevelopment = true
|
||||||
|
wpi.versions.wpilibVersion = 'YEAR.424242.+'
|
||||||
|
wpi.versions.wpimathVersion = 'YEAR.424242.+'
|
||||||
|
```
|
||||||
|
|
||||||
|
C++
|
||||||
|
```groovy
|
||||||
|
plugins {
|
||||||
|
id "cpp"
|
||||||
|
id "google-test-test-suite"
|
||||||
|
id "edu.wpi.first.GradleRIO" version "2023.0.0-alpha-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi.maven.useLocal = false
|
||||||
|
wpi.maven.useFrcMavenLocalDevelopment = true
|
||||||
|
wpi.versions.wpilibVersion = 'YEAR.424242.+'
|
||||||
|
wpi.versions.wpimathVersion = 'YEAR.424242.+'
|
||||||
|
```
|
||||||
|
|
||||||
|
# roboRIO Development
|
||||||
|
|
||||||
|
This repo contains a myRobot project built in way to do full project development without needing to do a full publish into GradleRIO. These also only require building the minimum amount of binaries for the roboRIO, so the builds are much faster as well.
|
||||||
|
|
||||||
|
The setup only works if the roboRIO is USB connected. If an alternate IP address is preferred, the `address` block in myRobot\build.gradle can be changed to point to another address.
|
||||||
|
|
||||||
|
The following 3 tasks can be used for deployment:
|
||||||
|
* `:myRobot:deployShared` deploys the C++ project using shared dependencies. Prefer this one for most C++ development.
|
||||||
|
* `:myRobot:deployStatic` deploys the C++ project with all dependencies statically linked.
|
||||||
|
* `:myRobot:deployJava` deploys the Java project and all required dependencies. Also installs the JRE if not currently installed.
|
||||||
|
|
||||||
|
Deploying any of these to the roboRIO will disable the current startup project until it is redeployed.
|
||||||
|
|
||||||
|
From here, ssh into the roboRIO using the `lvuser` account and run `frcRunRobot.sh` (It's in path).
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Faster Builds for Developers
|
|
||||||
|
|
||||||
When you run `./gradlew build`, it builds EVERYTHING. This means debug and release builds for desktop and all installed cross compilers. For many developers, this is way too much, and causes much developer pain.
|
|
||||||
|
|
||||||
To help with some of these things, common tasks have shortcuts to only build necessary things for common development and testing tasks.
|
|
||||||
|
|
||||||
## Development (Desktop)
|
|
||||||
|
|
||||||
For projects `wpiutil`, `ntcore`, `cscore`, `hal` `wpilibOldCommands`, `wpilibNewCommands` and `cameraserver`, a `testDesktopJava` and a `testDesktopCpp` task exists. These can be ran with `./gradlew :projectName:task`, and will only build the minimum things required to run those tests.
|
|
||||||
|
|
||||||
For `wpilibc`, a `testDesktopCpp` task exists. For `wpilibj`, a `testDesktopJava` task exists.
|
|
||||||
|
|
||||||
For `wpilibcExamples`, a `buildDesktopCpp` task exists (These can't be ran, but they can compile).
|
|
||||||
|
|
||||||
For `wpilibjExamples`, a `buildDesktopJava` task exists.
|
|
||||||
24
LICENSE.md
Normal file
24
LICENSE.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
Copyright (c) 2009-2022 FIRST and other WPILib contributors
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of FIRST, WPILib, nor the names of other WPILib
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY FIRST AND OTHER WPILIB CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
24
LICENSE.txt
24
LICENSE.txt
@@ -1,24 +0,0 @@
|
|||||||
Copyright (c) 2009-2018 FIRST
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
* Neither the name of the FIRST nor the
|
|
||||||
names of its contributors may be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY FIRST AND CONTRIBUTORS``AS IS'' AND ANY
|
|
||||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
27
MAINTAINERS.md
Normal file
27
MAINTAINERS.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
## Publishing Third Party Dependencies
|
||||||
|
Currently the 3rd party deps are imgui, opencv, and google test
|
||||||
|
|
||||||
|
For publishing these dependencies, the version needs to be manually updated in the publish.gradle file of their respective repository.
|
||||||
|
Then, in the azure build for the dependency you want to build for, manually start a pipeline build (As of current, this is the `Run Pipeline` button).
|
||||||
|
A variable needs to be added called `RUN_AZURE_ARTIFACTORY_RELEASE`, with a value of `true`. Then when the pipeline gets started, the final build outputs will be updated to artifactory.
|
||||||
|
|
||||||
|
To use newer versions of C++ dependencies, in `shared/config.gradle`, update the version related to the specific dependency.
|
||||||
|
For Java dependencies, there is likely a file related to the specific dependency in the shared folder. Update the version in there.
|
||||||
|
|
||||||
|
Note, changing artifact locations (This includes changing the artifact year currently, I have an issue open to change this) requires updating the `native-utils` plugin
|
||||||
|
|
||||||
|
## Publishing allwpilib
|
||||||
|
allwpilib publishes to the development repo on every push to main. To publish a release build, upload a new tag, and a release will automatically be built and published.
|
||||||
|
|
||||||
|
## Publishing desktop tools
|
||||||
|
Desktop tools publish to the development repo on every push to main. To publish a release build, upload a new tag, and a release will automatically be built and published.
|
||||||
|
|
||||||
|
## Publishing VS Code
|
||||||
|
Before publishing, make sure to update the gradlerio version in `vscode-wpilib/resources/gradle/version.txt` Also make sure the gradle wrapper version matches the wrapper required by gradlerio.
|
||||||
|
Upon pushing a tag, a release will be built, and the files will be uploaded to the releases on GitHub. For publishing to the marketplace, you need a Microsoft account and to be added as a maintainer.
|
||||||
|
|
||||||
|
## Publishing GradleRIO
|
||||||
|
Before publishing, make sure to update the version in build.gradle. Publishing must happen locally, using the command `./gradlew publishPlugin`. This does require your API key for publishing to be set.
|
||||||
|
|
||||||
|
## Building the installer
|
||||||
|
Update the GradleRIO version in gradle.properties, and in the scripts folder in vscode, update the vscode extension. Then push, it will build the installer on azure.
|
||||||
@@ -9,7 +9,7 @@ We provide two repositories. These repositories are:
|
|||||||
* (Development) https://frcmaven.wpi.edu/artifactory/development/
|
* (Development) https://frcmaven.wpi.edu/artifactory/development/
|
||||||
|
|
||||||
The release repository is where official WPILib releases are pushed.
|
The release repository is where official WPILib releases are pushed.
|
||||||
The development repository is where development releases of every commit to [master](https://github.com/wpilibsuite/allwpilib/tree/master) is pushed.
|
The development repository is where development releases of every commit to [main](https://github.com/wpilibsuite/allwpilib/tree/main) is pushed.
|
||||||
|
|
||||||
## Artifact classifiers
|
## Artifact classifiers
|
||||||
We provide two base types of artifacts.
|
We provide two base types of artifacts.
|
||||||
@@ -69,24 +69,53 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
|||||||
|
|
||||||
* wpiutil
|
* wpiutil
|
||||||
|
|
||||||
* hal
|
* wpigui
|
||||||
|
* imgui
|
||||||
|
|
||||||
|
* wpimath
|
||||||
* wpiutil
|
* wpiutil
|
||||||
|
|
||||||
* halsim
|
* wpinet
|
||||||
* imgui
|
|
||||||
* wpiutil
|
* wpiutil
|
||||||
|
|
||||||
* ntcore
|
* ntcore
|
||||||
* wpiutil
|
* wpiutil
|
||||||
|
* wpinet
|
||||||
|
|
||||||
|
* glass/libglass
|
||||||
|
* wpiutil
|
||||||
|
* wpimath
|
||||||
|
* wpigui
|
||||||
|
|
||||||
|
* glass/libglassnt
|
||||||
|
* wpiutil
|
||||||
|
* wpinet
|
||||||
|
* ntcore
|
||||||
|
* wpimath
|
||||||
|
* wpigui
|
||||||
|
|
||||||
|
* hal
|
||||||
|
* wpiutil
|
||||||
|
|
||||||
|
* halsim
|
||||||
|
* wpiutil
|
||||||
|
* wpinet
|
||||||
|
* ntcore
|
||||||
|
* wpimath
|
||||||
|
* wpigui
|
||||||
|
* libglass
|
||||||
|
* libglassnt
|
||||||
|
|
||||||
* cscore
|
* cscore
|
||||||
* opencv
|
* opencv
|
||||||
|
* wpinet
|
||||||
* wpiutil
|
* wpiutil
|
||||||
|
|
||||||
* cameraserver
|
* cameraserver
|
||||||
* ntcore
|
* ntcore
|
||||||
* cscore
|
* cscore
|
||||||
* opencv
|
* opencv
|
||||||
|
* wpinet
|
||||||
* wpiutil
|
* wpiutil
|
||||||
|
|
||||||
* wpilibj
|
* wpilibj
|
||||||
@@ -94,6 +123,7 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
|||||||
* cameraserver
|
* cameraserver
|
||||||
* ntcore
|
* ntcore
|
||||||
* cscore
|
* cscore
|
||||||
|
* wpinet
|
||||||
* wpiutil
|
* wpiutil
|
||||||
|
|
||||||
* wpilibc
|
* wpilibc
|
||||||
@@ -101,6 +131,8 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
|||||||
* cameraserver
|
* cameraserver
|
||||||
* ntcore
|
* ntcore
|
||||||
* cscore
|
* cscore
|
||||||
|
* wpimath
|
||||||
|
* wpinet
|
||||||
* wpiutil
|
* wpiutil
|
||||||
|
|
||||||
* wpilibNewCommands
|
* wpilibNewCommands
|
||||||
@@ -109,15 +141,10 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
|||||||
* cameraserver
|
* cameraserver
|
||||||
* ntcore
|
* ntcore
|
||||||
* cscore
|
* cscore
|
||||||
|
* wpimath
|
||||||
|
* wpinet
|
||||||
* wpiutil
|
* wpiutil
|
||||||
|
|
||||||
* wpilibNewCommands
|
|
||||||
* wpilibc
|
|
||||||
* hal
|
|
||||||
* cameraserver
|
|
||||||
* ntcore
|
|
||||||
* cscore
|
|
||||||
* wpiutil
|
|
||||||
|
|
||||||
### Third Party Artifacts
|
### Third Party Artifacts
|
||||||
|
|
||||||
@@ -128,3 +155,4 @@ All artifacts are based at `edu.wpi.first.thirdparty.frcYEAR` in the repository.
|
|||||||
* googletest
|
* googletest
|
||||||
* imgui
|
* imgui
|
||||||
* opencv
|
* opencv
|
||||||
|
* libssh
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# WPILib CMake Support
|
# WPILib CMake Support
|
||||||
|
|
||||||
WPILib is normally built with Gradle, however for some systems, such a linux based coprocessors, Gradle doesn't work correctly, especially if cscore is needed, which requires OpenCV. We provide the CMake build for these cases. Although it is supported on Windows, these docs will only go over linux builds.
|
WPILib is normally built with Gradle, however for some systems, such as Linux based coprocessors, Gradle doesn't work correctly, especially if cscore is needed, which requires OpenCV. Furthermore, the CMake build can be used for C++ development because it provides better build caching compared to Gradle. We provide the CMake build for these cases. Although it is supported on Windows, these docs will only go over Linux builds.
|
||||||
|
|
||||||
## Libraries that get built
|
## Libraries that get built
|
||||||
* wpiutil
|
* wpiutil
|
||||||
@@ -9,58 +9,75 @@ WPILib is normally built with Gradle, however for some systems, such a linux bas
|
|||||||
* cameraserver
|
* cameraserver
|
||||||
* hal
|
* hal
|
||||||
* wpilib
|
* wpilib
|
||||||
|
* halsim
|
||||||
|
* wpigui
|
||||||
|
* wpimath
|
||||||
|
|
||||||
By default, all libraries except for the HAL and WPILib get built with a default cmake setup. The libraries are built as shared libraries, and include the JNI libraries as well as building the Java jars.
|
By default, all libraries except for the HAL and WPILib get built with a default CMake setup. The libraries are built as shared libraries, and include the JNI libraries as well as building the Java JARs.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
The most common prerequisite is going to be OpenCV. OpenCV needs to be findable by cmake. On systems like the Jetson, this is installed by default. Otherwise, you will need to build cmake from source and install it.
|
The most common prerequisite is going to be OpenCV. OpenCV needs to be findable by CMake. On systems like the Jetson, this is installed by default. Otherwise, you will need to build OpenCV from source and install it.
|
||||||
|
|
||||||
In addition, if you want JNI and Java, you will need a JDK of at least version 8 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the jdk directory.
|
In addition, if you want JNI and Java, you will need a JDK of at least version 11 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the JDK directory.
|
||||||
|
|
||||||
|
If you are building with unit tests or simulation modules, you will also need an Internet connection for the initial setup process, as CMake will clone google-test and imgui from GitHub.
|
||||||
|
|
||||||
## Build Options
|
## Build Options
|
||||||
|
|
||||||
The following build options are available:
|
The following build options are available:
|
||||||
|
|
||||||
* WITHOUT_JAVA (OFF Default)
|
* `WITH_JAVA` (ON Default)
|
||||||
* Enabling this option will disable Java and JNI builds. If this is off, `BUILD_SHARED_LIBS` must be on. Otherwise cmake will error.
|
* This option will enable Java and JNI builds. If this is on, `WITH_SHARED_LIBS` must be on. Otherwise CMake will error.
|
||||||
* BUILD_SHARED_LIBS (ON Default)
|
* `WITH_SHARED_LIBS` (ON Default)
|
||||||
* Disabling this option will cause cmake to build static libraries instead of shared libraries. If this is off, `WITHOUT_JAVA` must be on. Otherwise cmake will error.
|
* This option will cause cmake to build static libraries instead of shared libraries. If this is off, `WITH_JAVA` must be off. Otherwise CMake will error.
|
||||||
* WITHOUT_CSCORE (OFF Default)
|
* `WITH_CSCORE` (ON Default)
|
||||||
* Enabling this option will cause cscore to not be built. This will also implicitly disable cameraserver, the hal and wpilib as well, irrespective of their specific options. If this is on, the opencv build requirement is removed.
|
* This option will cause cscore to be built. Turning this off will implicitly disable cameraserver, the hal and wpilib as well, irrespective of their specific options. If this is off, the OpenCV build requirement is removed.
|
||||||
* WITHOUT_ALLWPILIB (ON Default)
|
* `WITH_NTCORE` (ON Default)
|
||||||
* Disabling this option will build the hal and wpilib during the build. The HAL is the simulator hal, unless the external hal options are used. The cmake build has no capability to build for the RoboRIO.
|
* This option will cause ntcore to be built. Turning this off will implicitly disable wpinet and wpilib as well, irrespective of their specific options.
|
||||||
* USE_EXTERNAL_HAL (OFF Default)
|
* `WITH_WPIMATH` (ON Default)
|
||||||
|
* This option will build the wpimath library. This option must be on to build wpilib.
|
||||||
|
* `WITH_WPILIB` (ON Default)
|
||||||
|
* This option will build the hal and wpilibc/j during the build. The HAL is the simulator hal, unless the external hal options are used. The cmake build has no capability to build for the RoboRIO.
|
||||||
|
* `WITH_EXAMPLES` (ON Default)
|
||||||
|
* This option will build C++ examples.
|
||||||
|
* `WITH_TESTS` (ON Default)
|
||||||
|
* This option will build C++ unit tests. These can be run via `make test`.
|
||||||
|
* `WITH_GUI` (ON Default)
|
||||||
|
* This option will build GUI items.
|
||||||
|
* `WITH_SIMULATION_MODULES` (ON Default)
|
||||||
|
* This option will build simulation modules, including wpigui and the HALSim plugins.
|
||||||
|
* `WITH_EXTERNAL_HAL` (OFF Default)
|
||||||
* TODO
|
* TODO
|
||||||
* EXTERNAL_HAL_FILE
|
* `EXTERNAL_HAL_FILE`
|
||||||
* TODO
|
* TODO
|
||||||
* OPENCV_JAVA_INSTALL_DIR
|
* `OPENCV_JAVA_INSTALL_DIR`
|
||||||
* Set this option to the location of the archive of the OpenCV Java bindings (it should be called opencv-xxx.jar, with the x'es being version numbers). NOTE: set it to the LOCATION of the file, not the file itself!
|
* Set this option to the location of the archive of the OpenCV Java bindings (it should be called opencv-xxx.jar, with the x'es being version numbers). NOTE: set it to the LOCATION of the file, not the file itself!
|
||||||
|
|
||||||
## Build Setup
|
## Build Setup
|
||||||
|
|
||||||
The WPILib CMake build does not allow in source builds. Because the `build` directory is used by gradle, we recommend a `buildcmake` directory in the root. This folder is included in the gitignore.
|
The WPILib CMake build does not allow in source builds. Because the `build` directory is used by Gradle, we recommend a `build-cmake` directory in the root. This folder is included in the gitignore.
|
||||||
|
|
||||||
Once you have a build folder, run cmake configuration in that build directory with the following command.
|
Once you have a build folder, run CMake configuration in that build directory with the following command.
|
||||||
|
|
||||||
```
|
```
|
||||||
cmake path/to/allwpilib/root
|
cmake path/to/allwpilib/root
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to change any of the options, add `-DOPTIONHERE=VALUE` to the cmake command. This will check for any dependencies. If everything works properly this will succeed. If not, please check out the troubleshooting section for help.
|
If you want to change any of the options, add `-DOPTIONHERE=VALUE` to the `cmake` command. This will check for any dependencies. If everything works properly this will succeed. If not, please check out the troubleshooting section for help.
|
||||||
|
|
||||||
If you want, you can also use `ccmake` in order to visually set these properties as well.
|
If you want, you can also use `ccmake` in order to visually set these properties as well. [Here](https://cmake.org/cmake/help/v3.0/manual/ccmake.1.html) is the link to the documentation for that program.
|
||||||
https://cmake.org/cmake/help/v3.0/manual/ccmake.1.html
|
|
||||||
Here is the link to the documentation for that program.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Once you have cmake setup. run `make` from the directory you configured cmake in. This will build all libraries possible. If you have a multicore system, we recommend running make with multiple jobs. The usual rule of thumb is 1.5x the number of cores you have. To run a multiple job build, run the following command with x being the number of jobs you want.
|
Once you have cmake setup. run `make` from the directory you configured CMake in. This will build all libraries possible. If you have a multicore system, we recommend running `make` with multiple jobs. The usual rule of thumb is 1.5x the number of cores you have. To run a multiple job build, run the following command with x being the number of jobs you want.
|
||||||
|
|
||||||
```
|
```
|
||||||
make -jx
|
make -jx
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `ninja` generator is also supported, and can be enabled by passing `-GNinja` to the initial `cmake` command.
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
After build, the easiest way to use the libraries is to install them. Run the following command to install the libraries. This will install them so that they can be used from external cmake projects.
|
After build, the easiest way to use the libraries is to install them. Run the following command to install the libraries. This will install them so that they can be used from external cmake projects.
|
||||||
@@ -81,7 +98,7 @@ project(vision_app) # Project Name Here
|
|||||||
|
|
||||||
find_package(wpilib REQUIRED)
|
find_package(wpilib REQUIRED)
|
||||||
|
|
||||||
add_executable(my_vision_app main.cpp) # exectuable name as first parameter
|
add_executable(my_vision_app main.cpp) # executable name as first parameter
|
||||||
target_link_libraries(my_vision_app cameraserver ntcore cscore wpiutil)
|
target_link_libraries(my_vision_app cameraserver ntcore cscore wpiutil)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -137,4 +154,4 @@ CMake Error at /usr/share/cmake-3.5/Modules/FindPackageHandleStandardArgs.cmake:
|
|||||||
|
|
||||||
If this happens, make sure you have a JDK of at least version 8 installed, and that your JAVA_HOME variable is set properly to point to the JDK.
|
If this happens, make sure you have a JDK of at least version 8 installed, and that your JAVA_HOME variable is set properly to point to the JDK.
|
||||||
|
|
||||||
In addition, if you do not need Java, you can disable it with `-DWITHOUT_JAVA=ON`.
|
In addition, if you do not need Java, you can disable it with `-DWITH_JAVA=OFF`.
|
||||||
|
|||||||
122
README.md
122
README.md
@@ -1,21 +1,38 @@
|
|||||||
# WPILib Project
|
# WPILib Project
|
||||||
|
|
||||||
[](https://dev.azure.com/wpilib/wpilib/_build/latest?definitionId=1)
|
[](https://github.com/wpilibsuite/allwpilib/actions/workflows/gradle.yml)
|
||||||
|
[](https://github.wpilib.org/allwpilib/docs/development/cpp/)
|
||||||
|
[](https://github.wpilib.org/allwpilib/docs/development/java/)
|
||||||
|
|
||||||
Welcome to the WPILib project. This repository contains the HAL, WPILibJ, and WPILibC projects. These are the core libraries for creating robot programs for the roboRIO.
|
Welcome to the WPILib project. This repository contains the HAL, WPILibJ, and WPILibC projects. These are the core libraries for creating robot programs for the roboRIO.
|
||||||
|
|
||||||
- [WPILib Mission](#wpilib-mission)
|
- [WPILib Project](#wpilib-project)
|
||||||
|
- [WPILib Mission](#wpilib-mission)
|
||||||
- [Building WPILib](#building-wpilib)
|
- [Building WPILib](#building-wpilib)
|
||||||
- [Requirements](#requirements)
|
- [Requirements](#requirements)
|
||||||
- [Setup](#setup)
|
- [Setup](#setup)
|
||||||
- [Building](#building)
|
- [Building](#building)
|
||||||
- [Publishing](#publishing)
|
- [Faster builds](#faster-builds)
|
||||||
- [Structure and Organization](#structure-and-organization)
|
- [Using Development Builds](#using-development-builds)
|
||||||
|
- [Custom toolchain location](#custom-toolchain-location)
|
||||||
|
- [Formatting/Linting](#formattinglinting)
|
||||||
|
- [CMake](#cmake)
|
||||||
|
- [Publishing](#publishing)
|
||||||
|
- [Structure and Organization](#structure-and-organization)
|
||||||
- [Contributing to WPILib](#contributing-to-wpilib)
|
- [Contributing to WPILib](#contributing-to-wpilib)
|
||||||
|
|
||||||
## WPILib Mission
|
## WPILib Mission
|
||||||
|
|
||||||
The WPILib Mission is to enable FIRST Robotics teams to focus on writing game-specific software rather than focusing on hardware details - "raise the floor, don't lower the ceiling". We work to enable teams with limited programming knowledge and/or mentor experience to be as successful as possible, while not hampering the abilities of teams with more advanced programming capabilities. We support Kit of Parts control system components directly in the library. We also strive to keep parity between major features of each language (Java, C++, and NI's LabVIEW), so that teams aren't at a disadvantage for choosing a specific programming language. WPILib is an open source project, licensed under the BSD 3-clause license. You can find a copy of the license [here](LICENSE.txt).
|
The WPILib Mission is to enable FIRST Robotics teams to focus on writing game-specific software rather than focusing on hardware details - "raise the floor, don't lower the ceiling". We work to enable teams with limited programming knowledge and/or mentor experience to be as successful as possible, while not hampering the abilities of teams with more advanced programming capabilities. We support Kit of Parts control system components directly in the library. We also strive to keep parity between major features of each language (Java, C++, and NI's LabVIEW), so that teams aren't at a disadvantage for choosing a specific programming language. WPILib is an open source project, licensed under the BSD 3-clause license. You can find a copy of the license [here](LICENSE.md).
|
||||||
|
|
||||||
|
# Quick Start
|
||||||
|
|
||||||
|
Below is a list of instructions that guide you through cloning, building, publishing and using local allwpilib binaries in a robot project. This quick start is not intended as a replacement for the information further listed in this document.
|
||||||
|
|
||||||
|
1. Clone the repository with `git clone https://github.com/wpilibsuite/allwpilib.git`
|
||||||
|
2. Build the repository with `./gradlew build` or `./gradlew build --build-cache` if you have an internet connection
|
||||||
|
3. Publish the artifacts locally by running `./gradlew publish`
|
||||||
|
4. [Update your](DevelopmentBuilds.md) `build.gradle` [to use the artifacts](DevelopmentBuilds.md)
|
||||||
|
|
||||||
# Building WPILib
|
# Building WPILib
|
||||||
|
|
||||||
@@ -23,19 +40,28 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- A C++ compiler
|
- [JDK 11](https://adoptopenjdk.net/)
|
||||||
- On Linux, GCC works fine
|
- Note that the JRE is insufficient; the full JDK is required
|
||||||
- On Windows, you need Visual Studio 2019 (the free community edition works fine).
|
- On Ubuntu, run `sudo apt install openjdk-11-jdk`
|
||||||
Make sure to select the C++ Programming Language for installation
|
- On Windows, install the JDK 11 .msi from the link above
|
||||||
- [ARM Compiler Toolchain](https://github.com/wpilibsuite/roborio-toolchain/releases)
|
- On macOS, install the JDK 11 .pkg from the link above
|
||||||
* Note that for 2020 and beyond, you should use version 7 or greater of GCC
|
- C++ compiler
|
||||||
- Doxygen (Only required if you want to build the C++ documentation)
|
- On Linux, install GCC 11 or greater
|
||||||
|
- On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
|
||||||
|
- On macOS, install the Xcode command-line build tools via `xcode-select --install`
|
||||||
|
- ARM compiler toolchain
|
||||||
|
- Run `./gradlew installRoboRioToolchain` after cloning this repository
|
||||||
|
- If the WPILib installer was used, this toolchain is already installed
|
||||||
|
- Raspberry Pi toolchain (optional)
|
||||||
|
- Run `./gradlew installArm32Toolchain` after cloning this repository
|
||||||
|
|
||||||
|
On macOS ARM, run `softwareupdate --install-rosetta`. This is necessary to be able to use the macOS x86 roboRIO toolchain on ARM.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
Clone the WPILib repository. If the toolchains are not installed, install them, and make sure they are available on the system PATH.
|
Clone the WPILib repository and follow the instructions above for installing any required tooling.
|
||||||
|
|
||||||
See the [styleguide README](https://github.com/wpilibsuite/styleguide/blob/master/README.md) for wpiformat setup instructions.
|
See the [styleguide README](https://github.com/wpilibsuite/styleguide/blob/main/README.md) for wpiformat setup instructions. We use clang-format 14.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
@@ -51,33 +77,57 @@ To build a specific subproject, such as WPILibC, you must access the subproject
|
|||||||
./gradlew :wpilibc:build
|
./gradlew :wpilibc:build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The gradlew wrapper only exists in the root of the main project, so be sure to run all commands from there. All of the subprojects have build tasks that can be run. Gradle automatically determines and rebuilds dependencies, so if you make a change in the HAL and then run `./gradlew :wpilibc:build`, the HAL will be rebuilt, then WPILibC.
|
||||||
|
|
||||||
|
There are a few tasks other than `build` available. To see them, run the meta-task `tasks`. This will print a list of all available tasks, with a description of each task.
|
||||||
|
|
||||||
|
If opening from a fresh clone, generated java dependencies will not exist. Most IDEs will not run the generation tasks, which will cause lots of IDE errors. Manually run `./gradlew compileJava` from a terminal to run all the compile tasks, and then refresh your IDE's configuration (In VS Code open settings.gradle and save).
|
||||||
|
|
||||||
|
### Faster builds
|
||||||
|
|
||||||
|
`./gradlew build` builds _everything_, which includes debug and release builds for desktop and all installed cross compilers. Many developers don't need or want to build all of this. Therefore, common tasks have shortcuts to only build necessary components for common development and testing tasks.
|
||||||
|
|
||||||
|
`./gradlew testDesktopCpp` and `./gradlew testDesktopJava` will build and run the tests for `wpilibc` and `wpilibj` respectively. They will only build the minimum components required to run the tests.
|
||||||
|
|
||||||
|
`testDesktopCpp` and `testDesktopJava` tasks also exist for the projects `wpiutil`, `ntcore`, `cscore`, `hal` `wpilibNewCommands` and `cameraserver`. These can be ran with `./gradlew :projectName:task`.
|
||||||
|
|
||||||
|
`./gradlew buildDesktopCpp` and `./gradlew buildDesktopJava` will compile `wpilibcExamples` and `wpilibjExamples` respectively. The results can't be ran, but they can compile.
|
||||||
|
|
||||||
|
### Build Cache
|
||||||
|
|
||||||
|
Run with `--build-cache` on the command-line to use the shared [build cache](https://docs.gradle.org/current/userguide/build_cache.html) artifacts generated by the continuous integration server. Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew build --build-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Development Builds
|
||||||
|
|
||||||
|
Please read the documentation available [here](DevelopmentBuilds.md)
|
||||||
|
|
||||||
|
### Custom toolchain location
|
||||||
|
|
||||||
If you have installed the FRC Toolchain to a directory other than the default, or if the Toolchain location is not on your System PATH, you can pass the `toolChainPath` property to specify where it is located. Example:
|
If you have installed the FRC Toolchain to a directory other than the default, or if the Toolchain location is not on your System PATH, you can pass the `toolChainPath` property to specify where it is located. Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./gradlew build -PtoolChainPath=some/path/to/frc/toolchain/bin
|
./gradlew build -PtoolChainPath=some/path/to/frc/toolchain/bin
|
||||||
```
|
```
|
||||||
|
|
||||||
If you also want simulation to be built, add -PmakeSim. This requires gazebo_transport. We have tested on 14.04 and 15.05, but any correct install of Gazebo should work, even on Windows if you build Gazebo from source. Correct means CMake needs to be able to find gazebo-config.cmake. See [The Gazebo website](https://gazebosim.org/) for installation instructions.
|
### Formatting/linting
|
||||||
|
|
||||||
```bash
|
Once a PR has been submitted, formatting can be run in CI by commenting `/format` on the PR. A new commit will be pushed with the formatting changes.
|
||||||
./gradlew build -PmakeSim
|
|
||||||
```
|
|
||||||
|
|
||||||
If you prefer to use CMake directly, the you can still do so.
|
#### wpiformat
|
||||||
The common CMake tasks are wpilibcSim, frc_gazebo_plugins, and gz_msgs
|
|
||||||
|
|
||||||
```bash
|
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat -clang 14` on Windows or `python3 -m wpiformat -clang 14` on other platforms.
|
||||||
mkdir build #run this in the root of allwpilib
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
The gradlew wrapper only exists in the root of the main project, so be sure to run all commands from there. All of the subprojects have build tasks that can be run. Gradle automatically determines and rebuilds dependencies, so if you make a change in the HAL and then run `./gradlew :wpilibc:build`, the HAL will be rebuilt, then WPILibC.
|
#### Java Code Quality Tools
|
||||||
|
|
||||||
There are a few tasks other than `build` available. To see them, run the meta-task `tasks`. This will print a list of all available tasks, with a description of each task.
|
The Java code quality tools Checkstyle, PMD, and Spotless can be run via `./gradlew javaFormat`. SpotBugs can be run via the `spotbugsMain`, `spotbugsTest`, and `spotbugsDev` tasks. These tools will all be run automatically by the `build` task. To disable this behavior, pass the `-PskipJavaFormat` flag.
|
||||||
|
|
||||||
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.
|
If you only want to run the Java autoformatter, run `./gradlew spotlessApply`.
|
||||||
|
|
||||||
|
### CMake
|
||||||
|
|
||||||
CMake is also supported for building. See [README-CMAKE.md](README-CMAKE.md).
|
CMake is also supported for building. See [README-CMAKE.md](README-CMAKE.md).
|
||||||
|
|
||||||
@@ -94,13 +144,13 @@ The maven artifacts are described in [MavenArtifacts.md](MavenArtifacts.md)
|
|||||||
|
|
||||||
## Structure and Organization
|
## Structure and Organization
|
||||||
|
|
||||||
The main WPILib code you're probably looking for is in WPILibJ and WPILibC. Those directories are split into shared, sim, and athena. Athena contains the WPILib code meant to run on your roboRIO. Sim is WPILib code meant to run on your computer with Gazebo, and shared is code shared between the two. Shared code must be platform-independent, since it will be compiled with both the ARM cross-compiler and whatever desktop compiler you are using (g++, msvc, etc...).
|
The main WPILib code you're probably looking for is in WPILibJ and WPILibC. Those directories are split into shared, sim, and athena. Athena contains the WPILib code meant to run on your roboRIO. Sim is WPILib code meant to run on your computer, and shared is code shared between the two. Shared code must be platform-independent, since it will be compiled with both the ARM cross-compiler and whatever desktop compiler you are using (g++, msvc, etc...).
|
||||||
|
|
||||||
The Simulation directory contains extra simulation tools and libraries, such as gz_msgs and JavaGazebo. See sub-directories for more information.
|
|
||||||
|
|
||||||
The integration test directories for C++ and Java contain test code that runs on our test-system. When you submit code for review, it is tested by those programs. If you add new functionality you should make sure to write tests for it so we don't break it in the future.
|
The integration test directories for C++ and Java contain test code that runs on our test-system. When you submit code for review, it is tested by those programs. If you add new functionality you should make sure to write tests for it so we don't break it in the future.
|
||||||
|
|
||||||
The hal directory contains more C++ code meant to run on the roboRIO. HAL is an acronym for "Hardware Abstraction Layer", and it interfaces with the NI Libraries. The NI Libraries contain the low-level code for controlling devices on your robot. The NI Libraries are found in the ni-libraries folder.
|
The hal directory contains more C++ code meant to run on the roboRIO. HAL is an acronym for "Hardware Abstraction Layer", and it interfaces with the NI Libraries. The NI Libraries contain the low-level code for controlling devices on your robot. The NI Libraries are found in the [ni-libraries](https://github.com/wpilibsuite/ni-libraries) project.
|
||||||
|
|
||||||
|
The upstream_utils directory contains scripts for updating copies of thirdparty code in the repository.
|
||||||
|
|
||||||
The [styleguide repository](https://github.com/wpilibsuite/styleguide) contains our style guides for C++ and Java code. Anything submitted to the WPILib project needs to follow the code style guides outlined in there. For details about the style, please see the contributors document [here](CONTRIBUTING.md#coding-guidelines).
|
The [styleguide repository](https://github.com/wpilibsuite/styleguide) contains our style guides for C++ and Java code. Anything submitted to the WPILib project needs to follow the code style guides outlined in there. For details about the style, please see the contributors document [here](CONTRIBUTING.md#coding-guidelines).
|
||||||
|
|
||||||
|
|||||||
@@ -17,35 +17,35 @@ Program Locations
|
|||||||
------- ---------
|
------- ---------
|
||||||
RoboRIO Libraries ni-libraries
|
RoboRIO Libraries ni-libraries
|
||||||
Google Test gtest
|
Google Test gtest
|
||||||
LLVM wpiutil/src/main/native/include/wpi/{various files}
|
LLVM wpiutil/src/main/native/thirdparty/llvm
|
||||||
wpiutil/src/main/native/cpp/llvm/
|
wpiutil/src/test/native/cpp/llvm/
|
||||||
wpiutil/src/main/native/cpp/leb128.cpp
|
JSON for Modern C++ wpiutil/src/main/native/thirdparty/json
|
||||||
wpiutil/src/test/native/cpp/leb128Test.cpp
|
|
||||||
JSON for Modern C++ wpiutil/src/main/native/include/wpi/json.h
|
|
||||||
wpiutil/src/main/native/cpp/json_*.cpp
|
|
||||||
wpiutil/src/test/native/cpp/json/
|
wpiutil/src/test/native/cpp/json/
|
||||||
libuv wpiutil/src/main/native/include/uv.h
|
libuv wpinet/src/main/native/thirdparty/libuv/
|
||||||
wpiutil/src/main/native/include/uv/
|
fmtlib wpiutil/src/main/native/thirdparty/fmtlib/
|
||||||
wpiutil/src/main/native/libuv/
|
sigslot wpiutil/src/main/native/thirdparty/sigslot
|
||||||
sigslot wpiutil/src/main/native/include/wpi/Signal.h
|
tcpsockets wpinet/src/main/native/thirdparty/tcpsockets
|
||||||
wpiutil/src/test/native/cpp/sigslot/
|
MPack wpiutil/src/main/native/thirdparty/mpack
|
||||||
tcpsockets wpiutil/src/main/native/cpp/TCP{Stream,Connector,Acceptor}.cpp
|
Bootstrap wpinet/src/main/native/resources/bootstrap-*
|
||||||
wpiutil/src/main/native/include/wpi/TCP*.h
|
CoreUI wpinet/src/main/native/resources/coreui-*
|
||||||
Bootstrap wpiutil/src/main/native/resources/bootstrap-*
|
Feather Icons wpinet/src/main/native/resources/feather-*
|
||||||
CoreUI wpiutil/src/main/native/resources/coreui-*
|
jQuery wpinet/src/main/native/resources/jquery-*
|
||||||
Feather Icons wpiutil/src/main/native/resources/feather-*
|
popper.js wpinet/src/main/native/resources/popper-*
|
||||||
jQuery wpiutil/src/main/native/resources/jquery-*
|
units wpimath/src/main/native/include/units/
|
||||||
popper.js wpiutil/src/main/native/resources/popper-*
|
Eigen wpimath/src/main/native/thirdparty/eigen/include/
|
||||||
units wpiutil/src/main/native/include/units/units.h
|
|
||||||
Eigen wpiutil/src/main/native/eigeninclude/
|
|
||||||
StackWalker wpiutil/src/main/native/windows/StackWalker.*
|
StackWalker wpiutil/src/main/native/windows/StackWalker.*
|
||||||
|
GHC filesystem wpiutil/src/main/native/thirdparty/include/wpi/ghc/
|
||||||
Team 254 Library wpilibj/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java
|
Team 254 Library wpilibj/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java
|
||||||
wpilibj/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java
|
wpilibj/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java
|
||||||
wpilibc/src/main/native/include/spline/SplineParameterizer.h
|
wpilibc/src/main/native/include/spline/SplineParameterizer.h
|
||||||
wpilibc/src/main/native/include/trajectory/TrajectoryParameterizer.h
|
wpilibc/src/main/native/include/trajectory/TrajectoryParameterizer.h
|
||||||
wpilibc/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
|
wpilibc/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
|
||||||
Portable File Dialogs simulation/halsim_gui/src/main/native/include/portable-file-dialogs.h
|
Portable File Dialogs wpigui/src/main/native/include/portable-file-dialogs.h
|
||||||
|
Drake wpimath/src/main/native/thirdparty/drake/
|
||||||
|
wpimath/src/test/native/cpp/drake/
|
||||||
|
wpimath/src/test/native/include/drake/
|
||||||
|
V8 export-template wpiutil/src/main/native/include/wpi/SymbolExports.h
|
||||||
|
GCEM wpimath/src/main/native/thirdparty/gcem/include/
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
Google Test License
|
Google Test License
|
||||||
@@ -81,12 +81,247 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
LLVM Release License
|
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
---- LLVM Exceptions to the Apache 2.0 License ----
|
||||||
|
|
||||||
|
As an exception, if, as a result of your compiling your source code, portions
|
||||||
|
of this Software are embedded into an Object form of such source code, you
|
||||||
|
may redistribute such embedded portions in such Object form without complying
|
||||||
|
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||||
|
|
||||||
|
In addition, if you combine or link compiled forms of this Software with
|
||||||
|
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||||
|
court of competent jurisdiction determines that the patent provision (Section
|
||||||
|
3), the indemnity provision (Section 9) or other Section of the License
|
||||||
|
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||||
|
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||||
|
the License, but only in their entirety and only with respect to the Combined
|
||||||
|
Software.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
Software from third parties included in the LLVM Project:
|
||||||
|
==============================================================================
|
||||||
|
The LLVM Project contains third party software which is under different license
|
||||||
|
terms. All such code will be identified clearly using at least one of two
|
||||||
|
mechanisms:
|
||||||
|
1) It will be in a separate directory tree with its own `LICENSE.txt` or
|
||||||
|
`LICENSE` file at the top containing the specific license and restrictions
|
||||||
|
which apply to that software, or
|
||||||
|
2) It will contain specific license and restriction terms at the top of every
|
||||||
|
file.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
|
||||||
==============================================================================
|
==============================================================================
|
||||||
University of Illinois/NCSA
|
University of Illinois/NCSA
|
||||||
Open Source License
|
Open Source License
|
||||||
|
|
||||||
Copyright (c) 2003-2017 University of Illinois at Urbana-Champaign.
|
Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Developed by:
|
Developed by:
|
||||||
@@ -228,6 +463,32 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
MPacks License
|
||||||
|
==============================================================================
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2021 Nicholas Fraser and the MPack authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
Bootstrap License
|
Bootstrap License
|
||||||
==============================================================================
|
==============================================================================
|
||||||
@@ -807,3 +1068,177 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
=============
|
||||||
|
Drake Library
|
||||||
|
=============
|
||||||
|
All components of Drake are licensed under the BSD 3-Clause License
|
||||||
|
shown below. Where noted in the source code, some portions may
|
||||||
|
be subject to other permissive, non-viral licenses.
|
||||||
|
|
||||||
|
Copyright 2012-2016 Robot Locomotion Group @ CSAIL
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer. Redistributions
|
||||||
|
in binary form must reproduce the above copyright notice, this list of
|
||||||
|
conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution. Neither the name of
|
||||||
|
the Massachusetts Institute of Technology nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
=====================
|
||||||
|
Portable File Dialogs
|
||||||
|
=====================
|
||||||
|
Copyright © 2018—2020 Sam Hocevar <sam@hocevar.net>
|
||||||
|
|
||||||
|
This library is free software. It comes without any warranty, to
|
||||||
|
the extent permitted by applicable law. You can redistribute it
|
||||||
|
and/or modify it under the terms of the Do What the **** You Want
|
||||||
|
to Public License, Version 2, as published by the WTFPL Task Force.
|
||||||
|
See http://www.wtfpl.net/ for more details.
|
||||||
|
|
||||||
|
======================
|
||||||
|
Boost Software License
|
||||||
|
======================
|
||||||
|
Boost Software License - Version 1.0 - August 17th, 2003
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
do so, all subject to the following:
|
||||||
|
|
||||||
|
The copyright notices in the Software and this entire statement, including
|
||||||
|
the above license grant, this restriction and the following disclaimer,
|
||||||
|
must be included in all copies of the Software, in whole or in part, and
|
||||||
|
all derivative works of the Software, unless such copies or derivative
|
||||||
|
works are solely in the form of machine-executable object code generated by
|
||||||
|
a source language processor.
|
||||||
|
|
||||||
|
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
======
|
||||||
|
fmtlib
|
||||||
|
======
|
||||||
|
Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
--- Optional exception to the license ---
|
||||||
|
|
||||||
|
As an exception, if, as a result of your compiling your source code, portions
|
||||||
|
of this Software are embedded into a machine-executable object form of such
|
||||||
|
source code, you may redistribute such embedded portions in such object form
|
||||||
|
without including the above copyright and permission notices.
|
||||||
|
|
||||||
|
==============
|
||||||
|
GHC filesystem
|
||||||
|
==============
|
||||||
|
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
==================
|
||||||
|
V8 export-template
|
||||||
|
==================
|
||||||
|
Copyright 2014, the V8 project authors. All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
============
|
||||||
|
GCEM License
|
||||||
|
============
|
||||||
|
Copyright 2022 - ktholer (https://github.com/kthohr/gcem)
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|||||||
118
apriltag/CMakeLists.txt
Normal file
118
apriltag/CMakeLists.txt
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
project(apriltag)
|
||||||
|
|
||||||
|
include(CompileWarnings)
|
||||||
|
include(GenResources)
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
apriltaglib
|
||||||
|
GIT_REPOSITORY https://github.com/wpilibsuite/apriltag.git
|
||||||
|
GIT_TAG ad31e33d20f9782b7239cb15cde57c56c91383ad
|
||||||
|
)
|
||||||
|
|
||||||
|
# Don't use apriltag's CMakeLists.txt due to conflicting naming and JNI
|
||||||
|
FetchContent_GetProperties(apriltaglib)
|
||||||
|
if(NOT apriltaglib_POPULATED)
|
||||||
|
FetchContent_Populate(apriltaglib)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
aux_source_directory(${apriltaglib_SOURCE_DIR}/common APRILTAGLIB_COMMON_SRC)
|
||||||
|
file(GLOB TAG_FILES ${apriltaglib_SOURCE_DIR}/tag*.c)
|
||||||
|
set(APRILTAGLIB_SRCS ${apriltaglib_SOURCE_DIR}/apriltag.c ${apriltaglib_SOURCE_DIR}/apriltag_pose.c ${apriltaglib_SOURCE_DIR}/apriltag_quad_thresh.c)
|
||||||
|
|
||||||
|
file(GLOB apriltag_jni_src src/main/native/cpp/jni/AprilTagJNI.cpp)
|
||||||
|
|
||||||
|
if (WITH_JAVA)
|
||||||
|
find_package(Java REQUIRED)
|
||||||
|
find_package(JNI REQUIRED)
|
||||||
|
include(UseJava)
|
||||||
|
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
|
||||||
|
|
||||||
|
set(CMAKE_JNI_TARGET true)
|
||||||
|
|
||||||
|
file(GLOB EJML_JARS "${WPILIB_BINARY_DIR}/wpimath/thirdparty/ejml/*.jar")
|
||||||
|
file(GLOB JACKSON_JARS "${WPILIB_BINARY_DIR}/wpiutil/thirdparty/jackson/*.jar")
|
||||||
|
find_file(OPENCV_JAR_FILE
|
||||||
|
NAMES opencv-${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}.jar
|
||||||
|
PATHS ${OPENCV_JAVA_INSTALL_DIR} ${OpenCV_INSTALL_PATH}/bin ${OpenCV_INSTALL_PATH}/share/java
|
||||||
|
NO_DEFAULT_PATH)
|
||||||
|
|
||||||
|
set(CMAKE_JAVA_INCLUDE_PATH apriltag.jar ${EJML_JARS} ${JACKSON_JARS})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
|
||||||
|
file(GLOB_RECURSE JAVA_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} src/main/native/resources/*.json)
|
||||||
|
add_jar(apriltag_jar
|
||||||
|
SOURCES ${JAVA_SOURCES}
|
||||||
|
RESOURCES NAMESPACE "edu/wpi/first/apriltag" ${JAVA_RESOURCES}
|
||||||
|
INCLUDE_JARS wpimath_jar ${EJML_JARS} wpiutil_jar ${OPENCV_JAR_FILE}
|
||||||
|
OUTPUT_NAME apriltag
|
||||||
|
GENERATE_NATIVE_HEADERS apriltag_jni_headers)
|
||||||
|
|
||||||
|
get_property(APRILTAG_JAR_FILE TARGET apriltag_jar PROPERTY JAR_FILE)
|
||||||
|
install(FILES ${APRILTAG_JAR_FILE} DESTINATION "${java_lib_dest}")
|
||||||
|
|
||||||
|
set_property(TARGET apriltag_jar PROPERTY FOLDER "java")
|
||||||
|
|
||||||
|
add_library(apriltagjni ${apriltag_jni_src})
|
||||||
|
wpilib_target_warnings(apriltagjni)
|
||||||
|
target_link_libraries(apriltagjni PUBLIC apriltag)
|
||||||
|
|
||||||
|
set_property(TARGET apriltagjni PROPERTY FOLDER "libraries")
|
||||||
|
|
||||||
|
target_link_libraries(apriltagjni PRIVATE apriltag_jni_headers)
|
||||||
|
add_dependencies(apriltagjni apriltag_jar)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
install(TARGETS apriltagjni RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS apriltagjni EXPORT apriltagjni DESTINATION "${main_lib_dest}")
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
|
GENERATE_RESOURCES(src/main/native/resources/edu/wpi/first/apriltag generated/main/cpp APRILTAG frc apriltag_resources_src)
|
||||||
|
|
||||||
|
file(GLOB apriltag_native_src src/main/native/cpp/*.cpp)
|
||||||
|
|
||||||
|
add_library(apriltag ${apriltag_native_src} ${apriltag_resources_src} ${APRILTAGLIB_SRCS} ${APRILTAGLIB_COMMON_SRC} ${TAG_FILES})
|
||||||
|
set_target_properties(apriltag PROPERTIES DEBUG_POSTFIX "d")
|
||||||
|
|
||||||
|
set_property(TARGET apriltag PROPERTY FOLDER "libraries")
|
||||||
|
target_compile_features(apriltag PUBLIC cxx_std_20)
|
||||||
|
wpilib_target_warnings(apriltag)
|
||||||
|
# disable warnings that apriltaglib can't handle
|
||||||
|
if (MSVC)
|
||||||
|
target_compile_options(apriltag PRIVATE /wd4018)
|
||||||
|
else()
|
||||||
|
target_compile_options(apriltag PRIVATE -Wno-sign-compare -Wno-gnu-zero-variadic-macro-arguments)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(apriltag wpimath)
|
||||||
|
|
||||||
|
target_include_directories(apriltag PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${apriltaglib_SOURCE_DIR}>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
|
||||||
|
$<INSTALL_INTERFACE:${include_dest}/apriltag>)
|
||||||
|
|
||||||
|
install(TARGETS apriltag EXPORT apriltag DESTINATION "${main_lib_dest}")
|
||||||
|
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/apriltag")
|
||||||
|
|
||||||
|
if (WITH_JAVA AND MSVC)
|
||||||
|
install(TARGETS apriltag RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WITH_FLAT_INSTALL)
|
||||||
|
set (apriltag_config_dir ${wpilib_dest})
|
||||||
|
else()
|
||||||
|
set (apriltag_config_dir share/apriltag)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file(apriltag-config.cmake.in ${WPILIB_BINARY_DIR}/apriltag-config.cmake )
|
||||||
|
install(FILES ${WPILIB_BINARY_DIR}/apriltag-config.cmake DESTINATION ${apriltag_config_dir})
|
||||||
|
install(EXPORT apriltag DESTINATION ${apriltag_config_dir})
|
||||||
|
|
||||||
|
if (WITH_TESTS)
|
||||||
|
wpilib_add_test(apriltag src/test/native/cpp)
|
||||||
|
target_include_directories(apriltag_test PRIVATE src/test/native/include)
|
||||||
|
target_link_libraries(apriltag_test apriltag gmock_main)
|
||||||
|
endif()
|
||||||
7
apriltag/apriltag-config.cmake.in
Normal file
7
apriltag/apriltag-config.cmake.in
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
@FILENAME_DEP_REPLACE@
|
||||||
|
@WPIMATH_DEP_REPLACE@
|
||||||
|
@WPIUTIL_DEP_REPLACE@
|
||||||
|
|
||||||
|
@FILENAME_DEP_REPLACE@
|
||||||
|
include(${SELF_DIR}/apriltag.cmake)
|
||||||
88
apriltag/build.gradle
Normal file
88
apriltag/build.gradle
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
apply from: "${rootDir}/shared/resources.gradle"
|
||||||
|
|
||||||
|
ext {
|
||||||
|
nativeName = 'apriltag'
|
||||||
|
devMain = 'edu.wpi.first.apriltag.DevMain'
|
||||||
|
useJava = true
|
||||||
|
useCpp = true
|
||||||
|
sharedCvConfigs = [
|
||||||
|
apriltagDev : [],
|
||||||
|
apriltagTest: []]
|
||||||
|
staticCvConfigs = []
|
||||||
|
|
||||||
|
def generateTask = createGenerateResourcesTask('main', 'APRILTAG', 'frc', project)
|
||||||
|
|
||||||
|
tasks.withType(CppCompile) {
|
||||||
|
dependsOn generateTask
|
||||||
|
}
|
||||||
|
splitSetup = {
|
||||||
|
it.sources {
|
||||||
|
resourcesCpp(CppSourceSet) {
|
||||||
|
source {
|
||||||
|
srcDirs "$buildDir/generated/main/cpp", "$rootDir/shared/singlelib"
|
||||||
|
include '*.cpp'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluationDependsOn(':wpimath')
|
||||||
|
|
||||||
|
apply from: "${rootDir}/shared/jni/setupBuild.gradle"
|
||||||
|
apply from: "${rootDir}/shared/apriltaglib.gradle"
|
||||||
|
apply from: "${rootDir}/shared/opencv.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':wpimath')
|
||||||
|
devImplementation project(':wpimath')
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
resources {
|
||||||
|
srcDirs 'src/main/native/resources'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model {
|
||||||
|
components {}
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
if (!it.buildable || !(it instanceof NativeBinarySpec)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
it.cppCompiler.define 'WPILIB_EXPORTS'
|
||||||
|
|
||||||
|
if (it.component.name == "${nativeName}JNI") {
|
||||||
|
lib project: ':wpimath', library: 'wpimath', linkage: 'static'
|
||||||
|
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
|
||||||
|
} else {
|
||||||
|
lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
|
||||||
|
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeUtils.useRequiredLibrary(it, 'apriltaglib')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks {
|
||||||
|
def c = $.components
|
||||||
|
def found = false
|
||||||
|
def systemArch = getCurrentArch()
|
||||||
|
c.each {
|
||||||
|
if (it in NativeExecutableSpec && it.name == "${nativeName}Dev") {
|
||||||
|
it.binaries.each {
|
||||||
|
if (!found) {
|
||||||
|
def arch = it.targetPlatform.name
|
||||||
|
if (arch == systemArch) {
|
||||||
|
def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + 'lib'
|
||||||
|
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
apriltag/src/dev/java/edu/wpi/first/apriltag/DevMain.java
Normal file
20
apriltag/src/dev/java/edu/wpi/first/apriltag/DevMain.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
public final class DevMain {
|
||||||
|
/** Main entry point. */
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("Hello World!");
|
||||||
|
AprilTagDetector detector = new AprilTagDetector();
|
||||||
|
detector.addFamily("tag16h5");
|
||||||
|
AprilTagDetector.Config config = new AprilTagDetector.Config();
|
||||||
|
config.refineEdges = false;
|
||||||
|
detector.setConfig(config);
|
||||||
|
detector.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DevMain() {}
|
||||||
|
}
|
||||||
11
apriltag/src/dev/native/cpp/main.cpp
Normal file
11
apriltag/src/dev/native/cpp/main.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagDetector.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
frc::AprilTagDetector detector;
|
||||||
|
detector.AddFamily("tag16h5");
|
||||||
|
detector.SetConfig({.refineEdges = false});
|
||||||
|
}
|
||||||
47
apriltag/src/main/java/edu/wpi/first/apriltag/AprilTag.java
Normal file
47
apriltag/src/main/java/edu/wpi/first/apriltag/AprilTag.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import edu.wpi.first.math.geometry.Pose3d;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@SuppressWarnings("MemberName")
|
||||||
|
public class AprilTag {
|
||||||
|
@JsonProperty(value = "ID")
|
||||||
|
public int ID;
|
||||||
|
|
||||||
|
@JsonProperty(value = "pose")
|
||||||
|
public Pose3d pose;
|
||||||
|
|
||||||
|
@SuppressWarnings("ParameterName")
|
||||||
|
@JsonCreator
|
||||||
|
public AprilTag(
|
||||||
|
@JsonProperty(required = true, value = "ID") int ID,
|
||||||
|
@JsonProperty(required = true, value = "pose") Pose3d pose) {
|
||||||
|
this.ID = ID;
|
||||||
|
this.pose = pose;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof AprilTag) {
|
||||||
|
var other = (AprilTag) obj;
|
||||||
|
return ID == other.ID && pose.equals(other.pose);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(ID, pose);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AprilTag(ID: " + ID + ", pose: " + pose + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import edu.wpi.first.math.MatBuilder;
|
||||||
|
import edu.wpi.first.math.Matrix;
|
||||||
|
import edu.wpi.first.math.Nat;
|
||||||
|
import edu.wpi.first.math.numbers.N3;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/** A detection of an AprilTag tag. */
|
||||||
|
public class AprilTagDetection {
|
||||||
|
/**
|
||||||
|
* Gets the decoded tag's family name.
|
||||||
|
*
|
||||||
|
* @return Decoded family name
|
||||||
|
*/
|
||||||
|
public String getFamily() {
|
||||||
|
return m_family;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the decoded ID of the tag.
|
||||||
|
*
|
||||||
|
* @return Decoded ID
|
||||||
|
*/
|
||||||
|
public int getId() {
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets how many error bits were corrected. Note: accepting large numbers of corrected errors
|
||||||
|
* leads to greatly increased false positive rates. NOTE: As of this implementation, the detector
|
||||||
|
* cannot detect tags with a hamming distance greater than 2.
|
||||||
|
*
|
||||||
|
* @return Hamming distance (number of corrected error bits)
|
||||||
|
*/
|
||||||
|
public int getHamming() {
|
||||||
|
return m_hamming;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a measure of the quality of the binary decoding process: the average difference between
|
||||||
|
* the intensity of a data bit versus the decision threshold. Higher numbers roughly indicate
|
||||||
|
* better decodes. This is a reasonable measure of detection accuracy only for very small tags--
|
||||||
|
* not effective for larger tags (where we could have sampled anywhere within a bit cell and still
|
||||||
|
* gotten a good detection.)
|
||||||
|
*
|
||||||
|
* @return Decision margin
|
||||||
|
*/
|
||||||
|
public float getDecisionMargin() {
|
||||||
|
return m_decisionMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the 3x3 homography matrix describing the projection from an "ideal" tag (with corners at
|
||||||
|
* (-1,1), (1,1), (1,-1), and (-1, -1)) to pixels in the image.
|
||||||
|
*
|
||||||
|
* @return Homography matrix data
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.MethodReturnsInternalArray")
|
||||||
|
public double[] getHomography() {
|
||||||
|
return m_homography;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the 3x3 homography matrix describing the projection from an "ideal" tag (with corners at
|
||||||
|
* (-1,1), (1,1), (1,-1), and (-1, -1)) to pixels in the image.
|
||||||
|
*
|
||||||
|
* @return Homography matrix
|
||||||
|
*/
|
||||||
|
public Matrix<N3, N3> getHomographyMatrix() {
|
||||||
|
return new MatBuilder<>(Nat.N3(), Nat.N3()).fill(m_homography);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the center of the detection in image pixel coordinates.
|
||||||
|
*
|
||||||
|
* @return Center point X coordinate
|
||||||
|
*/
|
||||||
|
public double getCenterX() {
|
||||||
|
return m_centerX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the center of the detection in image pixel coordinates.
|
||||||
|
*
|
||||||
|
* @return Center point Y coordinate
|
||||||
|
*/
|
||||||
|
public double getCenterY() {
|
||||||
|
return m_centerY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a corner of the tag in image pixel coordinates. These always wrap counter-clock wise
|
||||||
|
* around the tag.
|
||||||
|
*
|
||||||
|
* @param ndx Corner index (range is 0-3, inclusive)
|
||||||
|
* @return Corner point X coordinate
|
||||||
|
*/
|
||||||
|
public double getCornerX(int ndx) {
|
||||||
|
return m_corners[ndx * 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a corner of the tag in image pixel coordinates. These always wrap counter-clock wise
|
||||||
|
* around the tag.
|
||||||
|
*
|
||||||
|
* @param ndx Corner index (range is 0-3, inclusive)
|
||||||
|
* @return Corner point Y coordinate
|
||||||
|
*/
|
||||||
|
public double getCornerY(int ndx) {
|
||||||
|
return m_corners[ndx * 2 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the corners of the tag in image pixel coordinates. These always wrap counter-clock wise
|
||||||
|
* around the tag.
|
||||||
|
*
|
||||||
|
* @return Corner point array (X and Y for each corner in order)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.MethodReturnsInternalArray")
|
||||||
|
public double[] getCorners() {
|
||||||
|
return m_corners;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String m_family;
|
||||||
|
private final int m_id;
|
||||||
|
private final int m_hamming;
|
||||||
|
private final float m_decisionMargin;
|
||||||
|
private final double[] m_homography;
|
||||||
|
private final double m_centerX;
|
||||||
|
private final double m_centerY;
|
||||||
|
private final double[] m_corners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new detection result. Used from JNI.
|
||||||
|
*
|
||||||
|
* @param family family
|
||||||
|
* @param id id
|
||||||
|
* @param hamming hamming
|
||||||
|
* @param decisionMargin dm
|
||||||
|
* @param homography homography
|
||||||
|
* @param centerX centerX
|
||||||
|
* @param centerY centerY
|
||||||
|
* @param corners corners
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.ArrayIsStoredDirectly")
|
||||||
|
public AprilTagDetection(
|
||||||
|
String family,
|
||||||
|
int id,
|
||||||
|
int hamming,
|
||||||
|
float decisionMargin,
|
||||||
|
double[] homography,
|
||||||
|
double centerX,
|
||||||
|
double centerY,
|
||||||
|
double[] corners) {
|
||||||
|
m_family = family;
|
||||||
|
m_id = id;
|
||||||
|
m_hamming = hamming;
|
||||||
|
m_decisionMargin = decisionMargin;
|
||||||
|
m_homography = homography;
|
||||||
|
m_centerX = centerX;
|
||||||
|
m_centerY = centerY;
|
||||||
|
m_corners = corners;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "DetectionResult [centerX="
|
||||||
|
+ m_centerX
|
||||||
|
+ ", centerY="
|
||||||
|
+ m_centerY
|
||||||
|
+ ", corners="
|
||||||
|
+ Arrays.toString(m_corners)
|
||||||
|
+ ", decisionMargin="
|
||||||
|
+ m_decisionMargin
|
||||||
|
+ ", hamming="
|
||||||
|
+ m_hamming
|
||||||
|
+ ", homography="
|
||||||
|
+ Arrays.toString(m_homography)
|
||||||
|
+ ", family="
|
||||||
|
+ m_family
|
||||||
|
+ ", id="
|
||||||
|
+ m_id
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import edu.wpi.first.apriltag.jni.AprilTagJNI;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An AprilTag detector engine. This is expensive to set up and tear down, so most use cases should
|
||||||
|
* only create one of these, add a family to it, set up any other configuration, and repeatedly call
|
||||||
|
* Detect().
|
||||||
|
*/
|
||||||
|
public class AprilTagDetector implements AutoCloseable {
|
||||||
|
/** Detector configuration. */
|
||||||
|
@SuppressWarnings("MemberName")
|
||||||
|
public static class Config {
|
||||||
|
/**
|
||||||
|
* How many threads should be used for computation. Default is single-threaded operation (1
|
||||||
|
* thread).
|
||||||
|
*/
|
||||||
|
public int numThreads = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quad decimation. Detection of quads can be done on a lower-resolution image, improving speed
|
||||||
|
* at a cost of pose accuracy and a slight decrease in detection rate. Decoding the binary
|
||||||
|
* payload is still done at full resolution. Default is 2.0.
|
||||||
|
*/
|
||||||
|
public float quadDecimate = 2.0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What Gaussian blur should be applied to the segmented image (used for quad detection). Very
|
||||||
|
* noisy images benefit from non-zero values (e.g. 0.8). Default is 0.0.
|
||||||
|
*/
|
||||||
|
public float quadSigma;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When true, the edges of the each quad are adjusted to "snap to" strong gradients nearby. This
|
||||||
|
* is useful when decimation is employed, as it can increase the quality of the initial quad
|
||||||
|
* estimate substantially. Generally recommended to be on (true). Default is true.
|
||||||
|
*
|
||||||
|
* <p>Very computationally inexpensive. Option is ignored if quad_decimate = 1.
|
||||||
|
*/
|
||||||
|
public boolean refineEdges = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much sharpening should be done to decoded images. This can help decode small tags but may
|
||||||
|
* or may not help in odd lighting conditions or low light conditions. Default is 0.25.
|
||||||
|
*/
|
||||||
|
public double decodeSharpening = 0.25;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug mode. When true, the decoder writes a variety of debugging images to the current
|
||||||
|
* working directory at various stages through the detection process. This is slow and should
|
||||||
|
* *not* be used on space-limited systems such as the RoboRIO. Default is disabled (false).
|
||||||
|
*/
|
||||||
|
public boolean debug;
|
||||||
|
|
||||||
|
public Config() {}
|
||||||
|
|
||||||
|
Config(
|
||||||
|
int numThreads,
|
||||||
|
float quadDecimate,
|
||||||
|
float quadSigma,
|
||||||
|
boolean refineEdges,
|
||||||
|
double decodeSharpening,
|
||||||
|
boolean debug) {
|
||||||
|
this.numThreads = numThreads;
|
||||||
|
this.quadDecimate = quadDecimate;
|
||||||
|
this.quadSigma = quadSigma;
|
||||||
|
this.refineEdges = refineEdges;
|
||||||
|
this.decodeSharpening = decodeSharpening;
|
||||||
|
this.debug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return numThreads
|
||||||
|
+ Float.hashCode(quadDecimate)
|
||||||
|
+ Float.hashCode(quadSigma)
|
||||||
|
+ Boolean.hashCode(refineEdges)
|
||||||
|
+ Double.hashCode(decodeSharpening)
|
||||||
|
+ Boolean.hashCode(debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof Config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Config other = (Config) obj;
|
||||||
|
return numThreads == other.numThreads
|
||||||
|
&& quadDecimate == other.quadDecimate
|
||||||
|
&& quadSigma == other.quadSigma
|
||||||
|
&& refineEdges == other.refineEdges
|
||||||
|
&& decodeSharpening == other.decodeSharpening
|
||||||
|
&& debug == other.debug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Quad threshold parameters. */
|
||||||
|
@SuppressWarnings("MemberName")
|
||||||
|
public static class QuadThresholdParameters {
|
||||||
|
/** Threshold used to reject quads containing too few pixels. Default is 5 pixels. */
|
||||||
|
public int minClusterPixels = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many corner candidates to consider when segmenting a group of pixels into a quad. Default
|
||||||
|
* is 10.
|
||||||
|
*/
|
||||||
|
public int maxNumMaxima = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Critical angle, in radians. The detector will reject quads where pairs of edges have angles
|
||||||
|
* that are close to straight or close to 180 degrees. Zero means that no quads are rejected.
|
||||||
|
* Default is 10 degrees.
|
||||||
|
*/
|
||||||
|
public double criticalAngle = 10 * Math.PI / 180.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When fitting lines to the contours, the maximum mean squared error allowed. This is useful in
|
||||||
|
* rejecting contours that are far from being quad shaped; rejecting these quads "early" saves
|
||||||
|
* expensive decoding processing. Default is 10.0.
|
||||||
|
*/
|
||||||
|
public float maxLineFitMSE = 10.0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum brightness offset. When we build our model of black & white pixels, we add an
|
||||||
|
* extra check that the white model must be (overall) brighter than the black model. How much
|
||||||
|
* brighter? (in pixel values, [0,255]). Default is 5.
|
||||||
|
*/
|
||||||
|
public int minWhiteBlackDiff = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the thresholded image be should be deglitched. Only useful for very noisy images.
|
||||||
|
* Default is disabled (false).
|
||||||
|
*/
|
||||||
|
public boolean deglitch;
|
||||||
|
|
||||||
|
public QuadThresholdParameters() {}
|
||||||
|
|
||||||
|
QuadThresholdParameters(
|
||||||
|
int minClusterPixels,
|
||||||
|
int maxNumMaxima,
|
||||||
|
double criticalAngle,
|
||||||
|
float maxLineFitMSE,
|
||||||
|
int minWhiteBlackDiff,
|
||||||
|
boolean deglitch) {
|
||||||
|
this.minClusterPixels = minClusterPixels;
|
||||||
|
this.maxNumMaxima = maxNumMaxima;
|
||||||
|
this.criticalAngle = criticalAngle;
|
||||||
|
this.maxLineFitMSE = maxLineFitMSE;
|
||||||
|
this.minWhiteBlackDiff = minWhiteBlackDiff;
|
||||||
|
this.deglitch = deglitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return minClusterPixels
|
||||||
|
+ maxNumMaxima
|
||||||
|
+ Double.hashCode(criticalAngle)
|
||||||
|
+ Float.hashCode(maxLineFitMSE)
|
||||||
|
+ minWhiteBlackDiff
|
||||||
|
+ Boolean.hashCode(deglitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof QuadThresholdParameters)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QuadThresholdParameters other = (QuadThresholdParameters) obj;
|
||||||
|
return minClusterPixels == other.minClusterPixels
|
||||||
|
&& maxNumMaxima == other.maxNumMaxima
|
||||||
|
&& criticalAngle == other.criticalAngle
|
||||||
|
&& maxLineFitMSE == other.maxLineFitMSE
|
||||||
|
&& minWhiteBlackDiff == other.minWhiteBlackDiff
|
||||||
|
&& deglitch == other.deglitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AprilTagDetector() {
|
||||||
|
m_native = AprilTagJNI.createDetector();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (m_native != 0) {
|
||||||
|
AprilTagJNI.destroyDetector(m_native);
|
||||||
|
}
|
||||||
|
m_native = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets detector configuration.
|
||||||
|
*
|
||||||
|
* @param config Configuration
|
||||||
|
*/
|
||||||
|
public void setConfig(Config config) {
|
||||||
|
AprilTagJNI.setDetectorConfig(m_native, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets detector configuration.
|
||||||
|
*
|
||||||
|
* @return Configuration
|
||||||
|
*/
|
||||||
|
public Config getConfig() {
|
||||||
|
return AprilTagJNI.getDetectorConfig(m_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets quad threshold parameters.
|
||||||
|
*
|
||||||
|
* @param params Parameters
|
||||||
|
*/
|
||||||
|
public void setQuadThresholdParameters(QuadThresholdParameters params) {
|
||||||
|
AprilTagJNI.setDetectorQTP(m_native, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets quad threshold parameters.
|
||||||
|
*
|
||||||
|
* @return Parameters
|
||||||
|
*/
|
||||||
|
public QuadThresholdParameters getQuadThresholdParameters() {
|
||||||
|
return AprilTagJNI.getDetectorQTP(m_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a family of tags to be detected.
|
||||||
|
*
|
||||||
|
* @param fam Family name, e.g. "tag16h5"
|
||||||
|
* @throws IllegalArgumentException if family name not recognized
|
||||||
|
*/
|
||||||
|
public void addFamily(String fam) {
|
||||||
|
addFamily(fam, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a family of tags to be detected.
|
||||||
|
*
|
||||||
|
* @param fam Family name, e.g. "tag16h5"
|
||||||
|
* @param bitsCorrected maximum number of bits to correct
|
||||||
|
* @throws IllegalArgumentException if family name not recognized
|
||||||
|
*/
|
||||||
|
public void addFamily(String fam, int bitsCorrected) {
|
||||||
|
if (!AprilTagJNI.addFamily(m_native, fam, bitsCorrected)) {
|
||||||
|
throw new IllegalArgumentException("unknown family name '" + fam + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a family of tags from the detector.
|
||||||
|
*
|
||||||
|
* @param fam Family name, e.g. "tag16h5"
|
||||||
|
*/
|
||||||
|
public void removeFamily(String fam) {
|
||||||
|
AprilTagJNI.removeFamily(m_native, fam);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Unregister all families. */
|
||||||
|
public void clearFamilies() {
|
||||||
|
AprilTagJNI.clearFamilies(m_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect tags from an 8-bit image.
|
||||||
|
*
|
||||||
|
* @param img 8-bit OpenCV Mat image
|
||||||
|
* @return Results (array of AprilTagDetection)
|
||||||
|
*/
|
||||||
|
public AprilTagDetection[] detect(Mat img) {
|
||||||
|
return AprilTagJNI.detect(m_native, img.cols(), img.rows(), img.cols(), img.dataAddr());
|
||||||
|
}
|
||||||
|
|
||||||
|
private long m_native;
|
||||||
|
}
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import edu.wpi.first.math.geometry.Pose3d;
|
||||||
|
import edu.wpi.first.math.geometry.Rotation3d;
|
||||||
|
import edu.wpi.first.math.geometry.Translation3d;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for representing a layout of AprilTags on a field and reading them from a JSON format.
|
||||||
|
*
|
||||||
|
* <p>The JSON format contains two top-level objects, "tags" and "field". The "tags" object is a
|
||||||
|
* list of all AprilTags contained within a layout. Each AprilTag serializes to a JSON object
|
||||||
|
* containing an ID and a Pose3d. The "field" object is a descriptor of the size of the field in
|
||||||
|
* meters with "width" and "length" values. This is to account for arbitrary field sizes when
|
||||||
|
* transforming the poses.
|
||||||
|
*
|
||||||
|
* <p>Pose3ds in the JSON are measured using the normal FRC coordinate system, NWU with the origin
|
||||||
|
* at the bottom-right corner of the blue alliance wall. {@link #setOrigin(OriginPosition)} can be
|
||||||
|
* used to change the poses returned from {@link AprilTagFieldLayout#getTagPose(int)} to be from the
|
||||||
|
* perspective of a specific alliance.
|
||||||
|
*
|
||||||
|
* <p>Tag poses represent the center of the tag, with a zero rotation representing a tag that is
|
||||||
|
* upright and facing away from the (blue) alliance wall (that is, towards the opposing alliance).
|
||||||
|
*/
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||||
|
public class AprilTagFieldLayout {
|
||||||
|
public enum OriginPosition {
|
||||||
|
kBlueAllianceWallRightSide,
|
||||||
|
kRedAllianceWallRightSide,
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<Integer, AprilTag> m_apriltags = new HashMap<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "field")
|
||||||
|
private FieldDimensions m_fieldDimensions;
|
||||||
|
|
||||||
|
private Pose3d m_origin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new AprilTagFieldLayout with values imported from a JSON file.
|
||||||
|
*
|
||||||
|
* @param path Path of the JSON file to import from.
|
||||||
|
* @throws IOException If reading from the file fails.
|
||||||
|
*/
|
||||||
|
public AprilTagFieldLayout(String path) throws IOException {
|
||||||
|
this(Path.of(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new AprilTagFieldLayout with values imported from a JSON file.
|
||||||
|
*
|
||||||
|
* @param path Path of the JSON file to import from.
|
||||||
|
* @throws IOException If reading from the file fails.
|
||||||
|
*/
|
||||||
|
public AprilTagFieldLayout(Path path) throws IOException {
|
||||||
|
AprilTagFieldLayout layout =
|
||||||
|
new ObjectMapper().readValue(path.toFile(), AprilTagFieldLayout.class);
|
||||||
|
m_apriltags.putAll(layout.m_apriltags);
|
||||||
|
m_fieldDimensions = layout.m_fieldDimensions;
|
||||||
|
setOrigin(OriginPosition.kBlueAllianceWallRightSide);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new AprilTagFieldLayout from a list of {@link AprilTag} objects.
|
||||||
|
*
|
||||||
|
* @param apriltags List of {@link AprilTag}.
|
||||||
|
* @param fieldLength Length of the field the layout is representing in meters.
|
||||||
|
* @param fieldWidth Width of the field the layout is representing in meters.
|
||||||
|
*/
|
||||||
|
public AprilTagFieldLayout(List<AprilTag> apriltags, double fieldLength, double fieldWidth) {
|
||||||
|
this(apriltags, new FieldDimensions(fieldLength, fieldWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
private AprilTagFieldLayout(
|
||||||
|
@JsonProperty(required = true, value = "tags") List<AprilTag> apriltags,
|
||||||
|
@JsonProperty(required = true, value = "field") FieldDimensions fieldDimensions) {
|
||||||
|
// To ensure the underlying semantics don't change with what kind of list is passed in
|
||||||
|
for (AprilTag tag : apriltags) {
|
||||||
|
m_apriltags.put(tag.ID, tag);
|
||||||
|
}
|
||||||
|
m_fieldDimensions = fieldDimensions;
|
||||||
|
setOrigin(OriginPosition.kBlueAllianceWallRightSide);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a List of the {@link AprilTag AprilTags} used in this layout.
|
||||||
|
*
|
||||||
|
* @return The {@link AprilTag AprilTags} used in this layout.
|
||||||
|
*/
|
||||||
|
@JsonProperty("tags")
|
||||||
|
public List<AprilTag> getTags() {
|
||||||
|
return new ArrayList<>(m_apriltags.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the origin based on a predefined enumeration of coordinate frame origins. The origins are
|
||||||
|
* calculated from the field dimensions.
|
||||||
|
*
|
||||||
|
* <p>This transforms the Pose3d objects returned by {@link #getTagPose(int)} to return the
|
||||||
|
* correct pose relative to a predefined coordinate frame.
|
||||||
|
*
|
||||||
|
* @param origin The predefined origin
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public void setOrigin(OriginPosition origin) {
|
||||||
|
switch (origin) {
|
||||||
|
case kBlueAllianceWallRightSide:
|
||||||
|
setOrigin(new Pose3d());
|
||||||
|
break;
|
||||||
|
case kRedAllianceWallRightSide:
|
||||||
|
setOrigin(
|
||||||
|
new Pose3d(
|
||||||
|
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
|
||||||
|
new Rotation3d(0, 0, Math.PI)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unsupported enum value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the origin for tag pose transformation.
|
||||||
|
*
|
||||||
|
* <p>This transforms the Pose3d objects returned by {@link #getTagPose(int)} to return the
|
||||||
|
* correct pose relative to the provided origin.
|
||||||
|
*
|
||||||
|
* @param origin The new origin for tag transformations
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public void setOrigin(Pose3d origin) {
|
||||||
|
m_origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an AprilTag pose by its ID.
|
||||||
|
*
|
||||||
|
* @param ID The ID of the tag.
|
||||||
|
* @return The pose corresponding to the ID passed in or an empty optional if a tag with that ID
|
||||||
|
* was not found.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("ParameterName")
|
||||||
|
public Optional<Pose3d> getTagPose(int ID) {
|
||||||
|
AprilTag tag = m_apriltags.get(ID);
|
||||||
|
if (tag == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(tag.pose.relativeTo(m_origin));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a AprilTagFieldLayout to a JSON file.
|
||||||
|
*
|
||||||
|
* @param path The path to write to.
|
||||||
|
* @throws IOException If writing to the file fails.
|
||||||
|
*/
|
||||||
|
public void serialize(String path) throws IOException {
|
||||||
|
serialize(Path.of(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a AprilTagFieldLayout to a JSON file.
|
||||||
|
*
|
||||||
|
* @param path The path to write to.
|
||||||
|
* @throws IOException If writing to the file fails.
|
||||||
|
*/
|
||||||
|
public void serialize(Path path) throws IOException {
|
||||||
|
new ObjectMapper().writeValue(path.toFile(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes a field layout from a resource within a jar file.
|
||||||
|
*
|
||||||
|
* @param resourcePath The absolute path of the resource
|
||||||
|
* @return The deserialized layout
|
||||||
|
* @throws IOException If the resource could not be loaded
|
||||||
|
*/
|
||||||
|
public static AprilTagFieldLayout loadFromResource(String resourcePath) throws IOException {
|
||||||
|
try (InputStream stream = AprilTagFieldLayout.class.getResourceAsStream(resourcePath);
|
||||||
|
InputStreamReader reader = new InputStreamReader(stream)) {
|
||||||
|
return new ObjectMapper().readerFor(AprilTagFieldLayout.class).readValue(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof AprilTagFieldLayout) {
|
||||||
|
var other = (AprilTagFieldLayout) obj;
|
||||||
|
return m_apriltags.equals(other.m_apriltags) && m_origin.equals(other.m_origin);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(m_apriltags, m_origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||||
|
private static class FieldDimensions {
|
||||||
|
@SuppressWarnings("MemberName")
|
||||||
|
@JsonProperty(value = "length")
|
||||||
|
public double fieldLength;
|
||||||
|
|
||||||
|
@SuppressWarnings("MemberName")
|
||||||
|
@JsonProperty(value = "width")
|
||||||
|
public double fieldWidth;
|
||||||
|
|
||||||
|
@JsonCreator()
|
||||||
|
FieldDimensions(
|
||||||
|
@JsonProperty(required = true, value = "length") double fieldLength,
|
||||||
|
@JsonProperty(required = true, value = "width") double fieldWidth) {
|
||||||
|
this.fieldLength = fieldLength;
|
||||||
|
this.fieldWidth = fieldWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
public enum AprilTagFields {
|
||||||
|
k2022RapidReact("2022-rapidreact.json"),
|
||||||
|
k2023ChargedUp("2023-chargedup.json");
|
||||||
|
|
||||||
|
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
|
||||||
|
|
||||||
|
/** Alias to the current game. */
|
||||||
|
public static final AprilTagFields kDefaultField = k2023ChargedUp;
|
||||||
|
|
||||||
|
public final String m_resourceFile;
|
||||||
|
|
||||||
|
AprilTagFields(String resourceFile) {
|
||||||
|
m_resourceFile = kBaseResourceDir + resourceFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import edu.wpi.first.math.geometry.Transform3d;
|
||||||
|
|
||||||
|
/** A pair of AprilTag pose estimates. */
|
||||||
|
@SuppressWarnings("MemberName")
|
||||||
|
public class AprilTagPoseEstimate {
|
||||||
|
/**
|
||||||
|
* Constructs a pose estimate.
|
||||||
|
*
|
||||||
|
* @param pose1 first pose
|
||||||
|
* @param pose2 second pose
|
||||||
|
* @param error1 error of first pose
|
||||||
|
* @param error2 error of second pose
|
||||||
|
*/
|
||||||
|
public AprilTagPoseEstimate(Transform3d pose1, Transform3d pose2, double error1, double error2) {
|
||||||
|
this.pose1 = pose1;
|
||||||
|
this.pose2 = pose2;
|
||||||
|
this.error1 = error1;
|
||||||
|
this.error2 = error2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ratio of pose reprojection errors, called ambiguity. Numbers above 0.2 are likely to be
|
||||||
|
* ambiguous.
|
||||||
|
*
|
||||||
|
* @return The ratio of pose reprojection errors.
|
||||||
|
*/
|
||||||
|
public double getAmbiguity() {
|
||||||
|
double min = Math.min(error1, error2);
|
||||||
|
double max = Math.max(error1, error2);
|
||||||
|
|
||||||
|
if (max > 0) {
|
||||||
|
return min / max;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Pose 1. */
|
||||||
|
public final Transform3d pose1;
|
||||||
|
|
||||||
|
/** Pose 2. */
|
||||||
|
public final Transform3d pose2;
|
||||||
|
|
||||||
|
/** Object-space error of pose 1. */
|
||||||
|
public final double error1;
|
||||||
|
|
||||||
|
/** Object-space error of pose 2. */
|
||||||
|
public final double error2;
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import edu.wpi.first.apriltag.jni.AprilTagJNI;
|
||||||
|
import edu.wpi.first.math.geometry.Transform3d;
|
||||||
|
|
||||||
|
/** Pose estimators for AprilTag tags. */
|
||||||
|
public class AprilTagPoseEstimator {
|
||||||
|
/** Configuration for the pose estimator. */
|
||||||
|
@SuppressWarnings("MemberName")
|
||||||
|
public static class Config {
|
||||||
|
/**
|
||||||
|
* Creates a pose estimator configuration.
|
||||||
|
*
|
||||||
|
* @param tagSize tag size, in meters
|
||||||
|
* @param fx camera horizontal focal length, in pixels
|
||||||
|
* @param fy camera vertical focal length, in pixels
|
||||||
|
* @param cx camera horizontal focal center, in pixels
|
||||||
|
* @param cy camera vertical focal center, in pixels
|
||||||
|
*/
|
||||||
|
public Config(double tagSize, double fx, double fy, double cx, double cy) {
|
||||||
|
this.tagSize = tagSize;
|
||||||
|
this.fx = fx;
|
||||||
|
this.fy = fy;
|
||||||
|
this.cx = cx;
|
||||||
|
this.cy = cy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double tagSize;
|
||||||
|
public double fx;
|
||||||
|
public double fy;
|
||||||
|
public double cx;
|
||||||
|
public double cy;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Double.hashCode(tagSize)
|
||||||
|
+ Double.hashCode(fx)
|
||||||
|
+ Double.hashCode(fy)
|
||||||
|
+ Double.hashCode(cx)
|
||||||
|
+ Double.hashCode(cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof Config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Config other = (Config) obj;
|
||||||
|
return tagSize == other.tagSize
|
||||||
|
&& fx == other.fx
|
||||||
|
&& fy == other.fy
|
||||||
|
&& cx == other.cx
|
||||||
|
&& cy == other.cy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates estimator.
|
||||||
|
*
|
||||||
|
* @param config Configuration
|
||||||
|
*/
|
||||||
|
public AprilTagPoseEstimator(Config config) {
|
||||||
|
m_config = new Config(config.tagSize, config.fx, config.fy, config.cx, config.cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets estimator configuration.
|
||||||
|
*
|
||||||
|
* @param config Configuration
|
||||||
|
*/
|
||||||
|
public void setConfig(Config config) {
|
||||||
|
m_config.tagSize = config.tagSize;
|
||||||
|
m_config.fx = config.fx;
|
||||||
|
m_config.fy = config.fy;
|
||||||
|
m_config.cx = config.cx;
|
||||||
|
m_config.cy = config.cy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets estimator configuration.
|
||||||
|
*
|
||||||
|
* @return Configuration
|
||||||
|
*/
|
||||||
|
public Config getConfig() {
|
||||||
|
return new Config(m_config.tagSize, m_config.fx, m_config.fy, m_config.cx, m_config.cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the pose of the tag using the homography method described in [1].
|
||||||
|
*
|
||||||
|
* @param detection Tag detection
|
||||||
|
* @return Pose estimate
|
||||||
|
*/
|
||||||
|
public Transform3d estimateHomography(AprilTagDetection detection) {
|
||||||
|
return estimateHomography(detection.getHomography());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the pose of the tag using the homography method described in [1].
|
||||||
|
*
|
||||||
|
* @param homography Homography 3x3 matrix data
|
||||||
|
* @return Pose estimate
|
||||||
|
*/
|
||||||
|
public Transform3d estimateHomography(double[] homography) {
|
||||||
|
return AprilTagJNI.estimatePoseHomography(
|
||||||
|
homography, m_config.tagSize, m_config.fx, m_config.fy, m_config.cx, m_config.cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the pose of the tag. This returns one or two possible poses for the tag, along with
|
||||||
|
* the object-space error of each.
|
||||||
|
*
|
||||||
|
* <p>This uses the homography method described in [1] for the initial estimate. Then Orthogonal
|
||||||
|
* Iteration [2] is used to refine this estimate. Then [3] is used to find a potential second
|
||||||
|
* local minima and Orthogonal Iteration is used to refine this second estimate.
|
||||||
|
*
|
||||||
|
* <p>[1]: E. Olson, “Apriltag: A robust and flexible visual fiducial system,” in 2011 IEEE
|
||||||
|
* International Conference on Robotics and Automation, May 2011, pp. 3400–3407.
|
||||||
|
*
|
||||||
|
* <p>[2]: Lu, G. D. Hager and E. Mjolsness, "Fast and globally convergent pose estimation from
|
||||||
|
* video images," in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 22, no.
|
||||||
|
* 6, pp. 610-622, June 2000. doi: 10.1109/34.862199
|
||||||
|
*
|
||||||
|
* <p>[3]: Schweighofer and A. Pinz, "Robust Pose Estimation from a Planar Target," in IEEE
|
||||||
|
* Transactions on Pattern Analysis and Machine Intelligence, vol. 28, no. 12, pp. 2024-2030, Dec.
|
||||||
|
* 2006. doi: 10.1109/TPAMI.2006.252
|
||||||
|
*
|
||||||
|
* @param detection Tag detection
|
||||||
|
* @param nIters Number of iterations
|
||||||
|
* @return Initial and (possibly) second pose estimates
|
||||||
|
*/
|
||||||
|
public AprilTagPoseEstimate estimateOrthogonalIteration(AprilTagDetection detection, int nIters) {
|
||||||
|
return estimateOrthogonalIteration(detection.getHomography(), detection.getCorners(), nIters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the pose of the tag. This returns one or two possible poses for the tag, along with
|
||||||
|
* the object-space error of each.
|
||||||
|
*
|
||||||
|
* @param homography Homography 3x3 matrix data
|
||||||
|
* @param corners Corner point array (X and Y for each corner in order)
|
||||||
|
* @param nIters Number of iterations
|
||||||
|
* @return Initial and (possibly) second pose estimates
|
||||||
|
*/
|
||||||
|
public AprilTagPoseEstimate estimateOrthogonalIteration(
|
||||||
|
double[] homography, double[] corners, int nIters) {
|
||||||
|
return AprilTagJNI.estimatePoseOrthogonalIteration(
|
||||||
|
homography,
|
||||||
|
corners,
|
||||||
|
m_config.tagSize,
|
||||||
|
m_config.fx,
|
||||||
|
m_config.fy,
|
||||||
|
m_config.cx,
|
||||||
|
m_config.cy,
|
||||||
|
nIters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates tag pose. This method is an easier to use interface to
|
||||||
|
* EstimatePoseOrthogonalIteration(), running 50 iterations and returning the pose with the lower
|
||||||
|
* object-space error.
|
||||||
|
*
|
||||||
|
* @param detection Tag detection
|
||||||
|
* @return Pose estimate
|
||||||
|
*/
|
||||||
|
public Transform3d estimate(AprilTagDetection detection) {
|
||||||
|
return estimate(detection.getHomography(), detection.getCorners());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates tag pose. This method is an easier to use interface to
|
||||||
|
* EstimatePoseOrthogonalIteration(), running 50 iterations and returning the pose with the lower
|
||||||
|
* object-space error.
|
||||||
|
*
|
||||||
|
* @param homography Homography 3x3 matrix data
|
||||||
|
* @param corners Corner point array (X and Y for each corner in order)
|
||||||
|
* @return Pose estimate
|
||||||
|
*/
|
||||||
|
public Transform3d estimate(double[] homography, double[] corners) {
|
||||||
|
return AprilTagJNI.estimatePose(
|
||||||
|
homography, corners, m_config.tagSize, m_config.fx, m_config.fy, m_config.cx, m_config.cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Config m_config;
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag.jni;
|
||||||
|
|
||||||
|
import edu.wpi.first.apriltag.AprilTagDetection;
|
||||||
|
import edu.wpi.first.apriltag.AprilTagDetector;
|
||||||
|
import edu.wpi.first.apriltag.AprilTagPoseEstimate;
|
||||||
|
import edu.wpi.first.math.geometry.Transform3d;
|
||||||
|
import edu.wpi.first.util.RuntimeLoader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class AprilTagJNI {
|
||||||
|
static boolean libraryLoaded = false;
|
||||||
|
|
||||||
|
static RuntimeLoader<AprilTagJNI> loader = null;
|
||||||
|
|
||||||
|
public static class Helper {
|
||||||
|
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||||
|
|
||||||
|
public static boolean getExtractOnStaticLoad() {
|
||||||
|
return extractOnStaticLoad.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setExtractOnStaticLoad(boolean load) {
|
||||||
|
extractOnStaticLoad.set(load);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (Helper.getExtractOnStaticLoad()) {
|
||||||
|
try {
|
||||||
|
loader =
|
||||||
|
new RuntimeLoader<>(
|
||||||
|
"apriltagjni", RuntimeLoader.getDefaultExtractionRoot(), AprilTagJNI.class);
|
||||||
|
loader.loadLibrary();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
libraryLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native long createDetector();
|
||||||
|
|
||||||
|
public static native void destroyDetector(long det);
|
||||||
|
|
||||||
|
public static native void setDetectorConfig(long det, AprilTagDetector.Config config);
|
||||||
|
|
||||||
|
public static native AprilTagDetector.Config getDetectorConfig(long det);
|
||||||
|
|
||||||
|
public static native void setDetectorQTP(
|
||||||
|
long det, AprilTagDetector.QuadThresholdParameters params);
|
||||||
|
|
||||||
|
public static native AprilTagDetector.QuadThresholdParameters getDetectorQTP(long det);
|
||||||
|
|
||||||
|
public static native boolean addFamily(long det, String fam, int bitsCorrected);
|
||||||
|
|
||||||
|
public static native void removeFamily(long det, String fam);
|
||||||
|
|
||||||
|
public static native void clearFamilies(long det);
|
||||||
|
|
||||||
|
public static native AprilTagDetection[] detect(
|
||||||
|
long det, int width, int height, int stride, long bufAddr);
|
||||||
|
|
||||||
|
public static native Transform3d estimatePoseHomography(
|
||||||
|
double[] homography, double tagSize, double fx, double fy, double cx, double cy);
|
||||||
|
|
||||||
|
public static native AprilTagPoseEstimate estimatePoseOrthogonalIteration(
|
||||||
|
double[] homography,
|
||||||
|
double[] corners,
|
||||||
|
double tagSize,
|
||||||
|
double fx,
|
||||||
|
double fy,
|
||||||
|
double cx,
|
||||||
|
double cy,
|
||||||
|
int nIters);
|
||||||
|
|
||||||
|
public static native Transform3d estimatePose(
|
||||||
|
double[] homography,
|
||||||
|
double[] corners,
|
||||||
|
double tagSize,
|
||||||
|
double fx,
|
||||||
|
double fy,
|
||||||
|
double cx,
|
||||||
|
double cy);
|
||||||
|
}
|
||||||
18
apriltag/src/main/native/cpp/AprilTag.cpp
Normal file
18
apriltag/src/main/native/cpp/AprilTag.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTag.h"
|
||||||
|
|
||||||
|
#include <wpi/json.h>
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
void frc::to_json(wpi::json& json, const AprilTag& apriltag) {
|
||||||
|
json = wpi::json{{"ID", apriltag.ID}, {"pose", apriltag.pose}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void frc::from_json(const wpi::json& json, AprilTag& apriltag) {
|
||||||
|
apriltag.ID = json.at("ID").get<int>();
|
||||||
|
apriltag.pose = json.at("pose").get<Pose3d>();
|
||||||
|
}
|
||||||
37
apriltag/src/main/native/cpp/AprilTagDetection.cpp
Normal file
37
apriltag/src/main/native/cpp/AprilTagDetection.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagDetection.h"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma warning(disable : 4200)
|
||||||
|
#elif defined(__clang__)
|
||||||
|
#pragma clang diagnostic ignored "-Wc99-extensions"
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "apriltag.h"
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
static_assert(sizeof(AprilTagDetection) == sizeof(apriltag_detection_t),
|
||||||
|
"structure sizes don't match");
|
||||||
|
static_assert(std::is_standard_layout_v<AprilTagDetection>,
|
||||||
|
"AprilTagDetection is not standard layout?");
|
||||||
|
|
||||||
|
std::string_view AprilTagDetection::GetFamily() const {
|
||||||
|
return static_cast<const apriltag_family_t*>(family)->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::span<const double, 9> AprilTagDetection::GetHomography() const {
|
||||||
|
return std::span<const double, 9>{static_cast<matd_t*>(H)->data, 9};
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Matrix3d AprilTagDetection::GetHomographyMatrix() const {
|
||||||
|
return Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>{
|
||||||
|
static_cast<matd_t*>(H)->data};
|
||||||
|
}
|
||||||
200
apriltag/src/main/native/cpp/AprilTagDetector.cpp
Normal file
200
apriltag/src/main/native/cpp/AprilTagDetector.cpp
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagDetector.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <numbers>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma warning(disable : 4200)
|
||||||
|
#elif defined(__clang__)
|
||||||
|
#pragma clang diagnostic ignored "-Wc99-extensions"
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "apriltag.h"
|
||||||
|
#include "tag16h5.h"
|
||||||
|
#include "tag25h9.h"
|
||||||
|
#include "tag36h11.h"
|
||||||
|
#include "tagCircle21h7.h"
|
||||||
|
#include "tagCircle49h12.h"
|
||||||
|
#include "tagCustom48h12.h"
|
||||||
|
#include "tagStandard41h12.h"
|
||||||
|
#include "tagStandard52h13.h"
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
AprilTagDetector::Results::Results(void* impl, const private_init&)
|
||||||
|
: span{reinterpret_cast<AprilTagDetection**>(
|
||||||
|
static_cast<zarray_t*>(impl)->data),
|
||||||
|
static_cast<size_t>(static_cast<zarray_t*>(impl)->size)},
|
||||||
|
m_impl{impl} {}
|
||||||
|
|
||||||
|
AprilTagDetector::Results& AprilTagDetector::Results::operator=(Results&& rhs) {
|
||||||
|
Destroy();
|
||||||
|
m_impl = rhs.m_impl;
|
||||||
|
rhs.m_impl = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagDetector::Results::Destroy() {
|
||||||
|
if (m_impl) {
|
||||||
|
apriltag_detections_destroy(static_cast<zarray_t*>(m_impl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagDetector::AprilTagDetector() : m_impl{apriltag_detector_create()} {}
|
||||||
|
|
||||||
|
AprilTagDetector& AprilTagDetector::operator=(AprilTagDetector&& rhs) {
|
||||||
|
Destroy();
|
||||||
|
m_impl = rhs.m_impl;
|
||||||
|
rhs.m_impl = nullptr;
|
||||||
|
m_families = std::move(rhs.m_families);
|
||||||
|
rhs.m_families.clear();
|
||||||
|
m_qtpCriticalAngle = rhs.m_qtpCriticalAngle;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagDetector::SetConfig(const Config& config) {
|
||||||
|
auto& impl = *static_cast<apriltag_detector_t*>(m_impl);
|
||||||
|
impl.nthreads = config.numThreads;
|
||||||
|
impl.quad_decimate = config.quadDecimate;
|
||||||
|
impl.quad_sigma = config.quadSigma;
|
||||||
|
impl.refine_edges = config.refineEdges;
|
||||||
|
impl.decode_sharpening = config.decodeSharpening;
|
||||||
|
impl.debug = config.debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagDetector::Config AprilTagDetector::GetConfig() const {
|
||||||
|
auto& impl = *static_cast<apriltag_detector_t*>(m_impl);
|
||||||
|
return {
|
||||||
|
.numThreads = impl.nthreads,
|
||||||
|
.quadDecimate = impl.quad_decimate,
|
||||||
|
.quadSigma = impl.quad_sigma,
|
||||||
|
.refineEdges = impl.refine_edges,
|
||||||
|
.decodeSharpening = impl.decode_sharpening,
|
||||||
|
.debug = impl.debug,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagDetector::SetQuadThresholdParameters(
|
||||||
|
const QuadThresholdParameters& params) {
|
||||||
|
auto& qtp = static_cast<apriltag_detector_t*>(m_impl)->qtp;
|
||||||
|
qtp.min_cluster_pixels = params.minClusterPixels;
|
||||||
|
qtp.max_nmaxima = params.maxNumMaxima;
|
||||||
|
qtp.critical_rad = params.criticalAngle.value();
|
||||||
|
qtp.cos_critical_rad = std::cos(params.criticalAngle.value());
|
||||||
|
qtp.max_line_fit_mse = params.maxLineFitMSE;
|
||||||
|
qtp.min_white_black_diff = params.minWhiteBlackDiff;
|
||||||
|
qtp.deglitch = params.deglitch;
|
||||||
|
|
||||||
|
m_qtpCriticalAngle = params.criticalAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagDetector::QuadThresholdParameters
|
||||||
|
AprilTagDetector::GetQuadThresholdParameters() const {
|
||||||
|
auto& qtp = static_cast<apriltag_detector_t*>(m_impl)->qtp;
|
||||||
|
return {
|
||||||
|
.minClusterPixels = qtp.min_cluster_pixels,
|
||||||
|
.maxNumMaxima = qtp.max_nmaxima,
|
||||||
|
.criticalAngle = m_qtpCriticalAngle,
|
||||||
|
.maxLineFitMSE = qtp.max_line_fit_mse,
|
||||||
|
.minWhiteBlackDiff = qtp.min_white_black_diff,
|
||||||
|
.deglitch = qtp.deglitch != 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AprilTagDetector::AddFamily(std::string_view fam, int bitsCorrected) {
|
||||||
|
auto& data = m_families[fam];
|
||||||
|
if (data) {
|
||||||
|
return true; // already detecting
|
||||||
|
}
|
||||||
|
// create the family
|
||||||
|
if (fam == "tag16h5") {
|
||||||
|
data = tag16h5_create();
|
||||||
|
} else if (fam == "tag25h9") {
|
||||||
|
data = tag25h9_create();
|
||||||
|
} else if (fam == "tag36h11") {
|
||||||
|
data = tag36h11_create();
|
||||||
|
} else if (fam == "tagCircle21h7") {
|
||||||
|
data = tagCircle21h7_create();
|
||||||
|
} else if (fam == "tagCircle49h12") {
|
||||||
|
data = tagCircle49h12_create();
|
||||||
|
} else if (fam == "tagStandard41h12") {
|
||||||
|
data = tagStandard41h12_create();
|
||||||
|
} else if (fam == "tagStandard52h13") {
|
||||||
|
data = tagStandard52h13_create();
|
||||||
|
} else if (fam == "tagCustom48h12") {
|
||||||
|
data = tagCustom48h12_create();
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
m_families.erase(fam); // don't keep null value
|
||||||
|
return false; // can't add
|
||||||
|
}
|
||||||
|
apriltag_detector_add_family_bits(static_cast<apriltag_detector_t*>(m_impl),
|
||||||
|
static_cast<apriltag_family_t*>(data),
|
||||||
|
bitsCorrected);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagDetector::RemoveFamily(std::string_view fam) {
|
||||||
|
auto it = m_families.find(fam);
|
||||||
|
if (it != m_families.end()) {
|
||||||
|
apriltag_detector_remove_family(
|
||||||
|
static_cast<apriltag_detector_t*>(m_impl),
|
||||||
|
static_cast<apriltag_family_t*>(it->second));
|
||||||
|
DestroyFamily(it->getKey(), it->second);
|
||||||
|
m_families.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagDetector::ClearFamilies() {
|
||||||
|
apriltag_detector_clear_families(static_cast<apriltag_detector_t*>(m_impl));
|
||||||
|
DestroyFamilies();
|
||||||
|
m_families.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagDetector::Results AprilTagDetector::Detect(int width, int height,
|
||||||
|
int stride, uint8_t* buf) {
|
||||||
|
image_u8_t img{width, height, stride, buf};
|
||||||
|
return {
|
||||||
|
apriltag_detector_detect(static_cast<apriltag_detector_t*>(m_impl), &img),
|
||||||
|
Results::private_init{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagDetector::Destroy() {
|
||||||
|
if (m_impl) {
|
||||||
|
apriltag_detector_destroy(static_cast<apriltag_detector_t*>(m_impl));
|
||||||
|
}
|
||||||
|
DestroyFamilies();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagDetector::DestroyFamilies() {
|
||||||
|
for (auto&& entry : m_families) {
|
||||||
|
DestroyFamily(entry.getKey(), entry.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagDetector::DestroyFamily(std::string_view name, void* data) {
|
||||||
|
auto fam = static_cast<apriltag_family_t*>(data);
|
||||||
|
if (name == "tag16h5") {
|
||||||
|
tag16h5_destroy(fam);
|
||||||
|
} else if (name == "tag25h9") {
|
||||||
|
tag25h9_destroy(fam);
|
||||||
|
} else if (name == "tag36h11") {
|
||||||
|
tag36h11_destroy(fam);
|
||||||
|
} else if (name == "tagCircle21h7") {
|
||||||
|
tagCircle21h7_destroy(fam);
|
||||||
|
} else if (name == "tagCircle49h12") {
|
||||||
|
tagCircle49h12_destroy(fam);
|
||||||
|
} else if (name == "tagStandard41h12") {
|
||||||
|
tagStandard41h12_destroy(fam);
|
||||||
|
} else if (name == "tagStandard52h13") {
|
||||||
|
tagStandard52h13_destroy(fam);
|
||||||
|
} else if (name == "tagCustom48h12") {
|
||||||
|
tagCustom48h12_destroy(fam);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp
Normal file
107
apriltag/src/main/native/cpp/AprilTagFieldLayout.cpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagFieldLayout.h"
|
||||||
|
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#include <units/angle.h>
|
||||||
|
#include <units/length.h>
|
||||||
|
#include <wpi/json.h>
|
||||||
|
#include <wpi/raw_istream.h>
|
||||||
|
#include <wpi/raw_ostream.h>
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
AprilTagFieldLayout::AprilTagFieldLayout(std::string_view path) {
|
||||||
|
std::error_code error_code;
|
||||||
|
|
||||||
|
wpi::raw_fd_istream input{path, error_code};
|
||||||
|
if (error_code) {
|
||||||
|
throw std::runtime_error(fmt::format("Cannot open file: {}", path));
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi::json json;
|
||||||
|
input >> json;
|
||||||
|
|
||||||
|
for (const auto& tag : json.at("tags").get<std::vector<AprilTag>>()) {
|
||||||
|
m_apriltags[tag.ID] = tag;
|
||||||
|
}
|
||||||
|
m_fieldWidth = units::meter_t{json.at("field").at("width").get<double>()};
|
||||||
|
m_fieldLength = units::meter_t{json.at("field").at("length").get<double>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagFieldLayout::AprilTagFieldLayout(std::vector<AprilTag> apriltags,
|
||||||
|
units::meter_t fieldLength,
|
||||||
|
units::meter_t fieldWidth)
|
||||||
|
: m_fieldLength(std::move(fieldLength)),
|
||||||
|
m_fieldWidth(std::move(fieldWidth)) {
|
||||||
|
for (const auto& tag : apriltags) {
|
||||||
|
m_apriltags[tag.ID] = tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagFieldLayout::SetOrigin(OriginPosition origin) {
|
||||||
|
switch (origin) {
|
||||||
|
case OriginPosition::kBlueAllianceWallRightSide:
|
||||||
|
SetOrigin(Pose3d{});
|
||||||
|
break;
|
||||||
|
case OriginPosition::kRedAllianceWallRightSide:
|
||||||
|
SetOrigin(Pose3d{Translation3d{m_fieldLength, m_fieldWidth, 0_m},
|
||||||
|
Rotation3d{0_deg, 0_deg, 180_deg}});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::invalid_argument("Invalid origin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagFieldLayout::SetOrigin(const Pose3d& origin) {
|
||||||
|
m_origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<frc::Pose3d> AprilTagFieldLayout::GetTagPose(int ID) const {
|
||||||
|
const auto& it = m_apriltags.find(ID);
|
||||||
|
if (it == m_apriltags.end()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return it->second.pose.RelativeTo(m_origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AprilTagFieldLayout::Serialize(std::string_view path) {
|
||||||
|
std::error_code error_code;
|
||||||
|
|
||||||
|
wpi::raw_fd_ostream output{path, error_code};
|
||||||
|
if (error_code) {
|
||||||
|
throw std::runtime_error(fmt::format("Cannot open file: {}", path));
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi::json json = *this;
|
||||||
|
output << json;
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void frc::to_json(wpi::json& json, const AprilTagFieldLayout& layout) {
|
||||||
|
std::vector<AprilTag> tagVector;
|
||||||
|
tagVector.reserve(layout.m_apriltags.size());
|
||||||
|
for (const auto& pair : layout.m_apriltags) {
|
||||||
|
tagVector.push_back(pair.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
json = wpi::json{{"field",
|
||||||
|
{{"length", layout.m_fieldLength.value()},
|
||||||
|
{"width", layout.m_fieldWidth.value()}}},
|
||||||
|
{"tags", tagVector}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void frc::from_json(const wpi::json& json, AprilTagFieldLayout& layout) {
|
||||||
|
layout.m_apriltags.clear();
|
||||||
|
for (const auto& tag : json.at("tags").get<std::vector<AprilTag>>()) {
|
||||||
|
layout.m_apriltags[tag.ID] = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.m_fieldLength =
|
||||||
|
units::meter_t{json.at("field").at("length").get<double>()};
|
||||||
|
layout.m_fieldWidth =
|
||||||
|
units::meter_t{json.at("field").at("width").get<double>()};
|
||||||
|
}
|
||||||
32
apriltag/src/main/native/cpp/AprilTagFields.cpp
Normal file
32
apriltag/src/main/native/cpp/AprilTagFields.cpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagFields.h"
|
||||||
|
|
||||||
|
#include <wpi/json.h>
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
|
||||||
|
// C++ generated from resource files
|
||||||
|
std::string_view GetResource_2022_rapidreact_json();
|
||||||
|
std::string_view GetResource_2023_chargedup_json();
|
||||||
|
|
||||||
|
AprilTagFieldLayout LoadAprilTagLayoutField(AprilTagField field) {
|
||||||
|
std::string_view fieldString;
|
||||||
|
switch (field) {
|
||||||
|
case AprilTagField::k2022RapidReact:
|
||||||
|
fieldString = GetResource_2022_rapidreact_json();
|
||||||
|
break;
|
||||||
|
case AprilTagField::k2023ChargedUp:
|
||||||
|
fieldString = GetResource_2023_chargedup_json();
|
||||||
|
break;
|
||||||
|
case AprilTagField::kNumFields:
|
||||||
|
throw std::invalid_argument("Invalid Field");
|
||||||
|
}
|
||||||
|
|
||||||
|
wpi::json json = wpi::json::parse(fieldString);
|
||||||
|
return json.get<AprilTagFieldLayout>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
20
apriltag/src/main/native/cpp/AprilTagPoseEstimate.cpp
Normal file
20
apriltag/src/main/native/cpp/AprilTagPoseEstimate.cpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagPoseEstimate.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
double AprilTagPoseEstimate::GetAmbiguity() const {
|
||||||
|
auto min = (std::min)(error1, error2);
|
||||||
|
auto max = (std::max)(error1, error2);
|
||||||
|
|
||||||
|
if (max > 0) {
|
||||||
|
return min / max;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
154
apriltag/src/main/native/cpp/AprilTagPoseEstimator.cpp
Normal file
154
apriltag/src/main/native/cpp/AprilTagPoseEstimator.cpp
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagPoseEstimator.h"
|
||||||
|
|
||||||
|
#include <Eigen/QR>
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagDetection.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma warning(disable : 4200)
|
||||||
|
#elif defined(__clang__)
|
||||||
|
#pragma clang diagnostic ignored "-Wc99-extensions"
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "apriltag.h"
|
||||||
|
#include "apriltag_pose.h"
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
static Eigen::Matrix3d OrthogonalizeRotationMatrix(
|
||||||
|
const Eigen::Matrix3d& input) {
|
||||||
|
Eigen::HouseholderQR<Eigen::Matrix3d> qr{input};
|
||||||
|
|
||||||
|
Eigen::Matrix3d Q = qr.householderQ();
|
||||||
|
Eigen::Matrix3d R = qr.matrixQR().triangularView<Eigen::Upper>();
|
||||||
|
|
||||||
|
// Fix signs in R if they're < 0 so it's close to an identity matrix
|
||||||
|
// (our QR decomposition implementation sometimes flips the signs of
|
||||||
|
// columns)
|
||||||
|
for (int colR = 0; colR < 3; ++colR) {
|
||||||
|
if (R(colR, colR) < 0) {
|
||||||
|
for (int rowQ = 0; rowQ < 3; ++rowQ) {
|
||||||
|
Q(rowQ, colR) = -Q(rowQ, colR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Q;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Transform3d MakePose(const apriltag_pose_t& pose) {
|
||||||
|
if (!pose.R || !pose.t) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {Translation3d{units::meter_t{pose.t->data[0]},
|
||||||
|
units::meter_t{pose.t->data[1]},
|
||||||
|
units::meter_t{pose.t->data[2]}},
|
||||||
|
Rotation3d{OrthogonalizeRotationMatrix(
|
||||||
|
Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>{
|
||||||
|
pose.R->data})}};
|
||||||
|
}
|
||||||
|
|
||||||
|
static apriltag_detection_info_t MakeDetectionInfo(
|
||||||
|
const apriltag_detection_t* det,
|
||||||
|
const AprilTagPoseEstimator::Config& config) {
|
||||||
|
return {const_cast<apriltag_detection_t*>(det),
|
||||||
|
config.tagSize.value(),
|
||||||
|
config.fx,
|
||||||
|
config.fy,
|
||||||
|
config.cx,
|
||||||
|
config.cy};
|
||||||
|
}
|
||||||
|
|
||||||
|
static apriltag_detection_t MakeBasicDet(
|
||||||
|
std::span<const double, 9> homography,
|
||||||
|
const std::span<const double, 8>* corners) {
|
||||||
|
apriltag_detection_t detection;
|
||||||
|
detection.H = matd_create(3, 3);
|
||||||
|
std::memcpy(detection.H->data, homography.data(), 9 * sizeof(double));
|
||||||
|
if (corners) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
detection.p[i][0] = (*corners)[i * 2];
|
||||||
|
detection.p[i][1] = (*corners)[i * 2 + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return detection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Transform3d DoEstimateHomography(
|
||||||
|
const apriltag_detection_t* detection,
|
||||||
|
const AprilTagPoseEstimator::Config& config) {
|
||||||
|
auto info = MakeDetectionInfo(detection, config);
|
||||||
|
apriltag_pose_t pose;
|
||||||
|
estimate_pose_for_tag_homography(&info, &pose);
|
||||||
|
return MakePose(pose);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform3d AprilTagPoseEstimator::EstimateHomography(
|
||||||
|
const AprilTagDetection& detection) const {
|
||||||
|
return DoEstimateHomography(
|
||||||
|
reinterpret_cast<const apriltag_detection_t*>(&detection), m_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform3d AprilTagPoseEstimator::EstimateHomography(
|
||||||
|
std::span<const double, 9> homography) const {
|
||||||
|
auto detection = MakeBasicDet(homography, nullptr);
|
||||||
|
auto rv = DoEstimateHomography(&detection, m_config);
|
||||||
|
matd_destroy(detection.H);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AprilTagPoseEstimate DoEstimateOrthogonalIteration(
|
||||||
|
const apriltag_detection_t* detection,
|
||||||
|
const AprilTagPoseEstimator::Config& config, int nIters) {
|
||||||
|
auto info = MakeDetectionInfo(detection, config);
|
||||||
|
apriltag_pose_t pose1, pose2;
|
||||||
|
double err1, err2;
|
||||||
|
estimate_tag_pose_orthogonal_iteration(&info, &err1, &pose1, &err2, &pose2,
|
||||||
|
nIters);
|
||||||
|
return {MakePose(pose1), MakePose(pose2), err1, err2};
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagPoseEstimate AprilTagPoseEstimator::EstimateOrthogonalIteration(
|
||||||
|
const AprilTagDetection& detection, int nIters) const {
|
||||||
|
return DoEstimateOrthogonalIteration(
|
||||||
|
reinterpret_cast<const apriltag_detection_t*>(&detection), m_config,
|
||||||
|
nIters);
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagPoseEstimate AprilTagPoseEstimator::EstimateOrthogonalIteration(
|
||||||
|
std::span<const double, 9> homography, std::span<const double, 8> corners,
|
||||||
|
int nIters) const {
|
||||||
|
auto detection = MakeBasicDet(homography, &corners);
|
||||||
|
auto rv = DoEstimateOrthogonalIteration(&detection, m_config, nIters);
|
||||||
|
matd_destroy(detection.H);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Transform3d DoEstimate(const apriltag_detection_t* detection,
|
||||||
|
const AprilTagPoseEstimator::Config& config) {
|
||||||
|
auto info = MakeDetectionInfo(detection, config);
|
||||||
|
apriltag_pose_t pose;
|
||||||
|
estimate_tag_pose(&info, &pose);
|
||||||
|
return MakePose(pose);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform3d AprilTagPoseEstimator::Estimate(
|
||||||
|
const AprilTagDetection& detection) const {
|
||||||
|
return DoEstimate(reinterpret_cast<const apriltag_detection_t*>(&detection),
|
||||||
|
m_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform3d AprilTagPoseEstimator::Estimate(
|
||||||
|
std::span<const double, 9> homography,
|
||||||
|
std::span<const double, 8> corners) const {
|
||||||
|
auto detection = MakeBasicDet(homography, &corners);
|
||||||
|
auto rv = DoEstimate(&detection, m_config);
|
||||||
|
matd_destroy(detection.H);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
595
apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp
Normal file
595
apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <wpi/jni_util.h>
|
||||||
|
|
||||||
|
#include "edu_wpi_first_apriltag_jni_AprilTagJNI.h"
|
||||||
|
#include "frc/apriltag/AprilTagDetector.h"
|
||||||
|
#include "frc/apriltag/AprilTagPoseEstimator.h"
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
using namespace wpi::java;
|
||||||
|
|
||||||
|
static JavaVM* jvm = nullptr;
|
||||||
|
|
||||||
|
static JClass detectionCls;
|
||||||
|
static JClass detectorConfigCls;
|
||||||
|
static JClass detectorQTPCls;
|
||||||
|
static JClass poseEstimateCls;
|
||||||
|
static JClass quaternionCls;
|
||||||
|
static JClass rotation3dCls;
|
||||||
|
static JClass transform3dCls;
|
||||||
|
static JClass translation3dCls;
|
||||||
|
static JException illegalArgEx;
|
||||||
|
static JException nullPointerEx;
|
||||||
|
|
||||||
|
static const JClassInit classes[] = {
|
||||||
|
{"edu/wpi/first/apriltag/AprilTagDetection", &detectionCls},
|
||||||
|
{"edu/wpi/first/apriltag/AprilTagDetector$Config", &detectorConfigCls},
|
||||||
|
{"edu/wpi/first/apriltag/AprilTagDetector$QuadThresholdParameters",
|
||||||
|
&detectorQTPCls},
|
||||||
|
{"edu/wpi/first/apriltag/AprilTagPoseEstimate", &poseEstimateCls},
|
||||||
|
{"edu/wpi/first/math/geometry/Quaternion", &quaternionCls},
|
||||||
|
{"edu/wpi/first/math/geometry/Rotation3d", &rotation3dCls},
|
||||||
|
{"edu/wpi/first/math/geometry/Transform3d", &transform3dCls},
|
||||||
|
{"edu/wpi/first/math/geometry/Translation3d", &translation3dCls}};
|
||||||
|
|
||||||
|
static const JExceptionInit exceptions[] = {
|
||||||
|
{"java/lang/IllegalArgumentException", &illegalArgEx},
|
||||||
|
{"java/lang/NullPointerException", &nullPointerEx}};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
|
jvm = vm;
|
||||||
|
|
||||||
|
JNIEnv* env;
|
||||||
|
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache references to classes
|
||||||
|
for (auto& c : classes) {
|
||||||
|
*c.cls = JClass(env, c.name);
|
||||||
|
if (!*c.cls) {
|
||||||
|
std::fprintf(stderr, "could not load class %s\n", c.name);
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& c : exceptions) {
|
||||||
|
*c.cls = JException(env, c.name);
|
||||||
|
if (!*c.cls) {
|
||||||
|
std::fprintf(stderr, "could not load exception %s\n", c.name);
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JNI_VERSION_1_6;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||||
|
JNIEnv* env;
|
||||||
|
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Delete global references
|
||||||
|
for (auto& c : classes) {
|
||||||
|
c.cls->free(env);
|
||||||
|
}
|
||||||
|
for (auto& c : exceptions) {
|
||||||
|
c.cls->free(env);
|
||||||
|
}
|
||||||
|
jvm = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Conversions from Java to C++ objects
|
||||||
|
//
|
||||||
|
|
||||||
|
static AprilTagDetector::Config FromJavaDetectorConfig(JNIEnv* env,
|
||||||
|
jobject jconfig) {
|
||||||
|
if (!jconfig) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
#define FIELD(name, sig) \
|
||||||
|
static jfieldID name##Field = nullptr; \
|
||||||
|
if (!name##Field) { \
|
||||||
|
name##Field = env->GetFieldID(detectorConfigCls, #name, sig); \
|
||||||
|
}
|
||||||
|
|
||||||
|
FIELD(numThreads, "I");
|
||||||
|
FIELD(quadDecimate, "F");
|
||||||
|
FIELD(quadSigma, "F");
|
||||||
|
FIELD(refineEdges, "Z");
|
||||||
|
FIELD(decodeSharpening, "D");
|
||||||
|
FIELD(debug, "Z");
|
||||||
|
|
||||||
|
#undef FIELD
|
||||||
|
|
||||||
|
#define FIELD(ctype, jtype, name) \
|
||||||
|
.name = static_cast<ctype>(env->Get##jtype##Field(jconfig, name##Field))
|
||||||
|
|
||||||
|
return {
|
||||||
|
FIELD(int, Int, numThreads),
|
||||||
|
FIELD(float, Float, quadDecimate),
|
||||||
|
FIELD(float, Float, quadSigma),
|
||||||
|
FIELD(bool, Boolean, refineEdges),
|
||||||
|
FIELD(double, Double, decodeSharpening),
|
||||||
|
FIELD(bool, Boolean, debug),
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef GET
|
||||||
|
#undef FIELD
|
||||||
|
}
|
||||||
|
|
||||||
|
static AprilTagDetector::QuadThresholdParameters FromJavaDetectorQTP(
|
||||||
|
JNIEnv* env, jobject jparams) {
|
||||||
|
if (!jparams) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
#define FIELD(name, sig) \
|
||||||
|
static jfieldID name##Field = nullptr; \
|
||||||
|
if (!name##Field) { \
|
||||||
|
name##Field = env->GetFieldID(detectorQTPCls, #name, sig); \
|
||||||
|
}
|
||||||
|
|
||||||
|
FIELD(minClusterPixels, "I");
|
||||||
|
FIELD(maxNumMaxima, "I");
|
||||||
|
FIELD(criticalAngle, "D");
|
||||||
|
FIELD(maxLineFitMSE, "F");
|
||||||
|
FIELD(minWhiteBlackDiff, "I");
|
||||||
|
FIELD(deglitch, "Z");
|
||||||
|
|
||||||
|
#undef FIELD
|
||||||
|
|
||||||
|
#define FIELD(ctype, jtype, name) \
|
||||||
|
.name = static_cast<ctype>(env->Get##jtype##Field(jparams, name##Field))
|
||||||
|
|
||||||
|
return {
|
||||||
|
FIELD(int, Int, minClusterPixels),
|
||||||
|
FIELD(int, Int, maxNumMaxima),
|
||||||
|
.criticalAngle = units::radian_t{static_cast<double>(
|
||||||
|
env->GetDoubleField(jparams, criticalAngleField))},
|
||||||
|
FIELD(float, Float, maxLineFitMSE),
|
||||||
|
FIELD(int, Int, minWhiteBlackDiff),
|
||||||
|
FIELD(bool, Boolean, deglitch),
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef GET
|
||||||
|
#undef FIELD
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Conversions from C++ to Java objects
|
||||||
|
//
|
||||||
|
|
||||||
|
static jobject MakeJObject(JNIEnv* env, const AprilTagDetection& detect) {
|
||||||
|
static jmethodID constructor = env->GetMethodID(
|
||||||
|
detectionCls, "<init>", "(Ljava/lang/String;IIF[DDD[D)V");
|
||||||
|
if (!constructor) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
JLocal<jstring> fam{env, MakeJString(env, detect.GetFamily())};
|
||||||
|
|
||||||
|
auto homography = detect.GetHomography();
|
||||||
|
JLocal<jdoubleArray> harr{
|
||||||
|
env, MakeJDoubleArray(
|
||||||
|
env, {reinterpret_cast<const jdouble*>(homography.data()),
|
||||||
|
homography.size()})};
|
||||||
|
|
||||||
|
double cornersBuf[8];
|
||||||
|
auto corners = detect.GetCorners(cornersBuf);
|
||||||
|
JLocal<jdoubleArray> carr{
|
||||||
|
env,
|
||||||
|
MakeJDoubleArray(env, {reinterpret_cast<const jdouble*>(corners.data()),
|
||||||
|
corners.size()})};
|
||||||
|
|
||||||
|
auto center = detect.GetCenter();
|
||||||
|
|
||||||
|
return env->NewObject(detectionCls, constructor, fam.obj(),
|
||||||
|
static_cast<jint>(detect.GetId()),
|
||||||
|
static_cast<jint>(detect.GetHamming()),
|
||||||
|
static_cast<jfloat>(detect.GetDecisionMargin()),
|
||||||
|
harr.obj(), static_cast<jdouble>(center.x),
|
||||||
|
static_cast<jdouble>(center.y), carr.obj());
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobjectArray MakeJObject(JNIEnv* env,
|
||||||
|
std::span<const AprilTagDetection* const> arr) {
|
||||||
|
jobjectArray jarr = env->NewObjectArray(arr.size(), detectionCls, nullptr);
|
||||||
|
if (!jarr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < arr.size(); ++i) {
|
||||||
|
JLocal<jobject> elem{env, MakeJObject(env, *arr[i])};
|
||||||
|
env->SetObjectArrayElement(jarr, i, elem.obj());
|
||||||
|
}
|
||||||
|
return jarr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject MakeJObject(JNIEnv* env,
|
||||||
|
const AprilTagDetector::Config& config) {
|
||||||
|
static jmethodID constructor =
|
||||||
|
env->GetMethodID(detectorConfigCls, "<init>", "(IFFZDZ)V");
|
||||||
|
if (!constructor) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return env->NewObject(detectorConfigCls, constructor,
|
||||||
|
static_cast<jint>(config.numThreads),
|
||||||
|
static_cast<jfloat>(config.quadDecimate),
|
||||||
|
static_cast<jfloat>(config.quadSigma),
|
||||||
|
static_cast<jboolean>(config.refineEdges),
|
||||||
|
static_cast<jdouble>(config.decodeSharpening),
|
||||||
|
static_cast<jboolean>(config.debug));
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject MakeJObject(
|
||||||
|
JNIEnv* env, const AprilTagDetector::QuadThresholdParameters& params) {
|
||||||
|
static jmethodID constructor =
|
||||||
|
env->GetMethodID(detectorQTPCls, "<init>", "(IIDFIZ)V");
|
||||||
|
if (!constructor) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return env->NewObject(detectorQTPCls, constructor,
|
||||||
|
static_cast<jint>(params.minClusterPixels),
|
||||||
|
static_cast<jint>(params.maxNumMaxima),
|
||||||
|
static_cast<jdouble>(params.criticalAngle),
|
||||||
|
static_cast<jfloat>(params.maxLineFitMSE),
|
||||||
|
static_cast<jint>(params.minWhiteBlackDiff),
|
||||||
|
static_cast<jboolean>(params.deglitch));
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject MakeJObject(JNIEnv* env, const Translation3d& xlate) {
|
||||||
|
static jmethodID constructor =
|
||||||
|
env->GetMethodID(translation3dCls, "<init>", "(DDD)V");
|
||||||
|
if (!constructor) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return env->NewObject(
|
||||||
|
translation3dCls, constructor, static_cast<jdouble>(xlate.X()),
|
||||||
|
static_cast<jdouble>(xlate.Y()), static_cast<jdouble>(xlate.Z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject MakeJObject(JNIEnv* env, const Quaternion& q) {
|
||||||
|
static jmethodID constructor =
|
||||||
|
env->GetMethodID(quaternionCls, "<init>", "(DDDD)V");
|
||||||
|
if (!constructor) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return env->NewObject(quaternionCls, constructor, static_cast<jdouble>(q.W()),
|
||||||
|
static_cast<jdouble>(q.X()),
|
||||||
|
static_cast<jdouble>(q.Y()),
|
||||||
|
static_cast<jdouble>(q.Z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject MakeJObject(JNIEnv* env, const Rotation3d& rot) {
|
||||||
|
static jmethodID constructor = env->GetMethodID(
|
||||||
|
rotation3dCls, "<init>", "(Ledu/wpi/first/math/geometry/Quaternion;)V");
|
||||||
|
if (!constructor) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
JLocal<jobject> q{env, MakeJObject(env, rot.GetQuaternion())};
|
||||||
|
return env->NewObject(rotation3dCls, constructor, q.obj());
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject MakeJObject(JNIEnv* env, const Transform3d& xform) {
|
||||||
|
static jmethodID constructor =
|
||||||
|
env->GetMethodID(transform3dCls, "<init>",
|
||||||
|
"(Ledu/wpi/first/math/geometry/Translation3d;"
|
||||||
|
"Ledu/wpi/first/math/geometry/Rotation3d;)V");
|
||||||
|
if (!constructor) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
JLocal<jobject> xlate{env, MakeJObject(env, xform.Translation())};
|
||||||
|
JLocal<jobject> rot{env, MakeJObject(env, xform.Rotation())};
|
||||||
|
return env->NewObject(transform3dCls, constructor, xlate.obj(), rot.obj());
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject MakeJObject(JNIEnv* env, const AprilTagPoseEstimate& est) {
|
||||||
|
static jmethodID constructor =
|
||||||
|
env->GetMethodID(poseEstimateCls, "<init>",
|
||||||
|
"(Ledu/wpi/first/math/geometry/Transform3d;"
|
||||||
|
"Ledu/wpi/first/math/geometry/Transform3d;DD)V");
|
||||||
|
if (!constructor) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
JLocal<jobject> pose1{env, MakeJObject(env, est.pose1)};
|
||||||
|
JLocal<jobject> pose2{env, MakeJObject(env, est.pose2)};
|
||||||
|
return env->NewObject(poseEstimateCls, constructor, pose1.obj(), pose2.obj(),
|
||||||
|
static_cast<jdouble>(est.error1),
|
||||||
|
static_cast<jdouble>(est.error2));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: createDetector
|
||||||
|
* Signature: ()J
|
||||||
|
*/
|
||||||
|
JNIEXPORT jlong JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_createDetector
|
||||||
|
(JNIEnv* env, jclass)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<jlong>(new AprilTagDetector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: destroyDetector
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_destroyDetector
|
||||||
|
(JNIEnv* env, jclass, jlong det)
|
||||||
|
{
|
||||||
|
delete reinterpret_cast<AprilTagDetector*>(det);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: setDetectorConfig
|
||||||
|
* Signature: (JLjava/lang/Object;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_setDetectorConfig
|
||||||
|
(JNIEnv* env, jclass, jlong det, jobject config)
|
||||||
|
{
|
||||||
|
if (det == 0) {
|
||||||
|
nullPointerEx.Throw(env, "det cannot be null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reinterpret_cast<AprilTagDetector*>(det)->SetConfig(
|
||||||
|
FromJavaDetectorConfig(env, config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: getDetectorConfig
|
||||||
|
* Signature: (J)Ljava/lang/Object;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobject JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_getDetectorConfig
|
||||||
|
(JNIEnv* env, jclass, jlong det)
|
||||||
|
{
|
||||||
|
if (det == 0) {
|
||||||
|
nullPointerEx.Throw(env, "det cannot be null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return MakeJObject(env,
|
||||||
|
reinterpret_cast<AprilTagDetector*>(det)->GetConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: setDetectorQTP
|
||||||
|
* Signature: (JLjava/lang/Object;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_setDetectorQTP
|
||||||
|
(JNIEnv* env, jclass, jlong det, jobject params)
|
||||||
|
{
|
||||||
|
if (det == 0) {
|
||||||
|
nullPointerEx.Throw(env, "det cannot be null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reinterpret_cast<AprilTagDetector*>(det)->SetQuadThresholdParameters(
|
||||||
|
FromJavaDetectorQTP(env, params));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: getDetectorQTP
|
||||||
|
* Signature: (J)Ljava/lang/Object;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobject JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_getDetectorQTP
|
||||||
|
(JNIEnv* env, jclass, jlong det)
|
||||||
|
{
|
||||||
|
if (det == 0) {
|
||||||
|
nullPointerEx.Throw(env, "det cannot be null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return MakeJObject(
|
||||||
|
env,
|
||||||
|
reinterpret_cast<AprilTagDetector*>(det)->GetQuadThresholdParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: addFamily
|
||||||
|
* Signature: (JLjava/lang/String;I)Z
|
||||||
|
*/
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_addFamily
|
||||||
|
(JNIEnv* env, jclass, jlong det, jstring fam, jint bitsCorrected)
|
||||||
|
{
|
||||||
|
if (det == 0) {
|
||||||
|
nullPointerEx.Throw(env, "det cannot be null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!fam) {
|
||||||
|
nullPointerEx.Throw(env, "fam cannot be null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return reinterpret_cast<AprilTagDetector*>(det)->AddFamily(
|
||||||
|
JStringRef{env, fam}, bitsCorrected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: removeFamily
|
||||||
|
* Signature: (JLjava/lang/String;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_removeFamily
|
||||||
|
(JNIEnv* env, jclass, jlong det, jstring fam)
|
||||||
|
{
|
||||||
|
if (det == 0) {
|
||||||
|
nullPointerEx.Throw(env, "det cannot be null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!fam) {
|
||||||
|
nullPointerEx.Throw(env, "fam cannot be null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reinterpret_cast<AprilTagDetector*>(det)->RemoveFamily(JStringRef{env, fam});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: clearFamilies
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_clearFamilies
|
||||||
|
(JNIEnv* env, jclass, jlong det)
|
||||||
|
{
|
||||||
|
if (det == 0) {
|
||||||
|
nullPointerEx.Throw(env, "det cannot be null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reinterpret_cast<AprilTagDetector*>(det)->ClearFamilies();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: detect
|
||||||
|
* Signature: (JIIIJ)[Ljava/lang/Object;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobjectArray JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_detect
|
||||||
|
(JNIEnv* env, jclass, jlong det, jint width, jint height, jint stride,
|
||||||
|
jlong bufAddr)
|
||||||
|
{
|
||||||
|
if (det == 0) {
|
||||||
|
nullPointerEx.Throw(env, "det cannot be null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (bufAddr == 0) {
|
||||||
|
nullPointerEx.Throw(env, "bufAddr cannot be null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return MakeJObject(
|
||||||
|
env, reinterpret_cast<AprilTagDetector*>(det)->Detect(
|
||||||
|
width, height, stride, reinterpret_cast<uint8_t*>(bufAddr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: estimatePoseHomography
|
||||||
|
* Signature: ([DDDDDD)Ljava/lang/Object;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobject JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePoseHomography
|
||||||
|
(JNIEnv* env, jclass, jdoubleArray homography, jdouble tagSize, jdouble fx,
|
||||||
|
jdouble fy, jdouble cx, jdouble cy)
|
||||||
|
{
|
||||||
|
if (!homography) {
|
||||||
|
nullPointerEx.Throw(env, "homography cannot be null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
JDoubleArrayRef harr{env, homography};
|
||||||
|
if (harr.size() != 9) {
|
||||||
|
illegalArgEx.Throw(env, "homography array must be size 9");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
|
||||||
|
return MakeJObject(env, estimator.EstimateHomography(
|
||||||
|
std::span<const double, 9>{harr.array()}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: estimatePoseOrthogonalIteration
|
||||||
|
* Signature: ([D[DDDDDDI)Ljava/lang/Object;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobject JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePoseOrthogonalIteration
|
||||||
|
(JNIEnv* env, jclass, jdoubleArray homography, jdoubleArray corners,
|
||||||
|
jdouble tagSize, jdouble fx, jdouble fy, jdouble cx, jdouble cy, jint nIters)
|
||||||
|
{
|
||||||
|
// homography
|
||||||
|
if (!homography) {
|
||||||
|
nullPointerEx.Throw(env, "homography cannot be null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
JDoubleArrayRef harr{env, homography};
|
||||||
|
if (harr.size() != 9) {
|
||||||
|
illegalArgEx.Throw(env, "homography array must be size 9");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// corners
|
||||||
|
if (!corners) {
|
||||||
|
nullPointerEx.Throw(env, "corners cannot be null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
JDoubleArrayRef carr{env, corners};
|
||||||
|
if (carr.size() != 8) {
|
||||||
|
illegalArgEx.Throw(env, "corners array must be size 8");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
|
||||||
|
return MakeJObject(env,
|
||||||
|
estimator.EstimateOrthogonalIteration(
|
||||||
|
std::span<const double, 9>{harr.array()},
|
||||||
|
std::span<const double, 8>{carr.array()}, nIters));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: edu_wpi_first_apriltag_jni_AprilTagJNI
|
||||||
|
* Method: estimatePose
|
||||||
|
* Signature: ([D[DDDDDD)Ljava/lang/Object;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobject JNICALL
|
||||||
|
Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePose
|
||||||
|
(JNIEnv* env, jclass, jdoubleArray homography, jdoubleArray corners,
|
||||||
|
jdouble tagSize, jdouble fx, jdouble fy, jdouble cx, jdouble cy)
|
||||||
|
{
|
||||||
|
// homography
|
||||||
|
if (!homography) {
|
||||||
|
nullPointerEx.Throw(env, "homography cannot be null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
JDoubleArrayRef harr{env, homography};
|
||||||
|
if (harr.size() != 9) {
|
||||||
|
illegalArgEx.Throw(env, "homography array must be size 9");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// corners
|
||||||
|
if (!corners) {
|
||||||
|
nullPointerEx.Throw(env, "corners cannot be null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
JDoubleArrayRef carr{env, corners};
|
||||||
|
if (carr.size() != 8) {
|
||||||
|
illegalArgEx.Throw(env, "corners array must be size 8");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy});
|
||||||
|
return MakeJObject(
|
||||||
|
env, estimator.Estimate(std::span<const double, 9>{harr.array()},
|
||||||
|
std::span<const double, 8>{carr.array()}));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
34
apriltag/src/main/native/include/frc/apriltag/AprilTag.h
Normal file
34
apriltag/src/main/native/include/frc/apriltag/AprilTag.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wpi/SymbolExports.h>
|
||||||
|
|
||||||
|
#include "frc/geometry/Pose3d.h"
|
||||||
|
|
||||||
|
namespace wpi {
|
||||||
|
class json;
|
||||||
|
} // namespace wpi
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
|
||||||
|
struct WPILIB_DLLEXPORT AprilTag {
|
||||||
|
int ID;
|
||||||
|
|
||||||
|
Pose3d pose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks equality between this AprilTag and another object.
|
||||||
|
*/
|
||||||
|
bool operator==(const AprilTag&) const = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
WPILIB_DLLEXPORT
|
||||||
|
void to_json(wpi::json& json, const AprilTag& apriltag);
|
||||||
|
|
||||||
|
WPILIB_DLLEXPORT
|
||||||
|
void from_json(const wpi::json& json, AprilTag& apriltag);
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <wpi/SymbolExports.h>
|
||||||
|
|
||||||
|
#include "frc/EigenCore.h"
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A detection of an AprilTag tag.
|
||||||
|
*/
|
||||||
|
class WPILIB_DLLEXPORT AprilTagDetection final {
|
||||||
|
public:
|
||||||
|
AprilTagDetection() = delete;
|
||||||
|
AprilTagDetection(const AprilTagDetection&) = delete;
|
||||||
|
AprilTagDetection& operator=(const AprilTagDetection&) = delete;
|
||||||
|
|
||||||
|
/** A point. Used for center and corner points. */
|
||||||
|
struct Point {
|
||||||
|
double x;
|
||||||
|
double y;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the decoded tag's family name.
|
||||||
|
*
|
||||||
|
* @return Decoded family name
|
||||||
|
*/
|
||||||
|
std::string_view GetFamily() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the decoded ID of the tag.
|
||||||
|
*
|
||||||
|
* @return Decoded ID
|
||||||
|
*/
|
||||||
|
int GetId() const { return id; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets how many error bits were corrected. Note: accepting large numbers of
|
||||||
|
* corrected errors leads to greatly increased false positive rates.
|
||||||
|
* NOTE: As of this implementation, the detector cannot detect tags with
|
||||||
|
* a hamming distance greater than 2.
|
||||||
|
*
|
||||||
|
* @return Hamming distance (number of corrected error bits)
|
||||||
|
*/
|
||||||
|
int GetHamming() const { return hamming; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a measure of the quality of the binary decoding process: the
|
||||||
|
* average difference between the intensity of a data bit versus
|
||||||
|
* the decision threshold. Higher numbers roughly indicate better
|
||||||
|
* decodes. This is a reasonable measure of detection accuracy
|
||||||
|
* only for very small tags-- not effective for larger tags (where
|
||||||
|
* we could have sampled anywhere within a bit cell and still
|
||||||
|
* gotten a good detection.)
|
||||||
|
*
|
||||||
|
* @return Decision margin
|
||||||
|
*/
|
||||||
|
float GetDecisionMargin() const { return decision_margin; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the 3x3 homography matrix describing the projection from an
|
||||||
|
* "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
|
||||||
|
* -1)) to pixels in the image.
|
||||||
|
*
|
||||||
|
* @return Homography matrix data
|
||||||
|
*/
|
||||||
|
std::span<const double, 9> GetHomography() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the 3x3 homography matrix describing the projection from an
|
||||||
|
* "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
|
||||||
|
* -1)) to pixels in the image.
|
||||||
|
*
|
||||||
|
* @return Homography matrix
|
||||||
|
*/
|
||||||
|
Eigen::Matrix3d GetHomographyMatrix() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the center of the detection in image pixel coordinates.
|
||||||
|
*
|
||||||
|
* @return Center point
|
||||||
|
*/
|
||||||
|
const Point& GetCenter() const { return *reinterpret_cast<const Point*>(c); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a corner of the tag in image pixel coordinates. These always
|
||||||
|
* wrap counter-clock wise around the tag.
|
||||||
|
*
|
||||||
|
* @param ndx Corner index (range is 0-3, inclusive)
|
||||||
|
* @return Corner point
|
||||||
|
*/
|
||||||
|
const Point& GetCorner(int ndx) const {
|
||||||
|
return *reinterpret_cast<const Point*>(p[ndx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the corners of the tag in image pixel coordinates. These always
|
||||||
|
* wrap counter-clock wise around the tag.
|
||||||
|
*
|
||||||
|
* @param cornersBuf Corner point array (X and Y for each corner in order)
|
||||||
|
* @return Corner point array (copy of cornersBuf span)
|
||||||
|
*/
|
||||||
|
std::span<double, 8> GetCorners(std::span<double, 8> cornersBuf) const {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
cornersBuf[i * 2] = p[i][0];
|
||||||
|
cornersBuf[i * 2 + 1] = p[i][1];
|
||||||
|
}
|
||||||
|
return cornersBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This class *must* be standard-layout-compatible with apriltag_detection
|
||||||
|
// as we use reinterpret_cast from that structure. This means the below
|
||||||
|
// members must exactly match the contents of the apriltag_detection struct.
|
||||||
|
|
||||||
|
// The tag family.
|
||||||
|
void* family;
|
||||||
|
|
||||||
|
// The decoded ID of the tag.
|
||||||
|
int id;
|
||||||
|
|
||||||
|
// How many error bits were corrected? Note: accepting large numbers of
|
||||||
|
// corrected errors leads to greatly increased false positive rates.
|
||||||
|
// NOTE: As of this implementation, the detector cannot detect tags with
|
||||||
|
// a hamming distance greater than 2.
|
||||||
|
int hamming;
|
||||||
|
|
||||||
|
// A measure of the quality of the binary decoding process: the
|
||||||
|
// average difference between the intensity of a data bit versus
|
||||||
|
// the decision threshold. Higher numbers roughly indicate better
|
||||||
|
// decodes. This is a reasonable measure of detection accuracy
|
||||||
|
// only for very small tags-- not effective for larger tags (where
|
||||||
|
// we could have sampled anywhere within a bit cell and still
|
||||||
|
// gotten a good detection.)
|
||||||
|
float decision_margin;
|
||||||
|
|
||||||
|
// The 3x3 homography matrix describing the projection from an
|
||||||
|
// "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
|
||||||
|
// -1)) to pixels in the image.
|
||||||
|
void* H;
|
||||||
|
|
||||||
|
// The center of the detection in image pixel coordinates.
|
||||||
|
double c[2];
|
||||||
|
|
||||||
|
// The corners of the tag in image pixel coordinates. These always
|
||||||
|
// wrap counter-clock wise around the tag.
|
||||||
|
double p[4][2];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
260
apriltag/src/main/native/include/frc/apriltag/AprilTagDetector.h
Normal file
260
apriltag/src/main/native/include/frc/apriltag/AprilTagDetector.h
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <units/angle.h>
|
||||||
|
#include <wpi/StringMap.h>
|
||||||
|
#include <wpi/SymbolExports.h>
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagDetection.h"
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An AprilTag detector engine. This is expensive to set up and tear down, so
|
||||||
|
* most use cases should only create one of these, add a family to it, set up
|
||||||
|
* any other configuration, and repeatedly call Detect().
|
||||||
|
*/
|
||||||
|
class WPILIB_DLLEXPORT AprilTagDetector {
|
||||||
|
public:
|
||||||
|
/** Detector configuration. */
|
||||||
|
struct Config {
|
||||||
|
bool operator==(const Config&) const = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many threads should be used for computation. Default is
|
||||||
|
* single-threaded operation (1 thread).
|
||||||
|
*/
|
||||||
|
int numThreads = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quad decimation. Detection of quads can be done on a lower-resolution
|
||||||
|
* image, improving speed at a cost of pose accuracy and a slight decrease
|
||||||
|
* in detection rate. Decoding the binary payload is still done at full
|
||||||
|
* resolution. Default is 2.0.
|
||||||
|
*/
|
||||||
|
float quadDecimate = 2.0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What Gaussian blur should be applied to the segmented image (used for
|
||||||
|
* quad detection). Very noisy images benefit from non-zero values (e.g.
|
||||||
|
* 0.8). Default is 0.0.
|
||||||
|
*/
|
||||||
|
float quadSigma = 0.0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When true, the edges of the each quad are adjusted to "snap to" strong
|
||||||
|
* gradients nearby. This is useful when decimation is employed, as it can
|
||||||
|
* increase the quality of the initial quad estimate substantially.
|
||||||
|
* Generally recommended to be on (true). Default is true.
|
||||||
|
*
|
||||||
|
* Very computationally inexpensive. Option is ignored if
|
||||||
|
* quad_decimate = 1.
|
||||||
|
*/
|
||||||
|
bool refineEdges = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much sharpening should be done to decoded images. This can help
|
||||||
|
* decode small tags but may or may not help in odd lighting conditions or
|
||||||
|
* low light conditions. Default is 0.25.
|
||||||
|
*/
|
||||||
|
double decodeSharpening = 0.25;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug mode. When true, the decoder writes a variety of debugging images
|
||||||
|
* to the current working directory at various stages through the detection
|
||||||
|
* process. This is slow and should *not* be used on space-limited systems
|
||||||
|
* such as the RoboRIO. Default is disabled (false).
|
||||||
|
*/
|
||||||
|
bool debug = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Quad threshold parameters. */
|
||||||
|
struct QuadThresholdParameters {
|
||||||
|
bool operator==(const QuadThresholdParameters&) const = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Threshold used to reject quads containing too few pixels. Default is 5
|
||||||
|
* pixels.
|
||||||
|
*/
|
||||||
|
int minClusterPixels = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many corner candidates to consider when segmenting a group of pixels
|
||||||
|
* into a quad. Default is 10.
|
||||||
|
*/
|
||||||
|
int maxNumMaxima = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Critical angle. The detector will reject quads where pairs of edges have
|
||||||
|
* angles that are close to straight or close to 180 degrees. Zero means
|
||||||
|
* that no quads are rejected. Default is 10 degrees.
|
||||||
|
*/
|
||||||
|
units::radian_t criticalAngle = 10_deg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When fitting lines to the contours, the maximum mean squared error
|
||||||
|
* allowed. This is useful in rejecting contours that are far from being
|
||||||
|
* quad shaped; rejecting these quads "early" saves expensive decoding
|
||||||
|
* processing. Default is 10.0.
|
||||||
|
*/
|
||||||
|
float maxLineFitMSE = 10.0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum brightness offset. When we build our model of black & white
|
||||||
|
* pixels, we add an extra check that the white model must be (overall)
|
||||||
|
* brighter than the black model. How much brighter? (in pixel values,
|
||||||
|
* [0,255]). Default is 5.
|
||||||
|
*/
|
||||||
|
int minWhiteBlackDiff = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the thresholded image be should be deglitched. Only useful for
|
||||||
|
* very noisy images. Default is disabled (false).
|
||||||
|
*/
|
||||||
|
bool deglitch = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of detection results. Each array element is a pointer to an
|
||||||
|
* AprilTagDetection.
|
||||||
|
*/
|
||||||
|
class WPILIB_DLLEXPORT Results
|
||||||
|
: public std::span<AprilTagDetection const* const> {
|
||||||
|
struct private_init {};
|
||||||
|
friend class AprilTagDetector;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Results() = default;
|
||||||
|
Results(void* impl, const private_init&);
|
||||||
|
~Results() { Destroy(); }
|
||||||
|
Results(const Results&) = delete;
|
||||||
|
Results& operator=(const Results&) = delete;
|
||||||
|
Results(Results&& rhs) : span{std::move(rhs)}, m_impl{rhs.m_impl} {
|
||||||
|
rhs.m_impl = nullptr;
|
||||||
|
}
|
||||||
|
Results& operator=(Results&& rhs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Destroy();
|
||||||
|
void* m_impl = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
AprilTagDetector();
|
||||||
|
~AprilTagDetector() { Destroy(); }
|
||||||
|
AprilTagDetector(const AprilTagDetector&) = delete;
|
||||||
|
AprilTagDetector& operator=(const AprilTagDetector&) = delete;
|
||||||
|
AprilTagDetector(AprilTagDetector&& rhs)
|
||||||
|
: m_impl{rhs.m_impl},
|
||||||
|
m_families{std::move(rhs.m_families)},
|
||||||
|
m_qtpCriticalAngle{rhs.m_qtpCriticalAngle} {
|
||||||
|
rhs.m_impl = nullptr;
|
||||||
|
}
|
||||||
|
AprilTagDetector& operator=(AprilTagDetector&& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @{
|
||||||
|
* @name Configuration functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets detector configuration.
|
||||||
|
*
|
||||||
|
* @param config Configuration
|
||||||
|
*/
|
||||||
|
void SetConfig(const Config& config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets detector configuration.
|
||||||
|
*
|
||||||
|
* @return Configuration
|
||||||
|
*/
|
||||||
|
Config GetConfig() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets quad threshold parameters.
|
||||||
|
*
|
||||||
|
* @param params Parameters
|
||||||
|
*/
|
||||||
|
void SetQuadThresholdParameters(const QuadThresholdParameters& params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets quad threshold parameters.
|
||||||
|
*
|
||||||
|
* @return Parameters
|
||||||
|
*/
|
||||||
|
QuadThresholdParameters GetQuadThresholdParameters() const;
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @{
|
||||||
|
* @name Tag family functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a family of tags to be detected.
|
||||||
|
*
|
||||||
|
* @param fam Family name, e.g. "tag16h5"
|
||||||
|
* @param bitsCorrected
|
||||||
|
* @return False if family can't be found
|
||||||
|
*/
|
||||||
|
bool AddFamily(std::string_view fam, int bitsCorrected = 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a family of tags from the detector.
|
||||||
|
*
|
||||||
|
* @param fam Family name, e.g. "tag16h5"
|
||||||
|
*/
|
||||||
|
void RemoveFamily(std::string_view fam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister all families.
|
||||||
|
*/
|
||||||
|
void ClearFamilies();
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect tags from an 8-bit image.
|
||||||
|
*
|
||||||
|
* @param width width of the image
|
||||||
|
* @param height height of the image
|
||||||
|
* @param stride number of bytes between image rows (often the same as width)
|
||||||
|
* @param buf image buffer
|
||||||
|
* @return Results (array of AprilTagDetection pointers)
|
||||||
|
*/
|
||||||
|
Results Detect(int width, int height, int stride, uint8_t* buf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect tags from an 8-bit image.
|
||||||
|
*
|
||||||
|
* @param width width of the image
|
||||||
|
* @param height height of the image
|
||||||
|
* @param buf image buffer
|
||||||
|
* @return Results (array of AprilTagDetection pointers)
|
||||||
|
*/
|
||||||
|
Results Detect(int width, int height, uint8_t* buf) {
|
||||||
|
return Detect(width, height, width, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Destroy();
|
||||||
|
void DestroyFamilies();
|
||||||
|
void DestroyFamily(std::string_view name, void* data);
|
||||||
|
|
||||||
|
void* m_impl;
|
||||||
|
wpi::StringMap<void*> m_families;
|
||||||
|
units::radian_t m_qtpCriticalAngle = 10_deg;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <opencv2/core/mat.hpp>
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagDetector.h"
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
|
||||||
|
inline AprilTagDetector::Results AprilTagDetect(AprilTagDetector& detector,
|
||||||
|
cv::Mat& image) {
|
||||||
|
return detector.Detect(image.cols, image.rows, image.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <units/length.h>
|
||||||
|
#include <wpi/SymbolExports.h>
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTag.h"
|
||||||
|
#include "frc/geometry/Pose3d.h"
|
||||||
|
|
||||||
|
namespace wpi {
|
||||||
|
class json;
|
||||||
|
} // namespace wpi
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
/**
|
||||||
|
* Class for representing a layout of AprilTags on a field and reading them from
|
||||||
|
* a JSON format.
|
||||||
|
*
|
||||||
|
* The JSON format contains two top-level objects, "tags" and "field".
|
||||||
|
* The "tags" object is a list of all AprilTags contained within a layout. Each
|
||||||
|
* AprilTag serializes to a JSON object containing an ID and a Pose3d. The
|
||||||
|
* "field" object is a descriptor of the size of the field in meters with
|
||||||
|
* "width" and "length" values. This is to account for arbitrary field sizes
|
||||||
|
* when transforming the poses.
|
||||||
|
*
|
||||||
|
* Pose3ds in the JSON are measured using the normal FRC coordinate system, NWU
|
||||||
|
* with the origin at the bottom-right corner of the blue alliance wall.
|
||||||
|
* SetOrigin(OriginPosition) can be used to change the poses returned from
|
||||||
|
* GetTagPose(int) to be from the perspective of a specific alliance.
|
||||||
|
*
|
||||||
|
* Tag poses represent the center of the tag, with a zero rotation representing
|
||||||
|
* a tag that is upright and facing away from the (blue) alliance wall (that is,
|
||||||
|
* towards the opposing alliance). */
|
||||||
|
class WPILIB_DLLEXPORT AprilTagFieldLayout {
|
||||||
|
public:
|
||||||
|
enum class OriginPosition {
|
||||||
|
kBlueAllianceWallRightSide,
|
||||||
|
kRedAllianceWallRightSide,
|
||||||
|
};
|
||||||
|
|
||||||
|
AprilTagFieldLayout() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new AprilTagFieldLayout with values imported from a JSON file.
|
||||||
|
*
|
||||||
|
* @param path Path of the JSON file to import from.
|
||||||
|
*/
|
||||||
|
explicit AprilTagFieldLayout(std::string_view path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new AprilTagFieldLayout from a vector of AprilTag objects.
|
||||||
|
*
|
||||||
|
* @param apriltags Vector of AprilTags.
|
||||||
|
* @param fieldLength Length of field the layout is representing.
|
||||||
|
* @param fieldWidth Width of field the layout is representing.
|
||||||
|
*/
|
||||||
|
AprilTagFieldLayout(std::vector<AprilTag> apriltags,
|
||||||
|
units::meter_t fieldLength, units::meter_t fieldWidth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the origin based on a predefined enumeration of coordinate frame
|
||||||
|
* origins. The origins are calculated from the field dimensions.
|
||||||
|
*
|
||||||
|
* This transforms the Pose3ds returned by GetTagPose(int) to return the
|
||||||
|
* correct pose relative to a predefined coordinate frame.
|
||||||
|
*
|
||||||
|
* @param origin The predefined origin
|
||||||
|
*/
|
||||||
|
void SetOrigin(OriginPosition origin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the origin for tag pose transformation.
|
||||||
|
*
|
||||||
|
* This tranforms the Pose3ds returned by GetTagPose(int) to return the
|
||||||
|
* correct pose relative to the provided origin.
|
||||||
|
*
|
||||||
|
* @param origin The new origin for tag transformations
|
||||||
|
*/
|
||||||
|
void SetOrigin(const Pose3d& origin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an AprilTag pose by its ID.
|
||||||
|
*
|
||||||
|
* @param ID The ID of the tag.
|
||||||
|
* @return The pose corresponding to the ID that was passed in or an empty
|
||||||
|
* optional if a tag with that ID is not found.
|
||||||
|
*/
|
||||||
|
std::optional<Pose3d> GetTagPose(int ID) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes an AprilTagFieldLayout to a JSON file.
|
||||||
|
*
|
||||||
|
* @param path The path to write the JSON file to.
|
||||||
|
*/
|
||||||
|
void Serialize(std::string_view path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks equality between this AprilTagFieldLayout and another object.
|
||||||
|
*/
|
||||||
|
bool operator==(const AprilTagFieldLayout&) const = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<int, AprilTag> m_apriltags;
|
||||||
|
units::meter_t m_fieldLength;
|
||||||
|
units::meter_t m_fieldWidth;
|
||||||
|
Pose3d m_origin;
|
||||||
|
|
||||||
|
friend WPILIB_DLLEXPORT void to_json(wpi::json& json,
|
||||||
|
const AprilTagFieldLayout& layout);
|
||||||
|
|
||||||
|
friend WPILIB_DLLEXPORT void from_json(const wpi::json& json,
|
||||||
|
AprilTagFieldLayout& layout);
|
||||||
|
};
|
||||||
|
|
||||||
|
WPILIB_DLLEXPORT
|
||||||
|
void to_json(wpi::json& json, const AprilTagFieldLayout& layout);
|
||||||
|
|
||||||
|
WPILIB_DLLEXPORT
|
||||||
|
void from_json(const wpi::json& json, AprilTagFieldLayout& layout);
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <wpi/SymbolExports.h>
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagFieldLayout.h"
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
|
||||||
|
enum class AprilTagField {
|
||||||
|
k2022RapidReact,
|
||||||
|
k2023ChargedUp,
|
||||||
|
|
||||||
|
// This is a placeholder for denoting the last supported field. This should
|
||||||
|
// always be the last entry in the enum and should not be used by users
|
||||||
|
kNumFields,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an AprilTagFieldLayout from a predefined field
|
||||||
|
*
|
||||||
|
* @param field The predefined field
|
||||||
|
*/
|
||||||
|
WPILIB_DLLEXPORT AprilTagFieldLayout
|
||||||
|
LoadAprilTagLayoutField(AprilTagField field);
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wpi/SymbolExports.h>
|
||||||
|
|
||||||
|
#include "frc/geometry/Transform3d.h"
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
|
||||||
|
/** A pair of AprilTag pose estimates. */
|
||||||
|
struct WPILIB_DLLEXPORT AprilTagPoseEstimate {
|
||||||
|
/** Pose 1. */
|
||||||
|
Transform3d pose1;
|
||||||
|
|
||||||
|
/** Pose 2. */
|
||||||
|
Transform3d pose2;
|
||||||
|
|
||||||
|
/** Object-space error of pose 1. */
|
||||||
|
double error1;
|
||||||
|
|
||||||
|
/** Object-space error of pose 2. */
|
||||||
|
double error2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ratio of pose reprojection errors, called ambiguity. Numbers
|
||||||
|
* above 0.2 are likely to be ambiguous.
|
||||||
|
*
|
||||||
|
* @return The ratio of pose reprojection errors.
|
||||||
|
*/
|
||||||
|
double GetAmbiguity() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include <units/length.h>
|
||||||
|
#include <wpi/SymbolExports.h>
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagPoseEstimate.h"
|
||||||
|
#include "frc/geometry/Transform3d.h"
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
|
||||||
|
class AprilTagDetection;
|
||||||
|
|
||||||
|
/** Pose estimators for AprilTag tags. */
|
||||||
|
class WPILIB_DLLEXPORT AprilTagPoseEstimator {
|
||||||
|
public:
|
||||||
|
/** Configuration for the pose estimator. */
|
||||||
|
struct Config {
|
||||||
|
bool operator==(const Config&) const = default;
|
||||||
|
|
||||||
|
/** The tag size. */
|
||||||
|
units::meter_t tagSize;
|
||||||
|
|
||||||
|
/** Camera horizontal focal length, in pixels. */
|
||||||
|
double fx;
|
||||||
|
|
||||||
|
/** Camera vertical focal length, in pixels. */
|
||||||
|
double fy;
|
||||||
|
|
||||||
|
/** Camera horizontal focal center, in pixels. */
|
||||||
|
double cx;
|
||||||
|
|
||||||
|
/** Camera vertical focal center, in pixels. */
|
||||||
|
double cy;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates estimator.
|
||||||
|
*
|
||||||
|
* @param config Configuration
|
||||||
|
*/
|
||||||
|
explicit AprilTagPoseEstimator(const Config& config) : m_config{config} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets estimator configuration.
|
||||||
|
*
|
||||||
|
* @param config Configuration
|
||||||
|
*/
|
||||||
|
void SetConfig(const Config& config) { m_config = config; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets estimator configuration.
|
||||||
|
*
|
||||||
|
* @return Configuration
|
||||||
|
*/
|
||||||
|
const Config& GetConfig() const { return m_config; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the pose of the tag using the homography method described in [1].
|
||||||
|
*
|
||||||
|
* @param detection Tag detection
|
||||||
|
* @return Pose estimate
|
||||||
|
*/
|
||||||
|
Transform3d EstimateHomography(const AprilTagDetection& detection) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the pose of the tag using the homography method described in [1].
|
||||||
|
*
|
||||||
|
* @param homography Homography 3x3 matrix data
|
||||||
|
* @return Pose estimate
|
||||||
|
*/
|
||||||
|
Transform3d EstimateHomography(std::span<const double, 9> homography) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the pose of the tag. This returns one or two possible poses for
|
||||||
|
* the tag, along with the object-space error of each.
|
||||||
|
*
|
||||||
|
* This uses the homography method described in [1] for the initial estimate.
|
||||||
|
* Then Orthogonal Iteration [2] is used to refine this estimate. Then [3] is
|
||||||
|
* used to find a potential second local minima and Orthogonal Iteration is
|
||||||
|
* used to refine this second estimate.
|
||||||
|
*
|
||||||
|
* [1]: E. Olson, “Apriltag: A robust and flexible visual fiducial system,” in
|
||||||
|
* 2011 IEEE International Conference on Robotics and Automation,
|
||||||
|
* May 2011, pp. 3400–3407.
|
||||||
|
* [2]: Lu, G. D. Hager and E. Mjolsness, "Fast and globally convergent pose
|
||||||
|
* estimation from video images," in IEEE Transactions on Pattern
|
||||||
|
* Analysis and Machine Intelligence, vol. 22, no. 6, pp. 610-622, June 2000.
|
||||||
|
* doi: 10.1109/34.862199
|
||||||
|
* [3]: Schweighofer and A. Pinz, "Robust Pose Estimation from a Planar
|
||||||
|
* Target," in IEEE Transactions on Pattern Analysis and Machine Intelligence,
|
||||||
|
* vol. 28, no. 12, pp. 2024-2030, Dec. 2006. doi: 10.1109/TPAMI.2006.252
|
||||||
|
*
|
||||||
|
* @param detection Tag detection
|
||||||
|
* @param nIters Number of iterations
|
||||||
|
* @return Initial and (possibly) second pose estimates
|
||||||
|
*/
|
||||||
|
AprilTagPoseEstimate EstimateOrthogonalIteration(
|
||||||
|
const AprilTagDetection& detection, int nIters) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the pose of the tag. This returns one or two possible poses for
|
||||||
|
* the tag, along with the object-space error of each.
|
||||||
|
*
|
||||||
|
* @param homography Homography 3x3 matrix data
|
||||||
|
* @param corners Corner point array (X and Y for each corner in order)
|
||||||
|
* @param nIters Number of iterations
|
||||||
|
* @return Initial and (possibly) second pose estimates
|
||||||
|
*/
|
||||||
|
AprilTagPoseEstimate EstimateOrthogonalIteration(
|
||||||
|
std::span<const double, 9> homography, std::span<const double, 8> corners,
|
||||||
|
int nIters) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates tag pose. This method is an easier to use interface to
|
||||||
|
* EstimatePoseOrthogonalIteration(), running 50 iterations and returning the
|
||||||
|
* pose with the lower object-space error.
|
||||||
|
*
|
||||||
|
* @param detection Tag detection
|
||||||
|
* @return Pose estimate
|
||||||
|
*/
|
||||||
|
Transform3d Estimate(const AprilTagDetection& detection) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates tag pose. This method is an easier to use interface to
|
||||||
|
* EstimatePoseOrthogonalIteration(), running 50 iterations and returning the
|
||||||
|
* pose with the lower object-space error.
|
||||||
|
*
|
||||||
|
* @param homography Homography 3x3 matrix data
|
||||||
|
* @param corners Corner point array (X and Y for each corner in order)
|
||||||
|
* @return Pose estimate
|
||||||
|
*/
|
||||||
|
Transform3d Estimate(std::span<const double, 9> homography,
|
||||||
|
std::span<const double, 8> corners) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Config m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
@@ -0,0 +1,415 @@
|
|||||||
|
{
|
||||||
|
"tags" : [ {
|
||||||
|
"ID" : 0,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : -0.0035306,
|
||||||
|
"y" : 7.578928199999999,
|
||||||
|
"z" : 0.8858503999999999
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 1.0,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 1,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 3.2327088,
|
||||||
|
"y" : 5.486654,
|
||||||
|
"z" : 1.7254728
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 1.0,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 2,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 3.067812,
|
||||||
|
"y" : 5.3305202,
|
||||||
|
"z" : 1.3762228
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.7071067811865476,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : -0.7071067811865475
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 3,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 0.0039878,
|
||||||
|
"y" : 5.058536999999999,
|
||||||
|
"z" : 0.80645
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 1.0,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 4,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 0.0039878,
|
||||||
|
"y" : 3.5124898,
|
||||||
|
"z" : 0.80645
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 1.0,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 5,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 0.12110719999999998,
|
||||||
|
"y" : 1.7178274,
|
||||||
|
"z" : 0.8906002000000001
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.9196502204050923,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.39273842708457407
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 6,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 0.8733027999999999,
|
||||||
|
"y" : 0.9412985999999999,
|
||||||
|
"z" : 0.8906002000000001
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.9196502204050923,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.39273842708457407
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 7,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 1.6150844,
|
||||||
|
"y" : 0.15725139999999999,
|
||||||
|
"z" : 0.8906002000000001
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.9196502204050923,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.39273842708457407
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 10,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 16.4627306,
|
||||||
|
"y" : 0.6506718,
|
||||||
|
"z" : 0.8858503999999999
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 6.123233995736766E-17,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 11,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 13.2350002,
|
||||||
|
"y" : 2.743454,
|
||||||
|
"z" : 1.7254728
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 6.123233995736766E-17,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 12,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 13.391388000000001,
|
||||||
|
"y" : 2.8998418,
|
||||||
|
"z" : 1.3762228
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.7071067811865476,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.7071067811865475
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 13,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 16.4552122,
|
||||||
|
"y" : 3.1755079999999998,
|
||||||
|
"z" : 0.80645
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 6.123233995736766E-17,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 14,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 16.4552122,
|
||||||
|
"y" : 4.7171356,
|
||||||
|
"z" : 0.80645
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 6.123233995736766E-17,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 15,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 16.3350194,
|
||||||
|
"y" : 6.5149729999999995,
|
||||||
|
"z" : 0.8937752
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : -0.37298778257580906,
|
||||||
|
"X" : -0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.9278362538989199
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 16,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 15.5904946,
|
||||||
|
"y" : 7.292695599999999,
|
||||||
|
"z" : 0.8906002000000001
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : -0.37298778257580906,
|
||||||
|
"X" : -0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.9278362538989199
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 17,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 14.847188999999998,
|
||||||
|
"y" : 8.0691228,
|
||||||
|
"z" : 0.8906002000000001
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : -0.37298778257580906,
|
||||||
|
"X" : -0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.9278362538989199
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 40,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 7.874127,
|
||||||
|
"y" : 4.9131728,
|
||||||
|
"z" : 0.7032752
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.5446390350150271,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.838670567945424
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 41,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 7.4312271999999995,
|
||||||
|
"y" : 3.759327,
|
||||||
|
"z" : 0.7032752
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : -0.20791169081775934,
|
||||||
|
"X" : -0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.9781476007338057
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 42,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 8.585073,
|
||||||
|
"y" : 3.3164272,
|
||||||
|
"z" : 0.7032752
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.838670567945424,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : -0.5446390350150271
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 43,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 9.0279728,
|
||||||
|
"y" : 4.470273,
|
||||||
|
"z" : 0.7032752
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.9781476007338057,
|
||||||
|
"X" : 0.0,
|
||||||
|
"Y" : 0.0,
|
||||||
|
"Z" : 0.20791169081775934
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 50,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 7.6790296,
|
||||||
|
"y" : 4.3261534,
|
||||||
|
"z" : 2.4177244
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.17729273396782605,
|
||||||
|
"X" : -0.22744989571511945,
|
||||||
|
"Y" : 0.04215534644161733,
|
||||||
|
"Z" : 0.9565859910053995
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 51,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 8.0182466,
|
||||||
|
"y" : 3.5642296,
|
||||||
|
"z" : 2.4177244
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : -0.5510435465842192,
|
||||||
|
"X" : -0.19063969497246985,
|
||||||
|
"Y" : -0.13102303230819815,
|
||||||
|
"Z" : 0.8017733354717242
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 52,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 8.7801704,
|
||||||
|
"y" : 3.9034466,
|
||||||
|
"z" : 2.4177244
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : -0.9565859910053994,
|
||||||
|
"X" : -0.04215534644161739,
|
||||||
|
"Y" : -0.22744989571511942,
|
||||||
|
"Z" : 0.17729273396782633
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"ID" : 53,
|
||||||
|
"pose" : {
|
||||||
|
"translation" : {
|
||||||
|
"x" : 8.4409534,
|
||||||
|
"y" : 4.6653704,
|
||||||
|
"z" : 2.4177244
|
||||||
|
},
|
||||||
|
"rotation" : {
|
||||||
|
"quaternion" : {
|
||||||
|
"W" : 0.8017733354717241,
|
||||||
|
"X" : -0.1310230323081982,
|
||||||
|
"Y" : 0.19063969497246983,
|
||||||
|
"Z" : 0.5510435465842194
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
"field" : {
|
||||||
|
"length" : 16.4592,
|
||||||
|
"width" : 8.2296
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
{
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"ID": 1,
|
||||||
|
"pose": {
|
||||||
|
"translation": {
|
||||||
|
"x": 15.513558,
|
||||||
|
"y": 1.071626,
|
||||||
|
"z": 0.462788
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"quaternion": {
|
||||||
|
"W": 0.0,
|
||||||
|
"X": 0.0,
|
||||||
|
"Y": 0.0,
|
||||||
|
"Z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": 2,
|
||||||
|
"pose": {
|
||||||
|
"translation": {
|
||||||
|
"x": 15.513558,
|
||||||
|
"y": 2.748026,
|
||||||
|
"z": 0.462788
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"quaternion": {
|
||||||
|
"W": 0.0,
|
||||||
|
"X": 0.0,
|
||||||
|
"Y": 0.0,
|
||||||
|
"Z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": 3,
|
||||||
|
"pose": {
|
||||||
|
"translation": {
|
||||||
|
"x": 15.513558,
|
||||||
|
"y": 4.424426,
|
||||||
|
"z": 0.462788
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"quaternion": {
|
||||||
|
"W": 0.0,
|
||||||
|
"X": 0.0,
|
||||||
|
"Y": 0.0,
|
||||||
|
"Z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": 4,
|
||||||
|
"pose": {
|
||||||
|
"translation": {
|
||||||
|
"x": 16.178784,
|
||||||
|
"y": 6.749796,
|
||||||
|
"z": 0.695452
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"quaternion": {
|
||||||
|
"W": 0.0,
|
||||||
|
"X": 0.0,
|
||||||
|
"Y": 0.0,
|
||||||
|
"Z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": 5,
|
||||||
|
"pose": {
|
||||||
|
"translation": {
|
||||||
|
"x": 0.36195,
|
||||||
|
"y": 6.749796,
|
||||||
|
"z": 0.695452
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"quaternion": {
|
||||||
|
"W": 1.0,
|
||||||
|
"X": 0.0,
|
||||||
|
"Y": 0.0,
|
||||||
|
"Z": 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": 6,
|
||||||
|
"pose": {
|
||||||
|
"translation": {
|
||||||
|
"x": 1.02743,
|
||||||
|
"y": 4.424426,
|
||||||
|
"z": 0.462788
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"quaternion": {
|
||||||
|
"W": 1.0,
|
||||||
|
"X": 0.0,
|
||||||
|
"Y": 0.0,
|
||||||
|
"Z": 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": 7,
|
||||||
|
"pose": {
|
||||||
|
"translation": {
|
||||||
|
"x": 1.02743,
|
||||||
|
"y": 2.748026,
|
||||||
|
"z": 0.462788
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"quaternion": {
|
||||||
|
"W": 1.0,
|
||||||
|
"X": 0.0,
|
||||||
|
"Y": 0.0,
|
||||||
|
"Z": 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": 8,
|
||||||
|
"pose": {
|
||||||
|
"translation": {
|
||||||
|
"x": 1.02743,
|
||||||
|
"y": 1.071626,
|
||||||
|
"z": 0.462788
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"quaternion": {
|
||||||
|
"W": 1.0,
|
||||||
|
"X": 0.0,
|
||||||
|
"Y": 0.0,
|
||||||
|
"Z": 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"field": {
|
||||||
|
"length": 16.54175,
|
||||||
|
"width": 8.0137
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
import edu.wpi.first.math.geometry.Transform3d;
|
||||||
|
import edu.wpi.first.math.util.Units;
|
||||||
|
import edu.wpi.first.util.RuntimeLoader;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.MutableStaticState")
|
||||||
|
class AprilTagDetectorTest {
|
||||||
|
@SuppressWarnings("MemberName")
|
||||||
|
AprilTagDetector detector;
|
||||||
|
|
||||||
|
static RuntimeLoader<Core> loader;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
try {
|
||||||
|
loader =
|
||||||
|
new RuntimeLoader<>(
|
||||||
|
Core.NATIVE_LIBRARY_NAME, RuntimeLoader.getDefaultExtractionRoot(), Core.class);
|
||||||
|
loader.loadLibrary();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
fail(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach() {
|
||||||
|
detector = new AprilTagDetector();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void afterEach() {
|
||||||
|
detector.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testConfigDefaults() {
|
||||||
|
var config = detector.getConfig();
|
||||||
|
assertEquals(new AprilTagDetector.Config(), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testQtpDefaults() {
|
||||||
|
var params = detector.getQuadThresholdParameters();
|
||||||
|
assertEquals(new AprilTagDetector.QuadThresholdParameters(), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSetConfigNumThreads() {
|
||||||
|
var newConfig = new AprilTagDetector.Config();
|
||||||
|
newConfig.numThreads = 2;
|
||||||
|
detector.setConfig(newConfig);
|
||||||
|
var config = detector.getConfig();
|
||||||
|
assertEquals(2, config.numThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testQtpMinClusterPixels() {
|
||||||
|
var newParams = new AprilTagDetector.QuadThresholdParameters();
|
||||||
|
newParams.minClusterPixels = 8;
|
||||||
|
detector.setQuadThresholdParameters(newParams);
|
||||||
|
var params = detector.getQuadThresholdParameters();
|
||||||
|
assertEquals(8, params.minClusterPixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAdd16h5() {
|
||||||
|
assertDoesNotThrow(() -> detector.addFamily("tag16h5"));
|
||||||
|
// duplicate addition is also okay
|
||||||
|
assertDoesNotThrow(() -> detector.addFamily("tag16h5"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAdd25h9() {
|
||||||
|
assertDoesNotThrow(() -> detector.addFamily("tag25h9"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAdd36h11() {
|
||||||
|
assertDoesNotThrow(() -> detector.addFamily("tag36h11"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAddMultiple() {
|
||||||
|
assertDoesNotThrow(() -> detector.addFamily("tag16h5"));
|
||||||
|
assertDoesNotThrow(() -> detector.addFamily("tag36h11"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRemoveFamily() {
|
||||||
|
// okay to remove non-existent family
|
||||||
|
detector.removeFamily("tag16h5");
|
||||||
|
|
||||||
|
// add and remove
|
||||||
|
detector.addFamily("tag16h5");
|
||||||
|
detector.removeFamily("tag16h5");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.AssignmentInOperand")
|
||||||
|
public Mat loadImage(String resource) throws IOException {
|
||||||
|
Mat encoded;
|
||||||
|
try (InputStream is = getClass().getResource(resource).openStream()) {
|
||||||
|
try (ByteArrayOutputStream os = new ByteArrayOutputStream(is.available())) {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = is.read(buffer)) != -1) {
|
||||||
|
os.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
encoded = new Mat(1, os.size(), CvType.CV_8U);
|
||||||
|
encoded.put(0, 0, os.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mat image = Imgcodecs.imdecode(encoded, Imgcodecs.IMREAD_COLOR);
|
||||||
|
encoded.release();
|
||||||
|
Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDecodeAndPose() {
|
||||||
|
detector.addFamily("tag16h5");
|
||||||
|
detector.addFamily("tag36h11");
|
||||||
|
|
||||||
|
Mat image;
|
||||||
|
try {
|
||||||
|
image = loadImage("tag1_640_480.jpg");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
fail(ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
AprilTagDetection[] results = detector.detect(image);
|
||||||
|
assertEquals(1, results.length);
|
||||||
|
assertEquals("tag36h11", results[0].getFamily());
|
||||||
|
assertEquals(1, results[0].getId());
|
||||||
|
assertEquals(0, results[0].getHamming());
|
||||||
|
|
||||||
|
var estimator =
|
||||||
|
new AprilTagPoseEstimator(new AprilTagPoseEstimator.Config(0.2, 500, 500, 320, 240));
|
||||||
|
AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 50);
|
||||||
|
assertEquals(new Transform3d(), est.pose2);
|
||||||
|
Transform3d pose = estimator.estimate(results[0]);
|
||||||
|
assertEquals(est.pose1, pose);
|
||||||
|
} finally {
|
||||||
|
image.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This tag is rotated such that the top is closer to the camera than the bottom. In the camera
|
||||||
|
* frame, with +x to the right, this is a rotation about +X by 45 degrees.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void testPoseRotatedX() {
|
||||||
|
detector.addFamily("tag16h5");
|
||||||
|
|
||||||
|
Mat image;
|
||||||
|
try {
|
||||||
|
image = loadImage("tag2_45deg_X.png");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
fail(ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
AprilTagDetection[] results = detector.detect(image);
|
||||||
|
assertEquals(1, results.length);
|
||||||
|
|
||||||
|
var estimator =
|
||||||
|
new AprilTagPoseEstimator(
|
||||||
|
new AprilTagPoseEstimator.Config(
|
||||||
|
0.2, 500, 500, image.cols() / 2.0, image.rows() / 2.0));
|
||||||
|
AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 50);
|
||||||
|
|
||||||
|
assertEquals(Units.degreesToRadians(45), est.pose1.getRotation().getX(), 0.1);
|
||||||
|
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getY(), 0.1);
|
||||||
|
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getZ(), 0.1);
|
||||||
|
} finally {
|
||||||
|
image.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This tag is rotated such that the right is closer to the camera than the left. In the camera
|
||||||
|
* frame, with +y down, this is a rotation of 45 degrees about +y.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void testPoseRotatedY() {
|
||||||
|
detector.addFamily("tag16h5");
|
||||||
|
|
||||||
|
Mat image;
|
||||||
|
try {
|
||||||
|
image = loadImage("tag2_45deg_y.png");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
fail(ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
AprilTagDetection[] results = detector.detect(image);
|
||||||
|
assertEquals(1, results.length);
|
||||||
|
|
||||||
|
var estimator =
|
||||||
|
new AprilTagPoseEstimator(
|
||||||
|
new AprilTagPoseEstimator.Config(
|
||||||
|
0.2, 500, 500, image.cols() / 2.0, image.rows() / 2.0));
|
||||||
|
AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 50);
|
||||||
|
|
||||||
|
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getX(), 0.1);
|
||||||
|
assertEquals(Units.degreesToRadians(45), est.pose1.getRotation().getY(), 0.1);
|
||||||
|
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getZ(), 0.1);
|
||||||
|
} finally {
|
||||||
|
image.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This tag is facing right at the camera -- no rotation should be observed. */
|
||||||
|
@Test
|
||||||
|
void testPoseStraightOn() {
|
||||||
|
detector.addFamily("tag16h5");
|
||||||
|
|
||||||
|
Mat image;
|
||||||
|
try {
|
||||||
|
image = loadImage("tag2_16h5_straight.png");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
fail(ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
AprilTagDetection[] results = detector.detect(image);
|
||||||
|
assertEquals(1, results.length);
|
||||||
|
|
||||||
|
var estimator =
|
||||||
|
new AprilTagPoseEstimator(
|
||||||
|
new AprilTagPoseEstimator.Config(
|
||||||
|
0.2, 500, 500, image.cols() / 2.0, image.rows() / 2.0));
|
||||||
|
AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 50);
|
||||||
|
|
||||||
|
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getX(), 0.1);
|
||||||
|
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getY(), 0.1);
|
||||||
|
assertEquals(Units.degreesToRadians(0), est.pose1.getRotation().getZ(), 0.1);
|
||||||
|
} finally {
|
||||||
|
image.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import edu.wpi.first.math.geometry.Pose3d;
|
||||||
|
import edu.wpi.first.math.geometry.Rotation3d;
|
||||||
|
import edu.wpi.first.math.geometry.Translation3d;
|
||||||
|
import edu.wpi.first.math.util.Units;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class AprilTagPoseSetOriginTest {
|
||||||
|
@Test
|
||||||
|
void transformationMatches() {
|
||||||
|
var layout =
|
||||||
|
new AprilTagFieldLayout(
|
||||||
|
List.of(
|
||||||
|
new AprilTag(1, new Pose3d(new Translation3d(0, 0, 0), new Rotation3d(0, 0, 0))),
|
||||||
|
new AprilTag(
|
||||||
|
2,
|
||||||
|
new Pose3d(
|
||||||
|
new Translation3d(
|
||||||
|
Units.feetToMeters(4.0), Units.feetToMeters(4), Units.feetToMeters(4)),
|
||||||
|
new Rotation3d(0, 0, Units.degreesToRadians(180))))),
|
||||||
|
Units.feetToMeters(54.0),
|
||||||
|
Units.feetToMeters(27.0));
|
||||||
|
layout.setOrigin(AprilTagFieldLayout.OriginPosition.kRedAllianceWallRightSide);
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
new Pose3d(
|
||||||
|
new Translation3d(Units.feetToMeters(54.0), Units.feetToMeters(27.0), 0.0),
|
||||||
|
new Rotation3d(0.0, 0.0, Units.degreesToRadians(180.0))),
|
||||||
|
layout.getTagPose(1).orElse(null));
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
new Pose3d(
|
||||||
|
new Translation3d(
|
||||||
|
Units.feetToMeters(50.0), Units.feetToMeters(23.0), Units.feetToMeters(4)),
|
||||||
|
new Rotation3d(0.0, 0.0, 0)),
|
||||||
|
layout.getTagPose(2).orElse(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import edu.wpi.first.math.geometry.Pose3d;
|
||||||
|
import edu.wpi.first.math.geometry.Rotation3d;
|
||||||
|
import edu.wpi.first.math.util.Units;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class AprilTagSerializationTest {
|
||||||
|
@Test
|
||||||
|
void deserializeMatches() {
|
||||||
|
var layout =
|
||||||
|
new AprilTagFieldLayout(
|
||||||
|
List.of(
|
||||||
|
new AprilTag(1, new Pose3d(0, 0, 0, new Rotation3d(0, 0, 0))),
|
||||||
|
new AprilTag(3, new Pose3d(0, 1, 0, new Rotation3d(0, 0, 0)))),
|
||||||
|
Units.feetToMeters(54.0),
|
||||||
|
Units.feetToMeters(27.0));
|
||||||
|
|
||||||
|
var objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
var deserialized =
|
||||||
|
assertDoesNotThrow(
|
||||||
|
() ->
|
||||||
|
objectMapper.readValue(
|
||||||
|
objectMapper.writeValueAsString(layout), AprilTagFieldLayout.class));
|
||||||
|
|
||||||
|
assertEquals(layout, deserialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi.first.apriltag;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import edu.wpi.first.math.geometry.Pose3d;
|
||||||
|
import edu.wpi.first.math.geometry.Rotation3d;
|
||||||
|
import edu.wpi.first.math.util.Units;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
|
|
||||||
|
class LoadConfigTest {
|
||||||
|
@ParameterizedTest
|
||||||
|
@EnumSource(AprilTagFields.class)
|
||||||
|
void testLoad(AprilTagFields field) {
|
||||||
|
AprilTagFieldLayout layout =
|
||||||
|
Assertions.assertDoesNotThrow(
|
||||||
|
() -> AprilTagFieldLayout.loadFromResource(field.m_resourceFile));
|
||||||
|
assertNotNull(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test2022RapidReact() throws IOException {
|
||||||
|
AprilTagFieldLayout layout =
|
||||||
|
AprilTagFieldLayout.loadFromResource(AprilTagFields.k2022RapidReact.m_resourceFile);
|
||||||
|
|
||||||
|
// Blue Hangar Truss - Hub
|
||||||
|
Pose3d expectedPose =
|
||||||
|
new Pose3d(
|
||||||
|
Units.inchesToMeters(127.272),
|
||||||
|
Units.inchesToMeters(216.01),
|
||||||
|
Units.inchesToMeters(67.932),
|
||||||
|
new Rotation3d(0, 0, 0));
|
||||||
|
Optional<Pose3d> maybePose = layout.getTagPose(1);
|
||||||
|
assertTrue(maybePose.isPresent());
|
||||||
|
assertEquals(expectedPose, maybePose.get());
|
||||||
|
|
||||||
|
// Blue Terminal Near Station
|
||||||
|
expectedPose =
|
||||||
|
new Pose3d(
|
||||||
|
Units.inchesToMeters(4.768),
|
||||||
|
Units.inchesToMeters(67.631),
|
||||||
|
Units.inchesToMeters(35.063),
|
||||||
|
new Rotation3d(0, 0, Math.toRadians(46.25)));
|
||||||
|
maybePose = layout.getTagPose(5);
|
||||||
|
assertTrue(maybePose.isPresent());
|
||||||
|
assertEquals(expectedPose, maybePose.get());
|
||||||
|
|
||||||
|
// Upper Hub Blue-Near
|
||||||
|
expectedPose =
|
||||||
|
new Pose3d(
|
||||||
|
Units.inchesToMeters(332.321),
|
||||||
|
Units.inchesToMeters(183.676),
|
||||||
|
Units.inchesToMeters(95.186),
|
||||||
|
new Rotation3d(0, Math.toRadians(26.75), Math.toRadians(69)));
|
||||||
|
maybePose = layout.getTagPose(53);
|
||||||
|
assertTrue(maybePose.isPresent());
|
||||||
|
assertEquals(expectedPose, maybePose.get());
|
||||||
|
|
||||||
|
// Doesn't exist
|
||||||
|
maybePose = layout.getTagPose(54);
|
||||||
|
assertFalse(maybePose.isPresent());
|
||||||
|
}
|
||||||
|
}
|
||||||
67
apriltag/src/test/native/cpp/AprilTagDetectorTest.cpp
Normal file
67
apriltag/src/test/native/cpp/AprilTagDetectorTest.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagDetector.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
TEST(AprilTagDetectorTest, ConfigDefaults) {
|
||||||
|
AprilTagDetector detector;
|
||||||
|
auto config = detector.GetConfig();
|
||||||
|
ASSERT_EQ(config, AprilTagDetector::Config{});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AprilTagDetectorTest, QtpDefaults) {
|
||||||
|
AprilTagDetector detector;
|
||||||
|
auto params = detector.GetQuadThresholdParameters();
|
||||||
|
ASSERT_EQ(params, AprilTagDetector::QuadThresholdParameters{});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AprilTagDetectorTest, SetConfigNumThreads) {
|
||||||
|
AprilTagDetector detector;
|
||||||
|
detector.SetConfig({.numThreads = 2});
|
||||||
|
auto config = detector.GetConfig();
|
||||||
|
ASSERT_EQ(config.numThreads, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AprilTagDetectorTest, QtpMinClusterPixels) {
|
||||||
|
AprilTagDetector detector;
|
||||||
|
detector.SetQuadThresholdParameters({.minClusterPixels = 8});
|
||||||
|
auto params = detector.GetQuadThresholdParameters();
|
||||||
|
ASSERT_EQ(params.minClusterPixels, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AprilTagDetectorTest, Add16h5) {
|
||||||
|
AprilTagDetector detector;
|
||||||
|
ASSERT_TRUE(detector.AddFamily("tag16h5"));
|
||||||
|
// duplicate addition is also okay
|
||||||
|
ASSERT_TRUE(detector.AddFamily("tag16h5"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AprilTagDetectorTest, Add25h9) {
|
||||||
|
AprilTagDetector detector;
|
||||||
|
ASSERT_TRUE(detector.AddFamily("tag25h9"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AprilTagDetectorTest, Add36h11) {
|
||||||
|
AprilTagDetector detector;
|
||||||
|
ASSERT_TRUE(detector.AddFamily("tag36h11"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AprilTagDetectorTest, AddMultiple) {
|
||||||
|
AprilTagDetector detector;
|
||||||
|
ASSERT_TRUE(detector.AddFamily("tag16h5"));
|
||||||
|
ASSERT_TRUE(detector.AddFamily("tag36h11"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AprilTagDetectorTest, RemoveFamily) {
|
||||||
|
AprilTagDetector detector;
|
||||||
|
// okay to remove non-existent family
|
||||||
|
detector.RemoveFamily("tag16h5");
|
||||||
|
|
||||||
|
// add and remove
|
||||||
|
detector.AddFamily("tag16h5");
|
||||||
|
detector.RemoveFamily("tag16h5");
|
||||||
|
}
|
||||||
27
apriltag/src/test/native/cpp/AprilTagJsonTest.cpp
Normal file
27
apriltag/src/test/native/cpp/AprilTagJsonTest.cpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <wpi/json.h>
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTag.h"
|
||||||
|
#include "frc/apriltag/AprilTagFieldLayout.h"
|
||||||
|
#include "frc/geometry/Pose3d.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
TEST(AprilTagJsonTest, DeserializeMatches) {
|
||||||
|
auto layout = AprilTagFieldLayout{
|
||||||
|
std::vector{
|
||||||
|
AprilTag{1, Pose3d{}},
|
||||||
|
AprilTag{3, Pose3d{0_m, 1_m, 0_m, Rotation3d{0_deg, 0_deg, 0_deg}}}},
|
||||||
|
54_ft, 27_ft};
|
||||||
|
|
||||||
|
AprilTagFieldLayout deserialized;
|
||||||
|
wpi::json json = layout;
|
||||||
|
EXPECT_NO_THROW(deserialized = json.get<AprilTagFieldLayout>());
|
||||||
|
EXPECT_EQ(layout, deserialized);
|
||||||
|
}
|
||||||
33
apriltag/src/test/native/cpp/AprilTagPoseSetOriginTest.cpp
Normal file
33
apriltag/src/test/native/cpp/AprilTagPoseSetOriginTest.cpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <wpi/json.h>
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTag.h"
|
||||||
|
#include "frc/apriltag/AprilTagFieldLayout.h"
|
||||||
|
#include "frc/geometry/Pose3d.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace frc;
|
||||||
|
|
||||||
|
TEST(AprilTagPoseSetOriginTest, TransformationMatches) {
|
||||||
|
auto layout = AprilTagFieldLayout{
|
||||||
|
std::vector<AprilTag>{
|
||||||
|
AprilTag{1,
|
||||||
|
Pose3d{0_ft, 0_ft, 0_ft, Rotation3d{0_deg, 0_deg, 0_deg}}},
|
||||||
|
AprilTag{
|
||||||
|
2, Pose3d{4_ft, 4_ft, 4_ft, Rotation3d{0_deg, 0_deg, 180_deg}}}},
|
||||||
|
54_ft, 27_ft};
|
||||||
|
|
||||||
|
layout.SetOrigin(
|
||||||
|
AprilTagFieldLayout::OriginPosition::kRedAllianceWallRightSide);
|
||||||
|
|
||||||
|
auto mirrorPose =
|
||||||
|
Pose3d{54_ft, 27_ft, 0_ft, Rotation3d{0_deg, 0_deg, 180_deg}};
|
||||||
|
EXPECT_EQ(mirrorPose, *layout.GetTagPose(1));
|
||||||
|
mirrorPose = Pose3d{50_ft, 23_ft, 4_ft, Rotation3d{0_deg, 0_deg, 0_deg}};
|
||||||
|
EXPECT_EQ(mirrorPose, *layout.GetTagPose(2));
|
||||||
|
}
|
||||||
61
apriltag/src/test/native/cpp/LoadConfigTest.cpp
Normal file
61
apriltag/src/test/native/cpp/LoadConfigTest.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "frc/apriltag/AprilTagFields.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace frc {
|
||||||
|
|
||||||
|
std::vector<AprilTagField> GetAllFields() {
|
||||||
|
std::vector<AprilTagField> output;
|
||||||
|
|
||||||
|
for (int i = 0; i < static_cast<int>(AprilTagField::kNumFields); ++i) {
|
||||||
|
output.push_back(static_cast<AprilTagField>(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AprilTagFieldsTest, TestLoad2022RapidReact) {
|
||||||
|
AprilTagFieldLayout layout =
|
||||||
|
LoadAprilTagLayoutField(AprilTagField::k2022RapidReact);
|
||||||
|
|
||||||
|
// Blue Hangar Truss - Hub
|
||||||
|
auto expectedPose =
|
||||||
|
Pose3d{127.272_in, 216.01_in, 67.932_in, Rotation3d{0_deg, 0_deg, 0_deg}};
|
||||||
|
auto maybePose = layout.GetTagPose(1);
|
||||||
|
EXPECT_TRUE(maybePose);
|
||||||
|
EXPECT_EQ(expectedPose, *maybePose);
|
||||||
|
|
||||||
|
// Blue Terminal Near Station
|
||||||
|
expectedPose = Pose3d{4.768_in, 67.631_in, 35.063_in,
|
||||||
|
Rotation3d{0_deg, 0_deg, 46.25_deg}};
|
||||||
|
maybePose = layout.GetTagPose(5);
|
||||||
|
EXPECT_TRUE(maybePose);
|
||||||
|
EXPECT_EQ(expectedPose, *maybePose);
|
||||||
|
|
||||||
|
// Upper Hub Blue-Near
|
||||||
|
expectedPose = Pose3d{332.321_in, 183.676_in, 95.186_in,
|
||||||
|
Rotation3d{0_deg, 26.75_deg, 69_deg}};
|
||||||
|
maybePose = layout.GetTagPose(53);
|
||||||
|
EXPECT_TRUE(maybePose);
|
||||||
|
EXPECT_EQ(expectedPose, *maybePose);
|
||||||
|
|
||||||
|
// Doesn't exist
|
||||||
|
maybePose = layout.GetTagPose(54);
|
||||||
|
EXPECT_FALSE(maybePose);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test all of the fields in the enum
|
||||||
|
class AllFieldsFixtureTest : public ::testing::TestWithParam<AprilTagField> {};
|
||||||
|
|
||||||
|
TEST_P(AllFieldsFixtureTest, CheckEntireEnum) {
|
||||||
|
AprilTagField field = GetParam();
|
||||||
|
EXPECT_NO_THROW(LoadAprilTagLayoutField(field));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(ValuesEnumTestInstTests, AllFieldsFixtureTest,
|
||||||
|
::testing::ValuesIn(GetAllFields()));
|
||||||
|
|
||||||
|
} // namespace frc
|
||||||
11
apriltag/src/test/native/cpp/main.cpp
Normal file
11
apriltag/src/test/native/cpp/main.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
int ret = RUN_ALL_TESTS();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -4,88 +4,89 @@ trigger:
|
|||||||
batch: true
|
batch: true
|
||||||
branches:
|
branches:
|
||||||
include:
|
include:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- stage: Build
|
- stage: Build
|
||||||
jobs:
|
jobs:
|
||||||
- job: IntegrationTests
|
- job: IntegrationTests
|
||||||
displayName: Integration Tests
|
displayName: Integration Tests
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'Ubuntu 16.04'
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
container:
|
container:
|
||||||
image: wpilib/roborio-cross-ubuntu:2020-18.04
|
image: wpilib/roborio-cross-ubuntu:2023-22.04
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
timeoutInMinutes: 0
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: Gradle@2
|
- task: Gradle@2
|
||||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
||||||
inputs:
|
inputs:
|
||||||
workingDirectory: ''
|
workingDirectory: ""
|
||||||
gradleWrapperFile: 'gradlew'
|
gradleWrapperFile: "gradlew"
|
||||||
gradleOptions: '-Xmx3072m'
|
gradleOptions: "-Xmx3072m"
|
||||||
publishJUnitResults: false
|
publishJUnitResults: false
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
testResultsFiles: "**/TEST-*.xml"
|
||||||
tasks: 'copyWpilibJIntegrationTestJarToOutput copyWpilibCTestLibrariesToOutput'
|
tasks: "copyWpilibJIntegrationTestJarToOutput copyWpilibCTestLibrariesToOutput"
|
||||||
options: '-Ponlylinuxathena -PbuildServer'
|
options: "-Ponlylinuxathena -PbuildServer -PskipJavaFormat"
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
- task: PublishPipelineArtifact@0
|
||||||
inputs:
|
inputs:
|
||||||
artifactName: 'Integration Tests'
|
artifactName: "Integration Tests"
|
||||||
targetPath: 'build/integrationTestFiles'
|
targetPath: "build/integrationTestFiles"
|
||||||
|
|
||||||
- stage: TestBench
|
- stage: TestBench
|
||||||
displayName: Test Bench
|
displayName: Test Bench
|
||||||
jobs:
|
condition: false
|
||||||
- job: Cpp
|
jobs:
|
||||||
displayName: C++
|
- job: Cpp
|
||||||
pool: RoboRioConnections
|
displayName: C++
|
||||||
timeoutInMinutes: 30
|
pool: RoboRioConnections
|
||||||
workspace:
|
timeoutInMinutes: 30
|
||||||
clean: all
|
workspace:
|
||||||
steps:
|
clean: all
|
||||||
- task: DownloadPipelineArtifact@0
|
steps:
|
||||||
inputs:
|
- task: DownloadPipelineArtifact@0
|
||||||
artifactName: 'Integration Tests'
|
inputs:
|
||||||
targetPath: 'build/integrationTestFiles'
|
artifactName: "Integration Tests"
|
||||||
|
targetPath: "build/integrationTestFiles"
|
||||||
|
|
||||||
- task: ShellScript@2
|
- task: ShellScript@2
|
||||||
displayName: Run C++ Tests
|
displayName: Run C++ Tests
|
||||||
inputs:
|
inputs:
|
||||||
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
|
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
|
||||||
args: 'cpp -A "--gtest_output=xml:/home/admin/testResults/cppreport.xml"'
|
args: 'cpp -A "--gtest_output=xml:/home/admin/testResults/cppreport.xml"'
|
||||||
|
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
displayName: Publish C++ Test Results
|
displayName: Publish C++ Test Results
|
||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'JUnit'
|
testResultsFormat: "JUnit"
|
||||||
testResultsFiles: '*.xml'
|
testResultsFiles: "*.xml"
|
||||||
testRunTitle: 'C++ Test Report'
|
testRunTitle: "C++ Test Report"
|
||||||
searchFolder: '$(System.DefaultWorkingDirectory)/test-reports'
|
searchFolder: "$(System.DefaultWorkingDirectory)/test-reports"
|
||||||
|
|
||||||
- job: Java
|
- job: Java
|
||||||
pool: RoboRioConnections
|
pool: RoboRioConnections
|
||||||
timeoutInMinutes: 30
|
timeoutInMinutes: 30
|
||||||
workspace:
|
workspace:
|
||||||
clean: all
|
clean: all
|
||||||
steps:
|
steps:
|
||||||
- task: DownloadPipelineArtifact@0
|
- task: DownloadPipelineArtifact@0
|
||||||
inputs:
|
inputs:
|
||||||
artifactName: 'Integration Tests'
|
artifactName: "Integration Tests"
|
||||||
targetPath: 'build/integrationTestFiles'
|
targetPath: "build/integrationTestFiles"
|
||||||
|
|
||||||
- task: ShellScript@2
|
- task: ShellScript@2
|
||||||
displayName: Run Java Tests
|
displayName: Run Java Tests
|
||||||
inputs:
|
inputs:
|
||||||
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
|
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
|
||||||
args: 'java'
|
args: "java"
|
||||||
|
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
displayName: Publish Java Test Results
|
displayName: Publish Java Test Results
|
||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'JUnit'
|
testResultsFormat: "JUnit"
|
||||||
testResultsFiles: '*.xml'
|
testResultsFiles: "*.xml"
|
||||||
testRunTitle: 'Java Test Report'
|
testRunTitle: "Java Test Report"
|
||||||
searchFolder: '$(System.DefaultWorkingDirectory)/test-reports'
|
searchFolder: "$(System.DefaultWorkingDirectory)/test-reports"
|
||||||
|
|||||||
@@ -1,417 +0,0 @@
|
|||||||
# Gradle
|
|
||||||
# Build your Java projects and run tests with Gradle using a Gradle wrapper script.
|
|
||||||
# Add steps that analyze code, save build artifacts, deploy, and more:
|
|
||||||
# https://docs.microsoft.com/vsts/pipelines/languages/java
|
|
||||||
|
|
||||||
resources:
|
|
||||||
containers:
|
|
||||||
- container: wpilib2020
|
|
||||||
image: wpilib/roborio-cross-ubuntu:2020-18.04
|
|
||||||
- container: raspbian
|
|
||||||
image: wpilib/raspbian-cross-ubuntu:10-18.04
|
|
||||||
- container: aarch64
|
|
||||||
image: wpilib/aarch64-cross-ubuntu:bionic-18.04
|
|
||||||
- container: ubuntu
|
|
||||||
image: wpilib/ubuntu-base:18.04
|
|
||||||
|
|
||||||
variables:
|
|
||||||
- group: Artifactory-Package-Publish
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
batch: true
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- master
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- stage: Build
|
|
||||||
jobs:
|
|
||||||
- job: Linux_Arm
|
|
||||||
pool:
|
|
||||||
vmImage: 'Ubuntu 16.04'
|
|
||||||
|
|
||||||
container: wpilib2020
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
publishJUnitResults: false
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-Ponlylinuxathena -PbuildServer'
|
|
||||||
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
publishJUnitResults: false
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-Ponlylinuxathena -PreleaseMode -PbuildServer'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Athena'
|
|
||||||
targetPath: 'build/allOutputs'
|
|
||||||
|
|
||||||
- job: Linux_Raspbian
|
|
||||||
pool:
|
|
||||||
vmImage: 'Ubuntu 16.04'
|
|
||||||
|
|
||||||
container: raspbian
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-Ponlylinuxraspbian -PbuildServer'
|
|
||||||
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-Ponlylinuxraspbian -PreleaseMode -PbuildServer'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Raspbian'
|
|
||||||
targetPath: 'build/allOutputs'
|
|
||||||
|
|
||||||
- job: Linux_Aarch64
|
|
||||||
pool:
|
|
||||||
vmImage: 'Ubuntu 16.04'
|
|
||||||
|
|
||||||
container: aarch64
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-Ponlylinuxaarch64bionic -PbuildServer'
|
|
||||||
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-Ponlylinuxaarch64bionic -PreleaseMode -PbuildServer'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Aarch64'
|
|
||||||
targetPath: 'build/allOutputs'
|
|
||||||
|
|
||||||
- job: Linux
|
|
||||||
pool:
|
|
||||||
vmImage: 'Ubuntu 16.04'
|
|
||||||
|
|
||||||
container: ubuntu
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-PbuildServer'
|
|
||||||
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-PreleaseMode -PbuildServer'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Linux'
|
|
||||||
targetPath: 'build/allOutputs'
|
|
||||||
|
|
||||||
- job: Styleguide
|
|
||||||
pool:
|
|
||||||
vmImage: 'Ubuntu 16.04'
|
|
||||||
|
|
||||||
container: ubuntu
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- script: |
|
|
||||||
sudo pip3 install wpiformat
|
|
||||||
displayName: 'Install wpiformat'
|
|
||||||
- script: |
|
|
||||||
git checkout -b master
|
|
||||||
wpiformat -clang 6.0
|
|
||||||
displayName: 'Run wpiformat'
|
|
||||||
- script: |
|
|
||||||
# Ensure formatter made no changes
|
|
||||||
git --no-pager diff --exit-code HEAD
|
|
||||||
displayName: 'Check wpiformat Output'
|
|
||||||
|
|
||||||
- job: CMakeBuild
|
|
||||||
pool:
|
|
||||||
vmImage: 'Ubuntu 16.04'
|
|
||||||
|
|
||||||
container: wpilib2020
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: CMake@1
|
|
||||||
inputs:
|
|
||||||
cmakeArgs: '-DWITHOUT_ALLWPILIB=OFF ..'
|
|
||||||
- script: |
|
|
||||||
make -j3
|
|
||||||
workingDirectory: 'build'
|
|
||||||
displayName: 'Build'
|
|
||||||
|
|
||||||
- job: Windows_64_Bit
|
|
||||||
pool:
|
|
||||||
vmImage: 'windows-2019'
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
steps:
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
jdkVersionOption: '1.11'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-PskipPMD -PbuildServer'
|
|
||||||
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
jdkVersionOption: '1.11'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-PskipPMD -PreleaseMode -PbuildServer'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Win64'
|
|
||||||
targetPath: 'build/allOutputs'
|
|
||||||
|
|
||||||
- job: Windows_32_Bit
|
|
||||||
pool:
|
|
||||||
vmImage: 'windows-2019'
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
steps:
|
|
||||||
- powershell: |
|
|
||||||
mkdir build
|
|
||||||
$ProgressPreference = 'SilentlyContinue'
|
|
||||||
wget "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.4%2B11/OpenJDK11U-jdk_x86-32_windows_hotspot_11.0.4_11.zip" -O "build\jdk.zip"
|
|
||||||
displayName: 'Download JDK'
|
|
||||||
- task: JavaToolInstaller@0
|
|
||||||
inputs:
|
|
||||||
jdkSourceOption: localDirectory
|
|
||||||
jdkFile: 'build/jdk.zip'
|
|
||||||
jdkDestinationDirectory: 'build/jdkinst'
|
|
||||||
jdkArchitectureOption: x86
|
|
||||||
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx1024m'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-PskipPMD -PbuildServer'
|
|
||||||
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx1024m'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-PskipPMD -PreleaseMode -PbuildServer'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Win32'
|
|
||||||
targetPath: 'build/allOutputs'
|
|
||||||
|
|
||||||
- job: Mac
|
|
||||||
pool:
|
|
||||||
vmImage: 'macOS-10.14'
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
steps:
|
|
||||||
- script: |
|
|
||||||
mkdir build
|
|
||||||
export JAVA_HOME=`/usr/libexec/java_home -v 11`
|
|
||||||
displayName: 'Setup JDK'
|
|
||||||
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
jdkVersionOption: '1.11'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-PbuildServer'
|
|
||||||
|
|
||||||
- task: Gradle@2
|
|
||||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
|
||||||
inputs:
|
|
||||||
workingDirectory: ''
|
|
||||||
gradleWrapperFile: 'gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
jdkVersionOption: '1.11'
|
|
||||||
publishJUnitResults: true
|
|
||||||
testResultsFiles: '**/TEST-*.xml'
|
|
||||||
tasks: 'build'
|
|
||||||
options: '-PreleaseMode -PbuildServer'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Mac'
|
|
||||||
targetPath: 'build/allOutputs'
|
|
||||||
|
|
||||||
- stage: Combine
|
|
||||||
jobs:
|
|
||||||
- job: CombineJob
|
|
||||||
pool:
|
|
||||||
vmImage: 'macOS-10.14'
|
|
||||||
|
|
||||||
timeoutInMinutes: 0
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout: none
|
|
||||||
- script: |
|
|
||||||
git clone https://github.com/wpilibsuite/build-tools
|
|
||||||
displayName: 'Clone Combiner'
|
|
||||||
- task: DownloadPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Mac'
|
|
||||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
|
||||||
- task: DownloadPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Win32'
|
|
||||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
|
||||||
- task: DownloadPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Win64'
|
|
||||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
|
||||||
- task: DownloadPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Linux'
|
|
||||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
|
||||||
- task: DownloadPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Raspbian'
|
|
||||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
|
||||||
- task: DownloadPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Athena'
|
|
||||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
|
||||||
- task: DownloadPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Aarch64'
|
|
||||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
|
||||||
|
|
||||||
# PR Builds
|
|
||||||
- task: Gradle@2
|
|
||||||
inputs:
|
|
||||||
workingDirectory: 'build-tools/combiner'
|
|
||||||
gradleWrapperFile: 'build-tools/combiner/gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
tasks: 'publish '
|
|
||||||
options: '-Pallwpilib'
|
|
||||||
condition: and(succeeded(), and(ne(variables['Build.SourceBranch'], 'refs/heads/master'), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))))
|
|
||||||
|
|
||||||
# Master Builds
|
|
||||||
- task: Gradle@2
|
|
||||||
inputs:
|
|
||||||
workingDirectory: 'build-tools/combiner'
|
|
||||||
gradleWrapperFile: 'build-tools/combiner/gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
tasks: 'publish '
|
|
||||||
options: '-Pallwpilib'
|
|
||||||
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
|
||||||
env:
|
|
||||||
RUN_AZURE_ARTIFACTORY_RELEASE: 'TRUE'
|
|
||||||
ARTIFACTORY_PUBLISH_USERNAME: $(PublishUserName)
|
|
||||||
ARTIFACTORY_PUBLISH_PASSWORD: $(PublishPassword)
|
|
||||||
|
|
||||||
# Tagged Builds
|
|
||||||
- task: Gradle@2
|
|
||||||
inputs:
|
|
||||||
workingDirectory: 'build-tools/combiner'
|
|
||||||
gradleWrapperFile: 'build-tools/combiner/gradlew'
|
|
||||||
gradleOptions: '-Xmx3072m'
|
|
||||||
tasks: 'publish '
|
|
||||||
options: '-Pallwpilib -PreleaseRepoPublish'
|
|
||||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
|
||||||
env:
|
|
||||||
RUN_AZURE_ARTIFACTORY_RELEASE: 'TRUE'
|
|
||||||
ARTIFACTORY_PUBLISH_USERNAME: $(PublishUserName)
|
|
||||||
ARTIFACTORY_PUBLISH_PASSWORD: $(PublishPassword)
|
|
||||||
|
|
||||||
- script: |
|
|
||||||
echo "##vso[task.setvariable variable=UserHome]$HOME"
|
|
||||||
displayName: 'Set Home Variable'
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'Maven'
|
|
||||||
targetPath: $(UserHome)/releases
|
|
||||||
87
build.gradle
87
build.gradle
@@ -1,29 +1,39 @@
|
|||||||
import edu.wpi.first.toolchain.*
|
import edu.wpi.first.toolchain.*
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url = 'https://frcmaven.wpi.edu/artifactory/ex-mvn'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.hubspot.jinjava:jinjava:2.6.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'base'
|
id 'base'
|
||||||
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '4.0.1'
|
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '2023.0.1'
|
||||||
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2020.2'
|
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2020.2'
|
||||||
id 'edu.wpi.first.NativeUtils' apply false
|
id 'edu.wpi.first.NativeUtils' apply false
|
||||||
id 'edu.wpi.first.GradleJni' version '0.10.1'
|
id 'edu.wpi.first.GradleJni' version '1.1.0'
|
||||||
id 'edu.wpi.first.GradleVsCode' version '0.11.0'
|
id 'edu.wpi.first.GradleVsCode'
|
||||||
id 'idea'
|
id 'idea'
|
||||||
id 'visual-studio'
|
id 'visual-studio'
|
||||||
id 'net.ltgt.errorprone' version '1.1.1' apply false
|
id 'net.ltgt.errorprone' version '2.0.2' apply false
|
||||||
id 'com.github.johnrengelman.shadow' version '5.2.0' apply false
|
id 'com.github.johnrengelman.shadow' version '7.1.2' apply false
|
||||||
|
id 'com.diffplug.spotless' version '6.12.0' apply false
|
||||||
|
id 'com.github.spotbugs' version '5.0.8' apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (project.hasProperty('buildServer')) {
|
wpilibVersioning.buildServerMode = project.hasProperty('buildServer')
|
||||||
wpilibVersioning.buildServerMode = true
|
wpilibVersioning.releaseMode = project.hasProperty('releaseMode')
|
||||||
}
|
|
||||||
|
|
||||||
if (project.hasProperty('releaseMode')) {
|
|
||||||
wpilibVersioning.releaseMode = true
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://frcmaven.wpi.edu/artifactory/ex-mvn'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (project.hasProperty('releaseMode')) {
|
if (project.hasProperty('releaseMode')) {
|
||||||
wpilibRepositories.addAllReleaseRepositories(it)
|
wpilibRepositories.addAllReleaseRepositories(it)
|
||||||
@@ -39,7 +49,7 @@ buildScan {
|
|||||||
publishAlways()
|
publishAlways()
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.licenseFile = files("$rootDir/LICENSE.txt", "$rootDir/ThirdPartyNotices.txt")
|
ext.licenseFile = files("$rootDir/LICENSE.md", "$rootDir/ThirdPartyNotices.txt")
|
||||||
|
|
||||||
if (project.hasProperty("publishVersion")) {
|
if (project.hasProperty("publishVersion")) {
|
||||||
wpilibVersioning.version.set(project.publishVersion)
|
wpilibVersioning.version.set(project.publishVersion)
|
||||||
@@ -77,7 +87,12 @@ task copyAllOutputs(type: Copy) {
|
|||||||
build.dependsOn copyAllOutputs
|
build.dependsOn copyAllOutputs
|
||||||
copyAllOutputs.dependsOn outputVersions
|
copyAllOutputs.dependsOn outputVersions
|
||||||
|
|
||||||
|
def copyReleaseOnly = project.hasProperty('ciReleaseOnly')
|
||||||
|
|
||||||
ext.addTaskToCopyAllOutputs = { task ->
|
ext.addTaskToCopyAllOutputs = { task ->
|
||||||
|
if (copyReleaseOnly && task.name.contains('debug')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
copyAllOutputs.dependsOn task
|
copyAllOutputs.dependsOn task
|
||||||
copyAllOutputs.inputs.file task.archivePath
|
copyAllOutputs.inputs.file task.archivePath
|
||||||
copyAllOutputs.from task.archivePath
|
copyAllOutputs.from task.archivePath
|
||||||
@@ -93,6 +108,11 @@ subprojects {
|
|||||||
subproj.apply plugin: MultiBuilds
|
subproj.apply plugin: MultiBuilds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins.withType(JavaPlugin) {
|
||||||
|
sourceCompatibility = 11
|
||||||
|
targetCompatibility = 11
|
||||||
|
}
|
||||||
|
|
||||||
apply from: "${rootDir}/shared/java/javastyle.gradle"
|
apply from: "${rootDir}/shared/java/javastyle.gradle"
|
||||||
|
|
||||||
// Disables doclint in java 8.
|
// Disables doclint in java 8.
|
||||||
@@ -103,6 +123,43 @@ subprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.compilerArgs.add '-XDstringConcat=inline'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enables UTF-8 support in Javadoc
|
||||||
|
tasks.withType(Javadoc) {
|
||||||
|
options.addStringOption("charset", "utf-8")
|
||||||
|
options.addStringOption("docencoding", "utf-8")
|
||||||
|
options.addStringOption("encoding", "utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign outputs with Developer ID
|
||||||
|
tasks.withType(AbstractLinkTask) { task ->
|
||||||
|
task.inputs.property "HasDeveloperId", project.hasProperty("developerID")
|
||||||
|
|
||||||
|
if (project.hasProperty("developerID")) {
|
||||||
|
// Don't sign any executables because codesign complains
|
||||||
|
// about relative rpath.
|
||||||
|
if (!(task instanceof LinkExecutable)) {
|
||||||
|
doLast {
|
||||||
|
// Get path to binary.
|
||||||
|
String path = task.getLinkedFile().getAsFile().get().getAbsolutePath()
|
||||||
|
exec {
|
||||||
|
workingDir rootDir
|
||||||
|
def args = [
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"codesign --force --strict --timestamp --options=runtime " +
|
||||||
|
"--verbose -s ${project.findProperty("developerID")} ${path}"
|
||||||
|
]
|
||||||
|
commandLine args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.getCurrentArch = {
|
ext.getCurrentArch = {
|
||||||
@@ -110,5 +167,5 @@ ext.getCurrentArch = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wrapper {
|
wrapper {
|
||||||
gradleVersion = '6.0'
|
gradleVersion = '7.5.1'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
url "https://plugins.gradle.org/m2/"
|
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
|
maven {
|
||||||
|
url = 'https://frcmaven.wpi.edu/artifactory/ex-gradle'
|
||||||
|
}
|
||||||
|
mavenCentral()
|
||||||
|
url "https://plugins.gradle.org/m2/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "edu.wpi.first:native-utils:2020.7.2"
|
implementation "edu.wpi.first:native-utils:2023.11.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import groovy.transform.CompileStatic
|
|
||||||
import javax.inject.Inject
|
|
||||||
import jaci.gradle.deploy.artifact.MavenArtifact
|
|
||||||
import jaci.gradle.deploy.context.DeployContext
|
|
||||||
import org.gradle.api.Project
|
|
||||||
import jaci.gradle.ActionWrapper
|
|
||||||
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
@CompileStatic
|
|
||||||
class JREArtifact extends MavenArtifact {
|
|
||||||
Function<DeployContext, Boolean> buildRequiresJre = (Function<DeployContext, Boolean>){ true }
|
|
||||||
|
|
||||||
void setJreDependency(String dep) {
|
|
||||||
dependency = project.dependencies.add(configuration(), dep)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
JREArtifact(String name, Project project) {
|
|
||||||
super(name, project)
|
|
||||||
configuration = project.configurations.create(configuration())
|
|
||||||
|
|
||||||
onlyIf = { DeployContext ctx ->
|
|
||||||
(buildRequiresJre.apply(ctx) && jreMissing(ctx)) || project.hasProperty("force-redeploy-jre")
|
|
||||||
}
|
|
||||||
|
|
||||||
predeploy << new ActionWrapper({ DeployContext ctx ->
|
|
||||||
ctx.logger.log('Deploying RoboRIO JRE (this will take a while)...')
|
|
||||||
})
|
|
||||||
|
|
||||||
directory = '/tmp'
|
|
||||||
filename = 'frcjre.ipk'
|
|
||||||
|
|
||||||
postdeploy << new ActionWrapper({ DeployContext ctx ->
|
|
||||||
ctx.logger.log('Installing JRE...')
|
|
||||||
ctx.execute('opkg remove frc2020-openjdk*; opkg install /tmp/frcjre.ipk; rm /tmp/frcjre.ipk')
|
|
||||||
ctx.logger.log('JRE Deployed!')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
String configuration() {
|
|
||||||
return name + 'frcjre'
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean jreMissing(DeployContext ctx) {
|
|
||||||
return ctx.execute('if [[ -f "/usr/local/frc/JRE/bin/java" ]]; then echo OK; else echo MISSING; fi').result.contains("MISSING")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,10 +6,14 @@ import java.nio.file.Files;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.gradle.api.tasks.Delete
|
||||||
|
|
||||||
import org.gradle.api.GradleException;
|
import org.gradle.api.GradleException;
|
||||||
import org.gradle.api.Plugin;
|
import org.gradle.api.Plugin;
|
||||||
import org.gradle.api.Project;
|
import org.gradle.api.Project;
|
||||||
import org.gradle.api.Task;
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.tasks.Copy;
|
||||||
|
import org.gradle.api.file.CopySpec;
|
||||||
import org.gradle.api.file.FileTree;
|
import org.gradle.api.file.FileTree;
|
||||||
import org.gradle.api.tasks.compile.JavaCompile;
|
import org.gradle.api.tasks.compile.JavaCompile;
|
||||||
import org.gradle.language.base.internal.ProjectLayout;
|
import org.gradle.language.base.internal.ProjectLayout;
|
||||||
@@ -32,6 +36,8 @@ import org.gradle.nativeplatform.toolchain.internal.ToolType;
|
|||||||
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
|
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
|
||||||
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
|
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
|
||||||
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
|
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
|
||||||
|
import org.gradle.nativeplatform.tasks.CreateStaticLibrary;
|
||||||
|
import org.gradle.nativeplatform.tasks.AbstractLinkTask;
|
||||||
import org.gradle.platform.base.BinarySpec;
|
import org.gradle.platform.base.BinarySpec;
|
||||||
import org.gradle.platform.base.ComponentSpec;
|
import org.gradle.platform.base.ComponentSpec;
|
||||||
import org.gradle.platform.base.ComponentSpecContainer;
|
import org.gradle.platform.base.ComponentSpecContainer;
|
||||||
@@ -40,7 +46,7 @@ import org.gradle.platform.base.ComponentType;
|
|||||||
import org.gradle.platform.base.TypeBuilder;
|
import org.gradle.platform.base.TypeBuilder;
|
||||||
import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
|
import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
|
||||||
import groovy.transform.CompileStatic;
|
import groovy.transform.CompileStatic;
|
||||||
import edu.wpi.first.nativeutils.tasks.ExportsGenerationTask
|
import edu.wpi.first.nativeutils.exports.ExportsGenerationTask
|
||||||
|
|
||||||
@CompileStatic
|
@CompileStatic
|
||||||
class SingleNativeBuild implements Plugin<Project> {
|
class SingleNativeBuild implements Plugin<Project> {
|
||||||
@@ -79,10 +85,18 @@ class SingleNativeBuild implements Plugin<Project> {
|
|||||||
components.each { component ->
|
components.each { component ->
|
||||||
if (component.name == "${nativeName}Base") {
|
if (component.name == "${nativeName}Base") {
|
||||||
base = (NativeLibrarySpec) component
|
base = (NativeLibrarySpec) component
|
||||||
} else if (component.name == "${nativeName}" || component.name == "${nativeName}JNI") {
|
} else if (component.name == "${nativeName}" || component.name == "${nativeName}JNI" || component.name == "${nativeName}JNICvStatic") {
|
||||||
subs << component
|
subs << component
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Delete deleteObjects = null
|
||||||
|
if (project.hasProperty('buildServer')) {
|
||||||
|
deleteObjects = project.tasks.create('deleteObjects', Delete)
|
||||||
|
project.tasks.named('build').configure { Task t ->
|
||||||
|
t.dependsOn deleteObjects
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
subs.each {
|
subs.each {
|
||||||
((NativeLibrarySpec) it).binaries.each { oBinary ->
|
((NativeLibrarySpec) it).binaries.each { oBinary ->
|
||||||
if (oBinary.buildable == false) {
|
if (oBinary.buildable == false) {
|
||||||
@@ -100,6 +114,23 @@ class SingleNativeBuild implements Plugin<Project> {
|
|||||||
baseBin = tmpBaseBin
|
baseBin = tmpBaseBin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (binary instanceof StaticLibraryBinarySpec) {
|
||||||
|
File intoDir = ((CreateStaticLibrary)((StaticLibraryBinarySpec)binary).tasks.createStaticLib).outputFile.get().asFile.parentFile
|
||||||
|
File fromDir = ((CreateStaticLibrary)((StaticLibraryBinarySpec)baseBin).tasks.createStaticLib).outputFile.get().asFile.parentFile
|
||||||
|
|
||||||
|
def copyBasePdbName = "copyBasePdbFor" + binary.buildTask.name
|
||||||
|
def copyTask = project.tasks.register(copyBasePdbName, Copy) { Copy t ->
|
||||||
|
t.from (fromDir)
|
||||||
|
t.include '*.pdb'
|
||||||
|
t.into intoDir
|
||||||
|
|
||||||
|
t.dependsOn (((StaticLibraryBinarySpec)baseBin).tasks.createStaticLib)
|
||||||
|
}
|
||||||
|
((CreateStaticLibrary)((StaticLibraryBinarySpec)binary).tasks.createStaticLib).dependsOn(copyTask)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
baseBin.tasks.withType(AbstractNativeSourceCompileTask) { oCompileTask ->
|
baseBin.tasks.withType(AbstractNativeSourceCompileTask) { oCompileTask ->
|
||||||
def compileTask = (AbstractNativeSourceCompileTask) oCompileTask
|
def compileTask = (AbstractNativeSourceCompileTask) oCompileTask
|
||||||
if (binary instanceof SharedLibraryBinarySpec) {
|
if (binary instanceof SharedLibraryBinarySpec) {
|
||||||
@@ -115,6 +146,10 @@ class SingleNativeBuild implements Plugin<Project> {
|
|||||||
tree.include '**/*.o'
|
tree.include '**/*.o'
|
||||||
tree.include '**/*.obj'
|
tree.include '**/*.obj'
|
||||||
link.source tree
|
link.source tree
|
||||||
|
if (project.hasProperty('buildServer')) {
|
||||||
|
deleteObjects.dependsOn link
|
||||||
|
deleteObjects.delete tree
|
||||||
|
}
|
||||||
} else if (binary instanceof StaticLibraryBinarySpec) {
|
} else if (binary instanceof StaticLibraryBinarySpec) {
|
||||||
def sBinary = (StaticLibraryBinarySpec) binary
|
def sBinary = (StaticLibraryBinarySpec) binary
|
||||||
ObjectFilesToBinary assemble = (ObjectFilesToBinary) sBinary.tasks.createStaticLib
|
ObjectFilesToBinary assemble = (ObjectFilesToBinary) sBinary.tasks.createStaticLib
|
||||||
@@ -124,6 +159,10 @@ class SingleNativeBuild implements Plugin<Project> {
|
|||||||
tree.include '**/*.o'
|
tree.include '**/*.o'
|
||||||
tree.include '**/*.obj'
|
tree.include '**/*.obj'
|
||||||
assemble.source tree
|
assemble.source tree
|
||||||
|
if (project.hasProperty('buildServer')) {
|
||||||
|
deleteObjects.dependsOn assemble
|
||||||
|
deleteObjects.delete tree
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
77
buildSrc/src/main/groovy/WPIJREArtifact.groovy
Normal file
77
buildSrc/src/main/groovy/WPIJREArtifact.groovy
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import groovy.transform.CompileStatic;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
|
||||||
|
import edu.wpi.first.deployutils.deploy.CommandDeployResult;
|
||||||
|
import edu.wpi.first.deployutils.deploy.artifact.MavenArtifact;
|
||||||
|
import edu.wpi.first.deployutils.deploy.context.DeployContext;
|
||||||
|
import edu.wpi.first.deployutils.deploy.target.RemoteTarget;
|
||||||
|
import edu.wpi.first.deployutils.PredicateWrapper;
|
||||||
|
import edu.wpi.first.deployutils.ActionWrapper;
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
public class WPIJREArtifact extends MavenArtifact {
|
||||||
|
private final String configName;
|
||||||
|
|
||||||
|
public String getConfigName() {
|
||||||
|
return configName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCheckJreVersion() {
|
||||||
|
return checkJreVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckJreVersion(boolean checkJreVersion) {
|
||||||
|
this.checkJreVersion = checkJreVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkJreVersion = true;
|
||||||
|
|
||||||
|
private final String artifactLocation = "edu.wpi.first.jdk:roborio-2023:17.0.5u7-1"
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public WPIJREArtifact(String name, RemoteTarget target) {
|
||||||
|
super(name, target);
|
||||||
|
String configName = name + "frcjre";
|
||||||
|
this.configName = configName;
|
||||||
|
Project project = target.getProject();
|
||||||
|
getConfiguration().set(project.getConfigurations().create(configName));
|
||||||
|
getDependency().set(project.getDependencies().add(configName, artifactLocation));
|
||||||
|
|
||||||
|
setOnlyIf(new PredicateWrapper({ DeployContext ctx ->
|
||||||
|
return jreMissing(ctx) || jreOutOfDate(ctx) || project.hasProperty("force-redeploy-jre");
|
||||||
|
}));
|
||||||
|
|
||||||
|
getDirectory().set("/tmp");
|
||||||
|
getFilename().set("frcjre.ipk");
|
||||||
|
|
||||||
|
getPostdeploy().add(new ActionWrapper({ DeployContext ctx ->
|
||||||
|
ctx.getLogger().log("Installing JRE...");
|
||||||
|
ctx.execute("opkg remove frc*-openjdk*; opkg install /tmp/frcjre.ipk; rm /tmp/frcjre.ipk");
|
||||||
|
ctx.getLogger().log("JRE Deployed!");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean jreMissing(DeployContext ctx) {
|
||||||
|
return ctx.execute("if [[ -f \"/usr/local/frc/JRE/bin/java\" ]]; then echo OK; else echo MISSING; fi").getResult().contains("MISSING");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean jreOutOfDate(DeployContext ctx) {
|
||||||
|
if (!checkJreVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String version = getDependency().get().getVersion();
|
||||||
|
CommandDeployResult cmdResult = ctx.execute("opkg list-installed | grep openjdk");
|
||||||
|
if (cmdResult.getExitCode() != 0) {
|
||||||
|
ctx.getLogger().log("JRE not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String result = cmdResult.getResult().trim();
|
||||||
|
ctx.getLogger().log("Searching for JRE " + version);
|
||||||
|
ctx.getLogger().log("Found JRE " + result);
|
||||||
|
boolean matches = result.contains(version);
|
||||||
|
ctx.getLogger().log(matches ? "JRE Is Correct Version" : "JRE is mismatched. Reinstalling");
|
||||||
|
return !matches;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ repoRootNameOverride {
|
|||||||
}
|
}
|
||||||
|
|
||||||
includeOtherLibs {
|
includeOtherLibs {
|
||||||
|
^fmt/
|
||||||
^hal/
|
^hal/
|
||||||
^networktables/
|
^networktables/
|
||||||
^opencv2/
|
^opencv2/
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ include(AddTest)
|
|||||||
find_package( OpenCV REQUIRED )
|
find_package( OpenCV REQUIRED )
|
||||||
|
|
||||||
# Java bindings
|
# Java bindings
|
||||||
if (NOT WITHOUT_JAVA)
|
if (WITH_JAVA)
|
||||||
find_package(Java REQUIRED)
|
find_package(Java REQUIRED)
|
||||||
include(UseJava)
|
include(UseJava)
|
||||||
set(CMAKE_JAVA_COMPILE_FLAGS "-Xlint:unchecked")
|
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
|
||||||
|
|
||||||
#find java files, copy them locally
|
#find java files, copy them locally
|
||||||
|
|
||||||
@@ -43,18 +43,18 @@ set_property(TARGET cameraserver PROPERTY FOLDER "libraries")
|
|||||||
install(TARGETS cameraserver EXPORT cameraserver DESTINATION "${main_lib_dest}")
|
install(TARGETS cameraserver EXPORT cameraserver DESTINATION "${main_lib_dest}")
|
||||||
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/cameraserver")
|
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/cameraserver")
|
||||||
|
|
||||||
if (NOT WITHOUT_JAVA AND MSVC)
|
if (WITH_JAVA AND MSVC)
|
||||||
install(TARGETS cameraserver RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
|
install(TARGETS cameraserver RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MSVC OR FLAT_INSTALL_WPILIB)
|
if (WITH_FLAT_INSTALL)
|
||||||
set (cameraserver_config_dir ${wpilib_dest})
|
set (cameraserver_config_dir ${wpilib_dest})
|
||||||
else()
|
else()
|
||||||
set (cameraserver_config_dir share/cameraserver)
|
set (cameraserver_config_dir share/cameraserver)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
configure_file(cameraserver-config.cmake.in ${CMAKE_BINARY_DIR}/cameraserver-config.cmake )
|
configure_file(cameraserver-config.cmake.in ${WPILIB_BINARY_DIR}/cameraserver-config.cmake )
|
||||||
install(FILES ${CMAKE_BINARY_DIR}/cameraserver-config.cmake DESTINATION ${cameraserver_config_dir})
|
install(FILES ${WPILIB_BINARY_DIR}/cameraserver-config.cmake DESTINATION ${cameraserver_config_dir})
|
||||||
install(EXPORT cameraserver DESTINATION ${cameraserver_config_dir})
|
install(EXPORT cameraserver DESTINATION ${cameraserver_config_dir})
|
||||||
|
|
||||||
file(GLOB multiCameraServer_src multiCameraServer/src/main/native/cpp/*.cpp)
|
file(GLOB multiCameraServer_src multiCameraServer/src/main/native/cpp/*.cpp)
|
||||||
|
|||||||
@@ -11,18 +11,20 @@ apply from: "${rootDir}/shared/javacpp/setupBuild.gradle"
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':wpiutil')
|
implementation project(':wpiutil')
|
||||||
|
implementation project(':wpinet')
|
||||||
implementation project(':ntcore')
|
implementation project(':ntcore')
|
||||||
implementation project(':cscore')
|
implementation project(':cscore')
|
||||||
devImplementation project(':wpiutil')
|
devImplementation project(':wpiutil')
|
||||||
|
devImplementation project(':wpinet')
|
||||||
devImplementation project(':ntcore')
|
devImplementation project(':ntcore')
|
||||||
devImplementation project(':cscore')
|
devImplementation project(':cscore')
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
sharedCvConfigs = [cameraserver : [],
|
sharedCvConfigs = [cameraserver : [],
|
||||||
cameraserverBase: [],
|
cameraserverBase: [],
|
||||||
cameraserverDev : [],
|
cameraserverDev : [],
|
||||||
cameraserverTest: []]
|
cameraserverTest: []]
|
||||||
staticCvConfigs = [:]
|
staticCvConfigs = [:]
|
||||||
useJava = true
|
useJava = true
|
||||||
useCpp = true
|
useCpp = true
|
||||||
@@ -32,14 +34,19 @@ apply from: "${rootDir}/shared/opencv.gradle"
|
|||||||
|
|
||||||
nativeUtils.exportsConfigs {
|
nativeUtils.exportsConfigs {
|
||||||
cameraserver {
|
cameraserver {
|
||||||
x86ExcludeSymbols = ['_CT??_R0?AV_System_error', '_CT??_R0?AVexception', '_CT??_R0?AVfailure',
|
x64ExcludeSymbols = [
|
||||||
'_CT??_R0?AVruntime_error', '_CT??_R0?AVsystem_error', '_CTA5?AVfailure',
|
'_CT??_R0?AV_System_error',
|
||||||
'_TI5?AVfailure', '_CT??_R0?AVout_of_range', '_CTA3?AVout_of_range',
|
'_CT??_R0?AVexception',
|
||||||
'_TI3?AVout_of_range', '_CT??_R0?AVbad_cast']
|
'_CT??_R0?AVfailure',
|
||||||
x64ExcludeSymbols = ['_CT??_R0?AV_System_error', '_CT??_R0?AVexception', '_CT??_R0?AVfailure',
|
'_CT??_R0?AVruntime_error',
|
||||||
'_CT??_R0?AVruntime_error', '_CT??_R0?AVsystem_error', '_CTA5?AVfailure',
|
'_CT??_R0?AVsystem_error',
|
||||||
'_TI5?AVfailure', '_CT??_R0?AVout_of_range', '_CTA3?AVout_of_range',
|
'_CTA5?AVfailure',
|
||||||
'_TI3?AVout_of_range', '_CT??_R0?AVbad_cast']
|
'_TI5?AVfailure',
|
||||||
|
'_CT??_R0?AVout_of_range',
|
||||||
|
'_CTA3?AVout_of_range',
|
||||||
|
'_TI3?AVout_of_range',
|
||||||
|
'_CT??_R0?AVbad_cast'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,8 +57,9 @@ model {
|
|||||||
if (!it.buildable || !(it instanceof NativeBinarySpec)) {
|
if (!it.buildable || !(it instanceof NativeBinarySpec)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lib project: ':ntcore', library: 'ntcore', linkage: 'shared'
|
project(':ntcore').addNtcoreDependency(it, 'shared')
|
||||||
lib project: ':cscore', library: 'cscore', linkage: 'shared'
|
lib project: ':cscore', library: 'cscore', linkage: 'shared'
|
||||||
|
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
|
||||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
|
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ include(CMakeFindDependencyMacro)
|
|||||||
@CSCORE_DEP_REPLACE@
|
@CSCORE_DEP_REPLACE@
|
||||||
find_dependency(OpenCV)
|
find_dependency(OpenCV)
|
||||||
|
|
||||||
|
@FILENAME_DEP_REPLACE@
|
||||||
include(${SELF_DIR}/cameraserver.cmake)
|
include(${SELF_DIR}/cameraserver.cmake)
|
||||||
|
|||||||
@@ -18,18 +18,21 @@ ext {
|
|||||||
|
|
||||||
apply from: "${rootDir}/shared/opencv.gradle"
|
apply from: "${rootDir}/shared/opencv.gradle"
|
||||||
|
|
||||||
mainClassName = 'Main'
|
mainClassName = 'edu.wpi.Main'
|
||||||
|
|
||||||
apply plugin: 'com.github.johnrengelman.shadow'
|
apply plugin: 'com.github.johnrengelman.shadow'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://frcmaven.wpi.edu/artifactory/ex-mvn'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.code.gson:gson:2.8.5'
|
implementation 'com.google.code.gson:gson:2.8.9'
|
||||||
|
|
||||||
implementation project(':wpiutil')
|
implementation project(':wpiutil')
|
||||||
|
implementation project(':wpinet')
|
||||||
implementation project(':ntcore')
|
implementation project(':ntcore')
|
||||||
implementation project(':cscore')
|
implementation project(':cscore')
|
||||||
implementation project(':cameraserver')
|
implementation project(':cameraserver')
|
||||||
@@ -38,7 +41,6 @@ dependencies {
|
|||||||
model {
|
model {
|
||||||
components {
|
components {
|
||||||
multiCameraServerCpp(NativeExecutableSpec) {
|
multiCameraServerCpp(NativeExecutableSpec) {
|
||||||
targetBuildTypes 'release'
|
|
||||||
sources {
|
sources {
|
||||||
cpp {
|
cpp {
|
||||||
source {
|
source {
|
||||||
@@ -52,10 +54,11 @@ model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
binaries.all { binary ->
|
binaries.all { binary ->
|
||||||
lib project: ':cameraserver', library: 'cameraserver', linkage: 'static'
|
lib project: ':cameraserver', library: 'cameraserver', linkage: 'static'
|
||||||
lib project: ':ntcore', library: 'ntcore', linkage: 'static'
|
project(':ntcore').addNtcoreDependency(binary, 'static')
|
||||||
lib project: ':cscore', library: 'cscore', linkage: 'static'
|
lib project: ':cscore', library: 'cscore', linkage: 'static'
|
||||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
|
lib project: ':wpinet', library: 'wpinet', linkage: 'static'
|
||||||
|
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,211 +0,0 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
|
|
||||||
import edu.wpi.cscore.VideoSource;
|
|
||||||
import edu.wpi.first.cameraserver.CameraServer;
|
|
||||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
|
||||||
|
|
||||||
/*
|
|
||||||
JSON format:
|
|
||||||
{
|
|
||||||
"team": <team number>,
|
|
||||||
"ntmode": <"client" or "server", "client" if unspecified>
|
|
||||||
"cameras": [
|
|
||||||
{
|
|
||||||
"name": <camera name>
|
|
||||||
"path": <path, e.g. "/dev/video0">
|
|
||||||
"pixel format": <"MJPEG", "YUYV", etc> // optional
|
|
||||||
"width": <video mode width> // optional
|
|
||||||
"height": <video mode height> // optional
|
|
||||||
"fps": <video mode fps> // optional
|
|
||||||
"brightness": <percentage brightness> // optional
|
|
||||||
"white balance": <"auto", "hold", value> // optional
|
|
||||||
"exposure": <"auto", "hold", value> // optional
|
|
||||||
"properties": [ // optional
|
|
||||||
{
|
|
||||||
"name": <property name>
|
|
||||||
"value": <property value>
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public final class Main {
|
|
||||||
private static String configFile = "/boot/frc.json";
|
|
||||||
|
|
||||||
@SuppressWarnings("MemberName")
|
|
||||||
public static class CameraConfig {
|
|
||||||
public String name;
|
|
||||||
public String path;
|
|
||||||
public JsonObject config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int team;
|
|
||||||
public static boolean server;
|
|
||||||
public static List<CameraConfig> cameras = new ArrayList<>();
|
|
||||||
|
|
||||||
private Main() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Report parse error.
|
|
||||||
*/
|
|
||||||
public static void parseError(String str) {
|
|
||||||
System.err.println("config error in '" + configFile + "': " + str);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read single camera configuration.
|
|
||||||
*/
|
|
||||||
public static boolean readCameraConfig(JsonObject config) {
|
|
||||||
CameraConfig cam = new CameraConfig();
|
|
||||||
|
|
||||||
// name
|
|
||||||
JsonElement nameElement = config.get("name");
|
|
||||||
if (nameElement == null) {
|
|
||||||
parseError("could not read camera name");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
cam.name = nameElement.getAsString();
|
|
||||||
|
|
||||||
// path
|
|
||||||
JsonElement pathElement = config.get("path");
|
|
||||||
if (pathElement == null) {
|
|
||||||
parseError("camera '" + cam.name + "': could not read path");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
cam.path = pathElement.getAsString();
|
|
||||||
|
|
||||||
cam.config = config;
|
|
||||||
|
|
||||||
cameras.add(cam);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read configuration file.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.CyclomaticComplexity")
|
|
||||||
public static boolean readConfig() {
|
|
||||||
// parse file
|
|
||||||
JsonElement top;
|
|
||||||
try {
|
|
||||||
top = new JsonParser().parse(Files.newBufferedReader(Paths.get(configFile)));
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.err.println("could not open '" + configFile + "': " + ex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// top level must be an object
|
|
||||||
if (!top.isJsonObject()) {
|
|
||||||
parseError("must be JSON object");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
JsonObject obj = top.getAsJsonObject();
|
|
||||||
|
|
||||||
// team number
|
|
||||||
JsonElement teamElement = obj.get("team");
|
|
||||||
if (teamElement == null) {
|
|
||||||
parseError("could not read team number");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
team = teamElement.getAsInt();
|
|
||||||
|
|
||||||
// ntmode (optional)
|
|
||||||
if (obj.has("ntmode")) {
|
|
||||||
String str = obj.get("ntmode").getAsString();
|
|
||||||
if ("client".equalsIgnoreCase(str)) {
|
|
||||||
server = false;
|
|
||||||
} else if ("server".equalsIgnoreCase(str)) {
|
|
||||||
server = true;
|
|
||||||
} else {
|
|
||||||
parseError("could not understand ntmode value '" + str + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cameras
|
|
||||||
JsonElement camerasElement = obj.get("cameras");
|
|
||||||
if (camerasElement == null) {
|
|
||||||
parseError("could not read cameras");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
JsonArray cameras = camerasElement.getAsJsonArray();
|
|
||||||
for (JsonElement camera : cameras) {
|
|
||||||
if (!readCameraConfig(camera.getAsJsonObject())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start running the camera.
|
|
||||||
*/
|
|
||||||
public static void startCamera(CameraConfig config) {
|
|
||||||
System.out.println("Starting camera '" + config.name + "' on " + config.path);
|
|
||||||
VideoSource camera = CameraServer.getInstance().startAutomaticCapture(
|
|
||||||
config.name, config.path);
|
|
||||||
|
|
||||||
Gson gson = new GsonBuilder().create();
|
|
||||||
|
|
||||||
camera.setConfigJson(gson.toJson(config.config));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main.
|
|
||||||
*/
|
|
||||||
public static void main(String... args) {
|
|
||||||
if (args.length > 0) {
|
|
||||||
configFile = args[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// read configuration
|
|
||||||
if (!readConfig()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// start NetworkTables
|
|
||||||
NetworkTableInstance ntinst = NetworkTableInstance.getDefault();
|
|
||||||
if (server) {
|
|
||||||
System.out.println("Setting up NetworkTables server");
|
|
||||||
ntinst.startServer();
|
|
||||||
} else {
|
|
||||||
System.out.println("Setting up NetworkTables client for team " + team);
|
|
||||||
ntinst.startClientTeam(team);
|
|
||||||
}
|
|
||||||
|
|
||||||
// start cameras
|
|
||||||
for (CameraConfig camera : cameras) {
|
|
||||||
startCamera(camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
// loop forever
|
|
||||||
for (;;) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(10000);
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
196
cameraserver/multiCameraServer/src/main/java/edu/wpi/Main.java
Normal file
196
cameraserver/multiCameraServer/src/main/java/edu/wpi/Main.java
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
|
|
||||||
|
package edu.wpi;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import edu.wpi.first.cameraserver.CameraServer;
|
||||||
|
import edu.wpi.first.cscore.VideoSource;
|
||||||
|
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/*
|
||||||
|
JSON format:
|
||||||
|
{
|
||||||
|
"team": <team number>,
|
||||||
|
"ntmode": <"client" or "server", "client" if unspecified>
|
||||||
|
"cameras": [
|
||||||
|
{
|
||||||
|
"name": <camera name>
|
||||||
|
"path": <path, e.g. "/dev/video0">
|
||||||
|
"pixel format": <"MJPEG", "YUYV", etc> // optional
|
||||||
|
"width": <video mode width> // optional
|
||||||
|
"height": <video mode height> // optional
|
||||||
|
"fps": <video mode fps> // optional
|
||||||
|
"brightness": <percentage brightness> // optional
|
||||||
|
"white balance": <"auto", "hold", value> // optional
|
||||||
|
"exposure": <"auto", "hold", value> // optional
|
||||||
|
"properties": [ // optional
|
||||||
|
{
|
||||||
|
"name": <property name>
|
||||||
|
"value": <property value>
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public final class Main {
|
||||||
|
private static String configFile = "/boot/frc.json";
|
||||||
|
|
||||||
|
@SuppressWarnings("MemberName")
|
||||||
|
public static class CameraConfig {
|
||||||
|
public String name;
|
||||||
|
public String path;
|
||||||
|
public JsonObject config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int team;
|
||||||
|
private static boolean server;
|
||||||
|
private static List<CameraConfig> cameras = new ArrayList<>();
|
||||||
|
|
||||||
|
private Main() {}
|
||||||
|
|
||||||
|
/** Report parse error. */
|
||||||
|
public static void parseError(String str) {
|
||||||
|
System.err.println("config error in '" + configFile + "': " + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read single camera configuration. */
|
||||||
|
public static boolean readCameraConfig(JsonObject config) {
|
||||||
|
CameraConfig cam = new CameraConfig();
|
||||||
|
|
||||||
|
// name
|
||||||
|
JsonElement nameElement = config.get("name");
|
||||||
|
if (nameElement == null) {
|
||||||
|
parseError("could not read camera name");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cam.name = nameElement.getAsString();
|
||||||
|
|
||||||
|
// path
|
||||||
|
JsonElement pathElement = config.get("path");
|
||||||
|
if (pathElement == null) {
|
||||||
|
parseError("camera '" + cam.name + "': could not read path");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cam.path = pathElement.getAsString();
|
||||||
|
|
||||||
|
cam.config = config;
|
||||||
|
|
||||||
|
cameras.add(cam);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read configuration file. */
|
||||||
|
public static boolean readConfig() {
|
||||||
|
// parse file
|
||||||
|
JsonElement top;
|
||||||
|
try {
|
||||||
|
top = new JsonParser().parse(Files.newBufferedReader(Paths.get(configFile)));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.err.println("could not open '" + configFile + "': " + ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// top level must be an object
|
||||||
|
if (!top.isJsonObject()) {
|
||||||
|
parseError("must be JSON object");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JsonObject obj = top.getAsJsonObject();
|
||||||
|
|
||||||
|
// team number
|
||||||
|
JsonElement teamElement = obj.get("team");
|
||||||
|
if (teamElement == null) {
|
||||||
|
parseError("could not read team number");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
team = teamElement.getAsInt();
|
||||||
|
|
||||||
|
// ntmode (optional)
|
||||||
|
if (obj.has("ntmode")) {
|
||||||
|
String str = obj.get("ntmode").getAsString();
|
||||||
|
if ("client".equalsIgnoreCase(str)) {
|
||||||
|
server = false;
|
||||||
|
} else if ("server".equalsIgnoreCase(str)) {
|
||||||
|
server = true;
|
||||||
|
} else {
|
||||||
|
parseError("could not understand ntmode value '" + str + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cameras
|
||||||
|
JsonElement camerasElement = obj.get("cameras");
|
||||||
|
if (camerasElement == null) {
|
||||||
|
parseError("could not read cameras");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JsonArray cameras = camerasElement.getAsJsonArray();
|
||||||
|
for (JsonElement camera : cameras) {
|
||||||
|
if (!readCameraConfig(camera.getAsJsonObject())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Start running the camera. */
|
||||||
|
public static void startCamera(CameraConfig config) {
|
||||||
|
System.out.println("Starting camera '" + config.name + "' on " + config.path);
|
||||||
|
VideoSource camera = CameraServer.startAutomaticCapture(config.name, config.path);
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder().create();
|
||||||
|
|
||||||
|
camera.setConfigJson(gson.toJson(config.config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Main. */
|
||||||
|
public static void main(String... args) {
|
||||||
|
if (args.length > 0) {
|
||||||
|
configFile = args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// read configuration
|
||||||
|
if (!readConfig()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start NetworkTables
|
||||||
|
NetworkTableInstance ntinst = NetworkTableInstance.getDefault();
|
||||||
|
if (server) {
|
||||||
|
System.out.println("Setting up NetworkTables server");
|
||||||
|
ntinst.startServer();
|
||||||
|
} else {
|
||||||
|
System.out.println("Setting up NetworkTables client for team " + team);
|
||||||
|
ntinst.setServerTeam(team);
|
||||||
|
ntinst.startClient4("multicameraserver");
|
||||||
|
}
|
||||||
|
|
||||||
|
// start cameras
|
||||||
|
for (CameraConfig camera : cameras) {
|
||||||
|
startCamera(camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop forever
|
||||||
|
for (; ; ) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(10000);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <networktables/NetworkTableInstance.h>
|
#include <networktables/NetworkTableInstance.h>
|
||||||
#include <wpi/StringRef.h>
|
#include <wpi/StringExtras.h>
|
||||||
|
#include <wpi/fmt/raw_ostream.h>
|
||||||
#include <wpi/json.h>
|
#include <wpi/json.h>
|
||||||
#include <wpi/raw_istream.h>
|
#include <wpi/raw_istream.h>
|
||||||
#include <wpi/raw_ostream.h>
|
#include <wpi/raw_ostream.h>
|
||||||
@@ -108,7 +108,7 @@ bool ReadConfig() {
|
|||||||
try {
|
try {
|
||||||
j = wpi::json::parse(is);
|
j = wpi::json::parse(is);
|
||||||
} catch (const wpi::json::parse_error& e) {
|
} catch (const wpi::json::parse_error& e) {
|
||||||
ParseError() << "byte " << e.byte << ": " << e.what() << '\n';
|
fmt::print(ParseError(), "byte {}: {}\n", e.byte, e.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,10 +130,9 @@ bool ReadConfig() {
|
|||||||
if (j.count("ntmode") != 0) {
|
if (j.count("ntmode") != 0) {
|
||||||
try {
|
try {
|
||||||
auto str = j.at("ntmode").get<std::string>();
|
auto str = j.at("ntmode").get<std::string>();
|
||||||
wpi::StringRef s(str);
|
if (wpi::equals_lower(str, "client")) {
|
||||||
if (s.equals_lower("client")) {
|
|
||||||
server = false;
|
server = false;
|
||||||
} else if (s.equals_lower("server")) {
|
} else if (wpi::equals_lower(str, "server")) {
|
||||||
server = true;
|
server = true;
|
||||||
} else {
|
} else {
|
||||||
ParseError() << "could not understand ntmode value '" << str << "'\n";
|
ParseError() << "could not understand ntmode value '" << str << "'\n";
|
||||||
@@ -146,7 +145,9 @@ bool ReadConfig() {
|
|||||||
// cameras
|
// cameras
|
||||||
try {
|
try {
|
||||||
for (auto&& camera : j.at("cameras")) {
|
for (auto&& camera : j.at("cameras")) {
|
||||||
if (!ReadCameraConfig(camera)) return false;
|
if (!ReadCameraConfig(camera)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (const wpi::json::exception& e) {
|
} catch (const wpi::json::exception& e) {
|
||||||
ParseError() << "could not read cameras: " << e.what() << '\n';
|
ParseError() << "could not read cameras: " << e.what() << '\n';
|
||||||
@@ -157,34 +158,42 @@ bool ReadConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StartCamera(const CameraConfig& config) {
|
void StartCamera(const CameraConfig& config) {
|
||||||
wpi::outs() << "Starting camera '" << config.name << "' on " << config.path
|
fmt::print("Starting camera '{}' on {}\n", config.name, config.path);
|
||||||
<< '\n';
|
auto camera =
|
||||||
auto camera = frc::CameraServer::GetInstance()->StartAutomaticCapture(
|
frc::CameraServer::StartAutomaticCapture(config.name, config.path);
|
||||||
config.name, config.path);
|
|
||||||
|
|
||||||
camera.SetConfigJson(config.config);
|
camera.SetConfigJson(config.config);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (argc >= 2) configFile = argv[1];
|
if (argc >= 2) {
|
||||||
|
configFile = argv[1];
|
||||||
|
}
|
||||||
|
|
||||||
// read configuration
|
// read configuration
|
||||||
if (!ReadConfig()) return EXIT_FAILURE;
|
if (!ReadConfig()) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
// start NetworkTables
|
// start NetworkTables
|
||||||
auto ntinst = nt::NetworkTableInstance::GetDefault();
|
auto ntinst = nt::NetworkTableInstance::GetDefault();
|
||||||
if (server) {
|
if (server) {
|
||||||
wpi::outs() << "Setting up NetworkTables server\n";
|
std::puts("Setting up NetworkTables server");
|
||||||
ntinst.StartServer();
|
ntinst.StartServer();
|
||||||
} else {
|
} else {
|
||||||
wpi::outs() << "Setting up NetworkTables client for team " << team << '\n';
|
fmt::print("Setting up NetworkTables client for team {}\n", team);
|
||||||
ntinst.StartClientTeam(team);
|
ntinst.StartClient4("multicameraserver");
|
||||||
|
ntinst.SetServerTeam(team);
|
||||||
}
|
}
|
||||||
|
|
||||||
// start cameras
|
// start cameras
|
||||||
for (auto&& camera : cameras) StartCamera(camera);
|
for (auto&& camera : cameras) {
|
||||||
|
StartCamera(camera);
|
||||||
|
}
|
||||||
|
|
||||||
// loop forever
|
// loop forever
|
||||||
for (;;) std::this_thread::sleep_for(std::chrono::seconds(10));
|
for (;;) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
package edu.wpi.first.cameraserver;
|
package edu.wpi.first.cameraserver;
|
||||||
|
|
||||||
public final class DevMain {
|
public final class DevMain {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {}
|
||||||
|
|
||||||
}
|
private DevMain() {}
|
||||||
|
|
||||||
private DevMain() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
int main() {}
|
int main() {}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,9 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
package edu.wpi.first.cameraserver;
|
package edu.wpi.first.cameraserver;
|
||||||
|
|
||||||
|
|
||||||
public interface CameraServerShared {
|
public interface CameraServerShared {
|
||||||
/**
|
/**
|
||||||
* get the main thread id func.
|
* get the main thread id func.
|
||||||
|
|||||||
@@ -1,56 +1,48 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
package edu.wpi.first.cameraserver;
|
package edu.wpi.first.cameraserver;
|
||||||
|
|
||||||
public final class CameraServerSharedStore {
|
public final class CameraServerSharedStore {
|
||||||
private static CameraServerShared cameraServerShared;
|
private static CameraServerShared cameraServerShared;
|
||||||
|
|
||||||
private CameraServerSharedStore() {
|
private CameraServerSharedStore() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the CameraServerShared object.
|
* Get the CameraServerShared object.
|
||||||
|
*
|
||||||
|
* @return The CameraServerSharedObject
|
||||||
*/
|
*/
|
||||||
public static synchronized CameraServerShared getCameraServerShared() {
|
public static synchronized CameraServerShared getCameraServerShared() {
|
||||||
if (cameraServerShared == null) {
|
if (cameraServerShared == null) {
|
||||||
cameraServerShared = new CameraServerShared() {
|
cameraServerShared =
|
||||||
|
new CameraServerShared() {
|
||||||
|
@Override
|
||||||
|
public void reportVideoServer(int id) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reportVideoServer(int id) {
|
public void reportUsbCamera(int id) {}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public void reportDriverStationError(String error) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reportUsbCamera(int id) {
|
public void reportAxisCamera(int id) {}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public Long getRobotMainThreadId() {
|
||||||
@Override
|
return null;
|
||||||
public void reportDriverStationError(String error) {
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reportAxisCamera(int id) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getRobotMainThreadId() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return cameraServerShared;
|
return cameraServerShared;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the CameraServerShared object.
|
* Set the CameraServerShared object.
|
||||||
|
*
|
||||||
|
* @param shared The CameraServerShared object.
|
||||||
*/
|
*/
|
||||||
public static synchronized void setCameraServerShared(CameraServerShared shared) {
|
public static synchronized void setCameraServerShared(CameraServerShared shared) {
|
||||||
cameraServerShared = shared;
|
cameraServerShared = shared;
|
||||||
|
|||||||
@@ -1,26 +1,24 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
package edu.wpi.first.vision;
|
package edu.wpi.first.vision;
|
||||||
|
|
||||||
import org.opencv.core.Mat;
|
import org.opencv.core.Mat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A vision pipeline is responsible for running a group of
|
* A vision pipeline is responsible for running a group of OpenCV algorithms to extract data from an
|
||||||
* OpenCV algorithms to extract data from an image.
|
* image.
|
||||||
*
|
*
|
||||||
* @see VisionRunner
|
* @see VisionRunner
|
||||||
* @see VisionThread
|
* @see VisionThread
|
||||||
*/
|
*/
|
||||||
public interface VisionPipeline {
|
public interface VisionPipeline {
|
||||||
/**
|
/**
|
||||||
* Processes the image input and sets the result objects.
|
* Processes the image input and sets the result objects. Implementations should make these
|
||||||
* Implementations should make these objects accessible.
|
* objects accessible.
|
||||||
|
*
|
||||||
|
* @param image The image to process.
|
||||||
*/
|
*/
|
||||||
void process(Mat image);
|
void process(Mat image);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
package edu.wpi.first.vision;
|
package edu.wpi.first.vision;
|
||||||
|
|
||||||
|
import edu.wpi.first.cameraserver.CameraServerSharedStore;
|
||||||
|
import edu.wpi.first.cscore.CvSink;
|
||||||
|
import edu.wpi.first.cscore.VideoSource;
|
||||||
import org.opencv.core.Mat;
|
import org.opencv.core.Mat;
|
||||||
|
|
||||||
import edu.wpi.cscore.CvSink;
|
|
||||||
import edu.wpi.cscore.VideoSource;
|
|
||||||
import edu.wpi.first.cameraserver.CameraServerSharedStore;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A vision runner is a convenient wrapper object to make it easy to run vision pipelines
|
* A vision runner is a convenient wrapper object to make it easy to run vision pipelines from robot
|
||||||
* from robot code. The easiest way to use this is to run it in a {@link VisionThread}
|
* code. The easiest way to use this is to run it in a {@link VisionThread} and use the listener to
|
||||||
* and use the listener to take snapshots of the pipeline's outputs.
|
* take snapshots of the pipeline's outputs.
|
||||||
*
|
*
|
||||||
* @see VisionPipeline
|
* @see VisionPipeline
|
||||||
* @see VisionThread
|
* @see VisionThread
|
||||||
@@ -45,17 +41,16 @@ public class VisionRunner<P extends VisionPipeline> {
|
|||||||
* @param pipeline the vision pipeline that ran
|
* @param pipeline the vision pipeline that ran
|
||||||
*/
|
*/
|
||||||
void copyPipelineOutputs(P pipeline);
|
void copyPipelineOutputs(P pipeline);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new vision runner. It will take images from the {@code videoSource}, send them to
|
* Creates a new vision runner. It will take images from the {@code videoSource}, send them to the
|
||||||
* the {@code pipeline}, and call the {@code listener} when the pipeline has finished to alert
|
* {@code pipeline}, and call the {@code listener} when the pipeline has finished to alert user
|
||||||
* user code when it is safe to access the pipeline's outputs.
|
* code when it is safe to access the pipeline's outputs.
|
||||||
*
|
*
|
||||||
* @param videoSource the video source to use to supply images for the pipeline
|
* @param videoSource the video source to use to supply images for the pipeline
|
||||||
* @param pipeline the vision pipeline to run
|
* @param pipeline the vision pipeline to run
|
||||||
* @param listener a function to call after the pipeline has finished running
|
* @param listener a function to call after the pipeline has finished running
|
||||||
*/
|
*/
|
||||||
public VisionRunner(VideoSource videoSource, P pipeline, Listener<? super P> listener) {
|
public VisionRunner(VideoSource videoSource, P pipeline, Listener<? super P> listener) {
|
||||||
this.m_pipeline = pipeline;
|
this.m_pipeline = pipeline;
|
||||||
@@ -64,15 +59,15 @@ public class VisionRunner<P extends VisionPipeline> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the pipeline one time, giving it the next image from the video source specified
|
* Runs the pipeline one time, giving it the next image from the video source specified in the
|
||||||
* in the constructor. This will block until the source either has an image or throws an error.
|
* constructor. This will block until the source either has an image or throws an error. If the
|
||||||
* If the source successfully supplied a frame, the pipeline's image input will be set,
|
* source successfully supplied a frame, the pipeline's image input will be set, the pipeline will
|
||||||
* the pipeline will run, and the listener specified in the constructor will be called to notify
|
* run, and the listener specified in the constructor will be called to notify it that the
|
||||||
* it that the pipeline ran.
|
* pipeline ran.
|
||||||
*
|
*
|
||||||
* <p>This method is exposed to allow teams to add additional functionality or have their own
|
* <p>This method is exposed to allow teams to add additional functionality or have their own ways
|
||||||
* ways to run the pipeline. Most teams, however, should just use {@link #runForever} in its own
|
* to run the pipeline. Most teams, however, should just use {@link #runForever} in its own thread
|
||||||
* thread using a {@link VisionThread}.</p>
|
* using a {@link VisionThread}.
|
||||||
*/
|
*/
|
||||||
public void runOnce() {
|
public void runOnce() {
|
||||||
Long id = CameraServerSharedStore.getCameraServerShared().getRobotMainThreadId();
|
Long id = CameraServerSharedStore.getCameraServerShared().getRobotMainThreadId();
|
||||||
@@ -98,11 +93,11 @@ public class VisionRunner<P extends VisionPipeline> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method that calls {@link #runOnce()} in an infinite loop. This must
|
* A convenience method that calls {@link #runOnce()} in an infinite loop. This must be run in a
|
||||||
* be run in a dedicated thread, and cannot be used in the main robot thread because
|
* dedicated thread, and cannot be used in the main robot thread because it will freeze the robot
|
||||||
* it will freeze the robot program.
|
* program.
|
||||||
*
|
*
|
||||||
* <p><strong>Do not call this method directly from the main thread.</strong></p>
|
* <p><strong>Do not call this method directly from the main thread.</strong>
|
||||||
*
|
*
|
||||||
* @throws IllegalStateException if this is called from the main robot thread
|
* @throws IllegalStateException if this is called from the main robot thread
|
||||||
* @see VisionThread
|
* @see VisionThread
|
||||||
@@ -119,9 +114,7 @@ public class VisionRunner<P extends VisionPipeline> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Stop a RunForever() loop. */
|
||||||
* Stop a RunForever() loop.
|
|
||||||
*/
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
m_enabled = false;
|
m_enabled = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
package edu.wpi.first.vision;
|
package edu.wpi.first.vision;
|
||||||
|
|
||||||
import edu.wpi.cscore.VideoSource;
|
import edu.wpi.first.cscore.VideoSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A vision thread is a special thread that runs a vision pipeline. It is a <i>daemon</i> thread;
|
* A vision thread is a special thread that runs a vision pipeline. It is a <i>daemon</i> thread; it
|
||||||
* it does not prevent the program from exiting when all other non-daemon threads
|
* does not prevent the program from exiting when all other non-daemon threads have finished
|
||||||
* have finished running.
|
* running.
|
||||||
*
|
*
|
||||||
* @see VisionPipeline
|
* @see VisionPipeline
|
||||||
* @see VisionRunner
|
* @see VisionRunner
|
||||||
@@ -34,14 +31,12 @@ public class VisionThread extends Thread {
|
|||||||
* equivalent to {@code new VisionThread(new VisionRunner<>(videoSource, pipeline, listener))}.
|
* equivalent to {@code new VisionThread(new VisionRunner<>(videoSource, pipeline, listener))}.
|
||||||
*
|
*
|
||||||
* @param videoSource the source for images the pipeline should process
|
* @param videoSource the source for images the pipeline should process
|
||||||
* @param pipeline the pipeline to run
|
* @param pipeline the pipeline to run
|
||||||
* @param listener the listener to copy outputs from the pipeline after it runs
|
* @param listener the listener to copy outputs from the pipeline after it runs
|
||||||
* @param <P> the type of the pipeline
|
* @param <P> the type of the pipeline
|
||||||
*/
|
*/
|
||||||
public <P extends VisionPipeline> VisionThread(VideoSource videoSource,
|
public <P extends VisionPipeline> VisionThread(
|
||||||
P pipeline,
|
VideoSource videoSource, P pipeline, VisionRunner.Listener<? super P> listener) {
|
||||||
VisionRunner.Listener<? super P> listener) {
|
|
||||||
this(new VisionRunner<>(videoSource, pipeline, listener));
|
this(new VisionRunner<>(videoSource, pipeline, listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classes in the {@code edu.wpi.first.vision} package are designed to
|
* Classes in the {@code edu.wpi.first.vision} package are designed to simplify using OpenCV vision
|
||||||
* simplify using OpenCV vision processing code from a robot program.
|
* processing code from a robot program.
|
||||||
|
*
|
||||||
|
* <p>An example use case for grabbing a yellow tote from 2015 in autonomous: <br>
|
||||||
*
|
*
|
||||||
* <p>An example use case for grabbing a yellow tote from 2015 in autonomous:
|
|
||||||
* <br>
|
|
||||||
* <pre><code>
|
* <pre><code>
|
||||||
* public class Robot extends IterativeRobot
|
* public class Robot extends TimedRobot
|
||||||
* implements VisionRunner.Listener<MyFindTotePipeline> {
|
* implements VisionRunner.Listener<MyFindTotePipeline> {
|
||||||
*
|
*
|
||||||
* // A USB camera connected to the roboRIO.
|
* // A USB camera connected to the roboRIO.
|
||||||
* private {@link edu.wpi.cscore.VideoSource VideoSource} usbCamera;
|
* private {@link edu.wpi.first.cscore.VideoSource VideoSource} usbCamera;
|
||||||
*
|
*
|
||||||
* // A vision pipeline. This could be handwritten or generated by GRIP.
|
* // A vision pipeline. This could be handwritten or generated by GRIP.
|
||||||
* // This has to implement {@link edu.wpi.first.vision.VisionPipeline}.
|
* // This has to implement {@link edu.wpi.first.vision.VisionPipeline}.
|
||||||
@@ -47,7 +44,7 @@
|
|||||||
*
|
*
|
||||||
* {@literal @}Override
|
* {@literal @}Override
|
||||||
* public void robotInit() {
|
* public void robotInit() {
|
||||||
* usbCamera = CameraServer.getInstance().startAutomaticCapture(0);
|
* usbCamera = CameraServer.startAutomaticCapture(0);
|
||||||
* findTotePipeline = new MyFindTotePipeline();
|
* findTotePipeline = new MyFindTotePipeline();
|
||||||
* findToteThread = new VisionThread(usbCamera, findTotePipeline, this);
|
* findToteThread = new VisionThread(usbCamera, findTotePipeline, this);
|
||||||
* }
|
* }
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
/*----------------------------------------------------------------------------*/
|
// Copyright (c) FIRST and other WPILib contributors.
|
||||||
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
|
// Open Source Software; you can modify and/or share it under the terms of
|
||||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
// the WPILib BSD license file in the root directory of this project.
|
||||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
||||||
/* the project. */
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "cameraserver/CameraServer.h"
|
#include "cameraserver/CameraServer.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <networktables/BooleanTopic.h>
|
||||||
|
#include <networktables/IntegerTopic.h>
|
||||||
#include <networktables/NetworkTable.h>
|
#include <networktables/NetworkTable.h>
|
||||||
#include <networktables/NetworkTableInstance.h>
|
#include <networktables/NetworkTableInstance.h>
|
||||||
|
#include <networktables/StringArrayTopic.h>
|
||||||
|
#include <networktables/StringTopic.h>
|
||||||
#include <wpi/DenseMap.h>
|
#include <wpi/DenseMap.h>
|
||||||
#include <wpi/ManagedStatic.h>
|
|
||||||
#include <wpi/SmallString.h>
|
#include <wpi/SmallString.h>
|
||||||
|
#include <wpi/StringExtras.h>
|
||||||
#include <wpi/StringMap.h>
|
#include <wpi/StringMap.h>
|
||||||
#include <wpi/mutex.h>
|
#include <wpi/mutex.h>
|
||||||
#include <wpi/raw_ostream.h>
|
|
||||||
|
|
||||||
#include "cameraserver/CameraServerShared.h"
|
#include "cameraserver/CameraServerShared.h"
|
||||||
#include "ntcore_cpp.h"
|
#include "ntcore_cpp.h"
|
||||||
@@ -26,9 +27,43 @@ using namespace frc;
|
|||||||
|
|
||||||
static constexpr char const* kPublishName = "/CameraPublisher";
|
static constexpr char const* kPublishName = "/CameraPublisher";
|
||||||
|
|
||||||
struct CameraServer::Impl {
|
namespace {
|
||||||
Impl();
|
|
||||||
std::shared_ptr<nt::NetworkTable> GetSourceTable(CS_Source source);
|
struct Instance;
|
||||||
|
|
||||||
|
struct PropertyPublisher {
|
||||||
|
PropertyPublisher(nt::NetworkTable& table, const cs::VideoEvent& event);
|
||||||
|
|
||||||
|
void Update(const cs::VideoEvent& event);
|
||||||
|
|
||||||
|
nt::BooleanEntry booleanValueEntry;
|
||||||
|
nt::IntegerEntry integerValueEntry;
|
||||||
|
nt::StringEntry stringValueEntry;
|
||||||
|
nt::IntegerPublisher minPublisher;
|
||||||
|
nt::IntegerPublisher maxPublisher;
|
||||||
|
nt::IntegerPublisher stepPublisher;
|
||||||
|
nt::IntegerPublisher defaultPublisher;
|
||||||
|
nt::StringArrayTopic choicesTopic;
|
||||||
|
nt::StringArrayPublisher choicesPublisher;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SourcePublisher {
|
||||||
|
SourcePublisher(Instance& inst, std::shared_ptr<nt::NetworkTable> table,
|
||||||
|
CS_Source source);
|
||||||
|
|
||||||
|
std::shared_ptr<nt::NetworkTable> table;
|
||||||
|
nt::StringPublisher sourcePublisher;
|
||||||
|
nt::StringPublisher descriptionPublisher;
|
||||||
|
nt::BooleanPublisher connectedPublisher;
|
||||||
|
nt::StringArrayPublisher streamsPublisher;
|
||||||
|
nt::StringEntry modeEntry;
|
||||||
|
nt::StringArrayPublisher modesPublisher;
|
||||||
|
wpi::DenseMap<CS_Property, PropertyPublisher> properties;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Instance {
|
||||||
|
Instance();
|
||||||
|
SourcePublisher* GetPublisher(CS_Source source);
|
||||||
std::vector<std::string> GetSinkStreamValues(CS_Sink sink);
|
std::vector<std::string> GetSinkStreamValues(CS_Sink sink);
|
||||||
std::vector<std::string> GetSourceStreamValues(CS_Source source);
|
std::vector<std::string> GetSourceStreamValues(CS_Source source);
|
||||||
void UpdateStreamValues();
|
void UpdateStreamValues();
|
||||||
@@ -39,42 +74,41 @@ struct CameraServer::Impl {
|
|||||||
wpi::StringMap<cs::VideoSource> m_sources;
|
wpi::StringMap<cs::VideoSource> m_sources;
|
||||||
wpi::StringMap<cs::VideoSink> m_sinks;
|
wpi::StringMap<cs::VideoSink> m_sinks;
|
||||||
wpi::DenseMap<CS_Sink, CS_Source> m_fixedSources;
|
wpi::DenseMap<CS_Sink, CS_Source> m_fixedSources;
|
||||||
wpi::DenseMap<CS_Source, std::shared_ptr<nt::NetworkTable>> m_tables;
|
wpi::DenseMap<CS_Source, SourcePublisher> m_publishers;
|
||||||
std::shared_ptr<nt::NetworkTable> m_publishTable;
|
std::shared_ptr<nt::NetworkTable> m_publishTable{
|
||||||
|
nt::NetworkTableInstance::GetDefault().GetTable(kPublishName)};
|
||||||
cs::VideoListener m_videoListener;
|
cs::VideoListener m_videoListener;
|
||||||
int m_tableListener;
|
int m_tableListener;
|
||||||
int m_nextPort;
|
int m_nextPort{CameraServer::kBasePort};
|
||||||
std::vector<std::string> m_addresses;
|
std::vector<std::string> m_addresses;
|
||||||
};
|
};
|
||||||
|
|
||||||
CameraServer* CameraServer::GetInstance() {
|
} // namespace
|
||||||
struct Creator {
|
|
||||||
static void* call() { return new CameraServer{}; }
|
static Instance& GetInstance() {
|
||||||
};
|
static Instance instance;
|
||||||
struct Deleter {
|
return instance;
|
||||||
static void call(void* ptr) { delete static_cast<CameraServer*>(ptr); }
|
|
||||||
};
|
|
||||||
static wpi::ManagedStatic<CameraServer, Creator, Deleter> instance;
|
|
||||||
return &(*instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static wpi::StringRef MakeSourceValue(CS_Source source,
|
static std::string_view MakeSourceValue(CS_Source source,
|
||||||
wpi::SmallVectorImpl<char>& buf) {
|
wpi::SmallVectorImpl<char>& buf) {
|
||||||
CS_Status status = 0;
|
CS_Status status = 0;
|
||||||
buf.clear();
|
buf.clear();
|
||||||
switch (cs::GetSourceKind(source, &status)) {
|
switch (cs::GetSourceKind(source, &status)) {
|
||||||
case CS_SOURCE_USB: {
|
case CS_SOURCE_USB: {
|
||||||
wpi::StringRef prefix{"usb:"};
|
std::string_view prefix{"usb:"};
|
||||||
buf.append(prefix.begin(), prefix.end());
|
buf.append(prefix.begin(), prefix.end());
|
||||||
auto path = cs::GetUsbCameraPath(source, &status);
|
auto path = cs::GetUsbCameraPath(source, &status);
|
||||||
buf.append(path.begin(), path.end());
|
buf.append(path.begin(), path.end());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CS_SOURCE_HTTP: {
|
case CS_SOURCE_HTTP: {
|
||||||
wpi::StringRef prefix{"ip:"};
|
std::string_view prefix{"ip:"};
|
||||||
buf.append(prefix.begin(), prefix.end());
|
buf.append(prefix.begin(), prefix.end());
|
||||||
auto urls = cs::GetHttpCameraUrls(source, &status);
|
auto urls = cs::GetHttpCameraUrls(source, &status);
|
||||||
if (!urls.empty()) buf.append(urls[0].begin(), urls[0].end());
|
if (!urls.empty()) {
|
||||||
|
buf.append(urls[0].begin(), urls[0].end());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CS_SOURCE_CV:
|
case CS_SOURCE_CV:
|
||||||
@@ -83,27 +117,29 @@ static wpi::StringRef MakeSourceValue(CS_Source source,
|
|||||||
return "unknown:";
|
return "unknown:";
|
||||||
}
|
}
|
||||||
|
|
||||||
return wpi::StringRef{buf.begin(), buf.size()};
|
return {buf.begin(), buf.size()};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string MakeStreamValue(const wpi::Twine& address, int port) {
|
static std::string MakeStreamValue(std::string_view address, int port) {
|
||||||
return ("mjpg:http://" + address + wpi::Twine(':') + wpi::Twine(port) +
|
return fmt::format("mjpg:http://{}:{}/?action=stream", address, port);
|
||||||
"/?action=stream")
|
|
||||||
.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<nt::NetworkTable> CameraServer::Impl::GetSourceTable(
|
SourcePublisher* Instance::GetPublisher(CS_Source source) {
|
||||||
CS_Source source) {
|
auto it = m_publishers.find(source);
|
||||||
std::scoped_lock lock(m_mutex);
|
if (it != m_publishers.end()) {
|
||||||
return m_tables.lookup(source);
|
return &it->second;
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> CameraServer::Impl::GetSinkStreamValues(CS_Sink sink) {
|
std::vector<std::string> Instance::GetSinkStreamValues(CS_Sink sink) {
|
||||||
CS_Status status = 0;
|
CS_Status status = 0;
|
||||||
|
|
||||||
// Ignore all but MjpegServer
|
// Ignore all but MjpegServer
|
||||||
if (cs::GetSinkKind(sink, &status) != CS_SINK_MJPEG)
|
if (cs::GetSinkKind(sink, &status) != CS_SINK_MJPEG) {
|
||||||
return std::vector<std::string>{};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Get port
|
// Get port
|
||||||
int port = cs::GetMjpegServerPort(sink, &status);
|
int port = cs::GetMjpegServerPort(sink, &status);
|
||||||
@@ -119,7 +155,9 @@ std::vector<std::string> CameraServer::Impl::GetSinkStreamValues(CS_Sink sink) {
|
|||||||
values.emplace_back(MakeStreamValue(cs::GetHostname() + ".local", port));
|
values.emplace_back(MakeStreamValue(cs::GetHostname() + ".local", port));
|
||||||
|
|
||||||
for (const auto& addr : m_addresses) {
|
for (const auto& addr : m_addresses) {
|
||||||
if (addr == "127.0.0.1") continue; // ignore localhost
|
if (addr == "127.0.0.1") {
|
||||||
|
continue; // ignore localhost
|
||||||
|
}
|
||||||
values.emplace_back(MakeStreamValue(addr, port));
|
values.emplace_back(MakeStreamValue(addr, port));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,17 +165,19 @@ std::vector<std::string> CameraServer::Impl::GetSinkStreamValues(CS_Sink sink) {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> CameraServer::Impl::GetSourceStreamValues(
|
std::vector<std::string> Instance::GetSourceStreamValues(CS_Source source) {
|
||||||
CS_Source source) {
|
|
||||||
CS_Status status = 0;
|
CS_Status status = 0;
|
||||||
|
|
||||||
// Ignore all but HttpCamera
|
// Ignore all but HttpCamera
|
||||||
if (cs::GetSourceKind(source, &status) != CS_SOURCE_HTTP)
|
if (cs::GetSourceKind(source, &status) != CS_SOURCE_HTTP) {
|
||||||
return std::vector<std::string>{};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Generate values
|
// Generate values
|
||||||
auto values = cs::GetHttpCameraUrls(source, &status);
|
auto values = cs::GetHttpCameraUrls(source, &status);
|
||||||
for (auto& value : values) value = "mjpg:" + value;
|
for (auto& value : values) {
|
||||||
|
value = "mjpg:" + value;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __FRC_ROBORIO__
|
#ifdef __FRC_ROBORIO__
|
||||||
// Look to see if we have a passthrough server for this source
|
// Look to see if we have a passthrough server for this source
|
||||||
@@ -159,8 +199,7 @@ std::vector<std::string> CameraServer::Impl::GetSourceStreamValues(
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraServer::Impl::UpdateStreamValues() {
|
void Instance::UpdateStreamValues() {
|
||||||
std::scoped_lock lock(m_mutex);
|
|
||||||
// Over all the sinks...
|
// Over all the sinks...
|
||||||
for (const auto& i : m_sinks) {
|
for (const auto& i : m_sinks) {
|
||||||
CS_Status status = 0;
|
CS_Status status = 0;
|
||||||
@@ -168,16 +207,23 @@ void CameraServer::Impl::UpdateStreamValues() {
|
|||||||
|
|
||||||
// Get the source's subtable (if none exists, we're done)
|
// Get the source's subtable (if none exists, we're done)
|
||||||
CS_Source source = m_fixedSources.lookup(sink);
|
CS_Source source = m_fixedSources.lookup(sink);
|
||||||
if (source == 0) source = cs::GetSinkSource(sink, &status);
|
if (source == 0) {
|
||||||
if (source == 0) continue;
|
source = cs::GetSinkSource(sink, &status);
|
||||||
auto table = m_tables.lookup(source);
|
}
|
||||||
if (table) {
|
if (source == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (auto publisher = GetPublisher(source)) {
|
||||||
// Don't set stream values if this is a HttpCamera passthrough
|
// Don't set stream values if this is a HttpCamera passthrough
|
||||||
if (cs::GetSourceKind(source, &status) == CS_SOURCE_HTTP) continue;
|
if (cs::GetSourceKind(source, &status) == CS_SOURCE_HTTP) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Set table value
|
// Set table value
|
||||||
auto values = GetSinkStreamValues(sink);
|
auto values = GetSinkStreamValues(sink);
|
||||||
if (!values.empty()) table->GetEntry("streams").SetStringArray(values);
|
if (!values.empty()) {
|
||||||
|
publisher->streamsPublisher.Set(values);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,11 +232,12 @@ void CameraServer::Impl::UpdateStreamValues() {
|
|||||||
CS_Source source = i.second.GetHandle();
|
CS_Source source = i.second.GetHandle();
|
||||||
|
|
||||||
// Get the source's subtable (if none exists, we're done)
|
// Get the source's subtable (if none exists, we're done)
|
||||||
auto table = m_tables.lookup(source);
|
if (auto publisher = GetPublisher(source)) {
|
||||||
if (table) {
|
|
||||||
// Set table value
|
// Set table value
|
||||||
auto values = GetSourceStreamValues(source);
|
auto values = GetSourceStreamValues(source);
|
||||||
if (!values.empty()) table->GetEntry("streams").SetStringArray(values);
|
if (!values.empty()) {
|
||||||
|
publisher->streamsPublisher.Set(values);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,79 +260,114 @@ static std::string PixelFormatToString(int pixelFormat) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string VideoModeToString(const cs::VideoMode& mode) {
|
static std::string VideoModeToString(const cs::VideoMode& mode) {
|
||||||
std::string rv;
|
return fmt::format("{}x{} {} {} fps", mode.width, mode.height,
|
||||||
wpi::raw_string_ostream oss{rv};
|
PixelFormatToString(mode.pixelFormat), mode.fps);
|
||||||
oss << mode.width << "x" << mode.height;
|
|
||||||
oss << " " << PixelFormatToString(mode.pixelFormat) << " ";
|
|
||||||
oss << mode.fps << " fps";
|
|
||||||
return oss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::string> GetSourceModeValues(int source) {
|
static std::vector<std::string> GetSourceModeValues(int source) {
|
||||||
std::vector<std::string> rv;
|
std::vector<std::string> rv;
|
||||||
CS_Status status = 0;
|
CS_Status status = 0;
|
||||||
for (const auto& mode : cs::EnumerateSourceVideoModes(source, &status))
|
for (const auto& mode : cs::EnumerateSourceVideoModes(source, &status)) {
|
||||||
rv.emplace_back(VideoModeToString(mode));
|
rv.emplace_back(VideoModeToString(mode));
|
||||||
|
}
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PutSourcePropertyValue(nt::NetworkTable* table,
|
PropertyPublisher::PropertyPublisher(nt::NetworkTable& table,
|
||||||
const cs::VideoEvent& event, bool isNew) {
|
const cs::VideoEvent& event) {
|
||||||
wpi::SmallString<64> name;
|
std::string name;
|
||||||
wpi::SmallString<64> infoName;
|
std::string infoName;
|
||||||
if (wpi::StringRef{event.name}.startswith("raw_")) {
|
if (wpi::starts_with(event.name, "raw_")) {
|
||||||
name = "RawProperty/";
|
name = fmt::format("RawProperty/{}", event.name);
|
||||||
name += event.name;
|
infoName = fmt::format("RawPropertyInfo/{}", event.name);
|
||||||
infoName = "RawPropertyInfo/";
|
|
||||||
infoName += event.name;
|
|
||||||
} else {
|
} else {
|
||||||
name = "Property/";
|
name = fmt::format("Property/{}", event.name);
|
||||||
name += event.name;
|
infoName = fmt::format("PropertyInfo/{}", event.name);
|
||||||
infoName = "PropertyInfo/";
|
|
||||||
infoName += event.name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wpi::SmallString<64> buf;
|
|
||||||
CS_Status status = 0;
|
CS_Status status = 0;
|
||||||
nt::NetworkTableEntry entry = table->GetEntry(name);
|
|
||||||
switch (event.propertyKind) {
|
switch (event.propertyKind) {
|
||||||
case CS_PROP_BOOLEAN:
|
case CS_PROP_BOOLEAN:
|
||||||
if (isNew)
|
booleanValueEntry = table.GetBooleanTopic(name).GetEntry(false);
|
||||||
entry.SetDefaultBoolean(event.value != 0);
|
booleanValueEntry.SetDefault(event.value != 0);
|
||||||
else
|
|
||||||
entry.SetBoolean(event.value != 0);
|
|
||||||
break;
|
break;
|
||||||
case CS_PROP_INTEGER:
|
|
||||||
case CS_PROP_ENUM:
|
case CS_PROP_ENUM:
|
||||||
if (isNew) {
|
choicesTopic =
|
||||||
entry.SetDefaultDouble(event.value);
|
table.GetStringArrayTopic(fmt::format("{}/choices", infoName));
|
||||||
table->GetEntry(infoName + "/min")
|
[[fallthrough]];
|
||||||
.SetDouble(cs::GetPropertyMin(event.propertyHandle, &status));
|
case CS_PROP_INTEGER:
|
||||||
table->GetEntry(infoName + "/max")
|
integerValueEntry = table.GetIntegerTopic(name).GetEntry(0);
|
||||||
.SetDouble(cs::GetPropertyMax(event.propertyHandle, &status));
|
minPublisher =
|
||||||
table->GetEntry(infoName + "/step")
|
table.GetIntegerTopic(fmt::format("{}/min", infoName)).Publish();
|
||||||
.SetDouble(cs::GetPropertyStep(event.propertyHandle, &status));
|
maxPublisher =
|
||||||
table->GetEntry(infoName + "/default")
|
table.GetIntegerTopic(fmt::format("{}/max", infoName)).Publish();
|
||||||
.SetDouble(cs::GetPropertyDefault(event.propertyHandle, &status));
|
stepPublisher =
|
||||||
} else {
|
table.GetIntegerTopic(fmt::format("{}/step", infoName)).Publish();
|
||||||
entry.SetDouble(event.value);
|
defaultPublisher =
|
||||||
}
|
table.GetIntegerTopic(fmt::format("{}/default", infoName)).Publish();
|
||||||
|
|
||||||
|
integerValueEntry.SetDefault(event.value);
|
||||||
|
minPublisher.Set(cs::GetPropertyMin(event.propertyHandle, &status));
|
||||||
|
maxPublisher.Set(cs::GetPropertyMax(event.propertyHandle, &status));
|
||||||
|
stepPublisher.Set(cs::GetPropertyStep(event.propertyHandle, &status));
|
||||||
|
defaultPublisher.Set(
|
||||||
|
cs::GetPropertyDefault(event.propertyHandle, &status));
|
||||||
break;
|
break;
|
||||||
case CS_PROP_STRING:
|
case CS_PROP_STRING:
|
||||||
if (isNew)
|
stringValueEntry = table.GetStringTopic(name).GetEntry("");
|
||||||
entry.SetDefaultString(event.valueStr);
|
stringValueEntry.SetDefault(event.valueStr);
|
||||||
else
|
|
||||||
entry.SetString(event.valueStr);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraServer::Impl::Impl()
|
void PropertyPublisher::Update(const cs::VideoEvent& event) {
|
||||||
: m_publishTable{nt::NetworkTableInstance::GetDefault().GetTable(
|
switch (event.propertyKind) {
|
||||||
kPublishName)},
|
case CS_PROP_BOOLEAN:
|
||||||
m_nextPort(kBasePort) {
|
if (booleanValueEntry) {
|
||||||
|
booleanValueEntry.Set(event.value != 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CS_PROP_INTEGER:
|
||||||
|
case CS_PROP_ENUM:
|
||||||
|
if (integerValueEntry) {
|
||||||
|
integerValueEntry.Set(event.value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CS_PROP_STRING:
|
||||||
|
if (stringValueEntry) {
|
||||||
|
stringValueEntry.Set(event.valueStr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SourcePublisher::SourcePublisher(Instance& inst,
|
||||||
|
std::shared_ptr<nt::NetworkTable> table,
|
||||||
|
CS_Source source)
|
||||||
|
: table{table},
|
||||||
|
sourcePublisher{table->GetStringTopic("source").Publish()},
|
||||||
|
descriptionPublisher{table->GetStringTopic("description").Publish()},
|
||||||
|
connectedPublisher{table->GetBooleanTopic("connected").Publish()},
|
||||||
|
streamsPublisher{table->GetStringArrayTopic("streams").Publish()},
|
||||||
|
modeEntry{table->GetStringTopic("mode").GetEntry("")},
|
||||||
|
modesPublisher{table->GetStringArrayTopic("modes").Publish()} {
|
||||||
|
CS_Status status = 0;
|
||||||
|
wpi::SmallString<64> buf;
|
||||||
|
sourcePublisher.Set(MakeSourceValue(source, buf));
|
||||||
|
wpi::SmallString<64> descBuf;
|
||||||
|
descriptionPublisher.Set(cs::GetSourceDescription(source, descBuf, &status));
|
||||||
|
connectedPublisher.Set(cs::IsSourceConnected(source, &status));
|
||||||
|
streamsPublisher.Set(inst.GetSourceStreamValues(source));
|
||||||
|
auto mode = cs::GetSourceVideoMode(source, &status);
|
||||||
|
modeEntry.SetDefault(VideoModeToString(mode));
|
||||||
|
modesPublisher.Set(GetSourceModeValues(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance::Instance() {
|
||||||
// We publish sources to NetworkTables using the following structure:
|
// We publish sources to NetworkTables using the following structure:
|
||||||
// "/CameraPublisher/{Source.Name}/" - root
|
// "/CameraPublisher/{Source.Name}/" - root
|
||||||
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
|
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
|
||||||
@@ -299,188 +381,107 @@ CameraServer::Impl::Impl()
|
|||||||
|
|
||||||
// Listener for video events
|
// Listener for video events
|
||||||
m_videoListener = cs::VideoListener{
|
m_videoListener = cs::VideoListener{
|
||||||
[=](const cs::VideoEvent& event) {
|
[=, this](const cs::VideoEvent& event) {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
CS_Status status = 0;
|
CS_Status status = 0;
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case cs::VideoEvent::kSourceCreated: {
|
case cs::VideoEvent::kSourceCreated: {
|
||||||
// Create subtable for the camera
|
// Create subtable for the camera
|
||||||
auto table = m_publishTable->GetSubTable(event.name);
|
auto table = m_publishTable->GetSubTable(event.name);
|
||||||
{
|
m_publishers.insert(
|
||||||
std::scoped_lock lock(m_mutex);
|
{event.sourceHandle,
|
||||||
m_tables.insert(std::make_pair(event.sourceHandle, table));
|
SourcePublisher{*this, table, event.sourceHandle}});
|
||||||
}
|
|
||||||
wpi::SmallString<64> buf;
|
|
||||||
table->GetEntry("source").SetString(
|
|
||||||
MakeSourceValue(event.sourceHandle, buf));
|
|
||||||
wpi::SmallString<64> descBuf;
|
|
||||||
table->GetEntry("description")
|
|
||||||
.SetString(cs::GetSourceDescription(event.sourceHandle, descBuf,
|
|
||||||
&status));
|
|
||||||
table->GetEntry("connected")
|
|
||||||
.SetBoolean(cs::IsSourceConnected(event.sourceHandle, &status));
|
|
||||||
table->GetEntry("streams").SetStringArray(
|
|
||||||
GetSourceStreamValues(event.sourceHandle));
|
|
||||||
auto mode = cs::GetSourceVideoMode(event.sourceHandle, &status);
|
|
||||||
table->GetEntry("mode").SetDefaultString(VideoModeToString(mode));
|
|
||||||
table->GetEntry("modes").SetStringArray(
|
|
||||||
GetSourceModeValues(event.sourceHandle));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case cs::VideoEvent::kSourceDestroyed: {
|
case cs::VideoEvent::kSourceDestroyed:
|
||||||
auto table = GetSourceTable(event.sourceHandle);
|
m_publishers.erase(event.sourceHandle);
|
||||||
if (table) {
|
|
||||||
table->GetEntry("source").SetString("");
|
|
||||||
table->GetEntry("streams").SetStringArray(
|
|
||||||
std::vector<std::string>{});
|
|
||||||
table->GetEntry("modes").SetStringArray(
|
|
||||||
std::vector<std::string>{});
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
case cs::VideoEvent::kSourceConnected:
|
||||||
case cs::VideoEvent::kSourceConnected: {
|
if (auto publisher = GetPublisher(event.sourceHandle)) {
|
||||||
auto table = GetSourceTable(event.sourceHandle);
|
|
||||||
if (table) {
|
|
||||||
// update the description too (as it may have changed)
|
// update the description too (as it may have changed)
|
||||||
wpi::SmallString<64> descBuf;
|
wpi::SmallString<64> descBuf;
|
||||||
table->GetEntry("description")
|
publisher->descriptionPublisher.Set(cs::GetSourceDescription(
|
||||||
.SetString(cs::GetSourceDescription(event.sourceHandle,
|
event.sourceHandle, descBuf, &status));
|
||||||
descBuf, &status));
|
publisher->connectedPublisher.Set(true);
|
||||||
table->GetEntry("connected").SetBoolean(true);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case cs::VideoEvent::kSourceDisconnected:
|
||||||
case cs::VideoEvent::kSourceDisconnected: {
|
if (auto publisher = GetPublisher(event.sourceHandle)) {
|
||||||
auto table = GetSourceTable(event.sourceHandle);
|
publisher->connectedPublisher.Set(false);
|
||||||
if (table) table->GetEntry("connected").SetBoolean(false);
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case cs::VideoEvent::kSourceVideoModesUpdated:
|
||||||
case cs::VideoEvent::kSourceVideoModesUpdated: {
|
if (auto publisher = GetPublisher(event.sourceHandle)) {
|
||||||
auto table = GetSourceTable(event.sourceHandle);
|
publisher->modesPublisher.Set(
|
||||||
if (table)
|
|
||||||
table->GetEntry("modes").SetStringArray(
|
|
||||||
GetSourceModeValues(event.sourceHandle));
|
GetSourceModeValues(event.sourceHandle));
|
||||||
break;
|
|
||||||
}
|
|
||||||
case cs::VideoEvent::kSourceVideoModeChanged: {
|
|
||||||
auto table = GetSourceTable(event.sourceHandle);
|
|
||||||
if (table)
|
|
||||||
table->GetEntry("mode").SetString(VideoModeToString(event.mode));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case cs::VideoEvent::kSourcePropertyCreated: {
|
|
||||||
auto table = GetSourceTable(event.sourceHandle);
|
|
||||||
if (table) PutSourcePropertyValue(table.get(), event, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case cs::VideoEvent::kSourcePropertyValueUpdated: {
|
|
||||||
auto table = GetSourceTable(event.sourceHandle);
|
|
||||||
if (table) PutSourcePropertyValue(table.get(), event, false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case cs::VideoEvent::kSourcePropertyChoicesUpdated: {
|
|
||||||
auto table = GetSourceTable(event.sourceHandle);
|
|
||||||
if (table) {
|
|
||||||
wpi::SmallString<64> name{"PropertyInfo/"};
|
|
||||||
name += event.name;
|
|
||||||
name += "/choices";
|
|
||||||
auto choices =
|
|
||||||
cs::GetEnumPropertyChoices(event.propertyHandle, &status);
|
|
||||||
table->GetEntry(name).SetStringArray(choices);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case cs::VideoEvent::kSourceVideoModeChanged:
|
||||||
|
if (auto publisher = GetPublisher(event.sourceHandle)) {
|
||||||
|
publisher->modeEntry.Set(VideoModeToString(event.mode));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case cs::VideoEvent::kSourcePropertyCreated:
|
||||||
|
if (auto publisher = GetPublisher(event.sourceHandle)) {
|
||||||
|
publisher->properties.insert(
|
||||||
|
{event.propertyHandle,
|
||||||
|
PropertyPublisher{*publisher->table, event}});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case cs::VideoEvent::kSourcePropertyValueUpdated:
|
||||||
|
if (auto publisher = GetPublisher(event.sourceHandle)) {
|
||||||
|
auto ppIt = publisher->properties.find(event.propertyHandle);
|
||||||
|
if (ppIt != publisher->properties.end()) {
|
||||||
|
ppIt->second.Update(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case cs::VideoEvent::kSourcePropertyChoicesUpdated:
|
||||||
|
if (auto publisher = GetPublisher(event.sourceHandle)) {
|
||||||
|
auto ppIt = publisher->properties.find(event.propertyHandle);
|
||||||
|
if (ppIt != publisher->properties.end() &&
|
||||||
|
ppIt->second.choicesTopic) {
|
||||||
|
auto choices =
|
||||||
|
cs::GetEnumPropertyChoices(event.propertyHandle, &status);
|
||||||
|
if (!ppIt->second.choicesPublisher) {
|
||||||
|
ppIt->second.choicesPublisher =
|
||||||
|
ppIt->second.choicesTopic.Publish();
|
||||||
|
}
|
||||||
|
ppIt->second.choicesPublisher.Set(choices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case cs::VideoEvent::kSinkSourceChanged:
|
case cs::VideoEvent::kSinkSourceChanged:
|
||||||
case cs::VideoEvent::kSinkCreated:
|
case cs::VideoEvent::kSinkCreated:
|
||||||
case cs::VideoEvent::kSinkDestroyed:
|
case cs::VideoEvent::kSinkDestroyed:
|
||||||
case cs::VideoEvent::kNetworkInterfacesChanged: {
|
case cs::VideoEvent::kNetworkInterfacesChanged:
|
||||||
m_addresses = cs::GetNetworkInterfaces();
|
m_addresses = cs::GetNetworkInterfaces();
|
||||||
UpdateStreamValues();
|
UpdateStreamValues();
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
0x4fff, true};
|
0x4fff, true};
|
||||||
|
|
||||||
// Listener for NetworkTable events
|
|
||||||
// We don't currently support changing settings via NT due to
|
|
||||||
// synchronization issues, so just update to current setting if someone
|
|
||||||
// else tries to change it.
|
|
||||||
wpi::SmallString<64> buf;
|
|
||||||
m_tableListener = nt::NetworkTableInstance::GetDefault().AddEntryListener(
|
|
||||||
kPublishName + wpi::Twine('/'),
|
|
||||||
[=](const nt::EntryNotification& event) {
|
|
||||||
wpi::StringRef relativeKey =
|
|
||||||
event.name.substr(wpi::StringRef(kPublishName).size() + 1);
|
|
||||||
|
|
||||||
// get source (sourceName/...)
|
|
||||||
auto subKeyIndex = relativeKey.find('/');
|
|
||||||
if (subKeyIndex == wpi::StringRef::npos) return;
|
|
||||||
wpi::StringRef sourceName = relativeKey.slice(0, subKeyIndex);
|
|
||||||
auto sourceIt = m_sources.find(sourceName);
|
|
||||||
if (sourceIt == m_sources.end()) return;
|
|
||||||
|
|
||||||
// get subkey
|
|
||||||
relativeKey = relativeKey.substr(subKeyIndex + 1);
|
|
||||||
|
|
||||||
// handle standard names
|
|
||||||
wpi::StringRef propName;
|
|
||||||
nt::NetworkTableEntry entry{event.entry};
|
|
||||||
if (relativeKey == "mode") {
|
|
||||||
// reset to current mode
|
|
||||||
entry.SetString(VideoModeToString(sourceIt->second.GetVideoMode()));
|
|
||||||
return;
|
|
||||||
} else if (relativeKey.startswith("Property/")) {
|
|
||||||
propName = relativeKey.substr(9);
|
|
||||||
} else if (relativeKey.startswith("RawProperty/")) {
|
|
||||||
propName = relativeKey.substr(12);
|
|
||||||
} else {
|
|
||||||
return; // ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
// everything else is a property
|
|
||||||
auto property = sourceIt->second.GetProperty(propName);
|
|
||||||
switch (property.GetKind()) {
|
|
||||||
case cs::VideoProperty::kNone:
|
|
||||||
return;
|
|
||||||
case cs::VideoProperty::kBoolean:
|
|
||||||
entry.SetBoolean(property.Get() != 0);
|
|
||||||
return;
|
|
||||||
case cs::VideoProperty::kInteger:
|
|
||||||
case cs::VideoProperty::kEnum:
|
|
||||||
entry.SetDouble(property.Get());
|
|
||||||
return;
|
|
||||||
case cs::VideoProperty::kString:
|
|
||||||
entry.SetString(property.GetString());
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_UPDATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraServer::CameraServer() : m_impl(new Impl) {}
|
|
||||||
|
|
||||||
CameraServer::~CameraServer() {}
|
|
||||||
|
|
||||||
cs::UsbCamera CameraServer::StartAutomaticCapture() {
|
cs::UsbCamera CameraServer::StartAutomaticCapture() {
|
||||||
cs::UsbCamera camera = StartAutomaticCapture(m_impl->m_defaultUsbDevice++);
|
cs::UsbCamera camera =
|
||||||
|
StartAutomaticCapture(::GetInstance().m_defaultUsbDevice++);
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
csShared->ReportUsbCamera(camera.GetHandle());
|
csShared->ReportUsbCamera(camera.GetHandle());
|
||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::UsbCamera CameraServer::StartAutomaticCapture(int dev) {
|
cs::UsbCamera CameraServer::StartAutomaticCapture(int dev) {
|
||||||
cs::UsbCamera camera{"USB Camera " + wpi::Twine(dev), dev};
|
cs::UsbCamera camera{fmt::format("USB Camera {}", dev), dev};
|
||||||
StartAutomaticCapture(camera);
|
StartAutomaticCapture(camera);
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
csShared->ReportUsbCamera(camera.GetHandle());
|
csShared->ReportUsbCamera(camera.GetHandle());
|
||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
|
cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
|
||||||
int dev) {
|
int dev) {
|
||||||
cs::UsbCamera camera{name, dev};
|
cs::UsbCamera camera{name, dev};
|
||||||
StartAutomaticCapture(camera);
|
StartAutomaticCapture(camera);
|
||||||
@@ -489,8 +490,8 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
|
|||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
|
cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
|
||||||
const wpi::Twine& path) {
|
std::string_view path) {
|
||||||
cs::UsbCamera camera{name, path};
|
cs::UsbCamera camera{name, path};
|
||||||
StartAutomaticCapture(camera);
|
StartAutomaticCapture(camera);
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
@@ -498,7 +499,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
|
|||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& host) {
|
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view host) {
|
||||||
return AddAxisCamera("Axis Camera", host);
|
return AddAxisCamera("Axis Camera", host);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,12 +511,12 @@ cs::AxisCamera CameraServer::AddAxisCamera(const std::string& host) {
|
|||||||
return AddAxisCamera("Axis Camera", host);
|
return AddAxisCamera("Axis Camera", host);
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::AxisCamera CameraServer::AddAxisCamera(wpi::ArrayRef<std::string> hosts) {
|
cs::AxisCamera CameraServer::AddAxisCamera(std::span<const std::string> hosts) {
|
||||||
return AddAxisCamera("Axis Camera", hosts);
|
return AddAxisCamera("Axis Camera", hosts);
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||||
const wpi::Twine& host) {
|
std::string_view host) {
|
||||||
cs::AxisCamera camera{name, host};
|
cs::AxisCamera camera{name, host};
|
||||||
StartAutomaticCapture(camera);
|
StartAutomaticCapture(camera);
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
@@ -523,7 +524,7 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
|||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||||
const char* host) {
|
const char* host) {
|
||||||
cs::AxisCamera camera{name, host};
|
cs::AxisCamera camera{name, host};
|
||||||
StartAutomaticCapture(camera);
|
StartAutomaticCapture(camera);
|
||||||
@@ -532,7 +533,7 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
|||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||||
const std::string& host) {
|
const std::string& host) {
|
||||||
cs::AxisCamera camera{name, host};
|
cs::AxisCamera camera{name, host};
|
||||||
StartAutomaticCapture(camera);
|
StartAutomaticCapture(camera);
|
||||||
@@ -541,8 +542,8 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
|||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||||
wpi::ArrayRef<std::string> hosts) {
|
std::span<const std::string> hosts) {
|
||||||
cs::AxisCamera camera{name, hosts};
|
cs::AxisCamera camera{name, hosts};
|
||||||
StartAutomaticCapture(camera);
|
StartAutomaticCapture(camera);
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
@@ -550,11 +551,11 @@ cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
|||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::MjpegServer CameraServer::AddSwitchedCamera(const wpi::Twine& name) {
|
cs::MjpegServer CameraServer::AddSwitchedCamera(std::string_view name) {
|
||||||
// create a dummy CvSource
|
// create a dummy CvSource
|
||||||
cs::CvSource source{name, cs::VideoMode::PixelFormat::kMJPEG, 160, 120, 30};
|
cs::CvSource source{name, cs::VideoMode::PixelFormat::kMJPEG, 160, 120, 30};
|
||||||
cs::MjpegServer server = StartAutomaticCapture(source);
|
cs::MjpegServer server = StartAutomaticCapture(source);
|
||||||
m_impl->m_fixedSources[server.GetHandle()] = source.GetHandle();
|
::GetInstance().m_fixedSources[server.GetHandle()] = source.GetHandle();
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
@@ -562,22 +563,23 @@ cs::MjpegServer CameraServer::AddSwitchedCamera(const wpi::Twine& name) {
|
|||||||
cs::MjpegServer CameraServer::StartAutomaticCapture(
|
cs::MjpegServer CameraServer::StartAutomaticCapture(
|
||||||
const cs::VideoSource& camera) {
|
const cs::VideoSource& camera) {
|
||||||
AddCamera(camera);
|
AddCamera(camera);
|
||||||
auto server = AddServer(wpi::Twine("serve_") + camera.GetName());
|
auto server = AddServer(fmt::format("serve_{}", camera.GetName()));
|
||||||
server.SetSource(camera);
|
server.SetSource(camera);
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::CvSink CameraServer::GetVideo() {
|
cs::CvSink CameraServer::GetVideo() {
|
||||||
|
auto& inst = ::GetInstance();
|
||||||
cs::VideoSource source;
|
cs::VideoSource source;
|
||||||
{
|
{
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
if (m_impl->m_primarySourceName.empty()) {
|
if (inst.m_primarySourceName.empty()) {
|
||||||
csShared->SetCameraServerError("no camera available");
|
csShared->SetCameraServerError("no camera available");
|
||||||
return cs::CvSink{};
|
return cs::CvSink{};
|
||||||
}
|
}
|
||||||
auto it = m_impl->m_sources.find(m_impl->m_primarySourceName);
|
auto it = inst.m_sources.find(inst.m_primarySourceName);
|
||||||
if (it == m_impl->m_sources.end()) {
|
if (it == inst.m_sources.end()) {
|
||||||
csShared->SetCameraServerError("no camera available");
|
csShared->SetCameraServerError("no camera available");
|
||||||
return cs::CvSink{};
|
return cs::CvSink{};
|
||||||
}
|
}
|
||||||
@@ -587,40 +589,40 @@ cs::CvSink CameraServer::GetVideo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cs::CvSink CameraServer::GetVideo(const cs::VideoSource& camera) {
|
cs::CvSink CameraServer::GetVideo(const cs::VideoSource& camera) {
|
||||||
|
auto& inst = ::GetInstance();
|
||||||
wpi::SmallString<64> name{"opencv_"};
|
wpi::SmallString<64> name{"opencv_"};
|
||||||
name += camera.GetName();
|
name += camera.GetName();
|
||||||
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
auto it = m_impl->m_sinks.find(name);
|
auto it = inst.m_sinks.find(name);
|
||||||
if (it != m_impl->m_sinks.end()) {
|
if (it != inst.m_sinks.end()) {
|
||||||
auto kind = it->second.GetKind();
|
auto kind = it->second.GetKind();
|
||||||
if (kind != cs::VideoSink::kCv) {
|
if (kind != cs::VideoSink::kCv) {
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
csShared->SetCameraServerError("expected OpenCV sink, but got " +
|
csShared->SetCameraServerError("expected OpenCV sink, but got {}",
|
||||||
wpi::Twine(kind));
|
static_cast<int>(kind));
|
||||||
return cs::CvSink{};
|
return cs::CvSink{};
|
||||||
}
|
}
|
||||||
return *static_cast<cs::CvSink*>(&it->second);
|
return *static_cast<cs::CvSink*>(&it->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::CvSink newsink{name};
|
cs::CvSink newsink{name.str()};
|
||||||
newsink.SetSource(camera);
|
newsink.SetSource(camera);
|
||||||
AddServer(newsink);
|
AddServer(newsink);
|
||||||
return newsink;
|
return newsink;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::CvSink CameraServer::GetVideo(const wpi::Twine& name) {
|
cs::CvSink CameraServer::GetVideo(std::string_view name) {
|
||||||
wpi::SmallString<64> nameBuf;
|
auto& inst = ::GetInstance();
|
||||||
wpi::StringRef nameStr = name.toStringRef(nameBuf);
|
|
||||||
cs::VideoSource source;
|
cs::VideoSource source;
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
auto it = m_impl->m_sources.find(nameStr);
|
auto it = inst.m_sources.find(name);
|
||||||
if (it == m_impl->m_sources.end()) {
|
if (it == inst.m_sources.end()) {
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
csShared->SetCameraServerError("could not find camera " + nameStr);
|
csShared->SetCameraServerError("could not find camera {}", name);
|
||||||
return cs::CvSink{};
|
return cs::CvSink{};
|
||||||
}
|
}
|
||||||
source = it->second;
|
source = it->second;
|
||||||
@@ -628,89 +630,99 @@ cs::CvSink CameraServer::GetVideo(const wpi::Twine& name) {
|
|||||||
return GetVideo(source);
|
return GetVideo(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::CvSource CameraServer::PutVideo(const wpi::Twine& name, int width,
|
cs::CvSource CameraServer::PutVideo(std::string_view name, int width,
|
||||||
int height) {
|
int height) {
|
||||||
cs::CvSource source{name, cs::VideoMode::kMJPEG, width, height, 30};
|
cs::CvSource source{name, cs::VideoMode::kMJPEG, width, height, 30};
|
||||||
StartAutomaticCapture(source);
|
StartAutomaticCapture(source);
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::MjpegServer CameraServer::AddServer(const wpi::Twine& name) {
|
cs::MjpegServer CameraServer::AddServer(std::string_view name) {
|
||||||
|
auto& inst = ::GetInstance();
|
||||||
int port;
|
int port;
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
port = m_impl->m_nextPort++;
|
port = inst.m_nextPort++;
|
||||||
}
|
}
|
||||||
return AddServer(name, port);
|
return AddServer(name, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::MjpegServer CameraServer::AddServer(const wpi::Twine& name, int port) {
|
cs::MjpegServer CameraServer::AddServer(std::string_view name, int port) {
|
||||||
cs::MjpegServer server{name, port};
|
cs::MjpegServer server{name, port};
|
||||||
AddServer(server);
|
AddServer(server);
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraServer::AddServer(const cs::VideoSink& server) {
|
void CameraServer::AddServer(const cs::VideoSink& server) {
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
auto& inst = ::GetInstance();
|
||||||
m_impl->m_sinks.try_emplace(server.GetName(), server);
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
|
inst.m_sinks.try_emplace(server.GetName(), server);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraServer::RemoveServer(const wpi::Twine& name) {
|
void CameraServer::RemoveServer(std::string_view name) {
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
auto& inst = ::GetInstance();
|
||||||
wpi::SmallString<64> nameBuf;
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
m_impl->m_sinks.erase(name.toStringRef(nameBuf));
|
inst.m_sinks.erase(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::VideoSink CameraServer::GetServer() {
|
cs::VideoSink CameraServer::GetServer() {
|
||||||
wpi::SmallString<64> name;
|
auto& inst = ::GetInstance();
|
||||||
|
std::string name;
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
if (m_impl->m_primarySourceName.empty()) {
|
if (inst.m_primarySourceName.empty()) {
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
csShared->SetCameraServerError("no camera available");
|
csShared->SetCameraServerError("no camera available");
|
||||||
return cs::VideoSink{};
|
return cs::VideoSink{};
|
||||||
}
|
}
|
||||||
name = "serve_";
|
name = fmt::format("serve_{}", inst.m_primarySourceName);
|
||||||
name += m_impl->m_primarySourceName;
|
|
||||||
}
|
}
|
||||||
return GetServer(name);
|
return GetServer(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
cs::VideoSink CameraServer::GetServer(const wpi::Twine& name) {
|
cs::VideoSink CameraServer::GetServer(std::string_view name) {
|
||||||
wpi::SmallString<64> nameBuf;
|
auto& inst = ::GetInstance();
|
||||||
wpi::StringRef nameStr = name.toStringRef(nameBuf);
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
auto it = inst.m_sinks.find(name);
|
||||||
auto it = m_impl->m_sinks.find(nameStr);
|
if (it == inst.m_sinks.end()) {
|
||||||
if (it == m_impl->m_sinks.end()) {
|
|
||||||
auto csShared = GetCameraServerShared();
|
auto csShared = GetCameraServerShared();
|
||||||
csShared->SetCameraServerError("could not find server " + nameStr);
|
csShared->SetCameraServerError("could not find server {}", name);
|
||||||
return cs::VideoSink{};
|
return cs::VideoSink{};
|
||||||
}
|
}
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraServer::AddCamera(const cs::VideoSource& camera) {
|
void CameraServer::AddCamera(const cs::VideoSource& camera) {
|
||||||
|
auto& inst = ::GetInstance();
|
||||||
std::string name = camera.GetName();
|
std::string name = camera.GetName();
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
if (m_impl->m_primarySourceName.empty()) m_impl->m_primarySourceName = name;
|
if (inst.m_primarySourceName.empty()) {
|
||||||
m_impl->m_sources.try_emplace(name, camera);
|
inst.m_primarySourceName = name;
|
||||||
|
}
|
||||||
|
inst.m_sources.try_emplace(name, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraServer::RemoveCamera(const wpi::Twine& name) {
|
void CameraServer::RemoveCamera(std::string_view name) {
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
auto& inst = ::GetInstance();
|
||||||
wpi::SmallString<64> nameBuf;
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
m_impl->m_sources.erase(name.toStringRef(nameBuf));
|
inst.m_sources.erase(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraServer::SetSize(int size) {
|
void CameraServer::SetSize(int size) {
|
||||||
std::scoped_lock lock(m_impl->m_mutex);
|
auto& inst = ::GetInstance();
|
||||||
if (m_impl->m_primarySourceName.empty()) return;
|
std::scoped_lock lock(inst.m_mutex);
|
||||||
auto it = m_impl->m_sources.find(m_impl->m_primarySourceName);
|
if (inst.m_primarySourceName.empty()) {
|
||||||
if (it == m_impl->m_sources.end()) return;
|
return;
|
||||||
if (size == kSize160x120)
|
}
|
||||||
|
auto it = inst.m_sources.find(inst.m_primarySourceName);
|
||||||
|
if (it == inst.m_sources.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (size == kSize160x120) {
|
||||||
it->second.SetResolution(160, 120);
|
it->second.SetResolution(160, 120);
|
||||||
else if (size == kSize320x240)
|
} else if (size == kSize320x240) {
|
||||||
it->second.SetResolution(320, 240);
|
it->second.SetResolution(320, 240);
|
||||||
else if (size == kSize640x480)
|
} else if (size == kSize640x480) {
|
||||||
it->second.SetResolution(640, 480);
|
it->second.SetResolution(640, 480);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user