├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE_APACHE ├── LICENSE_MIT ├── README.md ├── anafi-rs ├── Cargo.toml ├── examples │ └── liftoff.rs └── src │ └── lib.rs ├── arsdk-rs ├── Cargo.lock ├── Cargo.toml ├── LICENSE_APACHE ├── LICENSE_MIT └── src │ ├── ardrone3.rs │ ├── ardrone3 │ ├── gps_state.rs │ ├── piloting.rs │ ├── piloting │ │ └── pcmd.rs │ └── piloting_state.rs │ ├── command.rs │ ├── common.rs │ ├── frame.rs │ ├── handshake.rs │ ├── jumping_sumo.rs │ ├── lib.rs │ ├── listener.rs │ └── parse.rs ├── bebop2 ├── Cargo.toml ├── examples │ └── take_off.rs └── src │ └── lib.rs └── jumpingsumo-rs ├── Cargo.lock ├── Cargo.toml ├── LICENSE_APACHE ├── LICENSE_MIT ├── examples ├── jump.rs ├── new_sumo.rs └── spin.rs └── src └── lib.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous Integration 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | profile: minimal 14 | toolchain: stable 15 | override: true 16 | - uses: actions-rs/cargo@v1 17 | with: 18 | command: check 19 | args: --all-features --benches --bins --examples --tests 20 | 21 | test: 22 | name: Test Suite 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: actions-rs/toolchain@v1 27 | with: 28 | profile: minimal 29 | toolchain: stable 30 | override: true 31 | - uses: actions-rs/cargo@v1 32 | with: 33 | command: test 34 | args: --all-features 35 | 36 | fmt: 37 | name: Rustfmt 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/checkout@v2 41 | - uses: actions-rs/toolchain@v1 42 | with: 43 | profile: minimal 44 | toolchain: stable 45 | override: true 46 | - run: rustup component add rustfmt 47 | - uses: actions-rs/cargo@v1 48 | with: 49 | command: fmt 50 | args: --all -- --check 51 | 52 | clippy: 53 | name: Clippy 54 | runs-on: ubuntu-latest 55 | steps: 56 | - uses: actions/checkout@v2 57 | - uses: actions-rs/toolchain@v1 58 | with: 59 | profile: minimal 60 | toolchain: stable 61 | override: true 62 | - run: rustup component add clippy 63 | - uses: actions-rs/cargo@v1 64 | with: 65 | command: clippy 66 | args: -- -D warnings 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/*.rs.bk 3 | /.vscode 4 | .idea/ 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and 9 | expression, 10 | level of experience, education, socio-economic status, nationality, personal 11 | appearance, race, religion, or sexual identity and orientation. 12 | 13 | ## Our Standards 14 | 15 | Examples of behavior that contributes to creating a positive environment 16 | include: 17 | 18 | * Using welcoming and inclusive language 19 | * Being respectful of differing viewpoints and experiences 20 | * Gracefully accepting constructive criticism 21 | * Focusing on what is best for the community 22 | * Showing empathy towards other community members 23 | 24 | Examples of unacceptable behavior by participants include: 25 | 26 | * The use of sexualized language or imagery and unwelcome sexual attention or 27 | advances 28 | * Trolling, insulting/derogatory comments, and personal or political attacks 29 | * Public or private harassment 30 | * Publishing others' private information, such as a physical or electronic 31 | address, without explicit permission 32 | * Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | ## Our Responsibilities 36 | 37 | Project maintainers are responsible for clarifying the standards of acceptable 38 | behavior and are expected to take appropriate and fair corrective action in 39 | response to any instances of unacceptable behavior. 40 | 41 | Project maintainers have the right and responsibility to remove, edit, or 42 | reject comments, commits, code, wiki edits, issues, and other contributions 43 | that are not aligned to this Code of Conduct, or to ban temporarily or 44 | permanently any contributor for other behaviors that they deem inappropriate, 45 | threatening, offensive, or harmful. 46 | 47 | ## Scope 48 | 49 | This Code of Conduct applies within all project spaces, and it also applies when 50 | an individual is representing the project or its community in public spaces. 51 | Examples of representing a project or community include using an official 52 | project e-mail address, posting via an official social media account, or acting 53 | as an appointed representative at an online or offline event. Representation of 54 | a project may be further defined and clarified by project maintainers. 55 | 56 | ## Enforcement 57 | 58 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 59 | reported by contacting the project team at jeremy.lempereur@gmail.com. All 60 | complaints will be reviewed and investigated and will result in a response that 61 | is deemed necessary and appropriate to the circumstances. The project team is 62 | obligated to maintain confidentiality with regard to the reporter of an 63 | incident. 64 | Further details of specific enforcement policies may be posted separately. 65 | 66 | Project maintainers who do not follow or enforce the Code of Conduct in good 67 | faith may face temporary or permanent repercussions as determined by other 68 | members of the project's leadership. 69 | 70 | ## Attribution 71 | 72 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 73 | version 1.4, 74 | available at 75 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 76 | 77 | [homepage]: https://www.contributor-covenant.org 78 | 79 | For answers to common questions about this code of conduct, see 80 | https://www.contributor-covenant.org/faq 81 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "addr2line" 5 | version = "0.13.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" 8 | dependencies = [ 9 | "gimli", 10 | ] 11 | 12 | [[package]] 13 | name = "adler" 14 | version = "0.2.3" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 17 | 18 | [[package]] 19 | name = "ahash" 20 | version = "0.3.3" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "35b909d1c126f78ace756fc337133356c499eebeefcce930fa5fb018823f2b2d" 23 | dependencies = [ 24 | "const-random", 25 | ] 26 | 27 | [[package]] 28 | name = "aho-corasick" 29 | version = "0.6.10" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" 32 | dependencies = [ 33 | "memchr", 34 | ] 35 | 36 | [[package]] 37 | name = "anyhow" 38 | version = "1.0.28" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" 41 | 42 | [[package]] 43 | name = "arsdk-rs" 44 | version = "0.0.5" 45 | dependencies = [ 46 | "chrono", 47 | "dashmap", 48 | "log 0.4.8", 49 | "pnet", 50 | "scroll", 51 | "serde", 52 | "serde_json", 53 | "serde_with", 54 | "thiserror", 55 | ] 56 | 57 | [[package]] 58 | name = "async-log" 59 | version = "2.0.0" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "ac39aabd463ad2b1fadb34c22f48e5d160fa60b7cd69a426362e71bde49ebb6d" 62 | dependencies = [ 63 | "async-log-attributes", 64 | "backtrace", 65 | "log 0.4.8", 66 | ] 67 | 68 | [[package]] 69 | name = "async-log-attributes" 70 | version = "1.0.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "da97f8e61b19a72f67d8932de8b0905f7d41a1d7b9501b9938c7755f96f6362d" 73 | dependencies = [ 74 | "proc-macro2 0.4.30", 75 | "quote 0.6.13", 76 | "syn 0.15.44", 77 | ] 78 | 79 | [[package]] 80 | name = "atty" 81 | version = "0.2.14" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 84 | dependencies = [ 85 | "hermit-abi", 86 | "libc", 87 | "winapi 0.3.9", 88 | ] 89 | 90 | [[package]] 91 | name = "autocfg" 92 | version = "1.0.0" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 95 | 96 | [[package]] 97 | name = "backtrace" 98 | version = "0.3.50" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293" 101 | dependencies = [ 102 | "addr2line", 103 | "cfg-if", 104 | "libc", 105 | "miniz_oxide", 106 | "object", 107 | "rustc-demangle", 108 | ] 109 | 110 | [[package]] 111 | name = "bebop2" 112 | version = "0.1.0" 113 | dependencies = [ 114 | "arsdk-rs", 115 | "async-log", 116 | "env_logger", 117 | "log 0.4.8", 118 | "tokio", 119 | ] 120 | 121 | [[package]] 122 | name = "bitflags" 123 | version = "0.5.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23" 126 | 127 | [[package]] 128 | name = "bytes" 129 | version = "0.5.4" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" 132 | 133 | [[package]] 134 | name = "cfg-if" 135 | version = "0.1.10" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 138 | 139 | [[package]] 140 | name = "chrono" 141 | version = "0.4.11" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" 144 | dependencies = [ 145 | "num-integer", 146 | "num-traits", 147 | "time", 148 | ] 149 | 150 | [[package]] 151 | name = "const-random" 152 | version = "0.1.8" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" 155 | dependencies = [ 156 | "const-random-macro", 157 | "proc-macro-hack", 158 | ] 159 | 160 | [[package]] 161 | name = "const-random-macro" 162 | version = "0.1.8" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" 165 | dependencies = [ 166 | "getrandom", 167 | "proc-macro-hack", 168 | ] 169 | 170 | [[package]] 171 | name = "dashmap" 172 | version = "3.11.1" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "0f87a04c37da1d3d27db1fb7f372802b72fb8c3ff3e9c0914530995127f4a6a1" 175 | dependencies = [ 176 | "ahash", 177 | "cfg-if", 178 | "num_cpus", 179 | ] 180 | 181 | [[package]] 182 | name = "env_logger" 183 | version = "0.7.1" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 186 | dependencies = [ 187 | "atty", 188 | "humantime", 189 | "log 0.4.8", 190 | "regex", 191 | "termcolor", 192 | ] 193 | 194 | [[package]] 195 | name = "getrandom" 196 | version = "0.1.14" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 199 | dependencies = [ 200 | "cfg-if", 201 | "libc", 202 | "wasi", 203 | ] 204 | 205 | [[package]] 206 | name = "gimli" 207 | version = "0.22.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" 210 | 211 | [[package]] 212 | name = "glob" 213 | version = "0.2.11" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" 216 | 217 | [[package]] 218 | name = "hermit-abi" 219 | version = "0.1.12" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" 222 | dependencies = [ 223 | "libc", 224 | ] 225 | 226 | [[package]] 227 | name = "humantime" 228 | version = "1.3.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 231 | dependencies = [ 232 | "quick-error", 233 | ] 234 | 235 | [[package]] 236 | name = "ipnetwork" 237 | version = "0.15.1" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "a69dd5e3613374e74da81c251750153abe3bd0ad17641ea63d43d1e21d0dbd4d" 240 | dependencies = [ 241 | "serde", 242 | ] 243 | 244 | [[package]] 245 | name = "itoa" 246 | version = "0.4.5" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 249 | 250 | [[package]] 251 | name = "jumpingsumo-rs" 252 | version = "0.0.2" 253 | dependencies = [ 254 | "anyhow", 255 | "arsdk-rs", 256 | "chrono", 257 | ] 258 | 259 | [[package]] 260 | name = "kernel32-sys" 261 | version = "0.2.2" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 264 | dependencies = [ 265 | "winapi 0.2.8", 266 | "winapi-build", 267 | ] 268 | 269 | [[package]] 270 | name = "lazy_static" 271 | version = "1.4.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 274 | 275 | [[package]] 276 | name = "libc" 277 | version = "0.2.73" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" 280 | 281 | [[package]] 282 | name = "log" 283 | version = "0.3.9" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 286 | dependencies = [ 287 | "log 0.4.8", 288 | ] 289 | 290 | [[package]] 291 | name = "log" 292 | version = "0.4.8" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 295 | dependencies = [ 296 | "cfg-if", 297 | ] 298 | 299 | [[package]] 300 | name = "memchr" 301 | version = "2.3.3" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 304 | 305 | [[package]] 306 | name = "miniz_oxide" 307 | version = "0.4.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" 310 | dependencies = [ 311 | "adler", 312 | ] 313 | 314 | [[package]] 315 | name = "num-integer" 316 | version = "0.1.42" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" 319 | dependencies = [ 320 | "autocfg", 321 | "num-traits", 322 | ] 323 | 324 | [[package]] 325 | name = "num-traits" 326 | version = "0.2.11" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 329 | dependencies = [ 330 | "autocfg", 331 | ] 332 | 333 | [[package]] 334 | name = "num_cpus" 335 | version = "1.13.0" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 338 | dependencies = [ 339 | "hermit-abi", 340 | "libc", 341 | ] 342 | 343 | [[package]] 344 | name = "object" 345 | version = "0.20.0" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" 348 | 349 | [[package]] 350 | name = "pin-project-lite" 351 | version = "0.1.7" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" 354 | 355 | [[package]] 356 | name = "pnet" 357 | version = "0.25.0" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "5c08c2c6c26481fcbe49dc4405baedf47151f859c5a45d3f254c2ff74ce51cf0" 360 | dependencies = [ 361 | "ipnetwork", 362 | "pnet_base", 363 | "pnet_datalink", 364 | "pnet_packet", 365 | "pnet_sys", 366 | "pnet_transport", 367 | ] 368 | 369 | [[package]] 370 | name = "pnet_base" 371 | version = "0.22.0" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "4df28acf2fcc77436dd2b91a9a0c2bb617f9ca5f2acefee1a4135058b9f9801f" 374 | 375 | [[package]] 376 | name = "pnet_datalink" 377 | version = "0.25.0" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "545f8df67cbc53438f37f56e68ae5ca49beb3990e9fd7e9e214c8ffd36c0e0ea" 380 | dependencies = [ 381 | "ipnetwork", 382 | "libc", 383 | "pnet_base", 384 | "pnet_sys", 385 | "winapi 0.2.8", 386 | ] 387 | 388 | [[package]] 389 | name = "pnet_macros" 390 | version = "0.25.0" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "cf402424ca7281aa234b726c32bce5a8e2278c72f5863305e291ac3de08e16f8" 393 | dependencies = [ 394 | "regex", 395 | "syntex", 396 | "syntex_syntax", 397 | ] 398 | 399 | [[package]] 400 | name = "pnet_macros_support" 401 | version = "0.25.0" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "5e586854ba703c15f74c486e1a46624566b47f1f61cc8a6b02c6bbe5e34a383b" 404 | dependencies = [ 405 | "pnet_base", 406 | ] 407 | 408 | [[package]] 409 | name = "pnet_packet" 410 | version = "0.25.0" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "c44c075c6d4f2e814dba621a999838e4a4f749f6117024f52b05b3c559a4fd17" 413 | dependencies = [ 414 | "glob", 415 | "pnet_base", 416 | "pnet_macros", 417 | "pnet_macros_support", 418 | "syntex", 419 | ] 420 | 421 | [[package]] 422 | name = "pnet_sys" 423 | version = "0.25.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "82f881a6d75ac98c5541db6144682d1773bb14c6fc50c6ebac7086c8f7f23c29" 426 | dependencies = [ 427 | "libc", 428 | "winapi 0.2.8", 429 | "ws2_32-sys", 430 | ] 431 | 432 | [[package]] 433 | name = "pnet_transport" 434 | version = "0.25.0" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "1b75ccaee7b5daba9f9a7d47bceeb73cc32edde9952dc5409460d6621ec667b6" 437 | dependencies = [ 438 | "libc", 439 | "pnet_base", 440 | "pnet_packet", 441 | "pnet_sys", 442 | ] 443 | 444 | [[package]] 445 | name = "proc-macro-hack" 446 | version = "0.5.15" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" 449 | 450 | [[package]] 451 | name = "proc-macro2" 452 | version = "0.4.30" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 455 | dependencies = [ 456 | "unicode-xid 0.1.0", 457 | ] 458 | 459 | [[package]] 460 | name = "proc-macro2" 461 | version = "1.0.19" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" 464 | dependencies = [ 465 | "unicode-xid 0.2.0", 466 | ] 467 | 468 | [[package]] 469 | name = "quick-error" 470 | version = "1.2.3" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 473 | 474 | [[package]] 475 | name = "quote" 476 | version = "0.6.13" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 479 | dependencies = [ 480 | "proc-macro2 0.4.30", 481 | ] 482 | 483 | [[package]] 484 | name = "quote" 485 | version = "1.0.3" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 488 | dependencies = [ 489 | "proc-macro2 1.0.19", 490 | ] 491 | 492 | [[package]] 493 | name = "regex" 494 | version = "1.0.6" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "ee84f70c8c08744ea9641a731c7fadb475bf2ecc52d7f627feb833e0b3990467" 497 | dependencies = [ 498 | "aho-corasick", 499 | "memchr", 500 | "regex-syntax", 501 | "thread_local", 502 | "utf8-ranges", 503 | ] 504 | 505 | [[package]] 506 | name = "regex-syntax" 507 | version = "0.6.17" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 510 | 511 | [[package]] 512 | name = "rustc-demangle" 513 | version = "0.1.16" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" 516 | 517 | [[package]] 518 | name = "rustc-serialize" 519 | version = "0.3.24" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 522 | 523 | [[package]] 524 | name = "ryu" 525 | version = "1.0.4" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" 528 | 529 | [[package]] 530 | name = "scroll" 531 | version = "0.10.1" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" 534 | 535 | [[package]] 536 | name = "serde" 537 | version = "1.0.106" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" 540 | dependencies = [ 541 | "serde_derive", 542 | ] 543 | 544 | [[package]] 545 | name = "serde_derive" 546 | version = "1.0.106" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" 549 | dependencies = [ 550 | "proc-macro2 1.0.19", 551 | "quote 1.0.3", 552 | "syn 1.0.39", 553 | ] 554 | 555 | [[package]] 556 | name = "serde_json" 557 | version = "1.0.52" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" 560 | dependencies = [ 561 | "itoa", 562 | "ryu", 563 | "serde", 564 | ] 565 | 566 | [[package]] 567 | name = "serde_with" 568 | version = "1.4.0" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "89d3d595d64120bbbc70b7f6d5ae63298b62a3d9f373ec2f56acf5365ca8a444" 571 | dependencies = [ 572 | "serde", 573 | "serde_with_macros", 574 | ] 575 | 576 | [[package]] 577 | name = "serde_with_macros" 578 | version = "1.1.0" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "4070d2c9b9d258465ad1d82aabb985b84cd9a3afa94da25ece5a9938ba5f1606" 581 | dependencies = [ 582 | "proc-macro2 1.0.19", 583 | "quote 1.0.3", 584 | "syn 1.0.39", 585 | ] 586 | 587 | [[package]] 588 | name = "slab" 589 | version = "0.4.2" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 592 | 593 | [[package]] 594 | name = "syn" 595 | version = "0.15.44" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" 598 | dependencies = [ 599 | "proc-macro2 0.4.30", 600 | "quote 0.6.13", 601 | "unicode-xid 0.1.0", 602 | ] 603 | 604 | [[package]] 605 | name = "syn" 606 | version = "1.0.39" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" 609 | dependencies = [ 610 | "proc-macro2 1.0.19", 611 | "quote 1.0.3", 612 | "unicode-xid 0.2.0", 613 | ] 614 | 615 | [[package]] 616 | name = "syntex" 617 | version = "0.42.2" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "0a30b08a6b383a22e5f6edc127d169670d48f905bb00ca79a00ea3e442ebe317" 620 | dependencies = [ 621 | "syntex_errors", 622 | "syntex_syntax", 623 | ] 624 | 625 | [[package]] 626 | name = "syntex_errors" 627 | version = "0.42.0" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "04c48f32867b6114449155b2a82114b86d4b09e1bddb21c47ff104ab9172b646" 630 | dependencies = [ 631 | "libc", 632 | "log 0.3.9", 633 | "rustc-serialize", 634 | "syntex_pos", 635 | "term", 636 | "unicode-xid 0.0.3", 637 | ] 638 | 639 | [[package]] 640 | name = "syntex_pos" 641 | version = "0.42.0" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "3fd49988e52451813c61fecbe9abb5cfd4e1b7bb6cdbb980a6fbcbab859171a6" 644 | dependencies = [ 645 | "rustc-serialize", 646 | ] 647 | 648 | [[package]] 649 | name = "syntex_syntax" 650 | version = "0.42.0" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "7628a0506e8f9666fdabb5f265d0059b059edac9a3f810bda077abb5d826bd8d" 653 | dependencies = [ 654 | "bitflags", 655 | "libc", 656 | "log 0.3.9", 657 | "rustc-serialize", 658 | "syntex_errors", 659 | "syntex_pos", 660 | "term", 661 | "unicode-xid 0.0.3", 662 | ] 663 | 664 | [[package]] 665 | name = "term" 666 | version = "0.4.6" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" 669 | dependencies = [ 670 | "kernel32-sys", 671 | "winapi 0.2.8", 672 | ] 673 | 674 | [[package]] 675 | name = "termcolor" 676 | version = "1.1.0" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 679 | dependencies = [ 680 | "winapi-util", 681 | ] 682 | 683 | [[package]] 684 | name = "thiserror" 685 | version = "1.0.16" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "d12a1dae4add0f0d568eebc7bf142f145ba1aa2544cafb195c76f0f409091b60" 688 | dependencies = [ 689 | "thiserror-impl", 690 | ] 691 | 692 | [[package]] 693 | name = "thiserror-impl" 694 | version = "1.0.16" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "3f34e0c1caaa462fd840ec6b768946ea1e7842620d94fe29d5b847138f521269" 697 | dependencies = [ 698 | "proc-macro2 1.0.19", 699 | "quote 1.0.3", 700 | "syn 1.0.39", 701 | ] 702 | 703 | [[package]] 704 | name = "thread_local" 705 | version = "0.3.6" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 708 | dependencies = [ 709 | "lazy_static", 710 | ] 711 | 712 | [[package]] 713 | name = "time" 714 | version = "0.1.43" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 717 | dependencies = [ 718 | "libc", 719 | "winapi 0.3.9", 720 | ] 721 | 722 | [[package]] 723 | name = "tokio" 724 | version = "0.2.22" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" 727 | dependencies = [ 728 | "bytes", 729 | "num_cpus", 730 | "pin-project-lite", 731 | "slab", 732 | "tokio-macros", 733 | ] 734 | 735 | [[package]] 736 | name = "tokio-macros" 737 | version = "0.2.5" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" 740 | dependencies = [ 741 | "proc-macro2 1.0.19", 742 | "quote 1.0.3", 743 | "syn 1.0.39", 744 | ] 745 | 746 | [[package]] 747 | name = "unicode-xid" 748 | version = "0.0.3" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb" 751 | 752 | [[package]] 753 | name = "unicode-xid" 754 | version = "0.1.0" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 757 | 758 | [[package]] 759 | name = "unicode-xid" 760 | version = "0.2.0" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 763 | 764 | [[package]] 765 | name = "utf8-ranges" 766 | version = "1.0.4" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" 769 | 770 | [[package]] 771 | name = "wasi" 772 | version = "0.9.0+wasi-snapshot-preview1" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 775 | 776 | [[package]] 777 | name = "winapi" 778 | version = "0.2.8" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 781 | 782 | [[package]] 783 | name = "winapi" 784 | version = "0.3.9" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 787 | dependencies = [ 788 | "winapi-i686-pc-windows-gnu", 789 | "winapi-x86_64-pc-windows-gnu", 790 | ] 791 | 792 | [[package]] 793 | name = "winapi-build" 794 | version = "0.1.1" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 797 | 798 | [[package]] 799 | name = "winapi-i686-pc-windows-gnu" 800 | version = "0.4.0" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 803 | 804 | [[package]] 805 | name = "winapi-util" 806 | version = "0.1.5" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 809 | dependencies = [ 810 | "winapi 0.3.9", 811 | ] 812 | 813 | [[package]] 814 | name = "winapi-x86_64-pc-windows-gnu" 815 | version = "0.4.0" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 818 | 819 | [[package]] 820 | name = "ws2_32-sys" 821 | version = "0.2.1" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 824 | dependencies = [ 825 | "winapi 0.2.8", 826 | "winapi-build", 827 | ] 828 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "arsdk-rs", 4 | "jumpingsumo-rs", 5 | "bebop2", 6 | # "anafi-rs", 7 | ] 8 | -------------------------------------------------------------------------------- /LICENSE_APACHE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Jeremy Lempereur 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /LICENSE_MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Jeremy Lempereur 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arsdk-rs   [![crates.io](https://img.shields.io/crates/v/arsdk-rs.svg)](https://crates.io/crates/arsdk-rs) [![Documentation](https://docs.rs/arsdk-rs/badge.svg)](https://docs.rs/arsdk-rs) [![MPL 2.0 License](https://img.shields.io/badge/license-apache2-green.svg)](LICENSE-APACHE) [![MIT License](https://img.shields.io/badge/license-mit-blue.svg)](LICENSE-MIT) 2 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md) 3 | 4 | ## 🛠 Project status: Proof of concept 5 | 6 | This project was a Proof of concept Software Development Kit for Parrot drones. 7 | 8 | Due to the recent development and the pivotting of the Parrot company, we've moved our efforts to a new Open-source project called MAVLink. 9 | MAVLink is a messaging protocol that is compatible with multiple autopilots (PX4, ArduPilot), multiple hardware and there are many open-source projects build around it to support different aspect of controlling drones, planes and marien and other vehicles. 10 | 11 | ## ℹ Resources 12 | - AeroRust mav repository: https://github.com/AeroRust/mav 13 | - PX4: https://px4.io 14 | - ArduPilot: https://ardupilot.org 15 | - MAVLink: https://mavlink.io 16 | 17 | 18 | ## jumpingsumo-rs   [![crates.io](https://img.shields.io/crates/v/jumpingsumo-rs.svg)](https://crates.io/crates/jumpingsumo-rs) [![Documentation](https://docs.rs/jumpingsumo-rs/badge.svg)](https://docs.rs/jumpingsumo-rs) 19 | 20 | ## bebop2 21 | 22 | ### Not released yet 23 | 24 | ## Useful information related to the official C SDK 25 | 26 | ### Commands: 27 | 28 | Id's of the commands and their values can be found in the file: `build/libARCommands/libARCommands/ARCOMMANDS_Ids.h` 29 | 30 | 31 | ### Filters: 32 | 33 | @TODO Figure out what the Filters actually do. 34 | 35 | `eARCOMMANDS_FILTER_STATUS ARCOMMANDS_Filter_FilterCommand (ARCOMMANDS_Filter_t *filter, uint8_t *buffer, uint32_t len, eARCOMMANDS_FILTER_ERROR *error)` found in `build/libARCommands/gen/Sources/ARCOMMANDS_Filter.c` has 3 parameters in the order: 36 | 37 | 1. `commandFeature = ARCOMMANDS_ReadWrite_Read8FromBuffer` 38 | 2. `commandClass = ARCOMMANDS_ReadWrite_Read8FromBuffer` 39 | 3. `commandId = ARCOMMANDS_ReadWrite_Read16FromBuffer` 40 | 41 | Based on these parameters it triggers a filter behavior, e.g.: 42 | `filter->CmdGenericDefaultBehavior` 43 | 44 | ```c 45 | /** 46 | * @brief Status code for ARCOMMANDS_Filter_FilterCommand function 47 | */ 48 | typedef enum { 49 | ARCOMMANDS_FILTER_STATUS_ALLOWED = 0, ///< The command should pass the filter 50 | ARCOMMANDS_FILTER_STATUS_BLOCKED, ///< The command should not pass the filter 51 | ARCOMMANDS_FILTER_STATUS_UNKNOWN, ///< Unknown command. The command was possibly added in a newer version of libARCommands, or is an invalid command. 52 | ARCOMMANDS_FILTER_STATUS_ERROR, ///< The filtering of the command failed. 53 | } eARCOMMANDS_FILTER_STATUS; 54 | 55 | /** 56 | * @brief ARCOMMANDS_Filter object holder 57 | */ 58 | typedef struct ARCOMMANDS_Filter_t ARCOMMANDS_Filter_t; 59 | 60 | /** 61 | * @brief Creates a new ARCOMMANDS_Filter_t 62 | * @param defaultBehavior The default behavior of the filter (must be either ARCOMMANDS_FILTER_STATUS_BLOCKED or ARCOMMANDS_FILTER_STATUS_ALLOWED). 63 | * @param error Optionnal pointer which will hold the error code. 64 | * @warning This function allocates memory. 65 | * @note The memory must be freed by a call to ARCOMMANDS_Filter_DeleteFilter. 66 | * @return A new ARCOMMANDS_Filter_t instance. NULL in case of error. 67 | */ 68 | ARCOMMANDS_Filter_t* ARCOMMANDS_Filter_NewFilter (eARCOMMANDS_FILTER_STATUS defaultBehavior, eARCOMMANDS_FILTER_ERROR *error); 69 | ``` 70 | 71 | 72 | ## Sphinx simulator 73 | 74 | https://developer.parrot.com/docs/sphinx/ 75 | 76 | 1. Follow the installation guide 77 | 78 | 2. Simulate drone 79 | 80 | ```bash 81 | sphinx /opt/parrot-sphinx/usr/share/sphinx/drones/{DRONE}.drone::stolen_interface={YOUR_INTERFACE}:eth0:192.168.42.1/24 82 | ``` 83 | 84 | **NOTE:** Since we don't have video streaming handling from the drone, you should disable the front camera for `Bebeop2` (`::with_front_cam=0`). 85 | Otherwise you won't be able to connect (performa a handshake) to it: 86 | 87 | ```bash 88 | sphinx /opt/parrot-sphinx/usr/share/sphinx/drones/{DRONE}.drone::stolen_interface={YOUR_INTERFACE}:eth0:192.168.42.1/24::with_front_cam=0 89 | ``` 90 | 91 | 92 | * *You can find your interface with:* 93 | ```bash 94 | iwconfig 95 | ``` 96 | 97 | * Available drones: 98 | 99 | ```bash 100 | ls -1a /opt/parrot-sphinx/usr/share/sphinx/drones/ 101 | ``` 102 | 103 | * airborne.drone 104 | * anafi4k.drone 105 | * bebop2.drone 106 | * bebop.drone 107 | * bluegrass.drone 108 | * disco.drone 109 | * mambo.drone 110 | * swing.drone 111 | 112 | 113 | 3. Run examples with IP: 114 | 115 | ```rust 116 | use arsdk-rs::PARROT_SPHINX_IP; 117 | use std::net::{IpAddr, Ipv4Addr}; 118 | 119 | fn main() { 120 | let expected = IpAddr::V4(Ipv4Addr::new(10, 202, 0, 1)) 121 | assert_eq!(expected, PARROT_SPHINX_IP); 122 | } 123 | ``` 124 | 125 | ### Video stream: 126 | 127 | Based on [pyparrot](https://github.com/amymcgovern/pyparrot/blob/bf4775ec1199b282e4edde1e4a8e018dcc8725e0/pyparrot/DroneVision.py#L78) pointing to the [forum](http://forum.developer.parrot.com/t/streaming-address-of-mambo-fpv-for-videoprojection/6442/6). 128 | NOTE: It doesn't currently work. 129 | 130 | Bebop2 (double check): rtsp://10.202.0.1/media/stream2 131 | Anafi4k: rtsp://10.202.0.1/live 132 | 133 | ### Telemetry 134 | 135 | Documentation: 136 | * https://developer.parrot.com/docs/sphinx/visualization.html#tlm-data-logger 137 | 138 | 139 | ```bash 140 | tlm-data-logger inet:127.0.0.1:9060 141 | ``` 142 | 143 | 144 | ## Code of Conduct 145 | 146 | We have a Code of Conduct so as to create a more enjoyable community and 147 | work environment. Please see the [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) 148 | file for more details. 149 | 150 | ## License 151 | 152 | Licensed under either of 153 | 154 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 155 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 156 | 157 | at your option. 158 | 159 | Dual MIT/Apache2 is strictly more permissive 160 | -------------------------------------------------------------------------------- /anafi-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anafi-rs" 3 | version = "0.1.0" 4 | authors = ["o0Ignition0o "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [dependencies] 9 | arsdk-rs = { path = "../arsdk-rs" } 10 | 11 | [dev-dependencies] 12 | # Used for examples 13 | env_logger = "0.7" 14 | log = "0.4" 15 | tokio = {version = "0.2.22", features = ["rt-threaded", "macros", "time"]} 16 | -------------------------------------------------------------------------------- /anafi-rs/examples/liftoff.rs: -------------------------------------------------------------------------------- 1 | use log::info; 2 | use std::error::Error; 3 | 4 | use anafi_rs::prelude::*; 5 | use std::time::Duration; 6 | 7 | fn main() -> Result<(), Box> { 8 | env_logger::init(); 9 | 10 | let drone_ip: std::net::IpAddr = "192.168.42.1".parse()?; 11 | let drone = Anafi::connect(drone_ip.into())?; 12 | 13 | info!("Takeoff!"); 14 | 15 | for i in 0..50 { 16 | drone.take_off()?; 17 | } 18 | 19 | info!("Wait 5 seconds and fly UP"); 20 | std::thread::sleep(Duration::from_secs(5)); 21 | 22 | for i in 0..50 { 23 | drone.landing()?; 24 | } 25 | 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /anafi-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | use arsdk_rs::{ 2 | command::Feature, 3 | frame::{BufferID, Frame, Type}, 4 | }; 5 | 6 | pub use arsdk_rs::{ 7 | ardrone3::{ArDrone3, MediaStreaming, Piloting, PCMD}, 8 | prelude::*, 9 | }; 10 | 11 | pub mod prelude { 12 | pub use crate::Anafi; 13 | pub use arsdk_rs::{ 14 | ardrone3::{ArDrone3, Piloting, PCMD}, 15 | prelude::*, 16 | }; 17 | } 18 | 19 | pub struct Anafi { 20 | drone: Drone, 21 | } 22 | 23 | impl Anafi { 24 | pub fn connect(config: Config) -> Result { 25 | let drone = Drone::connect(config)?; 26 | 27 | Ok(Self { drone }) 28 | } 29 | 30 | /// - Captain #Ferris 🦀 :Take off... 🛫 31 | pub fn take_off(&self) -> Result<(), Error> { 32 | let feature = Feature::ArDrone3(Some(ArDrone3::Piloting(Piloting::TakeOff))); 33 | 34 | let frame = Frame::for_drone( 35 | &self.drone, 36 | Type::DataWithAck, 37 | BufferID::CDAck, 38 | Some(feature), 39 | ); 40 | 41 | self.drone.send_frame(frame) 42 | } 43 | 44 | pub fn up(&self, sequence_id: u8) -> Result<(), Error> { 45 | let feature = Feature::ArDrone3(Some(ArDrone3::Piloting(Piloting::PCMD(PCMD { 46 | flag: true, 47 | roll: 0, 48 | pitch: 0, 49 | yaw: 0, 50 | gaz: 100, 51 | timestamp: Utc::now(), 52 | sequence_id, 53 | })))); 54 | 55 | let frame = Frame::for_drone(&self.drone, Type::Data, BufferID::CDNonAck, Some(feature)); 56 | 57 | self.drone.send_frame(frame) 58 | } 59 | 60 | pub fn down(&self, sequence_id: u8) -> Result<(), Error> { 61 | let feature = Feature::ArDrone3(Some(ArDrone3::Piloting(Piloting::PCMD(PCMD { 62 | flag: true, 63 | roll: 0, 64 | pitch: 0, 65 | yaw: 0, 66 | gaz: -100, 67 | timestamp: Utc::now(), 68 | sequence_id, 69 | })))); 70 | 71 | let frame = Frame::for_drone(&self.drone, Type::Data, BufferID::CDNonAck, Some(feature)); 72 | 73 | self.drone.send_frame(frame) 74 | } 75 | 76 | pub fn landing(&self) -> Result<(), Error> { 77 | let feature = Feature::ArDrone3(Some(ArDrone3::Piloting(Piloting::Landing))); 78 | 79 | let frame = Frame::for_drone( 80 | &self.drone, 81 | Type::DataWithAck, 82 | BufferID::CDAck, 83 | Some(feature), 84 | ); 85 | 86 | self.drone.send_frame(frame) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /arsdk-rs/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ahash" 5 | version = "0.3.3" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "35b909d1c126f78ace756fc337133356c499eebeefcce930fa5fb018823f2b2d" 8 | dependencies = [ 9 | "const-random", 10 | ] 11 | 12 | [[package]] 13 | name = "aho-corasick" 14 | version = "0.6.10" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" 17 | dependencies = [ 18 | "memchr", 19 | ] 20 | 21 | [[package]] 22 | name = "anyhow" 23 | version = "1.0.28" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" 26 | 27 | [[package]] 28 | name = "arsdk-rs" 29 | version = "0.0.4" 30 | dependencies = [ 31 | "anyhow", 32 | "chrono", 33 | "dashmap", 34 | "pnet", 35 | "serde", 36 | "serde_json", 37 | ] 38 | 39 | [[package]] 40 | name = "autocfg" 41 | version = "1.0.0" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 44 | 45 | [[package]] 46 | name = "bitflags" 47 | version = "0.5.0" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23" 50 | 51 | [[package]] 52 | name = "cfg-if" 53 | version = "0.1.10" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 56 | 57 | [[package]] 58 | name = "chrono" 59 | version = "0.4.11" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" 62 | dependencies = [ 63 | "num-integer", 64 | "num-traits", 65 | "time", 66 | ] 67 | 68 | [[package]] 69 | name = "const-random" 70 | version = "0.1.8" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" 73 | dependencies = [ 74 | "const-random-macro", 75 | "proc-macro-hack", 76 | ] 77 | 78 | [[package]] 79 | name = "const-random-macro" 80 | version = "0.1.8" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" 83 | dependencies = [ 84 | "getrandom", 85 | "proc-macro-hack", 86 | ] 87 | 88 | [[package]] 89 | name = "dashmap" 90 | version = "3.11.1" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "0f87a04c37da1d3d27db1fb7f372802b72fb8c3ff3e9c0914530995127f4a6a1" 93 | dependencies = [ 94 | "ahash", 95 | "cfg-if", 96 | "num_cpus", 97 | ] 98 | 99 | [[package]] 100 | name = "getrandom" 101 | version = "0.1.14" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 104 | dependencies = [ 105 | "cfg-if", 106 | "libc", 107 | "wasi", 108 | ] 109 | 110 | [[package]] 111 | name = "glob" 112 | version = "0.2.11" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" 115 | 116 | [[package]] 117 | name = "hermit-abi" 118 | version = "0.1.12" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" 121 | dependencies = [ 122 | "libc", 123 | ] 124 | 125 | [[package]] 126 | name = "ipnetwork" 127 | version = "0.15.1" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "a69dd5e3613374e74da81c251750153abe3bd0ad17641ea63d43d1e21d0dbd4d" 130 | dependencies = [ 131 | "serde", 132 | ] 133 | 134 | [[package]] 135 | name = "itoa" 136 | version = "0.4.5" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 139 | 140 | [[package]] 141 | name = "kernel32-sys" 142 | version = "0.2.2" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 145 | dependencies = [ 146 | "winapi 0.2.8", 147 | "winapi-build", 148 | ] 149 | 150 | [[package]] 151 | name = "lazy_static" 152 | version = "1.4.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 155 | 156 | [[package]] 157 | name = "libc" 158 | version = "0.2.69" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" 161 | 162 | [[package]] 163 | name = "log" 164 | version = "0.3.9" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 167 | dependencies = [ 168 | "log 0.4.8", 169 | ] 170 | 171 | [[package]] 172 | name = "log" 173 | version = "0.4.8" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 176 | dependencies = [ 177 | "cfg-if", 178 | ] 179 | 180 | [[package]] 181 | name = "memchr" 182 | version = "2.3.3" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 185 | 186 | [[package]] 187 | name = "num-integer" 188 | version = "0.1.42" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" 191 | dependencies = [ 192 | "autocfg", 193 | "num-traits", 194 | ] 195 | 196 | [[package]] 197 | name = "num-traits" 198 | version = "0.2.11" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 201 | dependencies = [ 202 | "autocfg", 203 | ] 204 | 205 | [[package]] 206 | name = "num_cpus" 207 | version = "1.13.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 210 | dependencies = [ 211 | "hermit-abi", 212 | "libc", 213 | ] 214 | 215 | [[package]] 216 | name = "pnet" 217 | version = "0.25.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "5c08c2c6c26481fcbe49dc4405baedf47151f859c5a45d3f254c2ff74ce51cf0" 220 | dependencies = [ 221 | "ipnetwork", 222 | "pnet_base", 223 | "pnet_datalink", 224 | "pnet_packet", 225 | "pnet_sys", 226 | "pnet_transport", 227 | ] 228 | 229 | [[package]] 230 | name = "pnet_base" 231 | version = "0.22.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "4df28acf2fcc77436dd2b91a9a0c2bb617f9ca5f2acefee1a4135058b9f9801f" 234 | 235 | [[package]] 236 | name = "pnet_datalink" 237 | version = "0.25.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "545f8df67cbc53438f37f56e68ae5ca49beb3990e9fd7e9e214c8ffd36c0e0ea" 240 | dependencies = [ 241 | "ipnetwork", 242 | "libc", 243 | "pnet_base", 244 | "pnet_sys", 245 | "winapi 0.2.8", 246 | ] 247 | 248 | [[package]] 249 | name = "pnet_macros" 250 | version = "0.25.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "cf402424ca7281aa234b726c32bce5a8e2278c72f5863305e291ac3de08e16f8" 253 | dependencies = [ 254 | "regex", 255 | "syntex", 256 | "syntex_syntax", 257 | ] 258 | 259 | [[package]] 260 | name = "pnet_macros_support" 261 | version = "0.25.0" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "5e586854ba703c15f74c486e1a46624566b47f1f61cc8a6b02c6bbe5e34a383b" 264 | dependencies = [ 265 | "pnet_base", 266 | ] 267 | 268 | [[package]] 269 | name = "pnet_packet" 270 | version = "0.25.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "c44c075c6d4f2e814dba621a999838e4a4f749f6117024f52b05b3c559a4fd17" 273 | dependencies = [ 274 | "glob", 275 | "pnet_base", 276 | "pnet_macros", 277 | "pnet_macros_support", 278 | "syntex", 279 | ] 280 | 281 | [[package]] 282 | name = "pnet_sys" 283 | version = "0.25.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "82f881a6d75ac98c5541db6144682d1773bb14c6fc50c6ebac7086c8f7f23c29" 286 | dependencies = [ 287 | "libc", 288 | "winapi 0.2.8", 289 | "ws2_32-sys", 290 | ] 291 | 292 | [[package]] 293 | name = "pnet_transport" 294 | version = "0.25.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "1b75ccaee7b5daba9f9a7d47bceeb73cc32edde9952dc5409460d6621ec667b6" 297 | dependencies = [ 298 | "libc", 299 | "pnet_base", 300 | "pnet_packet", 301 | "pnet_sys", 302 | ] 303 | 304 | [[package]] 305 | name = "proc-macro-hack" 306 | version = "0.5.15" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" 309 | 310 | [[package]] 311 | name = "proc-macro2" 312 | version = "1.0.10" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" 315 | dependencies = [ 316 | "unicode-xid 0.2.0", 317 | ] 318 | 319 | [[package]] 320 | name = "quote" 321 | version = "1.0.3" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 324 | dependencies = [ 325 | "proc-macro2", 326 | ] 327 | 328 | [[package]] 329 | name = "regex" 330 | version = "1.0.6" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "ee84f70c8c08744ea9641a731c7fadb475bf2ecc52d7f627feb833e0b3990467" 333 | dependencies = [ 334 | "aho-corasick", 335 | "memchr", 336 | "regex-syntax", 337 | "thread_local", 338 | "utf8-ranges", 339 | ] 340 | 341 | [[package]] 342 | name = "regex-syntax" 343 | version = "0.6.17" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 346 | 347 | [[package]] 348 | name = "rustc-serialize" 349 | version = "0.3.24" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 352 | 353 | [[package]] 354 | name = "ryu" 355 | version = "1.0.4" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" 358 | 359 | [[package]] 360 | name = "serde" 361 | version = "1.0.106" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" 364 | dependencies = [ 365 | "serde_derive", 366 | ] 367 | 368 | [[package]] 369 | name = "serde_derive" 370 | version = "1.0.106" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" 373 | dependencies = [ 374 | "proc-macro2", 375 | "quote", 376 | "syn", 377 | ] 378 | 379 | [[package]] 380 | name = "serde_json" 381 | version = "1.0.51" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" 384 | dependencies = [ 385 | "itoa", 386 | "ryu", 387 | "serde", 388 | ] 389 | 390 | [[package]] 391 | name = "syn" 392 | version = "1.0.18" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" 395 | dependencies = [ 396 | "proc-macro2", 397 | "quote", 398 | "unicode-xid 0.2.0", 399 | ] 400 | 401 | [[package]] 402 | name = "syntex" 403 | version = "0.42.2" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "0a30b08a6b383a22e5f6edc127d169670d48f905bb00ca79a00ea3e442ebe317" 406 | dependencies = [ 407 | "syntex_errors", 408 | "syntex_syntax", 409 | ] 410 | 411 | [[package]] 412 | name = "syntex_errors" 413 | version = "0.42.0" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "04c48f32867b6114449155b2a82114b86d4b09e1bddb21c47ff104ab9172b646" 416 | dependencies = [ 417 | "libc", 418 | "log 0.3.9", 419 | "rustc-serialize", 420 | "syntex_pos", 421 | "term", 422 | "unicode-xid 0.0.3", 423 | ] 424 | 425 | [[package]] 426 | name = "syntex_pos" 427 | version = "0.42.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "3fd49988e52451813c61fecbe9abb5cfd4e1b7bb6cdbb980a6fbcbab859171a6" 430 | dependencies = [ 431 | "rustc-serialize", 432 | ] 433 | 434 | [[package]] 435 | name = "syntex_syntax" 436 | version = "0.42.0" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "7628a0506e8f9666fdabb5f265d0059b059edac9a3f810bda077abb5d826bd8d" 439 | dependencies = [ 440 | "bitflags", 441 | "libc", 442 | "log 0.3.9", 443 | "rustc-serialize", 444 | "syntex_errors", 445 | "syntex_pos", 446 | "term", 447 | "unicode-xid 0.0.3", 448 | ] 449 | 450 | [[package]] 451 | name = "term" 452 | version = "0.4.6" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" 455 | dependencies = [ 456 | "kernel32-sys", 457 | "winapi 0.2.8", 458 | ] 459 | 460 | [[package]] 461 | name = "thread_local" 462 | version = "0.3.6" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 465 | dependencies = [ 466 | "lazy_static", 467 | ] 468 | 469 | [[package]] 470 | name = "time" 471 | version = "0.1.43" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 474 | dependencies = [ 475 | "libc", 476 | "winapi 0.3.8", 477 | ] 478 | 479 | [[package]] 480 | name = "unicode-xid" 481 | version = "0.0.3" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb" 484 | 485 | [[package]] 486 | name = "unicode-xid" 487 | version = "0.2.0" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 490 | 491 | [[package]] 492 | name = "utf8-ranges" 493 | version = "1.0.4" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" 496 | 497 | [[package]] 498 | name = "wasi" 499 | version = "0.9.0+wasi-snapshot-preview1" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 502 | 503 | [[package]] 504 | name = "winapi" 505 | version = "0.2.8" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 508 | 509 | [[package]] 510 | name = "winapi" 511 | version = "0.3.8" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 514 | dependencies = [ 515 | "winapi-i686-pc-windows-gnu", 516 | "winapi-x86_64-pc-windows-gnu", 517 | ] 518 | 519 | [[package]] 520 | name = "winapi-build" 521 | version = "0.1.1" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 524 | 525 | [[package]] 526 | name = "winapi-i686-pc-windows-gnu" 527 | version = "0.4.0" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 530 | 531 | [[package]] 532 | name = "winapi-x86_64-pc-windows-gnu" 533 | version = "0.4.0" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 536 | 537 | [[package]] 538 | name = "ws2_32-sys" 539 | version = "0.2.1" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 542 | dependencies = [ 543 | "winapi 0.2.8", 544 | "winapi-build", 545 | ] 546 | -------------------------------------------------------------------------------- /arsdk-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arsdk-rs" 3 | version = "0.0.5" 4 | authors = ["o0Ignition0o ", "Lachezar Lechev "] 5 | edition = "2018" 6 | description = "Parrot drones SDK in Rust (AeroRust)" 7 | license = "MIT/Apache-2.0" 8 | keywords = ["AeroRust", "drone", "parrot", "sdk"] 9 | 10 | [dependencies] 11 | thiserror = "1.0" 12 | pnet = "0.25" 13 | serde = {version = "1.0", features = ["derive"]} 14 | serde_json = "1.0" 15 | serde_with = "1" 16 | dashmap = "3.11" 17 | chrono = "0.4" 18 | scroll = "0.10" 19 | log = "0.4" 20 | -------------------------------------------------------------------------------- /arsdk-rs/LICENSE_APACHE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Jeremy Lempereur 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /arsdk-rs/LICENSE_MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Jeremy Lempereur 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /arsdk-rs/src/ardrone3.rs: -------------------------------------------------------------------------------- 1 | mod gps_state; 2 | mod piloting; 3 | mod piloting_state; 4 | 5 | pub use gps_state::GPSState; 6 | pub use piloting::{pcmd::PCMD, Piloting}; 7 | pub use piloting_state::PilotingState; 8 | 9 | #[derive(Debug, Clone, Eq, PartialEq)] 10 | /// u8 11 | pub enum ArDrone3 { 12 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_PILOTING = 0 13 | Piloting(Piloting), 14 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_CAMERA = 1 15 | Camera, 16 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_PILOTINGSETTINGS = 2 17 | PilotingSettings, 18 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_MEDIARECORDEVENT = 3 19 | MediaRecordEvent, 20 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_PILOTINGSTATE = 4 21 | PilotingState(PilotingState), 22 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_ANIMATIONS = 5 23 | Animations, 24 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_PILOTINGSETTINGSSTATE = 6 25 | PilotingSettingsState, 26 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_MEDIARECORD = 7 27 | MediaRecord, 28 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_MEDIARECORDSTATE = 8 29 | MediaRecordState, 30 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_NETWORKSETTINGS = 9 31 | NetworkSettings, 32 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_NETWORKSETTINGSSTATE = 10 33 | NetworkSettingsState, 34 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_SPEEDSETTINGS = 11 35 | SpeedSettings, 36 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_SPEEDSETTINGSSTATE = 12 37 | SpeedSettingsState, 38 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_NETWORK = 13 39 | Network, 40 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_NETWORKSTATE = 14 41 | NetworkState, 42 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_SETTINGSSTATE = 16 43 | SettingsState, 44 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_PICTURESETTINGS = 19 45 | PictureSettings, 46 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_PICTURESETTINGSSTATE = 20 47 | PictureSettingsState, 48 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_MEDIASTREAMING = 21 49 | MediaStreaming(MediaStreaming), 50 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_MEDIASTREAMINGSTATE = 22 51 | /// 52 | /// TODO: More info on this command 53 | /// On how to start the video stream, look at: 54 | /// arsdk-native/packages/libARController/Sources/ARCONTROLLER_Stream.c:219 55 | /// 56 | /// Possible values: 57 | /// - ARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED_ENABLED 58 | /// Starts stream 59 | /// - ARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED_DISABLED 60 | /// 61 | /// ```c 62 | /// /** 63 | /// * @brief Current video streaming status. 64 | /// */ 65 | /// typedef enum 66 | /// { 67 | /// ARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED_ENABLED = 0, ///< Video streaming is enabled. 68 | /// ARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED_DISABLED = 1, ///< Video streaming is disabled. 69 | /// ARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED_ERROR = 2, ///< Video streaming failed to start. 70 | /// ARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED_MAX 71 | /// } eARCOMMANDS_ARDRONE3_MEDIASTREAMINGSTATE_VIDEOENABLECHANGED_ENABLED; 72 | /// ``` 73 | MediaStreamingState, 74 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_GPSSETTINGS = 23 75 | GPSSettings, 76 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_GPSSETTINGSSTATE = 24 77 | GPSSettingsState, 78 | /// Frame { frame_type: Data, buffer_id: DCNavdata, sequence_id: 69, 79 | /// feature: Some(Unknown { feature: 1, data: [25, 0, 0, 243, 0] }) } 80 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_CAMERASTATE = 25 81 | /// 1. u16: 82 | /// - ARCOMMANDS_ID_ARDRONE3_CAMERASTATE_CMD_ORIENTATION = 0, 83 | /// * _tilt: u8 84 | /// * _pan: u8 85 | /// - ARCOMMANDS_ID_ARDRONE3_CAMERASTATE_CMD_DEFAULTCAMERAORIENTATION = 1, 86 | /// - ARCOMMANDS_ID_ARDRONE3_CAMERASTATE_CMD_ORIENTATIONV2 = 2, 87 | /// * _tilt: float?! 88 | /// * _pan: float?! 89 | /// See also `ARCOMMANDS_ReadWrite_AddFloatToBuffer`: 90 | /// > // Add a float to the buffer 91 | /// > // Returns -1 if the buffer is not big enough 92 | /// > // Returns the new offset in the buffer on success 93 | /// > int32_t ARCOMMANDS_ReadWrite_AddFloatToBuffer (uint8_t *buffer, float newVal, int32_t oldOffset, int32_t buffCap) 94 | /// > { 95 | /// > union { 96 | /// > float f; 97 | /// > uint32_t u32; 98 | /// > } val = { .f = newVal }; 99 | /// > return ARCOMMANDS_ReadWrite_AddU32ToBuffer (buffer, val.u32, oldOffset, buffCap); 100 | /// > } 101 | /// - ARCOMMANDS_ID_ARDRONE3_CAMERASTATE_CMD_DEFAULTCAMERAORIENTATIONV2 = 3, 102 | /// - ARCOMMANDS_ID_ARDRONE3_CAMERASTATE_CMD_VELOCITYRANGE = 4, 103 | /// 104 | /// 2. _tilt - u8 105 | /// 3. _pan - u8 106 | CameraState, 107 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_ANTIFLICKERING = 29 108 | AntiFlickering, 109 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_ANTIFLICKERINGSTATE = 30 110 | AntiFlickeringState, 111 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_GPSSTATE = 31 112 | /// TODO: use the GPSState struct 113 | GPSState, 114 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_PROSTATE = 32 115 | ProState, 116 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_ACCESSORYSTATE = 33 117 | AccessoryState, 118 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_PILOTINGEVENT = 34 119 | PilotingEvent, 120 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_SOUND = 35 121 | Sound, 122 | /// ARCOMMANDS_ID_ARDRONE3_CLASS_SOUNDSTATE = 36 123 | SoundState, 124 | Unknown { 125 | ardrone3: u8, 126 | data: Vec, 127 | }, 128 | } 129 | 130 | impl Into for &ArDrone3 { 131 | fn into(self) -> u8 { 132 | match self { 133 | ArDrone3::Piloting(_) => 0, 134 | ArDrone3::Camera => 1, 135 | ArDrone3::PilotingSettings => 2, 136 | ArDrone3::MediaRecordEvent => 3, 137 | ArDrone3::PilotingState { .. } => 4, 138 | ArDrone3::Animations => 5, 139 | ArDrone3::PilotingSettingsState => 6, 140 | ArDrone3::MediaRecord => 7, 141 | ArDrone3::MediaRecordState => 8, 142 | ArDrone3::NetworkSettings => 9, 143 | ArDrone3::NetworkSettingsState => 10, 144 | ArDrone3::SpeedSettings => 11, 145 | ArDrone3::SpeedSettingsState => 12, 146 | ArDrone3::Network => 13, 147 | ArDrone3::NetworkState => 14, 148 | ArDrone3::SettingsState => 16, 149 | ArDrone3::PictureSettings => 19, 150 | ArDrone3::PictureSettingsState => 20, 151 | ArDrone3::MediaStreaming(_) => 21, 152 | ArDrone3::MediaStreamingState => 22, 153 | ArDrone3::GPSSettings => 23, 154 | ArDrone3::GPSSettingsState => 24, 155 | ArDrone3::CameraState => 28, 156 | ArDrone3::AntiFlickering => 29, 157 | ArDrone3::AntiFlickeringState => 30, 158 | ArDrone3::GPSState => 31, 159 | ArDrone3::ProState => 32, 160 | ArDrone3::AccessoryState => 33, 161 | ArDrone3::PilotingEvent => 34, 162 | ArDrone3::Sound => 35, 163 | ArDrone3::SoundState => 36, 164 | ArDrone3::Unknown { ardrone3, .. } => *ardrone3, 165 | } 166 | } 167 | } 168 | 169 | /// From pyparrot: 170 | /// For commands reference see (pyparrot/commandsandsensors/ardrone3.xml#L2965-L3008)[https://github.com/amymcgovern/pyparrot/blob/8b7091cdf9a411938566abd7962b05ef7df7adb3/pyparrot/commandsandsensors/ardrone3.xml#L2965-L3008] 171 | /// 172 | /// For EnableVideo see (pyparrot/Bebop.py#L448-L461)[https://github.com/amymcgovern/pyparrot/blob/bf4775ec1199b282e4edde1e4a8e018dcc8725e0/pyparrot/Bebop.py#L448-L461] 173 | /// 174 | /// ```python 175 | /// command_tuple = self.command_parser.get_command_tuple("ardrone3", "MediaStreaming", "VideoEnable") 176 | /// param_tuple = [1] # Enable 177 | /// param_type_tuple = ['u8'] 178 | /// self.drone_connection.send_param_command_packet(command_tuple,param_tuple,param_type_tuple) 179 | /// ``` 180 | #[derive(Debug, Clone, PartialEq, Eq)] 181 | pub enum MediaStreaming { 182 | /// EnableVideo = 0 183 | /// bool: u8 184 | EnableVideo(bool), 185 | // TODO: VideoStreamMode 186 | } 187 | 188 | pub mod scroll_impl { 189 | use super::*; 190 | use crate::{frame::Error, parse::read_unknown}; 191 | use scroll::{ctx, Endian, Pread, Pwrite}; 192 | 193 | impl<'a> ctx::TryFromCtx<'a, Endian> for ArDrone3 { 194 | type Error = Error; 195 | 196 | // and the lifetime annotation on `&'a [u8]` here 197 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 198 | let mut offset = 0; 199 | let ardrone3 = match src.gread_with::(&mut offset, ctx)? { 200 | 0 => Self::Piloting(src.gread_with::(&mut offset, ctx)?), 201 | // 1 => Self::Camera, 202 | // 2 => Self::PilotingSettings, 203 | // 3 => Self::MediaRecordEvent, 204 | 4 => Self::PilotingState(src.gread_with::(&mut offset, ctx)?), 205 | // 5 => Self::Animations, 206 | // 6 => Self::PilotingSettingsState, 207 | // 7 => Self::MediaRecord, 208 | // 8 => Self::MediaRecordState, 209 | // 9 => Self::NetworkSettings, 210 | // 10 => Self::NetworkSettingsState, 211 | // 11 => Self::SpeedSettings, 212 | // 12 => Self::SpeedSettingsState, 213 | // 13 => Self::Network, 214 | // 14 => Self::NetworkState, 215 | // 16 => Self::SettingsState, 216 | // 19 => Self::PictureSettings, 217 | // 20 => Self::PictureSettingsState, 218 | // 21 => Self::MediaStreaming, 219 | // 22 => Self::MediaStreamingState, 220 | // 23 => Self::GPSSettings, 221 | // 24 => Self::GPSSettingsState, 222 | // 25 => Self::CameraState, 223 | // 29 => Self::AntiFlickering, 224 | // 30 => Self::AntiFlickeringState, 225 | // 31 => Self::GPSState, 226 | // 32 => Self::ProState, 227 | // 33 => Self::AccessoryState, 228 | // 34 => Self::PilotingEvent, 229 | // 35 => Self::Sound, 230 | // 36 => Self::SoundState, 231 | // value => { 232 | // return Err(MessageError::OutOfBound { 233 | // value: value.into(), 234 | // param: "ArDrone3".to_string(), 235 | // }) 236 | // } 237 | unknown_ardrone3 => Self::Unknown { 238 | ardrone3: unknown_ardrone3, 239 | data: read_unknown(src, &mut offset)?, 240 | }, 241 | }; 242 | 243 | Ok((ardrone3, offset)) 244 | } 245 | } 246 | 247 | impl<'a> ctx::TryIntoCtx for ArDrone3 { 248 | type Error = Error; 249 | 250 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 251 | let mut offset = 0; 252 | 253 | this.gwrite_with::((&self).into(), &mut offset, ctx)?; 254 | 255 | match self { 256 | Self::Piloting(piloting) => { 257 | this.gwrite_with(piloting, &mut offset, ctx)?; 258 | } 259 | Self::MediaStreaming(streaming) => match streaming { 260 | MediaStreaming::EnableVideo(enabled) => { 261 | this.gwrite_with::(enabled.into(), &mut offset, ctx)?; 262 | } // _ => unimplemented!("Not all MediaStreaming options are impled!"), 263 | }, 264 | Self::PilotingState(piloting_state) => { 265 | this.gwrite_with(piloting_state, &mut offset, ctx)?; 266 | } 267 | _ => unimplemented!("Not all ArDrone3 Classes are impled!"), 268 | } 269 | 270 | Ok(offset) 271 | } 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /arsdk-rs/src/ardrone3/gps_state.rs: -------------------------------------------------------------------------------- 1 | /// u16 2 | #[derive(Debug, Clone, Eq, PartialEq)] 3 | pub enum GPSState { 4 | /// ARCOMMANDS_ID_ARDRONE3_GPSSTATE_CMD_NUMBEROFSATELLITECHANGED = 0 5 | /// 6 | /// > Frame { frame_type: DataWithAck, buffer_id: DCEvent, sequence_id: 2, 7 | /// > feature: Some(Unknown { feature: 1, data: [31, 0, 0, 12] }) } 8 | /// u16 => ARCOMMANDS_ID_ARDRONE3_GPSSTATE_CMD_NUMBEROFSATELLITECHANGED = [0, 0] 9 | /// u8 => _numberOfSatellite = 12 10 | NumberOfSatelliteChanged(u8), 11 | /// ARCOMMANDS_ID_ARDRONE3_GPSSTATE_CMD_HOMETYPEAVAILABILITYCHANGED = 1 12 | /// 13 | /// 1. Type (u32): 14 | /// - ARCOMMANDS_ARDRONE3_GPSSTATE_HOMETYPEAVAILABILITYCHANGED_TYPE_TAKEOFF = 0 15 | /// > The drone has enough information to return to the take off position 16 | /// - ARCOMMANDS_ARDRONE3_GPSSTATE_HOMETYPEAVAILABILITYCHANGED_TYPE_PILOT = 1 17 | /// > The drone has enough information to return to the pilot position 18 | /// - ARCOMMANDS_ARDRONE3_GPSSTATE_HOMETYPEAVAILABILITYCHANGED_TYPE_FIRST_FIX = 2 19 | /// > The drone has not enough information, it will return to the first GPS fix 20 | /// - ARCOMMANDS_ARDRONE3_GPSSTATE_HOMETYPEAVAILABILITYCHANGED_TYPE_FOLLOWEE = 3 21 | /// > The drone has enough information to return to the target of the current (or last) follow me 22 | /// - ARCOMMANDS_ARDRONE3_GPSSTATE_HOMETYPEAVAILABILITYCHANGED_TYPE_MAX 23 | /// TODO: Check what the `MAX` does! 24 | /// 25 | /// Last argument is: 26 | /// - uint8_t *_available 27 | /// > 1 if this type is available, 0 otherwise 28 | HomeTypeAvailabilityChanged, 29 | /// ARCOMMANDS_ID_ARDRONE3_GPSSTATE_CMD_HOMETYPECHOSENCHANGED = 2, 30 | HomeTypeChosenChanged, 31 | } 32 | 33 | mod scroll_impl { 34 | // TODO: impl TryFromCtx 35 | 36 | // TODO: impl TryIntoCtx 37 | } 38 | -------------------------------------------------------------------------------- /arsdk-rs/src/ardrone3/piloting.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod pcmd; 2 | 3 | use pcmd::PCMD; 4 | 5 | /// eARCOMMANDS_ID_ARDRONE3_PILOTING_CMD 6 | /// u16 7 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 8 | pub enum Piloting { 9 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_FLATTRIM = 0 10 | FlatTrim, 11 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_TAKEOFF = 1 12 | TakeOff, 13 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_PCMD = 2 14 | /// ARCOMMANDS_Decoder_ARDrone3PilotingPCMDCb (_flag, _roll, _pitch, _yaw, _gaz, _timestampAndSeqNum, ARCOMMANDS_Decoder_ARDrone3PilotingPCMDCustom); 15 | /// ARCOMMANDS_Decoder_ARDrone3PilotingPCMDDecodeArgs (uint8_t *_flag, int8_t *_roll, int8_t *_pitch, int8_t *_yaw, int8_t *_gaz, uint32_t *_timestampAndSeqNum) 16 | /// * @param _timestampAndSeqNum Command timestamp in milliseconds (low 24 bits) + command sequence number (high 8 bits) [0;255]. 17 | /// 1_588_771_372_921 18 | /// @see https://developer.parrot.com/docs/reference/bebop_2/index.html#move-the-drone 19 | PCMD(PCMD), 20 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_LANDING = 3 21 | Landing, 22 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_EMERGENCY = 4 23 | /// Frame { frame_type: DataWithAck, buffer_id: DCEvent, sequence_id: 0, 24 | /// feature: Some(ArDrone3(Some(Unknown { ardrone3: 4, data: [1, 0, 0, 0, 0, 0] }))) } 25 | Emergency, 26 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_NAVIGATEHOME = 5 27 | /// requires: uint8_t _start 28 | /// as u8 29 | NavigateHome, 30 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_AUTOTAKEOFFMODE = 6 31 | AutoTakeOffMode, 32 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_MOVEBY = 7 33 | MoveBy, 34 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_USERTAKEOFF = 8 35 | UserTakeOff, 36 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_CIRCLE = 9 37 | Circle, 38 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_MOVETO = 10 39 | MoveTo, 40 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_CANCELMOVETO = 11 41 | CancelMoveTo, 42 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_STARTPILOTEDPOI = 12 43 | StartPilotedPOI, 44 | /// ARCOMMANDS_ID_ARDRONE3_PILOTING_CMD_STOPPILOTEDPOI = 13 45 | StopPilotedPOI, 46 | } 47 | 48 | impl Into for &Piloting { 49 | fn into(self) -> u16 { 50 | use Piloting::*; 51 | 52 | match self { 53 | FlatTrim => 0, 54 | TakeOff => 1, 55 | PCMD(_) => 2, 56 | Landing => 3, 57 | Emergency => 4, 58 | NavigateHome => 5, 59 | AutoTakeOffMode => 6, 60 | MoveBy => 7, 61 | UserTakeOff => 8, 62 | Circle => 9, 63 | MoveTo => 10, 64 | CancelMoveTo => 11, 65 | StartPilotedPOI => 12, 66 | StopPilotedPOI => 13, 67 | } 68 | } 69 | } 70 | 71 | mod scroll_impl { 72 | use super::*; 73 | use crate::frame::Error; 74 | use scroll::{ctx, Endian, Pread, Pwrite}; 75 | 76 | impl<'a> ctx::TryFromCtx<'a, Endian> for Piloting { 77 | type Error = Error; 78 | 79 | // and the lifetime annotation on `&'a [u8]` here 80 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 81 | let mut offset = 0; 82 | 83 | let piloting = match src.gread_with::(&mut offset, ctx)? { 84 | 0 => Piloting::FlatTrim, 85 | 1 => Piloting::TakeOff, 86 | 2 => Piloting::PCMD(src.gread_with(&mut offset, ctx)?), 87 | 3 => Piloting::Landing, 88 | 4 => Piloting::Emergency, 89 | 5 => Piloting::NavigateHome, 90 | 6 => Piloting::AutoTakeOffMode, 91 | 7 => Piloting::MoveBy, 92 | 8 => Piloting::UserTakeOff, 93 | 9 => Piloting::Circle, 94 | 10 => Piloting::MoveTo, 95 | 11 => Piloting::CancelMoveTo, 96 | 12 => Piloting::StartPilotedPOI, 97 | 13 => Piloting::StopPilotedPOI, 98 | value => { 99 | return Err(Error::OutOfBound { 100 | value: value.into(), 101 | param: "Piloting".to_string(), 102 | }) 103 | } 104 | }; 105 | 106 | Ok((piloting, offset)) 107 | } 108 | } 109 | 110 | impl<'a> ctx::TryIntoCtx for Piloting { 111 | type Error = Error; 112 | 113 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 114 | let mut offset = 0; 115 | this.gwrite_with::((&self).into(), &mut offset, ctx)?; 116 | 117 | match self { 118 | // Piloting::FlatTrim => {} 119 | Piloting::TakeOff => {} 120 | Piloting::PCMD(pcmd) => { 121 | this.gwrite_with(pcmd, &mut offset, ctx)?; 122 | } 123 | // Piloting::Landing => {} 124 | // Piloting::Emergency => {} 125 | // Piloting::NavigateHome => {} 126 | // Piloting::AutoTakeOffMode => {} 127 | // Piloting::MoveBy => {} 128 | // Piloting::UserTakeOff => {} 129 | // Piloting::Circle => {} 130 | // Piloting::MoveTo => {} 131 | // Piloting::CancelMoveTo => {} 132 | // Piloting::StartPilotedPOI => {} 133 | // Piloting::StopPilotedPOI => {} 134 | _ => {} 135 | } 136 | 137 | Ok(offset) 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /arsdk-rs/src/ardrone3/piloting/pcmd.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | 3 | /// Parrot Piloting Command 4 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 5 | pub struct PCMD { 6 | /// 1 if the roll and pitch values should be taken in consideration. 0 otherwise 7 | pub flag: bool, 8 | pub roll: i8, 9 | pub pitch: i8, 10 | pub yaw: i8, 11 | pub gaz: i8, 12 | pub timestamp: DateTime, 13 | // TODO: How should we handle the `sequence_id` in order not to show it to the user? 14 | pub sequence_id: u8, 15 | } 16 | 17 | mod scroll_impl { 18 | use super::*; 19 | use crate::frame::Error; 20 | use chrono::TimeZone; 21 | use scroll::{ctx, Endian, Pread, Pwrite}; 22 | 23 | impl<'a> ctx::TryFromCtx<'a, Endian> for PCMD { 24 | type Error = Error; 25 | 26 | // and the lifetime annotation on `&'a [u8]` here 27 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 28 | let mut offset = 0; 29 | let flag = match src.gread_with::(&mut offset, ctx)? { 30 | 0 => false, 31 | 1 => true, 32 | value => { 33 | return Err(Self::Error::OutOfBound { 34 | value: value.into(), 35 | param: "flag".to_string(), 36 | }) 37 | } 38 | }; 39 | 40 | let roll = src.gread_with(&mut offset, ctx)?; 41 | let pitch = src.gread_with(&mut offset, ctx)?; 42 | let yaw = src.gread_with(&mut offset, ctx)?; 43 | let gaz = src.gread_with(&mut offset, ctx)?; 44 | 45 | let timestamp_and_seq = src.gread_with::(&mut offset, ctx)?; 46 | 47 | Ok(( 48 | PCMD { 49 | flag, 50 | roll, 51 | pitch, 52 | yaw, 53 | gaz, 54 | timestamp: timestamp_and_seq.timestamp, 55 | sequence_id: timestamp_and_seq.sequence_id, 56 | }, 57 | offset, 58 | )) 59 | } 60 | } 61 | 62 | impl<'a> ctx::TryIntoCtx for PCMD { 63 | type Error = Error; 64 | 65 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 66 | let mut offset = 0; 67 | this.gwrite_with::(self.flag.into(), &mut offset, ctx)?; 68 | this.gwrite_with(self.roll, &mut offset, ctx)?; 69 | this.gwrite_with(self.pitch, &mut offset, ctx)?; 70 | this.gwrite_with(self.yaw, &mut offset, ctx)?; 71 | this.gwrite_with(self.gaz, &mut offset, ctx)?; 72 | let timestamp_and_seq = TimestampAndSeq { 73 | timestamp: self.timestamp, 74 | sequence_id: self.sequence_id, 75 | }; 76 | 77 | this.gwrite_with(timestamp_and_seq, &mut offset, ctx)?; 78 | 79 | Ok(offset) 80 | } 81 | } 82 | 83 | impl<'a> ctx::TryFromCtx<'a, Endian> for TimestampAndSeq { 84 | type Error = Error; 85 | 86 | // and the lifetime annotation on `&'a [u8]` here 87 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 88 | let mut offset = 0; 89 | 90 | // we always use Little-endian 91 | let timestamp_and_seq = src.gread_with::(&mut offset, ctx)?.to_le_bytes(); 92 | // 24 bits 93 | let timestamp_i64 = i64::from_le_bytes([ 94 | timestamp_and_seq[0], 95 | timestamp_and_seq[1], 96 | timestamp_and_seq[2], 97 | 0, 98 | 0, 99 | 0, 100 | 0, 101 | 0, 102 | ]); 103 | let timestamp = Utc.timestamp_millis(timestamp_i64); 104 | // 8 bits 105 | let sequence_id = timestamp_and_seq[3]; 106 | 107 | Ok(( 108 | Self { 109 | timestamp, 110 | sequence_id, 111 | }, 112 | offset, 113 | )) 114 | } 115 | } 116 | 117 | impl<'a> ctx::TryIntoCtx for TimestampAndSeq { 118 | type Error = Error; 119 | 120 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 121 | let mut offset = 0; 122 | 123 | let milliseconds = self.timestamp.timestamp_millis(); 124 | // from byte 5 to 8 = 3 bytes 125 | // always use Little-endian! 126 | let bytes = &milliseconds.to_le_bytes()[5..]; 127 | 128 | this.gwrite_with(bytes, &mut offset, ())?; 129 | this.gwrite_with(self.sequence_id, &mut offset, ctx)?; 130 | 131 | Ok(offset) 132 | } 133 | } 134 | 135 | struct TimestampAndSeq { 136 | timestamp: DateTime, 137 | sequence_id: u8, 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /arsdk-rs/src/ardrone3/piloting_state.rs: -------------------------------------------------------------------------------- 1 | /// u16 2 | #[derive(Debug, Clone, Eq, PartialEq)] 3 | pub enum PilotingState { 4 | /// typedef enum { 5 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_FLATTRIMCHANGED = 0, 6 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_FLYINGSTATECHANGED = 1, 7 | /// } 8 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_FLATTRIMCHANGED = 0, 9 | FlatTrimChanged, 10 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_FLYINGSTATECHANGED = 1, 11 | /// 12 | /// Example 1: 13 | /// ```bash 14 | /// [2020-07-26T08:05:46Z DEBUG arsdk_rs::listener] Bytes: 4 126 0 15 0 0 0 1 4 1 0 7 0 0 0 15 | /// [2020-07-26T08:05:46Z INFO arsdk_rs::parse] Frame: Frame { frame_type: DataWithAck, buffer_id: DCEvent, sequence_id: 0, feature: Some(ArDrone3(Some(Unknown { ardrone3: 4, data: [1, 0, 7, 0, 0, 0] }))) } 16 | /// ``` 17 | /// 18 | /// Example 2: 19 | /// ```bash 20 | /// Frame { frame_type: DataWithAck, buffer_id: DCEvent, sequence_id: 0, feature: Some(ArDrone3(Some(Unknown { ardrone3: 4, data: [1, 0, 0, 0, 0, 0] }))) } 21 | /// ``` 22 | /// u16 [1, 0] - FLYINGSTATECHANGED 23 | FlyingStateChanged, 24 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_ALERTSTATECHANGED = 2, 25 | AlterStateChanged, 26 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_NAVIGATEHOMESTATECHANGED = 3, 27 | NavigateHomeStateChanged, 28 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_POSITIONCHANGED = 4, 29 | PositionChanged, 30 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_SPEEDCHANGED = 5, 31 | /// 32 | /// Example: 33 | /// Frame { frame_type: Data, buffer_id: DCNavdata, sequence_id: 0, feature: Some(ArDrone3(Some(Unknown { ardrone3: 4, data: [5, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0] }))) } 34 | /// u16 [5, 0] - SPEEDCHANGED 35 | /// 3 x (4?) per float [0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0] 36 | /// 37 | /// ```c 38 | /// currIndexInBuffer = ARCOMMANDS_ReadWrite_AddU16ToBuffer (buffer, ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_SPEEDCHANGED, currIndexInBuffer, buffLen); 39 | /// currIndexInBuffer = ARCOMMANDS_ReadWrite_AddFloatToBuffer (buffer, _speedX, currIndexInBuffer, buffLen); 40 | /// currIndexInBuffer = ARCOMMANDS_ReadWrite_AddFloatToBuffer (buffer, _speedY, currIndexInBuffer, buffLen); 41 | /// currIndexInBuffer = ARCOMMANDS_ReadWrite_AddFloatToBuffer (buffer, _speedZ, currIndexInBuffer, buffLen); 42 | /// ``` 43 | SpeedChanged, 44 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_ATTITUDECHANGED = 6, 45 | /// Frame { frame_type: Data, buffer_id: DCNavdata, sequence_id: 40, feature: Some(ArDrone3(Some(PilotingState { data: [6, 0, 44, 49, 49, 55, 153, 38, 7, 185, 107, 25, 201, 63] }))) } 46 | AttitudeChanged, 47 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_AUTOTAKEOFFMODECHANGED = 7, 48 | AutoTakeOffModeChanged, 49 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_ALTITUDECHANGED = 8, 50 | AltitudeChanged, 51 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_GPSLOCATIONCHANGED = 9, 52 | GpsLocationChanged, 53 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_LANDINGSTATECHANGED = 10, 54 | /// 55 | /// ```c 56 | /// /* 57 | /// * @brief Drone landing state 58 | /// */ 59 | /// typedef enum 60 | /// { 61 | /// ARCOMMANDS_ARDRONE3_PILOTINGSTATE_LANDINGSTATECHANGED_STATE_LINEAR = 0, ///< Linear landing 62 | /// ARCOMMANDS_ARDRONE3_PILOTINGSTATE_LANDINGSTATECHANGED_STATE_SPIRAL = 1, ///< Spiral landing 63 | /// ARCOMMANDS_ARDRONE3_PILOTINGSTATE_LANDINGSTATECHANGED_STATE_MAX 64 | /// } eARCOMMANDS_ARDRONE3_PILOTINGSTATE_LANDINGSTATECHANGED_STATE; 65 | /// ``` 66 | LandingStateChanged, 67 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_AIRSPEEDCHANGED = 11, 68 | AirspeedChanged, 69 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_MOVETOCHANGED = 12, 70 | MoveToChanged, 71 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_MOTIONSTATE = 13, 72 | MotionState, 73 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_PILOTEDPOI = 14, 74 | PilotedPOI, 75 | /// ARCOMMANDS_ID_ARDRONE3_PILOTINGSTATE_CMD_RETURNHOMEBATTERYCAPACITY = 15, 76 | ReturnHomeBatteryCapacity, 77 | Unknown { 78 | piloting_state: u16, 79 | data: Vec, 80 | }, 81 | } 82 | 83 | impl Into for &PilotingState { 84 | fn into(self) -> u16 { 85 | use PilotingState::*; 86 | 87 | match self { 88 | FlatTrimChanged => 0, 89 | FlyingStateChanged => 1, 90 | AlterStateChanged => 2, 91 | NavigateHomeStateChanged => 3, 92 | PositionChanged => 4, 93 | SpeedChanged => 5, 94 | AttitudeChanged => 6, 95 | AutoTakeOffModeChanged => 7, 96 | AltitudeChanged => 8, 97 | GpsLocationChanged => 9, 98 | LandingStateChanged => 10, 99 | AirspeedChanged => 11, 100 | MoveToChanged => 12, 101 | MotionState => 13, 102 | PilotedPOI => 14, 103 | ReturnHomeBatteryCapacity => 15, 104 | Unknown { piloting_state, .. } => *piloting_state, 105 | } 106 | } 107 | } 108 | 109 | pub mod scroll_impl { 110 | use super::*; 111 | use crate::{frame::Error, parse::read_unknown}; 112 | use scroll::{ctx, Endian, Pread, Pwrite}; 113 | 114 | impl<'a> ctx::TryFromCtx<'a, Endian> for PilotingState { 115 | type Error = Error; 116 | 117 | // and the lifetime annotation on `&'a [u8]` here 118 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 119 | let mut offset = 0; 120 | 121 | #[allow(clippy::match_single_binding)] 122 | let piloting_state = match src.gread_with::(&mut offset, ctx)? { 123 | unknown => Self::Unknown { 124 | piloting_state: unknown, 125 | data: read_unknown(src, &mut offset)?, 126 | }, 127 | }; 128 | 129 | Ok((piloting_state, offset)) 130 | } 131 | } 132 | 133 | impl<'a> ctx::TryIntoCtx for PilotingState { 134 | type Error = Error; 135 | 136 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 137 | let mut offset = 0; 138 | 139 | this.gwrite_with::((&self).into(), &mut offset, ctx)?; 140 | 141 | match self { 142 | PilotingState::Unknown { data, .. } => { 143 | this.gwrite_with(data.as_slice(), &mut offset, ())?; 144 | } 145 | _ => unimplemented!("Not all PilotingState are impled!"), 146 | } 147 | 148 | Ok(offset) 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /arsdk-rs/src/command.rs: -------------------------------------------------------------------------------- 1 | use crate::ardrone3::ArDrone3; 2 | use crate::common; 3 | use crate::jumping_sumo; 4 | 5 | #[derive(Debug, PartialEq, Eq, Clone)] 6 | /// u8 7 | pub enum Feature { 8 | Common(Option), // ARCOMMANDS_ID_FEATURE_COMMON = 0, 9 | ArDrone3(Option), // ARCOMMANDS_ID_FEATURE_ARDRONE3 = 1, 10 | Minidrone, // ARCOMMANDS_ID_FEATURE_MINIDRONE = 2, 11 | JumpingSumo(jumping_sumo::Class), // ARCOMMANDS_ID_FEATURE_JUMPINGSUMO = 3, 12 | SkyController, // ARCOMMANDS_ID_FEATURE_SKYCONTROLLER = 4, 13 | PowerUp, // ARCOMMANDS_ID_FEATURE_POWERUP = 8, 14 | /// ARCOMMANDS_ID_FEATURE_GENERIC = 133, 15 | /// 16 | /// For details on the Generic check: 17 | /// 1. `ARCOMMANDS_ID_GENERIC_COMMONEVENTSTATE_CMD_SETDRONESETTINGS` in `libARCommands/gen/Sources/ARCOMMANDS_Decoder.c` 18 | /// 2.``ARCOMMANDS_ID_GENERIC_CMD_DRONESETTINGSCHANGED` in `libARCommands/gen/Sources/ARCOMMANDS_Decoder.c` 19 | /// 3. `ARCOMMANDS_Generic_DroneSettings_t` in `libARCommands/libARCommands/ARCOMMANDS_Types.h` 20 | /// 4. `ARCOMMANDS_Generic_DroneSettingsChanged_t` in `libARCommands/libARCommands/ARCOMMANDS_Types.h` 21 | Generic, 22 | FollowMe, // ARCOMMANDS_ID_FEATURE_FOLLOW_ME = 134, 23 | Wifi, // ARCOMMANDS_ID_FEATURE_WIFI = 135, 24 | RC, // ARCOMMANDS_ID_FEATURE_RC = 136, 25 | DroneManager, // ARCOMMANDS_ID_FEATURE_DRONE_MANAGER = 137, 26 | Mapper, // ARCOMMANDS_ID_FEATURE_MAPPER = 138, 27 | Debug, // ARCOMMANDS_ID_FEATURE_DEBUG = 139, 28 | ControllerInfo, // ARCOMMANDS_ID_FEATURE_CONTROLLER_INFO = 140, 29 | MapperMini, // ARCOMMANDS_ID_FEATURE_MAPPER_MINI = 141, 30 | ThermalCam, // ARCOMMANDS_ID_FEATURE_THERMAL_CAM = 142, 31 | Animation, // ARCOMMANDS_ID_FEATURE_ANIMATION = 144, 32 | SequoiaCam, // ARCOMMANDS_ID_FEATURE_SEQUOIA_CAM = 147, 33 | /// Unknown 149 from anafi4k 34 | /// Frame { frame_type: Data, buffer_id: DCNavdata, sequence_id: 14, feature: Some(Unknown { feature: 149, data: [0, 3, 0, 91, 33] }) } 35 | /// Unknown 148 from anafi4k 36 | /// Frame { frame_type: Data, buffer_id: DCNavdata, sequence_id: 5, feature: Some(Unknown { feature: 148, data: [0, 6, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 223, 46, 229, 182, 0, 0, 0, 0, 0, 0, 0, 0, 222, 4, 180, 66, 0, 0, 0, 128, 0, 0, 0, 0] }) } 37 | /// Unknown 143 from anafi4k 38 | /// Frame { frame_type: Data, buffer_id: DCNavdata, sequence_id: 4, feature: Some(Unknown { feature: 143, data: [0, 22, 0, 0, 0, 0, 128, 63] }) } 39 | /// Unknown 155 from anafi4k 40 | /// Frame { frame_type: Data, buffer_id: PING, sequence_id: 0, feature: Some(Unknown { feature: 155, data: [216, 221, 13, 0, 0, 0, 0] }) } 41 | /// Unknown 56 42 | /// Frame { frame_type: Data, buffer_id: PING, sequence_id: 1, feature: Some(Unknown { feature: 56, data: [129, 253, 13, 0, 0, 0, 0] }) } 43 | /// Unknown 59 44 | /// Frame { frame_type: Data, buffer_id: PING, sequence_id: 2, feature: Some(Unknown { feature: 59, data: [82, 29, 14, 0, 0, 0, 0] }) } 45 | 46 | /// Temporary Enum for storing unknown Features: 47 | /// TODO: REMOVE! 48 | Unknown { 49 | feature: u8, 50 | data: Vec, 51 | }, 52 | } 53 | 54 | // --------------------- Conversion impls --------------------- // 55 | 56 | impl Into for &Feature { 57 | fn into(self) -> u8 { 58 | use Feature::*; 59 | 60 | match self { 61 | Common(_) => 0, 62 | ArDrone3(_) => 1, 63 | Minidrone => 2, 64 | JumpingSumo(_) => 3, 65 | SkyController => 4, 66 | PowerUp => 8, 67 | Generic => 133, 68 | FollowMe => 134, 69 | Wifi => 135, 70 | RC => 136, 71 | DroneManager => 137, 72 | Mapper => 138, 73 | Debug => 139, 74 | ControllerInfo => 140, 75 | MapperMini => 141, 76 | ThermalCam => 142, 77 | Animation => 144, 78 | SequoiaCam => 147, 79 | // Temporary Enum for storing unknown Features: 80 | // TODO: REMOVE! 81 | Unknown { feature, .. } => *feature, 82 | } 83 | } 84 | } 85 | 86 | pub mod scroll_impl { 87 | use super::*; 88 | use crate::{frame::Error, parse::read_unknown}; 89 | use scroll::{ctx, Endian, Pread, Pwrite}; 90 | 91 | impl<'a> ctx::TryFromCtx<'a, Endian> for Feature { 92 | type Error = Error; 93 | 94 | // and the lifetime annotation on `&'a [u8]` here 95 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 96 | let mut offset = 0; 97 | 98 | let feature = match src.gread_with::(&mut offset, ctx)? { 99 | 0 => { 100 | let class = if !src[offset..].is_empty() { 101 | let common = src.gread_with(&mut offset, ctx)?; 102 | Some(common) 103 | } else { 104 | None 105 | }; 106 | 107 | Self::Common(class) 108 | } 109 | 1 => { 110 | let ardrone3 = if !src[offset..].is_empty() { 111 | let ardrone3 = src.gread_with::(&mut offset, ctx)?; 112 | 113 | Some(ardrone3) 114 | } else { 115 | None 116 | }; 117 | 118 | Self::ArDrone3(ardrone3) 119 | } 120 | // 2 => Self::Minidrone, 121 | 3 => { 122 | let js_class = src.gread_with(&mut offset, ctx)?; 123 | 124 | Self::JumpingSumo(js_class) 125 | } 126 | // 4 => Self::SkyController, 127 | // 8 => Self::PowerUp, 128 | // 133 => Self::Generic, 129 | // 134 => Self::FollowMe, 130 | // 135 => Self::Wifi, 131 | // 136 => Self::RC, 132 | // 137 => Self::DroneManager, 133 | // 138 => Self::Mapper, 134 | // 139 => Self::Debug, 135 | // 140 => Self::ControllerInfo, 136 | // 141 => Self::MapperMini, 137 | // 142 => Self::ThermalCam, 138 | // 144 => Self::Animation, 139 | // 147 => Self::SequoiaCam, 140 | // value => { 141 | // return Err(Self::Error::OutOfBound { 142 | // value: value.into(), 143 | // param: "Feature".to_string(), 144 | // }) 145 | // } 146 | // Temporary Enum for storing unknown Features: 147 | // TODO: REMOVE! 148 | unknown_feature => Self::Unknown { 149 | feature: unknown_feature, 150 | data: read_unknown(src, &mut offset)?, 151 | }, 152 | }; 153 | 154 | Ok((feature, offset)) 155 | } 156 | } 157 | 158 | impl<'a> ctx::TryIntoCtx for Feature { 159 | type Error = Error; 160 | 161 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 162 | let mut offset = 0; 163 | 164 | this.gwrite_with::((&self).into(), &mut offset, ctx)?; 165 | 166 | match self { 167 | // Self::Common(common) => { 168 | // this.gwrite_with(common, &mut offset, ctx)?; 169 | // } 170 | Self::ArDrone3(ardrone3) => { 171 | if let Some(ardrone3) = ardrone3 { 172 | this.gwrite_with(ardrone3, &mut offset, ctx)?; 173 | } 174 | // else leave it empty 175 | } 176 | Self::JumpingSumo(js) => { 177 | this.gwrite_with(js, &mut offset, ctx)?; 178 | } 179 | Self::Unknown { data, .. } => { 180 | this.gwrite_with(data.as_slice(), &mut offset, ())?; 181 | } 182 | _ => unimplemented!("Not all Features are impled"), 183 | } 184 | 185 | Ok(offset) 186 | } 187 | } 188 | } 189 | 190 | // --------------------- Tests --------------------- // 191 | 192 | #[cfg(test)] 193 | mod command_tests { 194 | use super::*; 195 | 196 | #[test] 197 | fn test_feature() { 198 | assert_feature( 199 | Feature::Common(Some(common::Class::Common(common::Common::AllStates))), 200 | 0, 201 | ); 202 | assert_feature( 203 | Feature::ArDrone3(Some(ArDrone3::Piloting(crate::ardrone3::Piloting::TakeOff))), 204 | 1, 205 | ); 206 | assert_feature(Feature::Minidrone, 2); 207 | assert_feature( 208 | Feature::JumpingSumo(jumping_sumo::Class::Piloting( 209 | jumping_sumo::PilotingID::Pilot(jumping_sumo::PilotState::default()), 210 | )), 211 | 3, 212 | ); 213 | assert_feature(Feature::SkyController, 4); 214 | assert_feature(Feature::PowerUp, 8); 215 | assert_feature(Feature::Generic, 133); 216 | assert_feature(Feature::FollowMe, 134); 217 | assert_feature(Feature::Wifi, 135); 218 | assert_feature(Feature::RC, 136); 219 | assert_feature(Feature::DroneManager, 137); 220 | assert_feature(Feature::Mapper, 138); 221 | assert_feature(Feature::Debug, 139); 222 | assert_feature(Feature::ControllerInfo, 140); 223 | assert_feature(Feature::MapperMini, 141); 224 | assert_feature(Feature::ThermalCam, 142); 225 | assert_feature(Feature::Animation, 144); 226 | assert_feature(Feature::SequoiaCam, 147); 227 | } 228 | 229 | fn assert_feature(ref f: Feature, v: u8) { 230 | let as_u8: u8 = f.into(); 231 | assert_eq!(v, as_u8); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /arsdk-rs/src/common.rs: -------------------------------------------------------------------------------- 1 | use chrono::{offset::Utc, DateTime}; 2 | use std::ffi::CString; 3 | 4 | #[derive(Debug, PartialEq, Eq, Clone)] 5 | /// u8 6 | pub enum Class { 7 | Network, // ARCOMMANDS_ID_COMMON_CLASS_NETWORK = 0, 8 | NetworkEvent, // ARCOMMANDS_ID_COMMON_CLASS_NETWORKEVENT = 1, 9 | Settings, // ARCOMMANDS_ID_COMMON_CLASS_SETTINGS = 2, 10 | SettingsState, // ARCOMMANDS_ID_COMMON_CLASS_SETTINGSSTATE = 3, 11 | Common(Common), // ARCOMMANDS_ID_COMMON_CLASS_COMMON = 4, 12 | /// ARCOMMANDS_ID_COMMON_CLASS_COMMONSTATE = 5, 13 | /// 14 | /// Bytes: 2 127 0 [12 0 0 0] [0] [5] [1 0] 100 15 | /// Common Common BatterStateChanged 100% 16 | /// _percent: u8 17 | /// 18 | /// eARCOMMANDS_ID_COMMON_COMMONSTATE_CMD: u16 19 | //// 20 | /// typedef enum { 21 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_ALLSTATESCHANGED = 0, 22 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_BATTERYSTATECHANGED = 1, 23 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_MASSSTORAGESTATELISTCHANGED = 2, 24 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_MASSSTORAGEINFOSTATELISTCHANGED = 3, 25 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_CURRENTDATECHANGED = 4, 26 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_CURRENTTIMECHANGED = 5, 27 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_MASSSTORAGEINFOREMAININGLISTCHANGED = 6, 28 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_WIFISIGNALCHANGED = 7, 29 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_SENSORSSTATESLISTCHANGED = 8, 30 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_PRODUCTMODEL = 9, 31 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_COUNTRYLISTKNOWN = 10, 32 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_DEPRECATEDMASSSTORAGECONTENTCHANGED = 11, 33 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_MASSSTORAGECONTENT = 12, 34 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_MASSSTORAGECONTENTFORCURRENTRUN = 13, 35 | /// ARCOMMANDS_ID_COMMON_COMMONSTATE_CMD_VIDEORECORDINGTIMESTAMP = 14, 36 | /// } eARCOMMANDS_ID_COMMON_COMMONSTATE_CMD; 37 | CommonState, 38 | Overheat, // ARCOMMANDS_ID_COMMON_CLASS_OVERHEAT = 6, 39 | OverheatState, // ARCOMMANDS_ID_COMMON_CLASS_OVERHEATSTATE = 7, 40 | Controller, // ARCOMMANDS_ID_COMMON_CLASS_CONTROLLER = 8, 41 | WifiSettings, // ARCOMMANDS_ID_COMMON_CLASS_WIFISETTINGS = 9, 42 | WifiSettingsState, // ARCOMMANDS_ID_COMMON_CLASS_WIFISETTINGSSTATE = 10, 43 | Mavlink, // ARCOMMANDS_ID_COMMON_CLASS_MAVLINK = 11, 44 | MavlinkState, // ARCOMMANDS_ID_COMMON_CLASS_MAVLINKSTATE = 12, 45 | FlightPlanSettings, // ARCOMMANDS_ID_COMMON_CLASS_FLIGHTPLANSETTINGS = 32, 46 | FlightPlanSettingsState, // ARCOMMANDS_ID_COMMON_CLASS_FLIGHTPLANSETTINGSSTATE = 33, 47 | /// ARCOMMANDS_ID_COMMON_CLASS_CALIBRATION = 13 48 | /// 49 | /// First cmd: 50 | /// u16 - ARCOMMANDS_ID_COMMON_CALIBRATION_CMD_MAGNETOCALIBRATION 51 | /// u8 - _calibrate 52 | /// 53 | /// Second cmd: 54 | /// u16 - ARCOMMANDS_ID_COMMON_CALIBRATION_CMD_PITOTCALIBRATION 55 | /// u8 - _calibrate 56 | Calibration, 57 | /// ARCOMMANDS_ID_COMMON_CLASS_CALIBRATIONSTATE = 14 58 | /// u16 - ARCOMMANDS_ID_COMMON_CALIBRATIONSTATE_CMD_MAGNETOCALIBRATIONSTATECHANGED 59 | /// u8 - _xAxisCalibration 60 | /// u8 - _yAxisCalibration 61 | /// u8 - _zAxisCalibration 62 | /// u8 - _calibrationFailed 63 | CalibrationState, 64 | CameraSettingsState, // ARCOMMANDS_ID_COMMON_CLASS_CAMERASETTINGSSTATE = 15, 65 | Gps, // ARCOMMANDS_ID_COMMON_CLASS_GPS = 16, 66 | FlightPlanState, // ARCOMMANDS_ID_COMMON_CLASS_FLIGHTPLANSTATE = 17, 67 | FlightPlanEvent, // ARCOMMANDS_ID_COMMON_CLASS_FLIGHTPLANEVENT = 19, 68 | ArLibsVersionsState, // ARCOMMANDS_ID_COMMON_CLASS_ARLIBSVERSIONSSTATE = 18, 69 | Audio, // ARCOMMANDS_ID_COMMON_CLASS_AUDIO = 20, 70 | AudioState, // ARCOMMANDS_ID_COMMON_CLASS_AUDIOSTATE = 21, 71 | HeadLights, // ARCOMMANDS_ID_COMMON_CLASS_HEADLIGHTS = 22, 72 | HeadLightsState, // ARCOMMANDS_ID_COMMON_CLASS_HEADLIGHTSSTATE = 23, 73 | Animations, // ARCOMMANDS_ID_COMMON_CLASS_ANIMATIONS = 24, 74 | AnimationsState, // ARCOMMANDS_ID_COMMON_CLASS_ANIMATIONSSTATE = 25, 75 | Accessory, // ARCOMMANDS_ID_COMMON_CLASS_ACCESSORY = 26, 76 | AccessoryState, // ARCOMMANDS_ID_COMMON_CLASS_ACCESSORYSTATE = 27, 77 | Charger, // ARCOMMANDS_ID_COMMON_CLASS_CHARGER = 28, 78 | ChargerState, // ARCOMMANDS_ID_COMMON_CLASS_CHARGERSTATE = 29, 79 | Runstate, // ARCOMMANDS_ID_COMMON_CLASS_RUNSTATE = 30, 80 | Factory, // ARCOMMANDS_ID_COMMON_CLASS_FACTORY = 31, 81 | Unknown { 82 | class: u8, 83 | data: Vec, 84 | }, 85 | } 86 | 87 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 88 | /// u8 89 | pub enum Common { 90 | /// ARCOMMANDS_ID_COMMON_COMMON_CMD_ALLSTATES = 0, 91 | AllStates, 92 | /// ARCOMMANDS_ID_COMMON_COMMON_CMD_CURRENTDATE = 1, 93 | CurrentDate(DateTime), 94 | /// ARCOMMANDS_ID_COMMON_COMMON_CMD_CURRENTTIME = 2, 95 | CurrentTime(DateTime), 96 | /// ARCOMMANDS_ID_COMMON_COMMON_CMD_REBOOT = 3, 97 | Reboot, 98 | } 99 | 100 | // "yyyy-MM-dd"forCommon.Common.CurrentDate. Ex:2015-08-27 101 | fn format_date(date: &DateTime) -> CString { 102 | let format = date.format("%Y-%m-%d").to_string(); 103 | 104 | CString::new(format.as_bytes()).expect("CString::new failed with formatted date") 105 | } 106 | // "’T’HHmmssZZZ"forCommon.Common.CurrentTime. Ex:T101527+0200. 107 | fn format_time(time: &DateTime) -> CString { 108 | let format = time.format("T%H%M%S%z").to_string(); 109 | 110 | CString::new(format.as_bytes()).expect("CString::new failed with formatted date") 111 | } 112 | 113 | // --------------------- Conversion impls --------------------- // 114 | 115 | impl Into for &Class { 116 | fn into(self) -> u8 { 117 | use Class::*; 118 | match self { 119 | Network => 0, 120 | NetworkEvent => 1, 121 | Settings => 2, 122 | SettingsState => 3, 123 | Common(_) => 4, 124 | CommonState => 5, 125 | Overheat => 6, 126 | OverheatState => 7, 127 | Controller => 8, 128 | WifiSettings => 9, 129 | WifiSettingsState => 10, 130 | Mavlink => 11, 131 | MavlinkState => 12, 132 | FlightPlanSettings => 32, 133 | FlightPlanSettingsState => 33, 134 | Calibration => 13, 135 | CalibrationState => 14, 136 | CameraSettingsState => 15, 137 | Gps => 16, 138 | FlightPlanState => 17, 139 | FlightPlanEvent => 19, 140 | ArLibsVersionsState => 18, 141 | Audio => 20, 142 | AudioState => 21, 143 | HeadLights => 22, 144 | HeadLightsState => 23, 145 | Animations => 24, 146 | AnimationsState => 25, 147 | Accessory => 26, 148 | AccessoryState => 27, 149 | Charger => 28, 150 | ChargerState => 29, 151 | Runstate => 30, 152 | Factory => 31, 153 | Unknown { class, .. } => *class, 154 | } 155 | } 156 | } 157 | 158 | impl Into for Common { 159 | fn into(self) -> u8 { 160 | match self { 161 | Self::AllStates => 0, 162 | Self::CurrentDate(_) => 1, 163 | Self::CurrentTime(_) => 2, 164 | Self::Reboot => 3, 165 | } 166 | } 167 | } 168 | 169 | pub mod scroll_impl { 170 | use super::*; 171 | use crate::frame::Error; 172 | use scroll::{ctx, Endian, Pread, Pwrite}; 173 | 174 | impl<'a> ctx::TryFromCtx<'a, Endian> for Class { 175 | type Error = Error; 176 | 177 | // and the lifetime annotation on `&'a [u8]` here 178 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 179 | let mut offset = 0; 180 | 181 | let class = match src.gread_with::(&mut offset, ctx)? { 182 | 0 => Self::Network, 183 | 1 => Self::NetworkEvent, 184 | 2 => Self::Settings, 185 | 3 => Self::SettingsState, 186 | 4 => { 187 | let common = src.gread_with(&mut offset, ctx)?; 188 | 189 | Self::Common(common) 190 | } 191 | // 5 => Self::CommonState, 192 | // 6 => Self::Overheat, 193 | // 7 => Self::OverheatState, 194 | // 8 => Self::Controller, 195 | // 9 => Self::WifiSettings, 196 | // 10 => Self::WifiSettingsState, 197 | // 11 => Self::Mavlink, 198 | // 12 => Self::MavlinkState, 199 | // 13 => Self::Calibration, 200 | // 14 => Self::CalibrationState, 201 | // 15 => Self::CameraSettingsState, 202 | // 16 => Self::Gps, 203 | // 17 => Self::FlightPlanState, 204 | // 18 => Self::ArLibsVersionsState, 205 | // 19 => Self::FlightPlanEvent, 206 | // 20 => Self::Audio, 207 | // 21 => Self::AudioState, 208 | // 22 => Self::HeadLights, 209 | // 23 => Self::HeadLightsState, 210 | // 24 => Self::Animations, 211 | // 25 => Self::AnimationsState, 212 | // 26 => Self::Accessory, 213 | // 27 => Self::AccessoryState, 214 | // 28 => Self::Charger, 215 | // 29 => Self::ChargerState, 216 | // 30 => Self::Runstate, 217 | // 31 => Self::Factory, 218 | // 32 => Self::FlightPlanSettings, 219 | // 33 => Self::FlightPlanSettingsState, 220 | unknown_class => Self::Unknown { 221 | class: unknown_class, 222 | data: crate::parse::read_unknown(src, &mut offset)?, 223 | }, 224 | }; 225 | 226 | Ok((class, offset)) 227 | } 228 | } 229 | 230 | impl<'a> ctx::TryIntoCtx for Class { 231 | type Error = Error; 232 | 233 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 234 | let mut offset = 0; 235 | this.gwrite_with::((&self).into(), &mut offset, ctx)?; 236 | 237 | match self { 238 | Self::Common(common) => { 239 | this.gwrite_with(common, &mut offset, ctx)?; 240 | } 241 | _ => unimplemented!("Not all Class are impled"), 242 | }; 243 | 244 | Ok(offset) 245 | } 246 | } 247 | 248 | impl<'a> ctx::TryFromCtx<'a, Endian> for Common { 249 | type Error = Error; 250 | 251 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 252 | use Common::*; 253 | let mut offset = 0; 254 | 255 | let common = match src.gread_with::(&mut offset, ctx)? { 256 | 0 => AllStates, 257 | // @TODO: FIX THIS! 258 | 1 => CurrentDate(Utc::now()), 259 | // @TODO: FIX THIS! 260 | 2 => CurrentTime(Utc::now()), 261 | 3 => Reboot, 262 | value => { 263 | return Err(Error::OutOfBound { 264 | value: value.into(), 265 | param: "Common".to_string(), 266 | }) 267 | } 268 | }; 269 | 270 | Ok((common, offset)) 271 | } 272 | } 273 | 274 | impl<'a> ctx::TryIntoCtx for Common { 275 | type Error = Error; 276 | 277 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 278 | let mut offset = 0; 279 | 280 | this.gwrite_with::(self.into(), &mut offset, ctx)?; 281 | 282 | match self { 283 | Self::CurrentDate(date) => { 284 | let date = format_date(&date); 285 | // null terminated C string 286 | this.gwrite_with(date.as_bytes_with_nul(), &mut offset, ())?; 287 | } 288 | Self::CurrentTime(time) => { 289 | // null terminated C string 290 | let time = format_time(&time); 291 | 292 | // null terminated C string 293 | this.gwrite_with(time.as_bytes_with_nul(), &mut offset, ())?; 294 | } 295 | _ => unimplemented!("Not all Common are impled"), 296 | } 297 | 298 | Ok(offset) 299 | } 300 | } 301 | } 302 | // --------------------- Tests --------------------- // 303 | 304 | #[cfg(test)] 305 | mod common_tests { 306 | use super::*; 307 | // use chrono::prelude::*; 308 | // #[test] 309 | // fn test_format_time() { 310 | // // `2014-07-08T09:10:11Z` 311 | // let test_time = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); 312 | // assert_eq!( 313 | // "2014-07-08".to_string(), 314 | // String::from_utf8_lossy(&*format_date(&test_time)) 315 | // ); 316 | 317 | // assert_eq!( 318 | // "T091011+0000".to_string(), 319 | // String::from_utf8_lossy(&*format_time(&test_time)) 320 | // ); 321 | // } 322 | 323 | #[test] 324 | fn test_class() { 325 | assert_class(Class::Network, 0); 326 | assert_class(Class::NetworkEvent, 1); 327 | assert_class(Class::Settings, 2); 328 | assert_class(Class::SettingsState, 3); 329 | assert_class(Class::Common(Common::AllStates), 4); 330 | assert_class(Class::CommonState, 5); 331 | assert_class(Class::Overheat, 6); 332 | assert_class(Class::OverheatState, 7); 333 | assert_class(Class::Controller, 8); 334 | assert_class(Class::WifiSettings, 9); 335 | assert_class(Class::WifiSettingsState, 10); 336 | assert_class(Class::Mavlink, 11); 337 | assert_class(Class::MavlinkState, 12); 338 | assert_class(Class::FlightPlanSettings, 32); 339 | assert_class(Class::FlightPlanSettingsState, 33); 340 | assert_class(Class::Calibration, 13); 341 | assert_class(Class::CalibrationState, 14); 342 | assert_class(Class::CameraSettingsState, 15); 343 | assert_class(Class::Gps, 16); 344 | assert_class(Class::FlightPlanState, 17); 345 | assert_class(Class::FlightPlanEvent, 19); 346 | assert_class(Class::ArLibsVersionsState, 18); 347 | assert_class(Class::Audio, 20); 348 | assert_class(Class::AudioState, 21); 349 | assert_class(Class::HeadLights, 22); 350 | assert_class(Class::HeadLightsState, 23); 351 | assert_class(Class::Animations, 24); 352 | assert_class(Class::AnimationsState, 25); 353 | assert_class(Class::Accessory, 26); 354 | assert_class(Class::AccessoryState, 27); 355 | assert_class(Class::Charger, 28); 356 | assert_class(Class::ChargerState, 29); 357 | assert_class(Class::Runstate, 30); 358 | assert_class(Class::Factory, 31); 359 | } 360 | 361 | #[test] 362 | fn test_common() { 363 | assert_common(Common::AllStates, 0); 364 | assert_common(Common::CurrentDate(chrono::offset::Utc::now()), 1); 365 | assert_common(Common::CurrentTime(chrono::offset::Utc::now()), 2); 366 | assert_common(Common::Reboot, 3); 367 | } 368 | 369 | fn assert_class(dc: Class, v: u8) { 370 | let as_u8: u8 = (&dc).into(); 371 | assert_eq!(v, as_u8); 372 | } 373 | 374 | fn assert_common(c: Common, v: u8) { 375 | let as_u8: u8 = c.into(); 376 | assert_eq!(v, as_u8); 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /arsdk-rs/src/frame.rs: -------------------------------------------------------------------------------- 1 | use crate::{command, Drone}; 2 | use std::convert::TryFrom; 3 | use std::fmt; 4 | use thiserror::Error; 5 | 6 | #[derive(Debug, Error)] 7 | pub enum Error { 8 | #[error("Message parsing error")] 9 | Scroll(#[from] scroll::Error), 10 | #[error("Out of bound value {value} for {param}")] 11 | OutOfBound { 12 | // TODO: See how should we handle this for each individual case 13 | // Use the largest possible value 14 | value: u64, 15 | param: String, 16 | }, 17 | #[error("Expected {expected} bytes, got {actual}")] 18 | BytesLength { expected: u32, actual: u32 }, 19 | } 20 | 21 | #[derive(Debug, Clone, PartialEq, Eq)] 22 | pub enum FrameType { 23 | Known(Frame), 24 | Unknown(UnknownFrame), 25 | } 26 | 27 | #[derive(Debug, Clone, PartialEq, Eq)] 28 | /// Unknown Frame 29 | pub struct UnknownFrame { 30 | key: String, 31 | value: u64, 32 | data: Vec, 33 | } 34 | 35 | #[derive(Debug, Clone, PartialEq, Eq)] 36 | pub struct Frame { 37 | pub frame_type: Type, 38 | pub buffer_id: BufferID, 39 | pub sequence_id: u8, 40 | /// Example of empty feature: 41 | /// ```bash 42 | /// [2020-07-25T18:51:13Z DEBUG arsdk_rs] Bytes: 1 139 0 8 0 0 0 1 43 | /// [2020-07-25T18:51:13Z INFO arsdk_rs::parse] Frame: Frame { frame_type: Ack, buffer_id: ACKFromSendWithAck, sequence_id: 0, feature: Some(ArDrone3(None)) } 44 | /// ``` 45 | pub feature: Option, 46 | } 47 | 48 | impl Frame { 49 | pub fn new( 50 | frame_type: Type, 51 | buffer_id: BufferID, 52 | sequence_id: u8, 53 | feature: Option, 54 | ) -> Self { 55 | Self { 56 | frame_type, 57 | buffer_id, 58 | sequence_id, 59 | feature, 60 | } 61 | } 62 | 63 | pub fn for_drone( 64 | drone: &Drone, 65 | frame_type: Type, 66 | buffer_id: BufferID, 67 | feature: Option, 68 | ) -> Frame { 69 | Frame::new( 70 | frame_type, 71 | buffer_id, 72 | drone.inner.sequence_id(buffer_id), 73 | feature, 74 | ) 75 | } 76 | } 77 | 78 | // --------------------- Types --------------------- // 79 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 80 | pub enum Type { 81 | /// ARNETWORKAL_FRAME_TYPE_UNINITIALIZED 0 82 | Uninitialized = 0, 83 | /// ARNETWORKAL_FRAME_TYPE_ACK 1 84 | Ack = 1, 85 | /// ARNETWORKAL_FRAME_TYPE_DATA 2 86 | Data = 2, 87 | /// ARNETWORKAL_FRAME_TYPE_DATA_LOW_LATENCY 3 88 | LowLatency = 3, 89 | /// ARNETWORKAL_FRAME_TYPE_DATA_WITH_ACK 4 90 | DataWithAck = 4, 91 | /// ARNETWORKAL_FRAME_TYPE_MAX 5 92 | Max = 5, 93 | } 94 | 95 | impl fmt::Display for Type { 96 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 97 | write!(f, "Type::")?; 98 | 99 | match self { 100 | Self::Uninitialized => write!(f, "Uninitialized"), 101 | Self::Ack => write!(f, "Ack"), 102 | Self::Data => write!(f, "Data"), 103 | Self::LowLatency => write!(f, "LowLatency"), 104 | Self::DataWithAck => write!(f, "DataWithAck"), 105 | Self::Max => write!(f, "Max"), 106 | } 107 | } 108 | } 109 | 110 | #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] 111 | pub enum BufferID { 112 | /// pings from device 113 | PING = 0, 114 | /// respond to pings 115 | PONG = 1, 116 | /// C: 117 | /// #define BD_NET_CD_NONACK_ID 10 118 | /// 119 | /// PyParrot: 120 | /// 'SEND_NO_ACK': 10, # not-ack commandsandsensors (piloting and camera rotations) 121 | CDNonAck = 10, 122 | /// C: 123 | /// `#define BD_NET_CD_ACK_ID 11` 124 | /// 125 | /// PyParrot: 126 | /// 'SEND_WITH_ACK': 11, # ack commandsandsensors (all piloting commandsandsensors) 127 | CDAck = 11, 128 | /// C: 129 | /// `#define BD_NET_CD_EMERGENCY_ID 12` 130 | /// 131 | /// PyParrot: 132 | /// `'SEND_HIGH_PRIORITY': 12, # emergency commandsandsensors` 133 | CDEmergency = 12, 134 | /// C: 135 | /// #define BD_NET_CD_VIDEO_ACK_ID 13 136 | /// 137 | /// PyParrot: 138 | /// `'VIDEO_ACK': 13, # ack for video` 139 | CDVideoAck = 13, 140 | /// C: 141 | /// #define BD_NET_DC_VIDEO_DATA_ID 125 142 | /// 143 | /// PyParrot: 144 | /// `'VIDEO_DATA' : 125, # video data` 145 | DCVideo = 125, 146 | /// C: 147 | /// #define BD_NET_DC_EVENT_ID 126 148 | /// 149 | /// PyParrot: 150 | // 'NO_ACK_DRONE_DATA' : 126, # data from drone (including battery and others), no ack 151 | /// 152 | DCEvent = 126, 153 | /// C: 154 | /// #define BD_NET_DC_NAVDATA_ID 127 155 | /// 156 | /// PyParrot: 157 | /// `'ACK_DRONE_DATA' : 127, # drone data that needs an ack` 158 | DCNavdata = 127, 159 | /// @TODO: Find the corresponding C definition if there is one and name the new enum variant! 160 | /// PyParrot: 161 | /// `'ACK_FROM_SEND_WITH_ACK': 139 # 128 + buffer id for 'SEND_WITH_ACK' is 139` 162 | /// Type = Ack = 1 163 | /// BufferId = ACKFromSendWithAck = 139 164 | /// Sequence = 1 165 | /// length = 8 166 | /// Feature = ArDrone3 = 1 167 | /// 1 139 1 8 0 0 0 1 168 | ACKFromSendWithAck = 139, 169 | } 170 | 171 | // --------------------- Conversion impls --------------------- // 172 | impl TryFrom for Type { 173 | type Error = Error; 174 | fn try_from(v: u8) -> Result { 175 | match v { 176 | 0 => Ok(Self::Uninitialized), 177 | 1 => Ok(Self::Ack), 178 | 2 => Ok(Self::Data), 179 | 3 => Ok(Self::LowLatency), 180 | 4 => Ok(Self::DataWithAck), 181 | 5 => Ok(Self::Max), 182 | _ => Err(Error::OutOfBound { 183 | value: v.into(), 184 | param: "Type".to_string(), 185 | }), 186 | } 187 | } 188 | } 189 | 190 | impl Into for Type { 191 | fn into(self) -> u8 { 192 | match self { 193 | Self::Uninitialized => 0, 194 | Self::Ack => 1, 195 | Self::Data => 2, 196 | Self::LowLatency => 3, 197 | Self::DataWithAck => 4, 198 | Self::Max => 5, 199 | } 200 | } 201 | } 202 | 203 | impl TryFrom for BufferID { 204 | type Error = Error; 205 | fn try_from(v: u8) -> Result { 206 | match v { 207 | 0 => Ok(Self::PING), 208 | 1 => Ok(Self::PONG), 209 | 10 => Ok(Self::CDNonAck), 210 | 11 => Ok(Self::CDAck), 211 | 12 => Ok(Self::CDEmergency), 212 | 13 => Ok(Self::CDVideoAck), 213 | 125 => Ok(Self::DCVideo), 214 | 126 => Ok(Self::DCEvent), 215 | 127 => Ok(Self::DCNavdata), 216 | 139 => Ok(Self::ACKFromSendWithAck), 217 | _ => Err(Error::OutOfBound { 218 | value: v.into(), 219 | param: "BufferID".to_string(), 220 | }), 221 | } 222 | } 223 | } 224 | 225 | impl Into for BufferID { 226 | fn into(self) -> u8 { 227 | match self { 228 | Self::PING => 0, 229 | Self::PONG => 1, 230 | Self::CDNonAck => 10, 231 | Self::CDAck => 11, 232 | Self::CDEmergency => 12, 233 | Self::CDVideoAck => 13, 234 | Self::DCVideo => 125, 235 | Self::DCEvent => 126, 236 | Self::DCNavdata => 127, 237 | Self::ACKFromSendWithAck => 139, 238 | } 239 | } 240 | } 241 | 242 | impl fmt::Display for BufferID { 243 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 244 | write!(f, "BufferID::")?; 245 | 246 | match self { 247 | Self::PING => write!(f, "PING"), 248 | Self::PONG => write!(f, "PONG"), 249 | Self::CDNonAck => write!(f, "CDNonAck"), 250 | Self::CDAck => write!(f, "CDAck"), 251 | Self::CDEmergency => write!(f, "CDEmergency"), 252 | Self::CDVideoAck => write!(f, "CDVideoAck"), 253 | Self::DCVideo => write!(f, "DCVideo"), 254 | Self::DCEvent => write!(f, "DCEvent"), 255 | Self::DCNavdata => write!(f, "DCNavdata"), 256 | Self::ACKFromSendWithAck => write!(f, "ACKFromSendWithAck"), 257 | } 258 | } 259 | } 260 | 261 | pub mod impl_scroll { 262 | use super::*; 263 | use crate::{command::Feature, parse::read_unknown}; 264 | 265 | use scroll::{ctx, Endian, Pread, Pwrite}; 266 | 267 | impl<'a> ctx::TryFromCtx<'a, Endian> for FrameType { 268 | type Error = Error; 269 | 270 | // and the lifetime annotation on `&'a [u8]` here 271 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 272 | let mut actual_buf_len = 0; 273 | 274 | match src.gread_with(&mut actual_buf_len, ctx) { 275 | Ok(frame) => Ok((FrameType::Known(frame), actual_buf_len)), 276 | Err(Error::OutOfBound { param, value }) => { 277 | let unknown_frame = FrameType::Unknown(UnknownFrame { 278 | key: param, 279 | value, 280 | data: read_unknown(src, &mut 0)?, 281 | }); 282 | Ok((unknown_frame, actual_buf_len)) 283 | } 284 | Err(err) => Err(err), 285 | } 286 | } 287 | } 288 | 289 | impl<'a> ctx::TryFromCtx<'a, Endian> for Frame { 290 | type Error = Error; 291 | 292 | // and the lifetime annotation on `&'a [u8]` here 293 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 294 | let mut actual_buf_len = 0; 295 | let frame_type = src.gread_with(&mut actual_buf_len, ctx)?; 296 | let buffer_id = src.gread_with(&mut actual_buf_len, ctx)?; 297 | let sequence_id = src.gread_with(&mut actual_buf_len, ctx)?; 298 | let buf_len: u32 = src.gread_with(&mut actual_buf_len, ctx)?; 299 | 300 | // TODO: Fix this as it might fail, use TryFrom 301 | let buf_len_usize = buf_len as usize; 302 | 303 | let feature = if buf_len >= 8 { 304 | // we can receive multiple frames, so the feature should be limited 305 | // to buf_len from source 306 | 307 | // for the PING & PONG we don't reasonable data 308 | // only data to echo back from PING 309 | if [BufferID::PING, BufferID::PONG].contains(&buffer_id) { 310 | // even if it's a known one, the PING doesn't send sane data 311 | let feature = src[..buf_len_usize].gread_with(&mut actual_buf_len, ctx)?; 312 | 313 | let mut feature_data = [0_u8; 256]; 314 | let actual_written = feature_data.gwrite_with( 315 | &src[actual_buf_len..buf_len_usize], 316 | &mut 0, 317 | (), 318 | )?; 319 | 320 | actual_buf_len += actual_written; 321 | 322 | Some(Feature::Unknown { 323 | feature, 324 | data: feature_data[..actual_written].to_vec(), 325 | }) 326 | } else { 327 | let feature = 328 | src[..buf_len_usize].gread_with::(&mut actual_buf_len, ctx)?; 329 | 330 | Some(feature) 331 | } 332 | } else { 333 | None 334 | }; 335 | 336 | if buf_len_usize != actual_buf_len { 337 | Err(Error::BytesLength { 338 | expected: buf_len, 339 | // @TODO: actual_buf_len as u32 can fail (TryFrom is impled for usize) 340 | actual: actual_buf_len as u32, 341 | }) 342 | } else { 343 | Ok(( 344 | Frame { 345 | frame_type, 346 | buffer_id, 347 | sequence_id, 348 | feature, 349 | }, 350 | actual_buf_len, 351 | )) 352 | } 353 | } 354 | } 355 | 356 | impl<'a> ctx::TryIntoCtx for Frame { 357 | type Error = Error; 358 | 359 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 360 | let mut offset = 0; 361 | 362 | this.gwrite_with::(self.frame_type.into(), &mut offset, ctx)?; 363 | this.gwrite_with::(self.buffer_id.into(), &mut offset, ctx)?; 364 | this.gwrite_with::(self.sequence_id, &mut offset, ctx)?; 365 | 366 | let buf_length_offset = offset; 367 | // reserve bytes for the buffer length (u32) 368 | this.gwrite_with::(0, &mut offset, ctx)?; 369 | 370 | if let Some(feature) = self.feature { 371 | this.gwrite_with::(feature, &mut offset, ctx)?; 372 | }; 373 | 374 | // 7 bytes + feature_length bytes = buf.length 375 | this.pwrite_with::(offset as u32, buf_length_offset, ctx)?; 376 | 377 | Ok(offset) 378 | } 379 | } 380 | 381 | impl<'a> ctx::TryFromCtx<'a, Endian> for Type { 382 | type Error = Error; 383 | 384 | // and the lifetime annotation on `&'a [u8]` here 385 | fn try_from_ctx(src: &'a [u8], _ctx: Endian) -> Result<(Self, usize), Self::Error> { 386 | let mut offset = 0; 387 | let frame_value = src.gread::(&mut offset)?; 388 | 389 | let frame_type = match frame_value { 390 | 0 => Self::Uninitialized, 391 | 1 => Self::Ack, 392 | 2 => Self::Data, 393 | 3 => Self::LowLatency, 394 | 4 => Self::DataWithAck, 395 | 5 => Self::Max, 396 | value => { 397 | return Err(Error::OutOfBound { 398 | value: value.into(), 399 | param: "Type".to_string(), 400 | }) 401 | } 402 | }; 403 | 404 | Ok((frame_type, offset)) 405 | } 406 | } 407 | 408 | impl<'a> ctx::TryIntoCtx for Type { 409 | type Error = Error; 410 | 411 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 412 | Ok(this.pwrite_with::(self.into(), 0, ctx)?) 413 | } 414 | } 415 | 416 | impl<'a> ctx::TryFromCtx<'a, Endian> for BufferID { 417 | type Error = Error; 418 | 419 | // and the lifetime annotation on `&'a [u8]` here 420 | fn try_from_ctx(src: &'a [u8], _ctx: Endian) -> Result<(Self, usize), Self::Error> { 421 | let mut offset = 0; 422 | let id_value = src.gread::(&mut offset)?; 423 | 424 | let buffer_id = match id_value { 425 | 0 => Self::PING, 426 | 1 => Self::PONG, 427 | 10 => Self::CDNonAck, 428 | 11 => Self::CDAck, 429 | 12 => Self::CDEmergency, 430 | 13 => Self::CDVideoAck, 431 | 125 => Self::DCVideo, 432 | 126 => Self::DCEvent, 433 | 127 => Self::DCNavdata, 434 | 139 => Self::ACKFromSendWithAck, 435 | value => { 436 | return Err(Error::OutOfBound { 437 | value: value.into(), 438 | param: "BufferID".to_string(), 439 | }) 440 | } 441 | }; 442 | 443 | Ok((buffer_id, offset)) 444 | } 445 | } 446 | 447 | impl<'a> ctx::TryIntoCtx for BufferID { 448 | type Error = Error; 449 | 450 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 451 | Ok(this.pwrite_with::(self.into(), 0, ctx)?) 452 | } 453 | } 454 | } 455 | 456 | // --------------------- Tests --------------------- // 457 | 458 | #[cfg(test)] 459 | mod frame_tests { 460 | use super::*; 461 | use crate::{ 462 | ardrone3::{self, ArDrone3}, 463 | common::{self, Class as CommonClass}, 464 | jumping_sumo::*, 465 | }; 466 | use chrono::{TimeZone, Utc}; 467 | use scroll::{Pread, Pwrite, LE}; 468 | 469 | use command::Feature; 470 | use std::convert::TryInto; 471 | 472 | #[test] 473 | fn test_full_frame() { 474 | let message: [u8; 14] = [ 475 | 0x2, 0xa, 0x67, 0xe, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x9c, 476 | ]; 477 | 478 | let pilot_state = PilotState { 479 | flag: true, 480 | speed: 0, 481 | turn: -100, 482 | }; 483 | 484 | let frame = Frame { 485 | frame_type: Type::Data, 486 | buffer_id: BufferID::CDNonAck, 487 | sequence_id: 103, 488 | feature: Some(command::Feature::JumpingSumo(Class::Piloting( 489 | PilotingID::Pilot(pilot_state), 490 | ))), 491 | }; 492 | 493 | assert_frames_match(&message, frame) 494 | } 495 | 496 | #[test] 497 | #[ignore] 498 | fn test_common_date_command() { 499 | let message = [ 500 | 0x4, 0xb, 0x1, 0x15, 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x32, 0x30, 0x32, 0x30, 0x2d, 0x30, 501 | 0x34, 0x2d, 0x32, 0x36, 0x0, 502 | ]; 503 | 504 | let date = Utc.ymd(2020, 04, 26).and_hms(15, 06, 11); 505 | 506 | let frame = Frame { 507 | frame_type: Type::DataWithAck, 508 | buffer_id: BufferID::CDAck, 509 | sequence_id: 1, 510 | feature: Some(command::Feature::Common(Some(CommonClass::Common( 511 | common::Common::CurrentDate(date), 512 | )))), 513 | }; 514 | 515 | assert_frames_match(&message, frame); 516 | } 517 | 518 | #[test] 519 | #[ignore] 520 | fn test_common_time_command() { 521 | let message: [u8; 22] = [ 522 | 0x4, 0xb, 0x2, 0x15, 0x0, 0x0, 0x0, // Feature::Common 523 | 0x0, // common::Class::Common 524 | 0x4, // Current time 525 | 0x2, // 12 bytes incl nul 526 | 0x0, 0x54, 0x31, 0x35, 0x30, 0x36, 0x31, 0x31, 0x30, 0x30, 0x30, 0x0, 527 | ]; 528 | 529 | let date = Utc.ymd(2020, 04, 26).and_hms(15, 06, 11); 530 | 531 | let frame = Frame { 532 | frame_type: Type::DataWithAck, 533 | buffer_id: BufferID::CDAck, 534 | sequence_id: 2, 535 | feature: Some(command::Feature::Common(Some(CommonClass::Common( 536 | common::Common::CurrentTime(date), 537 | )))), 538 | }; 539 | 540 | assert_frames_match(&message, frame); 541 | } 542 | 543 | #[test] 544 | /// [2] Type::Data 545 | /// [10] BufferId::CDNonAck 546 | /// [103] Sequence ID 547 | /// [14, 0, 0, 0] Length 14 548 | /// [3] Feature JS 549 | /// [0] - JS Class - Piloting 550 | /// [0, 0] Pilot (PCMD) 551 | /// [1] flag: true 552 | /// [0] speed 553 | /// [156] turn: -100 554 | fn test_jumpingsumo_move_command() { 555 | let message: [u8; 14] = [ 556 | 0x2, 0xa, 0x67, 0xe, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x9c, 557 | ]; 558 | 559 | let pilot_state = PilotState { 560 | flag: true, 561 | speed: 0, 562 | turn: -100, 563 | }; 564 | 565 | let frame = Frame { 566 | frame_type: Type::Data, 567 | buffer_id: BufferID::CDNonAck, 568 | sequence_id: 103, 569 | feature: Some(command::Feature::JumpingSumo(Class::Piloting( 570 | PilotingID::Pilot(pilot_state), 571 | ))), 572 | }; 573 | 574 | assert_frames_match(&message, frame); 575 | } 576 | 577 | #[test] 578 | fn test_jumpingsumo_jump_command() { 579 | // type buf seq [ len ] [JS Anim Jump DATA ] 580 | let message: [u8; 15] = [ 581 | 0x4, 0xb, 0x1, 0xf, 0x0, 0x0, 0x0, 0x3, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 582 | ]; 583 | 584 | let frame = Frame { 585 | frame_type: Type::DataWithAck, 586 | buffer_id: BufferID::CDAck, 587 | sequence_id: 1, 588 | feature: Some(command::Feature::JumpingSumo(Class::Animations(Anim::Jump))), 589 | }; 590 | 591 | assert_frames_match(&message, frame); 592 | } 593 | 594 | #[test] 595 | fn test_ping_feature_from_anafi4k() { 596 | let message: [u8; 15] = [2, 0, 2, 15, 0, 0, 0, 155, 216, 221, 13, 0, 0, 0, 0]; 597 | let frame = Frame { 598 | frame_type: Type::Data, 599 | buffer_id: BufferID::PING, 600 | sequence_id: 2, 601 | feature: Some(command::Feature::Unknown { 602 | feature: 155, 603 | data: vec![216, 221, 13, 0, 0, 0, 0], 604 | }), 605 | }; 606 | 607 | assert_frames_match(&message, frame); 608 | } 609 | 610 | #[test] 611 | #[ignore] 612 | // TODO: Impl CommonState! 613 | fn test_feature_common_state() { 614 | let message: [u8; 12] = [ 615 | 2, 127, 20, 12, 0, 0, 0, // common 616 | 0, // Common State 617 | 5, // 618 | 1, 0, 100, 619 | ]; 620 | 621 | let frame = Frame { 622 | frame_type: Type::Data, 623 | buffer_id: BufferID::DCNavdata, 624 | sequence_id: 20, 625 | feature: Some(command::Feature::Common(Some(CommonClass::CommonState))), 626 | }; 627 | 628 | assert_frames_match(&message, frame); 629 | } 630 | 631 | #[test] 632 | #[ignore = "We've received it from Anafi4k and it doesn't make sense atm"] 633 | /// [1] Type::Ack 634 | /// [139] BufferID::ACKFromSendWithAck 635 | /// [4] Sequence ID 636 | /// [8, 0, 0, 0] 8 length 637 | /// [3] Jumping Sumo 638 | fn test_unknown_jumping_sumo_feature_from_anafi() { 639 | let message: [u8; 8] = [1, 139, 4, 8, 0, 0, 0, 3]; 640 | 641 | let frame = Frame { 642 | frame_type: Type::Ack, 643 | buffer_id: BufferID::ACKFromSendWithAck, 644 | sequence_id: 4, 645 | feature: Some(Feature::Unknown { 646 | feature: 3, 647 | data: vec![], 648 | }), 649 | }; 650 | 651 | assert_frames_match(&message, frame); 652 | } 653 | 654 | #[test] 655 | /// [2] Type::Data 656 | /// [127] BufferID::DCNavadata 657 | /// [206] Sequence ID 658 | /// [15, 0, 0, 0] 15 length 659 | /// [1] ArDrone3 660 | /// [4] Piloting state 661 | /// [21, 0] 662 | /// [126, 163, 163, 64] Data? 663 | fn test_unknown_ardrone3_piloting_state_feature() { 664 | let message: [u8; 15] = [2, 127, 206, 15, 0, 0, 0, 1, 4, 21, 0, 126, 163, 163, 64]; 665 | 666 | let frame = Frame { 667 | frame_type: Type::Data, 668 | buffer_id: BufferID::DCNavdata, 669 | sequence_id: 206, 670 | feature: Some(Feature::ArDrone3(Some(ArDrone3::PilotingState( 671 | ardrone3::PilotingState::Unknown { 672 | piloting_state: 21, 673 | data: message[11..].to_vec(), 674 | }, 675 | )))), 676 | }; 677 | 678 | assert_frames_match(&message, frame); 679 | } 680 | 681 | fn assert_frames_match(expected: &[u8], frame: Frame) { 682 | // Check the value at the Frame length bytes 3 to 7 683 | let buf_len: u32 = (&expected[3..7]) 684 | .pread_with(0, LE) 685 | .expect("should read a u32"); 686 | 687 | assert_eq!(buf_len as usize, expected.len()); 688 | 689 | // Deserialize a Frame 690 | let deserialized = expected 691 | .pread_with::(0, LE) 692 | .expect("Should deserialize"); 693 | 694 | assert_eq!(frame, deserialized); 695 | let mut actual = [0_u8; 256]; 696 | assert!( 697 | actual.len() > buf_len as usize, 698 | "Whoopsy... our serialization buffer is not that big!" 699 | ); 700 | 701 | let mut offset = 0; 702 | let actual_written = actual 703 | .gwrite_with(frame, &mut offset, LE) 704 | .expect("Should serialize"); 705 | 706 | assert_eq!(expected, &actual[..offset]); 707 | assert_eq!(buf_len as usize, actual_written); 708 | } 709 | // 0x2 0xb 0x1 0xf 0x0 0x0 0x0 0x3 0x2 0x3 0x0 0x0 0x0 0x0 0x0 710 | 711 | #[test] 712 | fn test_frame() { 713 | assert_frame(Type::Uninitialized, 0); 714 | assert_frame(Type::Ack, 1); 715 | assert_frame(Type::Data, 2); 716 | assert_frame(Type::LowLatency, 3); 717 | assert_frame(Type::DataWithAck, 4); 718 | assert_frame(Type::Max, 5); 719 | } 720 | 721 | #[test] 722 | fn test_command() { 723 | assert_command(BufferID::CDNonAck, 10); 724 | assert_command(BufferID::CDAck, 11); 725 | assert_command(BufferID::CDEmergency, 12); 726 | assert_command(BufferID::CDVideoAck, 13); 727 | assert_command(BufferID::DCVideo, 125); 728 | assert_command(BufferID::DCEvent, 126); 729 | assert_command(BufferID::DCNavdata, 127); 730 | } 731 | 732 | fn assert_frame(t: Type, v: u8) { 733 | assert_eq!(t, v.try_into().unwrap()); 734 | let as_u8: u8 = t.into(); 735 | assert_eq!(v, as_u8); 736 | } 737 | 738 | fn assert_command(c: BufferID, v: u8) { 739 | assert_eq!(c, v.try_into().unwrap()); 740 | let as_u8: u8 = c.into(); 741 | assert_eq!(v, as_u8); 742 | } 743 | } 744 | -------------------------------------------------------------------------------- /arsdk-rs/src/handshake.rs: -------------------------------------------------------------------------------- 1 | use log::{error, info}; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_with::with_prefix; 4 | use std::{ 5 | io::{Read, Write}, 6 | net::{Shutdown, SocketAddr, TcpStream}, 7 | string::FromUtf8Error, 8 | }; 9 | use thiserror::Error; 10 | 11 | #[derive(Serialize, Debug)] 12 | pub(crate) struct Request { 13 | controller_name: String, 14 | controller_type: String, 15 | d2c_port: u16, 16 | #[serde(flatten, with = "prefix_arstream2_client")] 17 | pub arstream2: Option, 18 | } 19 | 20 | #[derive(Deserialize, Debug)] 21 | /// Request: "{\"controller_name\":\"arsdk-rs\",\"controller_type\":\"computer\",\"d2c_port\":43210}" 22 | /// Response: "{ \"status\": 0, \"c2d_port\": 54321, \"c2d_update_port\": 51, \"c2d_user_port\": 21, \"qos_mode\": 0, \"arstream2_server_stream_port\": 5004, \"arstream2_server_control_port\": 5005 }\u{0}" 23 | /// `\u{0}` causes issues, but for now we `trim_end_matches` 24 | /// Error: trailing characters at line 1 column 171 25 | pub struct Response { 26 | #[serde(default)] 27 | pub arstream_fragment_maximum_number: Option, 28 | #[serde(default)] 29 | pub arstream_fragment_size: Option, 30 | #[serde(default)] 31 | pub arstream_max_ack_interval: Option, 32 | #[serde(default, flatten, with = "prefix_arstream2_server")] 33 | pub arstream2: Option, 34 | pub c2d_port: u16, 35 | pub c2d_update_port: u16, 36 | pub c2d_user_port: u16, 37 | pub status: i8, 38 | // @TODO: Check what this field is for. 39 | /// Bool?! 40 | pub qos_mode: u8, 41 | // @TODO: Check what this field is for **and** if it's a bool at all 42 | /// Bool?! 43 | #[serde(default)] 44 | pub proto_v: Option, 45 | } 46 | 47 | with_prefix!(prefix_arstream2_client "arstream2_client_"); 48 | with_prefix!(prefix_arstream2_server "arstream2_server_"); 49 | 50 | #[derive(Debug, Serialize, Deserialize)] 51 | pub struct ArStream2 { 52 | stream_port: u16, 53 | control_port: u16, 54 | } 55 | #[derive(Debug, Error)] 56 | pub enum Error { 57 | #[error("Couldn't connect for handshake {0:?}")] 58 | Io(#[from] std::io::Error), 59 | #[error("Connection refused - {0:?}")] 60 | ConnectionRefused(Response), 61 | #[error("Maximum allowed retries reached for {target}")] 62 | Retry { target: SocketAddr }, 63 | #[error("Json (de)serialization - {0:?}")] 64 | Json(#[from] serde_json::Error), 65 | /// Primarily used for logging the response string 66 | #[error("Response String - {0:?}")] 67 | ResponseString(#[from] FromUtf8Error), 68 | } 69 | 70 | pub(crate) fn perform_handshake( 71 | init_address: SocketAddr, 72 | d2c_port: u16, 73 | ) -> Result { 74 | let request = Request { 75 | controller_name: "arsdk-rs".to_string(), 76 | controller_type: "computer".to_string(), 77 | d2c_port, 78 | // Anafi4k: 79 | // self.stream_port = 55004 80 | // self.stream_control_port = 55005 81 | arstream2: Some(ArStream2 { 82 | stream_port: 44445, 83 | control_port: 44446, 84 | }), 85 | // TODO: Check when we don't need `arstream2` 86 | // the pyparrot has a check for setting `arstream2` when: 87 | // `if(self.drone_type in ("Anafi", "Bebop", "Bebop2", "Disco")):` 88 | // arstream2: None, 89 | }; 90 | 91 | info!("Connecting controller {}", request.controller_name); 92 | 93 | let mut handshake_stream = retry(10, init_address)?; 94 | 95 | info!("Request: {}", serde_json::to_string(&request)?); 96 | let request_string = serde_json::to_vec(&request)?; 97 | 98 | handshake_stream.write_all(&request_string)?; 99 | 100 | let mut buf = [0_u8; 256]; 101 | let read = handshake_stream.read(&mut buf)?; 102 | info!("Read {} bytes!", read); 103 | 104 | let response_string = String::from_utf8(buf[..read].to_vec())?; 105 | 106 | info!("Response: {}", response_string); 107 | 108 | handshake_stream.shutdown(Shutdown::Both)?; 109 | 110 | let response: Response = serde_json::from_str(&response_string.trim_end_matches('\u{0}'))?; 111 | 112 | if response.status != 0 { 113 | Err(Error::ConnectionRefused(response)) 114 | } else { 115 | Ok(response) 116 | } 117 | } 118 | 119 | fn retry(times: usize, target: SocketAddr) -> Result { 120 | let connection_timeout = std::time::Duration::from_secs(2); 121 | let read_timeout = std::time::Duration::from_secs(2); 122 | let mut retry = 0; 123 | 124 | let mut res = TcpStream::connect_timeout(&target, connection_timeout); 125 | 126 | while res.is_err() && retry < times { 127 | retry += 1; 128 | res = TcpStream::connect_timeout(&target, connection_timeout); 129 | } 130 | 131 | let tcp_stream = match res { 132 | Ok(tcp_stream) => tcp_stream, 133 | Err(err) => { 134 | error!("TCP Stream failed: {}", &err); 135 | 136 | return Err(err.into()); 137 | } 138 | }; 139 | 140 | info!("{}: TCP Stream initialized", target); 141 | 142 | tcp_stream.set_read_timeout(Some(read_timeout))?; 143 | 144 | Ok(tcp_stream) 145 | } 146 | -------------------------------------------------------------------------------- /arsdk-rs/src/jumping_sumo.rs: -------------------------------------------------------------------------------- 1 | // use arsdk_derive::FramePart; 2 | 3 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 4 | // TODO: Check where and how this is encoded? 5 | pub enum JumpType { 6 | LONG, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_PILOTING = 0, 7 | HIGH, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_PILOTING = 0, 8 | DEFAULT, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_PILOTING = 0, 9 | } 10 | 11 | #[derive(Debug, PartialEq, Eq, Clone)] 12 | /// u8 13 | pub enum Class { 14 | Piloting(PilotingID), // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_PILOTING = 0, 15 | PilotingState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_PILOTINGSTATE = 1, 16 | Animations(Anim), // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_ANIMATIONS = 2, 17 | AnimationsState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_ANIMATIONSSTATE = 3, 18 | SettingsState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_SETTINGSSTATE = 5, 19 | MediaRecord, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_MEDIARECORD = 6, 20 | MediaRecordState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_MEDIARECORDSTATE = 7, 21 | NetworkSettings, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_NETWORKSETTINGS = 8, 22 | NetworkSettingsState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_NETWORKSETTINGSSTATE = 9, 23 | Network, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_NETWORK = 10, 24 | NetworkState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_NETWORKSTATE = 11, 25 | AutioSettings, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_AUDIOSETTINGS = 12, 26 | AudioSettingsState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_AUDIOSETTINGSSTATE = 13, 27 | Roadplan, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_ROADPLAN = 14, 28 | RoadplanState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_ROADPLANSTATE = 15, 29 | SpeedSettings, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_SPEEDSETTINGS = 16, 30 | SpeedSettingsState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_SPEEDSETTINGSSTATE = 17, 31 | MediaStreaming, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_MEDIASTREAMING = 18, 32 | MediaStreamingState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_MEDIASTREAMINGSTATE = 19, 33 | MediaRecordEvent, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_MEDIARECORDEVENT = 20, 34 | VideoSettings, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_VIDEOSETTINGS = 21, 35 | VideoSettingsState, // ARCOMMANDS_ID_JUMPINGSUMO_CLASS_VIDEOSETTINGSSTATE = 22, 36 | } 37 | 38 | // #[derive(Debug, PartialEq, Eq, Clone, Copy, FramePart)] 39 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 40 | pub enum Anim { 41 | JumpStop, // ARCOMMANDS_ID_JUMPINGSUMO_ANIMATIONS_CMD_JUMPSTOP = 0, 42 | JumpCancel, // ARCOMMANDS_ID_JUMPINGSUMO_ANIMATIONS_CMD_JUMPCANCEL = 1, 43 | JumpLoad, // ARCOMMANDS_ID_JUMPINGSUMO_ANIMATIONS_CMD_JUMPLOAD = 2, 44 | Jump, // ARCOMMANDS_ID_JUMPINGSUMO_ANIMATIONS_CMD_JUMP = 3, 45 | SimpleAnimation, // ARCOMMANDS_ID_JUMPINGSUMO_ANIMATIONS_CMD_SIMPLEANIMATION = 4, 46 | } 47 | 48 | #[derive(Debug, PartialEq, Eq, Clone)] 49 | /// u16 50 | pub enum PilotingID { 51 | Pilot(PilotState), // ARCOMMANDS_ID_JUMPINGSUMO_PILOTING_CMD_PCMD = 0, 52 | Posture, // ARCOMMANDS_ID_JUMPINGSUMO_PILOTING_CMD_POSTURE = 1, 53 | AddCapOffset, // ARCOMMANDS_ID_JUMPINGSUMO_PILOTING_CMD_ADDCAPOFFSET = 2, 54 | } 55 | 56 | #[derive(Default, Debug, PartialEq, Eq, Clone)] 57 | pub struct PilotState { 58 | pub flag: bool, 59 | pub speed: i8, 60 | pub turn: i8, 61 | } 62 | 63 | // --------------------- Conversion impls --------------------- // 64 | 65 | impl Into for &Class { 66 | fn into(self) -> u8 { 67 | match self { 68 | Class::Piloting(_) => 0, 69 | Class::PilotingState => 1, 70 | Class::Animations(_) => 2, 71 | Class::AnimationsState => 3, 72 | Class::SettingsState => 5, 73 | Class::MediaRecord => 6, 74 | Class::MediaRecordState => 7, 75 | Class::NetworkSettings => 8, 76 | Class::NetworkSettingsState => 9, 77 | Class::Network => 10, 78 | Class::NetworkState => 11, 79 | Class::AutioSettings => 12, 80 | Class::AudioSettingsState => 13, 81 | Class::Roadplan => 14, 82 | Class::RoadplanState => 15, 83 | Class::SpeedSettings => 16, 84 | Class::SpeedSettingsState => 17, 85 | Class::MediaStreaming => 18, 86 | Class::MediaStreamingState => 19, 87 | Class::MediaRecordEvent => 20, 88 | Class::VideoSettings => 21, 89 | Class::VideoSettingsState => 22, 90 | } 91 | } 92 | } 93 | 94 | impl Into for Anim { 95 | fn into(self) -> u8 { 96 | match self { 97 | Self::JumpStop => 0, 98 | Self::JumpCancel => 1, 99 | Self::JumpLoad => 2, 100 | Self::Jump => 3, 101 | Self::SimpleAnimation => 4, 102 | } 103 | } 104 | } 105 | 106 | impl Into for &PilotingID { 107 | fn into(self) -> u16 { 108 | match self { 109 | PilotingID::Pilot(_) => 0, 110 | PilotingID::Posture => 1, 111 | PilotingID::AddCapOffset => 2, 112 | } 113 | } 114 | } 115 | pub mod scroll_impl { 116 | use super::*; 117 | use crate::frame::Error; 118 | use scroll::{ctx, Endian, Pread, Pwrite}; 119 | 120 | impl<'a> ctx::TryFromCtx<'a, Endian> for Class { 121 | type Error = Error; 122 | 123 | // and the lifetime annotation on `&'a [u8]` here 124 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 125 | let mut offset = 0; 126 | 127 | let class = match src.gread_with::(&mut offset, ctx)? { 128 | 0 => { 129 | let pilot_state = src.gread_with(&mut offset, ctx)?; 130 | 131 | Self::Piloting(pilot_state) 132 | } 133 | 1 => Self::PilotingState, 134 | 2 => { 135 | let anim = src.gread_with(&mut offset, ctx)?; 136 | 137 | Self::Animations(anim) 138 | } 139 | 3 => Self::AnimationsState, 140 | 5 => Self::SettingsState, 141 | 6 => Self::MediaRecord, 142 | 7 => Self::MediaRecordState, 143 | 8 => Self::NetworkSettings, 144 | 9 => Self::NetworkSettingsState, 145 | 10 => Self::Network, 146 | 11 => Self::NetworkState, 147 | 12 => Self::AutioSettings, 148 | 13 => Self::AudioSettingsState, 149 | 14 => Self::Roadplan, 150 | 15 => Self::RoadplanState, 151 | 16 => Self::SpeedSettings, 152 | 17 => Self::SpeedSettingsState, 153 | 18 => Self::MediaStreaming, 154 | 19 => Self::MediaStreamingState, 155 | 20 => Self::MediaRecordEvent, 156 | 21 => Self::VideoSettings, 157 | 22 => Self::VideoSettingsState, 158 | value => { 159 | return Err(Self::Error::OutOfBound { 160 | value: value.into(), 161 | param: "Class".to_string(), 162 | }) 163 | } 164 | }; 165 | 166 | Ok((class, offset)) 167 | } 168 | } 169 | 170 | impl<'a> ctx::TryIntoCtx for Class { 171 | type Error = Error; 172 | 173 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 174 | let mut offset = 0; 175 | 176 | this.gwrite_with::((&self).into(), &mut offset, ctx)?; 177 | match self { 178 | Self::Piloting(piloting_id) => { 179 | this.gwrite_with(piloting_id, &mut offset, ctx)?; 180 | } 181 | Self::Animations(anim) => { 182 | this.gwrite_with(anim, &mut offset, ctx)?; 183 | } 184 | _ => unimplemented!("Not all Class are impled"), 185 | } 186 | 187 | Ok(offset) 188 | } 189 | } 190 | 191 | impl<'a> ctx::TryFromCtx<'a, Endian> for PilotingID { 192 | type Error = Error; 193 | 194 | // and the lifetime annotation on `&'a [u8]` here 195 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 196 | let mut offset = 0; 197 | 198 | let piloting_id = match src.gread_with::(&mut offset, ctx)? { 199 | 0 => { 200 | let pilot_state = src.gread_with(&mut offset, ctx)?; 201 | 202 | Self::Pilot(pilot_state) 203 | } 204 | 1 => Self::Posture, 205 | 2 => Self::AddCapOffset, 206 | value => { 207 | return Err(Self::Error::OutOfBound { 208 | value: value.into(), 209 | param: "PilotingId".to_string(), 210 | }) 211 | } 212 | }; 213 | 214 | Ok((piloting_id, offset)) 215 | } 216 | } 217 | 218 | impl<'a> ctx::TryIntoCtx for PilotingID { 219 | type Error = Error; 220 | 221 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 222 | let mut offset = 0; 223 | this.gwrite_with::((&self).into(), &mut offset, ctx)?; 224 | 225 | match self { 226 | Self::Pilot(state) => { 227 | this.gwrite_with(state, &mut offset, ctx)?; 228 | } 229 | _ => unimplemented!("Not all PilotingID are impled"), 230 | } 231 | 232 | Ok(offset) 233 | } 234 | } 235 | 236 | impl<'a> ctx::TryFromCtx<'a, Endian> for PilotState { 237 | type Error = Error; 238 | 239 | // and the lifetime annotation on `&'a [u8]` here 240 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 241 | let mut offset = 0; 242 | 243 | let flag = match src.gread_with::(&mut offset, ctx)? { 244 | 0 => false, 245 | 1 => true, 246 | // @TODO: should we mention that it is for PilotState as well and how? 247 | value => { 248 | return Err(Self::Error::OutOfBound { 249 | value: value.into(), 250 | param: "flag".to_string(), 251 | }) 252 | } 253 | }; 254 | let speed: i8 = src.gread_with(&mut offset, ctx)?; 255 | let turn: i8 = src.gread_with(&mut offset, ctx)?; 256 | 257 | let pilot_state = PilotState { flag, speed, turn }; 258 | 259 | Ok((pilot_state, offset)) 260 | } 261 | } 262 | 263 | impl<'a> ctx::TryIntoCtx for PilotState { 264 | type Error = Error; 265 | 266 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 267 | let mut offset = 0; 268 | this.gwrite_with::(self.flag.into(), &mut offset, ctx)?; 269 | this.gwrite_with(self.speed, &mut offset, ctx)?; 270 | this.gwrite_with(self.turn, &mut offset, ctx)?; 271 | 272 | Ok(offset) 273 | } 274 | } 275 | 276 | impl<'a> ctx::TryFromCtx<'a, Endian> for Anim { 277 | type Error = Error; 278 | 279 | // and the lifetime annotation on `&'a [u8]` here 280 | fn try_from_ctx(src: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> { 281 | let mut offset = 0; 282 | 283 | let anim = match src.gread_with::(&mut offset, ctx)? { 284 | 0 => Self::JumpStop, 285 | 1 => Self::JumpCancel, 286 | 2 => Self::JumpLoad, 287 | 3 => Self::Jump, 288 | 4 => Self::SimpleAnimation, 289 | value => { 290 | return Err(Self::Error::OutOfBound { 291 | value: value.into(), 292 | param: "Anim".to_string(), 293 | }) 294 | } 295 | }; 296 | 297 | let mut anim_data = [0_u8; 5]; 298 | src.gread_inout_with(&mut offset, &mut anim_data, ctx)?; 299 | 300 | Ok((anim, offset)) 301 | } 302 | } 303 | 304 | impl<'a> ctx::TryIntoCtx for Anim { 305 | type Error = Error; 306 | 307 | fn try_into_ctx(self, this: &mut [u8], ctx: Endian) -> Result { 308 | let mut offset: usize = 0; 309 | this.gwrite_with::((self).into(), &mut offset, ctx)?; 310 | // TODO: FIX THIS! 311 | let dummy_anim = [0_u8; 5]; 312 | this.gwrite_with(dummy_anim.as_ref(), &mut offset, ())?; 313 | 314 | Ok(offset) 315 | } 316 | } 317 | } 318 | 319 | // --------------------- Tests --------------------- // 320 | 321 | #[cfg(test)] 322 | mod jumping_dumo_tests { 323 | use super::*; 324 | use std::borrow::Borrow; 325 | 326 | #[test] 327 | fn test_piloting_command() { 328 | assert_piloting(PilotingID::Pilot(PilotState::default()), 0); 329 | assert_piloting(PilotingID::Posture, 1); 330 | assert_piloting(PilotingID::AddCapOffset, 2); 331 | } 332 | 333 | #[test] 334 | fn test_anim() { 335 | assert_anim(Anim::JumpStop, 0); 336 | assert_anim(Anim::JumpCancel, 1); 337 | assert_anim(Anim::JumpLoad, 2); 338 | assert_anim(Anim::Jump, 3); 339 | assert_anim(Anim::SimpleAnimation, 4); 340 | } 341 | 342 | #[test] 343 | fn test_class() { 344 | assert_class(Class::Piloting(PilotingID::Pilot(PilotState::default())), 0); 345 | assert_class(Class::PilotingState, 1); 346 | assert_class(Class::Animations(Anim::Jump), 2); 347 | assert_class(Class::AnimationsState, 3); 348 | assert_class(Class::SettingsState, 5); 349 | assert_class(Class::MediaRecord, 6); 350 | assert_class(Class::MediaRecordState, 7); 351 | assert_class(Class::NetworkSettings, 8); 352 | assert_class(Class::NetworkSettingsState, 9); 353 | assert_class(Class::Network, 10); 354 | assert_class(Class::NetworkState, 11); 355 | assert_class(Class::AutioSettings, 12); 356 | assert_class(Class::AudioSettingsState, 13); 357 | assert_class(Class::Roadplan, 14); 358 | assert_class(Class::RoadplanState, 15); 359 | assert_class(Class::SpeedSettings, 16); 360 | assert_class(Class::SpeedSettingsState, 17); 361 | assert_class(Class::MediaStreaming, 18); 362 | assert_class(Class::MediaStreamingState, 19); 363 | assert_class(Class::MediaRecordEvent, 20); 364 | assert_class(Class::VideoSettings, 21); 365 | assert_class(Class::VideoSettingsState, 22); 366 | } 367 | 368 | fn assert_class(dc: impl Borrow, v: u8) { 369 | let as_u8: u8 = dc.borrow().into(); 370 | assert_eq!(v, as_u8); 371 | } 372 | 373 | fn assert_anim(a: Anim, v: u8) { 374 | let as_u8: u8 = a.into(); 375 | assert_eq!(v, as_u8); 376 | } 377 | 378 | fn assert_piloting(pc: impl Borrow, v: u16) { 379 | let as_u8: u16 = pc.borrow().into(); 380 | assert_eq!(v, as_u8); 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /arsdk-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | use crate::frame::{Frame, FrameType}; 2 | use chrono::{DateTime, Utc}; 3 | use dashmap::DashMap; 4 | use log::{error, info}; 5 | use pnet::datalink; 6 | use scroll::{ctx::TryIntoCtx, Pread, LE}; 7 | use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; 8 | use std::sync::{ 9 | mpsc::{sync_channel, Receiver, SendError, SyncSender}, 10 | Arc, 11 | }; 12 | use thiserror::Error; 13 | 14 | // re-export chrono 15 | pub use chrono; 16 | 17 | pub const INIT_PORT: u16 = 44444; 18 | pub const LISTEN_PORT: u16 = 43210; 19 | pub const PARROT_SPHINX_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(10, 202, 0, 1)); 20 | pub const PARROT_SPHINX_CONFIG: Config = Config { 21 | drone_addr: PARROT_SPHINX_IP, 22 | // @TODO: Once we fix the Date Time sending, set to `TRUE` 23 | send_datetime: false, 24 | }; 25 | 26 | pub mod ardrone3; 27 | pub mod command; 28 | pub mod common; 29 | pub mod frame; 30 | mod handshake; 31 | pub mod jumping_sumo; 32 | pub mod listener; 33 | pub mod parse; 34 | 35 | pub(crate) use handshake::perform_handshake; 36 | use listener::Listener; 37 | 38 | pub mod prelude { 39 | pub use crate::{ 40 | frame, Config, ConnectionError, Drone, Error, PARROT_SPHINX_CONFIG, PARROT_SPHINX_IP, 41 | }; 42 | pub use chrono::{DateTime, Utc}; 43 | } 44 | 45 | #[derive(Debug, Error)] 46 | pub enum Error { 47 | #[error("Sending command")] 48 | Send(#[from] SendError>), 49 | #[error("Receiving a Frame")] 50 | Receive(#[from] frame::Error), 51 | } 52 | 53 | #[derive(Debug, Error)] 54 | pub enum ConnectionError { 55 | #[error("Drone {0}")] 56 | Drone(#[from] Error), 57 | #[error("Sending Date & Time Frames {0}")] 58 | DateTime(#[from] frame::Error), 59 | #[error("Couldn't bind to local socket {addr} - {error}")] 60 | Io { 61 | error: std::io::Error, 62 | addr: SocketAddr, 63 | }, 64 | #[error("Couldn't find local ip in the target network {0}")] 65 | DroneAddr(IpAddr), 66 | #[error("Making Handshake {0}")] 67 | Handshake(#[from] handshake::Error), 68 | } 69 | 70 | #[derive(Debug)] 71 | pub struct Config { 72 | pub drone_addr: IpAddr, 73 | /// Wheather or not to set after connecting (by sending a frame) the current DateTime to the Drone: 74 | /// 75 | /// ```rust 76 | /// use chrono::{DateTime, Utc}; 77 | /// let now: DateTime = Utc::now(); 78 | /// ``` 79 | pub send_datetime: bool, 80 | } 81 | 82 | impl From for Config 83 | where 84 | I: Into, 85 | { 86 | fn from(ip: I) -> Self { 87 | Self { 88 | drone_addr: ip.into(), 89 | send_datetime: false, 90 | } 91 | } 92 | } 93 | 94 | #[derive(Clone, Debug)] 95 | pub struct Drone { 96 | inner: Arc, 97 | } 98 | 99 | #[derive(Debug)] 100 | struct DroneInner { 101 | // Each frame::BufferID gets its own sequence_id 102 | sequence_ids: DashMap, 103 | sender: SyncSender>, 104 | } 105 | 106 | impl Drone { 107 | /// Connects to a drone 108 | /// 109 | /// * Spawns Listener at LISTENER_PORT 110 | /// * Performs Handshake at INIT_PORT 111 | /// * Spawns Command sender at `c2d_port` 112 | pub fn connect(config: Config) -> Result { 113 | let local_ip = local_ip(config.drone_addr) 114 | .ok_or_else(|| ConnectionError::DroneAddr(config.drone_addr))?; 115 | 116 | // @TODO: Check if we're going to miss any messages between spawning the listener and the receiver of commands 117 | let (tx_cmd, rx_cmd) = sync_channel(200); 118 | 119 | let drone = Self { 120 | inner: Arc::new(DroneInner { 121 | sequence_ids: DashMap::new(), 122 | sender: tx_cmd, 123 | }), 124 | }; 125 | 126 | let local_listener = SocketAddr::new(local_ip, LISTEN_PORT); 127 | info!("{}: Spawning Listener", &&local_listener); 128 | 129 | spawn_listener(drone.clone(), local_listener)?; 130 | 131 | let init_addr = SocketAddr::new(config.drone_addr, INIT_PORT); 132 | 133 | info!("Init address {}", &init_addr); 134 | 135 | let handshake_response = perform_handshake(init_addr, local_listener.port())?; 136 | let cmd_sender_target = SocketAddr::new(config.drone_addr, handshake_response.c2d_port); 137 | 138 | info!("{}: Spawning CMD Sender", cmd_sender_target); 139 | 140 | spawn_cmd_sender(rx_cmd, local_ip, cmd_sender_target)?; 141 | 142 | if config.send_datetime { 143 | drone.send_datetime(Utc::now())?; 144 | } 145 | 146 | Ok(drone) 147 | } 148 | 149 | pub fn send_frame(&self, frame: frame::Frame) -> Result<(), Error> { 150 | let mut raw_message = [0_u8; 2048]; 151 | let written = frame.try_into_ctx(&mut raw_message, LE)?; 152 | 153 | self.send_raw_message(&raw_message[0..written]) 154 | } 155 | 156 | pub fn send_raw_message(&self, raw_message: &[u8]) -> Result<(), Error> { 157 | Ok(self.inner.sender.send(raw_message.to_vec())?) 158 | } 159 | 160 | pub fn send_datetime(&self, date: DateTime) -> Result<(), Error> { 161 | use command::Feature::Common; 162 | use common::Class; 163 | use frame::{BufferID, Type}; 164 | 165 | let date_feature = Common(Some(Class::Common(common::Common::CurrentDate(date)))); 166 | 167 | let frame = Frame::for_drone( 168 | &self, 169 | Type::DataWithAck, 170 | BufferID::CDAck, 171 | Some(date_feature), 172 | ); 173 | 174 | self.send_frame(frame)?; 175 | 176 | let time_feature = Common(Some(Class::Common(common::Common::CurrentTime(date)))); 177 | let frame = Frame::for_drone( 178 | &self, 179 | Type::DataWithAck, 180 | BufferID::CDAck, 181 | Some(time_feature), 182 | ); 183 | 184 | self.send_frame(frame) 185 | } 186 | 187 | fn send_pong(&self, feature: Option) -> Result<(), Error> { 188 | let frame_type = frame::Type::Data; 189 | let buffer_id = frame::BufferID::PONG; 190 | 191 | // send the same feature back 192 | let pong = frame::Frame::for_drone(&self, frame_type, buffer_id, feature); 193 | 194 | self.send_frame(pong) 195 | } 196 | } 197 | 198 | impl DroneInner { 199 | pub(crate) fn sequence_id(&self, buffer_id: frame::BufferID) -> u8 { 200 | if let Some(mut sequence_id) = self.sequence_ids.get_mut(&buffer_id) { 201 | let command_id = *sequence_id; 202 | *sequence_id = sequence_id.overflowing_add(1).0; 203 | 204 | command_id 205 | } else { 206 | self.sequence_ids.insert(buffer_id, 0); 207 | 0 208 | } 209 | } 210 | } 211 | 212 | // returns ip of the interface that is in the same network as the target 213 | fn local_ip(target: IpAddr) -> Option { 214 | datalink::interfaces() 215 | .into_iter() 216 | .filter_map(|interface| interface.ips.into_iter().find(|ip| ip.contains(target))) 217 | .map(|ip_network| ip_network.ip()) 218 | .next() 219 | } 220 | 221 | fn spawn_listener(drone: Drone, addr: SocketAddr) -> Result<(), ConnectionError> { 222 | let listener_socket = 223 | UdpSocket::bind(addr).map_err(|error| ConnectionError::Io { error, addr })?; 224 | 225 | std::thread::spawn(move || { 226 | let listener = Listener { 227 | drone: drone.clone(), 228 | socket: listener_socket, 229 | }; 230 | 231 | listener.listen(); 232 | }); 233 | 234 | Ok(()) 235 | } 236 | 237 | pub(crate) fn print_buf(buf: &[u8]) -> String { 238 | buf.iter() 239 | .map(|byte| format!("{}", byte)) 240 | .collect::>() 241 | .join(" ") 242 | } 243 | 244 | fn spawn_cmd_sender( 245 | rx: Receiver>, 246 | local_ip: IpAddr, 247 | target_addr: SocketAddr, 248 | ) -> Result<(), ConnectionError> { 249 | let local_addr = SocketAddr::new(local_ip, target_addr.port()); 250 | 251 | let socket = UdpSocket::bind(local_addr).map_err(|error| ConnectionError::Io { 252 | error, 253 | addr: local_addr, 254 | })?; 255 | 256 | std::thread::spawn(move || loop { 257 | let frame_to_send = match rx.recv() { 258 | Ok(frame) => frame, 259 | Err(err) => { 260 | error!("Receiving Frame for sending failed: {:?}", &err); 261 | continue; 262 | } 263 | }; 264 | 265 | info!("Frame to sent: {:?}", &frame_to_send); 266 | 267 | let frame = frame_to_send.pread_with::(0, LE); 268 | 269 | info!( 270 | "Sent Frame (length: {}) => {:?}", 271 | frame_to_send.len(), 272 | &frame 273 | ); 274 | 275 | let size = socket 276 | .send_to(&frame_to_send, target_addr) 277 | .expect("something terrible happened"); 278 | 279 | assert_eq!(size, frame_to_send.len()) 280 | }); 281 | 282 | Ok(()) 283 | } 284 | 285 | /// we receive 2 frames sometimes 286 | 287 | #[cfg(test)] 288 | mod test { 289 | use super::*; 290 | use crate::parse::parse_message_frames; 291 | use ardrone3::ArDrone3; 292 | use command::Feature; 293 | use frame::{BufferID, Type}; 294 | 295 | #[test] 296 | fn receiving_two_frames_at_once() { 297 | let received: [u8; 58] = [ 298 | // Type::Data = 2 299 | // BufferID::PING = 0 300 | // Sequence: 4 301 | // Frame size: 23 302 | // Feature: 9 303 | 2, 0, 4, 23, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 57, 252, 225, 47, 0, 0, 0, 0, 304 | // Type::Data = 2 305 | // BufferID::DCNavdata = 127 306 | // Sequence: 71 307 | // Frame size: 35 308 | // ArDrone3 - Feature - 1 309 | // Piloting State - 4 310 | // Data? 311 | 2, 127, 71, 35, 0, 0, 0, 1, 4, 4, 0, 0, 0, 0, 0, 0, 64, 127, 64, 0, 0, 0, 0, 0, 64, 127, 312 | 64, 0, 0, 0, 0, 0, 64, 127, 64, 313 | ]; 314 | let frames = parse_message_frames(&received); 315 | 316 | assert_eq!(2, frames.len()); 317 | 318 | let first_expected = Frame { 319 | frame_type: Type::Data, 320 | buffer_id: BufferID::PING, 321 | sequence_id: 4, 322 | feature: Some(Feature::Unknown { 323 | feature: 9, 324 | // data starts at 8th bytes 325 | // 23 the frame is 23 bytes long 326 | data: received[8..23].to_vec(), 327 | }), 328 | }; 329 | 330 | // data starts at 8th bytes 331 | let second_expected = Frame { 332 | frame_type: Type::Data, 333 | buffer_id: BufferID::DCNavdata, 334 | sequence_id: 71, 335 | feature: Some(Feature::ArDrone3(Some(ArDrone3::PilotingState( 336 | ardrone3::PilotingState::Unknown { 337 | piloting_state: 4, 338 | data: received[34..].to_vec(), 339 | }, 340 | )))), 341 | }; 342 | 343 | assert_eq!( 344 | &FrameType::Known(first_expected), 345 | frames[0] 346 | .as_ref() 347 | .expect("Should deserialize first (PING) frame") 348 | ); 349 | assert_eq!( 350 | &FrameType::Known(second_expected), 351 | frames[1] 352 | .as_ref() 353 | .expect("Should deserialize second (DCNavdata) frame") 354 | ); 355 | } 356 | 357 | #[test] 358 | #[ignore] 359 | fn receiving_two_frames_at_once_2() { 360 | let _message: [u8; 46] = [ 361 | // frist frame 362 | 2, 0, 23, 23, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 123, 61, 147, 11, 0, 0, 0, 0, 363 | // second frame 364 | 2, 127, 26, 23, 0, 0, 0, 1, 4, 5, 0, 22, 144, 125, 184, 167, 42, 112, 58, 252, 101, 132, 365 | 185, 366 | ]; 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /arsdk-rs/src/listener.rs: -------------------------------------------------------------------------------- 1 | use crate::{parse::handle_bytes, print_buf, Drone}; 2 | use log::debug; 3 | use std::net::UdpSocket; 4 | 5 | pub struct Listener { 6 | pub drone: Drone, 7 | pub socket: UdpSocket, 8 | } 9 | 10 | impl Listener { 11 | /// Blocking listener in a infinite loop 12 | pub fn listen(&self) { 13 | loop { 14 | let mut buf = [0_u8; 256]; 15 | if let Ok((bytes_read, origin)) = self.socket.recv_from(&mut buf) { 16 | debug!("Received: {} bytes from {}", bytes_read, origin); 17 | debug!("Bytes: {}", print_buf(&buf[..bytes_read])); 18 | 19 | handle_bytes(&self.drone, &buf[..bytes_read]); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /arsdk-rs/src/parse.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | command::Feature, 3 | frame::{BufferID, Error, Frame, Type}, 4 | print_buf, Drone, FrameType, 5 | }; 6 | use log::{error, info}; 7 | use scroll::{Pread, Pwrite, LE}; 8 | 9 | /// - Parses Frames 10 | /// - Sends PING response to cmd Sender 11 | /// - Logs unknown frames 12 | pub(crate) fn handle_bytes(drone: &Drone, raw_frames: &[u8]) { 13 | let frames = parse_message_frames(&raw_frames); 14 | 15 | for result in frames.iter() { 16 | match result { 17 | Ok(FrameType::Known(frame)) => info!("Frame: {:?}", frame), 18 | Ok(FrameType::Unknown(unknown)) => { 19 | info!("Unknown Frame: {:?}", unknown); 20 | info!("Bytes: {}", print_buf(raw_frames)); 21 | } 22 | Err(err) => error!("Receiving Frame: {:?}", err), 23 | } 24 | } 25 | 26 | for result in frames.iter() { 27 | match result { 28 | // PING-PONG 29 | Ok(FrameType::Known(frame)) if frame.buffer_id == BufferID::PING => { 30 | if let Err(err) = drone.send_pong(frame.feature.clone()) { 31 | error!("Sending Frame to Commander: {}", err) 32 | } 33 | } 34 | // Data that needs Ack coming from the Drone 35 | Ok(FrameType::Known(frame)) if frame.buffer_id == BufferID::DCNavdata => { 36 | let ack_type = Type::Ack; 37 | let ack_buffer = BufferID::CDAck; 38 | let ack_sequence_id = frame.sequence_id; 39 | let feature = frame.feature.as_ref().and_then(|feature| { 40 | match feature { 41 | // we return an empty ArDrone3 as the drone does for our Frames 42 | Feature::ArDrone3(_) => Some(Feature::ArDrone3(None)), 43 | // 44 | _ => None, 45 | } 46 | }); 47 | 48 | let ack_frame = Frame { 49 | frame_type: ack_type, 50 | buffer_id: ack_buffer, 51 | feature, 52 | sequence_id: ack_sequence_id, 53 | }; 54 | if let Err(err) = drone.send_frame(ack_frame) { 55 | error!("Sending Frame to Commander: {}", err) 56 | } 57 | } 58 | // we handled the error cases in the upper portion of the function 59 | _ => {} 60 | } 61 | } 62 | } 63 | 64 | /// Parses the Frames from a buffer 65 | pub(crate) fn parse_message_frames(buf: &[u8]) -> Vec> { 66 | let mut offset = 0; 67 | // TODO: Check how many frames can we receive at once 68 | // reasonable given that we receive at most (MAYBE?!) 2 frames 69 | let mut frames = Vec::with_capacity(3); 70 | 71 | let mut tried = 1; 72 | // try to read all the buf length & limit to 3 Frames of read 73 | while offset != buf.len() && tried <= 3 { 74 | let frame = buf.gread_with(&mut offset, LE); 75 | 76 | frames.push(frame); 77 | tried += 1; 78 | } 79 | 80 | frames 81 | } 82 | 83 | /// Helper function to read unknown / not implemented Frames 84 | pub(crate) fn read_unknown(src: &[u8], offset: &mut usize) -> Result, crate::frame::Error> { 85 | let mut feature_data = [0_u8; 256]; 86 | let actual_written = feature_data.gwrite_with(&src[*offset..], &mut 0, ())?; 87 | 88 | assert_eq!(actual_written, feature_data[..actual_written].len()); 89 | 90 | *offset += actual_written; 91 | 92 | Ok(feature_data[..actual_written].to_vec()) 93 | } 94 | 95 | #[cfg(test)] 96 | mod parse_message_frames { 97 | use super::*; 98 | use crate::jumping_sumo as js; 99 | use crate::{ 100 | command::Feature, 101 | frame::{BufferID, Frame, FrameType, Type}, 102 | }; 103 | #[test] 104 | fn test_parsable_messages() { 105 | let jump_message: [u8; 15] = [ 106 | 0x4, 0xb, 0x1, 0xf, 0x0, 0x0, 0x0, 0x3, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 107 | ]; 108 | 109 | let jump_frame = Frame { 110 | frame_type: Type::DataWithAck, 111 | buffer_id: BufferID::CDAck, 112 | sequence_id: 1, 113 | feature: Some(Feature::JumpingSumo(js::Class::Animations(js::Anim::Jump))), 114 | }; 115 | 116 | let move_message: [u8; 14] = [ 117 | 0x2, 0xa, 0x67, 0xe, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x9c, 118 | ]; 119 | 120 | let pilot_state = js::PilotState { 121 | flag: true, 122 | speed: 0, 123 | turn: -100, 124 | }; 125 | 126 | let move_frame = Frame { 127 | frame_type: Type::Data, 128 | buffer_id: BufferID::CDNonAck, 129 | sequence_id: 103, 130 | feature: Some(Feature::JumpingSumo(js::Class::Piloting( 131 | js::PilotingID::Pilot(pilot_state), 132 | ))), 133 | }; 134 | 135 | let buf = { 136 | let mut vec = jump_message.to_vec(); 137 | vec.extend_from_slice(&move_message); 138 | vec 139 | }; 140 | 141 | let expected = [FrameType::Known(jump_frame), FrameType::Known(move_frame)]; 142 | 143 | let actual = parse_message_frames(&buf); 144 | 145 | assert_eq!(expected.len(), actual.len()); 146 | 147 | for (expected, parsed) in expected.iter().zip(actual) { 148 | let actual = parsed.expect("This should be Ok(_)"); 149 | 150 | assert_eq!(expected, &actual); 151 | } 152 | } 153 | 154 | #[test] 155 | fn test_feature_common_none() { 156 | let buf: [u8; 8] = [1, 139, 0, 8, 0, 0, 0, 0]; 157 | 158 | let frame = Frame { 159 | frame_type: Type::Ack, 160 | buffer_id: BufferID::ACKFromSendWithAck, 161 | sequence_id: 0, 162 | feature: Some(Feature::Common(None)), 163 | }; 164 | 165 | let actual = parse_message_frames(&buf); 166 | 167 | assert_eq!(actual.len(), 1); 168 | let actual = actual 169 | .into_iter() 170 | .next() 171 | .expect("Should have 1 parsed frame") 172 | .expect("Should be Ok(_)"); 173 | 174 | assert_eq!(FrameType::Known(frame), actual); 175 | } 176 | 177 | #[test] 178 | 179 | /// [2] Type - Data 180 | /// [0] BufferID - Ping 181 | /// [1] Sequence id 1 182 | /// [23,0,0,0] length 23 183 | /// [0, 0, 0, 0, 0, 0, 0, 233, 72, 37, 42, 0, 0, 0, 0] Ping's Gibberish Data 184 | /// 185 | /// Second: 186 | /// [4] Type - DataWithAck 187 | /// [126] BufferID - DCEvent 188 | /// [1] Sequence id 1 189 | /// [12, 0, 0, 0] Length 12 190 | /// [0] Feature - Common 191 | /// [14] Class - CalibrationState 192 | /// [1, 0, 0] - x y z axis 193 | /// 194 | fn test_two_frames_ping_and_common_class_calibration_state_unknown() { 195 | let buf: [u8; 35] = [ 196 | // first: 197 | 2, 0, 1, 23, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 233, 72, 37, 42, 0, 0, 0, 0, 198 | // second: 199 | 4, 126, 1, 12, 0, 0, 0, 0, 14, 1, 0, 0, 200 | ]; 201 | 202 | let expected = [ 203 | FrameType::Known(Frame { 204 | frame_type: Type::Data, 205 | buffer_id: BufferID::PING, 206 | sequence_id: 1, 207 | feature: Some(Feature::Unknown { 208 | feature: 3, 209 | data: vec![0, 0, 0, 0, 0, 0, 0, 233, 72, 37, 42, 0, 0, 0, 0], 210 | }), 211 | }), 212 | FrameType::Known(Frame { 213 | frame_type: Type::DataWithAck, 214 | buffer_id: BufferID::DCEvent, 215 | sequence_id: 1, 216 | feature: Some(Feature::Common(Some(crate::common::Class::Unknown { 217 | // CalibrationState 218 | class: 14, 219 | // x y z axis 220 | data: vec![1, 0, 0], 221 | }))), 222 | }), 223 | ]; 224 | 225 | let actual = parse_message_frames(&buf); 226 | assert_eq!(expected.len(), actual.len()); 227 | 228 | for (expected, parsed) in expected.iter().zip(actual) { 229 | let actual = parsed.expect("This should be Ok(_)"); 230 | 231 | assert_eq!(expected, &actual); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /bebop2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bebop2" 3 | version = "0.1.0" 4 | authors = ["Lachezar Lechev "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | arsdk-rs = { path = "../arsdk-rs" } 9 | 10 | [dev-dependencies] 11 | # Used for examples 12 | env_logger = "0.7" 13 | log = "0.4" 14 | async-log = "^2.0" 15 | tokio = {version = "0.2.22", features = ["rt-threaded", "macros", "time"]} 16 | -------------------------------------------------------------------------------- /bebop2/examples/take_off.rs: -------------------------------------------------------------------------------- 1 | use log::info; 2 | use std::error::Error; 3 | 4 | use bebop2::prelude::*; 5 | use std::time::Duration; 6 | use tokio::time::delay_for; 7 | // use smol::Timer; 8 | 9 | fn async_setup_logger() { 10 | let logger = env_logger::Logger::from_default_env(); 11 | 12 | async_log::Logger::wrap(logger, || 12) 13 | .start(log::LevelFilter::Trace) 14 | .unwrap(); 15 | } 16 | 17 | #[tokio::main] 18 | async fn main() -> Result<(), Box> { 19 | // env_logger::init(); 20 | async_setup_logger(); 21 | 22 | let drone = Bebop2::connect(PARROT_SPHINX_CONFIG)?; 23 | 24 | info!("Takeoff!"); 25 | drone.take_off()?; 26 | 27 | info!("Wait 5 seconds and fly UP"); 28 | delay_for(Duration::from_secs(5)).await; 29 | for i in 0..254 { 30 | drone.up(i)?; 31 | delay_for(Duration::from_millis(25)).await; 32 | } 33 | 34 | info!("Wait 5 seconds and fly DOWN"); 35 | delay_for(Duration::from_secs(5)).await; 36 | 37 | for i in 0..220 { 38 | drone.down(i)?; 39 | delay_for(Duration::from_millis(25)).await; 40 | } 41 | 42 | info!("Hover for 4 seconds before landing"); 43 | delay_for(Duration::from_secs(4)).await; 44 | 45 | for _ in 0..50 { 46 | drone.landing()?; 47 | delay_for(Duration::from_millis(25)).await; 48 | } 49 | 50 | loop {} 51 | // }) 52 | } 53 | -------------------------------------------------------------------------------- /bebop2/src/lib.rs: -------------------------------------------------------------------------------- 1 | use arsdk_rs::{ 2 | command::Feature, 3 | frame::{BufferID, Frame, Type}, 4 | }; 5 | 6 | pub use arsdk_rs::{ 7 | ardrone3::{ArDrone3, MediaStreaming, Piloting, PCMD}, 8 | prelude::*, 9 | }; 10 | 11 | pub mod prelude { 12 | pub use crate::Bebop2; 13 | pub use arsdk_rs::{ 14 | ardrone3::{ArDrone3, Piloting, PCMD}, 15 | prelude::*, 16 | }; 17 | } 18 | 19 | pub struct Bebop2 { 20 | drone: Drone, 21 | } 22 | 23 | impl Bebop2 { 24 | pub fn connect(config: Config) -> Result { 25 | let drone = Drone::connect(config)?; 26 | 27 | Ok(Self { drone }) 28 | } 29 | 30 | /// - Captain #Ferris 🦀 :Take off... 🛫 31 | pub fn take_off(&self) -> Result<(), Error> { 32 | let feature = Feature::ArDrone3(Some(ArDrone3::Piloting(Piloting::TakeOff))); 33 | 34 | let frame = Frame::for_drone( 35 | &self.drone, 36 | Type::DataWithAck, 37 | BufferID::CDAck, 38 | Some(feature), 39 | ); 40 | 41 | self.drone.send_frame(frame) 42 | } 43 | 44 | pub fn up(&self, sequence_id: u8) -> Result<(), Error> { 45 | let feature = Feature::ArDrone3(Some(ArDrone3::Piloting(Piloting::PCMD(PCMD { 46 | flag: true, 47 | roll: 0, 48 | pitch: 0, 49 | yaw: 0, 50 | gaz: 100, 51 | timestamp: Utc::now(), 52 | sequence_id, 53 | })))); 54 | 55 | let frame = Frame::for_drone(&self.drone, Type::Data, BufferID::CDNonAck, Some(feature)); 56 | 57 | self.drone.send_frame(frame) 58 | } 59 | 60 | pub fn down(&self, sequence_id: u8) -> Result<(), Error> { 61 | let feature = Feature::ArDrone3(Some(ArDrone3::Piloting(Piloting::PCMD(PCMD { 62 | flag: true, 63 | roll: 0, 64 | pitch: 0, 65 | yaw: 0, 66 | gaz: -100, 67 | timestamp: Utc::now(), 68 | sequence_id, 69 | })))); 70 | 71 | let frame = Frame::for_drone(&self.drone, Type::Data, BufferID::CDNonAck, Some(feature)); 72 | 73 | self.drone.send_frame(frame) 74 | } 75 | 76 | pub fn landing(&self) -> Result<(), Error> { 77 | let feature = Feature::ArDrone3(Some(ArDrone3::Piloting(Piloting::Landing))); 78 | 79 | let frame = Frame::for_drone( 80 | &self.drone, 81 | Type::DataWithAck, 82 | BufferID::CDAck, 83 | Some(feature), 84 | ); 85 | 86 | self.drone.send_frame(frame) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /jumpingsumo-rs/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ahash" 5 | version = "0.3.3" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "35b909d1c126f78ace756fc337133356c499eebeefcce930fa5fb018823f2b2d" 8 | dependencies = [ 9 | "const-random", 10 | ] 11 | 12 | [[package]] 13 | name = "aho-corasick" 14 | version = "0.6.10" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" 17 | dependencies = [ 18 | "memchr", 19 | ] 20 | 21 | [[package]] 22 | name = "anyhow" 23 | version = "1.0.28" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" 26 | 27 | [[package]] 28 | name = "arsdk-rs" 29 | version = "0.0.4" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "d65e0ceffd8a9bd5815c25951aa7979d041417e8ad046f65669f4a76615d0af3" 32 | dependencies = [ 33 | "anyhow", 34 | "chrono", 35 | "dashmap", 36 | "pnet", 37 | "serde", 38 | "serde_json", 39 | ] 40 | 41 | [[package]] 42 | name = "autocfg" 43 | version = "1.0.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 46 | 47 | [[package]] 48 | name = "bitflags" 49 | version = "0.5.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23" 52 | 53 | [[package]] 54 | name = "cfg-if" 55 | version = "0.1.10" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 58 | 59 | [[package]] 60 | name = "chrono" 61 | version = "0.4.11" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" 64 | dependencies = [ 65 | "num-integer", 66 | "num-traits", 67 | "time", 68 | ] 69 | 70 | [[package]] 71 | name = "const-random" 72 | version = "0.1.8" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" 75 | dependencies = [ 76 | "const-random-macro", 77 | "proc-macro-hack", 78 | ] 79 | 80 | [[package]] 81 | name = "const-random-macro" 82 | version = "0.1.8" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" 85 | dependencies = [ 86 | "getrandom", 87 | "proc-macro-hack", 88 | ] 89 | 90 | [[package]] 91 | name = "dashmap" 92 | version = "3.11.1" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "0f87a04c37da1d3d27db1fb7f372802b72fb8c3ff3e9c0914530995127f4a6a1" 95 | dependencies = [ 96 | "ahash", 97 | "cfg-if", 98 | "num_cpus", 99 | ] 100 | 101 | [[package]] 102 | name = "getrandom" 103 | version = "0.1.14" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 106 | dependencies = [ 107 | "cfg-if", 108 | "libc", 109 | "wasi", 110 | ] 111 | 112 | [[package]] 113 | name = "glob" 114 | version = "0.2.11" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" 117 | 118 | [[package]] 119 | name = "hermit-abi" 120 | version = "0.1.12" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" 123 | dependencies = [ 124 | "libc", 125 | ] 126 | 127 | [[package]] 128 | name = "ipnetwork" 129 | version = "0.15.1" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "a69dd5e3613374e74da81c251750153abe3bd0ad17641ea63d43d1e21d0dbd4d" 132 | dependencies = [ 133 | "serde", 134 | ] 135 | 136 | [[package]] 137 | name = "itoa" 138 | version = "0.4.5" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 141 | 142 | [[package]] 143 | name = "jumpingsumo-rs" 144 | version = "0.0.1" 145 | dependencies = [ 146 | "anyhow", 147 | "arsdk-rs", 148 | "chrono", 149 | ] 150 | 151 | [[package]] 152 | name = "kernel32-sys" 153 | version = "0.2.2" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 156 | dependencies = [ 157 | "winapi 0.2.8", 158 | "winapi-build", 159 | ] 160 | 161 | [[package]] 162 | name = "lazy_static" 163 | version = "1.4.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 166 | 167 | [[package]] 168 | name = "libc" 169 | version = "0.2.69" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" 172 | 173 | [[package]] 174 | name = "log" 175 | version = "0.3.9" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 178 | dependencies = [ 179 | "log 0.4.8", 180 | ] 181 | 182 | [[package]] 183 | name = "log" 184 | version = "0.4.8" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 187 | dependencies = [ 188 | "cfg-if", 189 | ] 190 | 191 | [[package]] 192 | name = "memchr" 193 | version = "2.3.3" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 196 | 197 | [[package]] 198 | name = "num-integer" 199 | version = "0.1.42" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" 202 | dependencies = [ 203 | "autocfg", 204 | "num-traits", 205 | ] 206 | 207 | [[package]] 208 | name = "num-traits" 209 | version = "0.2.11" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 212 | dependencies = [ 213 | "autocfg", 214 | ] 215 | 216 | [[package]] 217 | name = "num_cpus" 218 | version = "1.13.0" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 221 | dependencies = [ 222 | "hermit-abi", 223 | "libc", 224 | ] 225 | 226 | [[package]] 227 | name = "pnet" 228 | version = "0.25.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "5c08c2c6c26481fcbe49dc4405baedf47151f859c5a45d3f254c2ff74ce51cf0" 231 | dependencies = [ 232 | "ipnetwork", 233 | "pnet_base", 234 | "pnet_datalink", 235 | "pnet_packet", 236 | "pnet_sys", 237 | "pnet_transport", 238 | ] 239 | 240 | [[package]] 241 | name = "pnet_base" 242 | version = "0.22.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "4df28acf2fcc77436dd2b91a9a0c2bb617f9ca5f2acefee1a4135058b9f9801f" 245 | 246 | [[package]] 247 | name = "pnet_datalink" 248 | version = "0.25.0" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "545f8df67cbc53438f37f56e68ae5ca49beb3990e9fd7e9e214c8ffd36c0e0ea" 251 | dependencies = [ 252 | "ipnetwork", 253 | "libc", 254 | "pnet_base", 255 | "pnet_sys", 256 | "winapi 0.2.8", 257 | ] 258 | 259 | [[package]] 260 | name = "pnet_macros" 261 | version = "0.25.0" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "cf402424ca7281aa234b726c32bce5a8e2278c72f5863305e291ac3de08e16f8" 264 | dependencies = [ 265 | "regex", 266 | "syntex", 267 | "syntex_syntax", 268 | ] 269 | 270 | [[package]] 271 | name = "pnet_macros_support" 272 | version = "0.25.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "5e586854ba703c15f74c486e1a46624566b47f1f61cc8a6b02c6bbe5e34a383b" 275 | dependencies = [ 276 | "pnet_base", 277 | ] 278 | 279 | [[package]] 280 | name = "pnet_packet" 281 | version = "0.25.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "c44c075c6d4f2e814dba621a999838e4a4f749f6117024f52b05b3c559a4fd17" 284 | dependencies = [ 285 | "glob", 286 | "pnet_base", 287 | "pnet_macros", 288 | "pnet_macros_support", 289 | "syntex", 290 | ] 291 | 292 | [[package]] 293 | name = "pnet_sys" 294 | version = "0.25.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "82f881a6d75ac98c5541db6144682d1773bb14c6fc50c6ebac7086c8f7f23c29" 297 | dependencies = [ 298 | "libc", 299 | "winapi 0.2.8", 300 | "ws2_32-sys", 301 | ] 302 | 303 | [[package]] 304 | name = "pnet_transport" 305 | version = "0.25.0" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "1b75ccaee7b5daba9f9a7d47bceeb73cc32edde9952dc5409460d6621ec667b6" 308 | dependencies = [ 309 | "libc", 310 | "pnet_base", 311 | "pnet_packet", 312 | "pnet_sys", 313 | ] 314 | 315 | [[package]] 316 | name = "proc-macro-hack" 317 | version = "0.5.15" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" 320 | 321 | [[package]] 322 | name = "proc-macro2" 323 | version = "1.0.10" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" 326 | dependencies = [ 327 | "unicode-xid 0.2.0", 328 | ] 329 | 330 | [[package]] 331 | name = "quote" 332 | version = "1.0.3" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 335 | dependencies = [ 336 | "proc-macro2", 337 | ] 338 | 339 | [[package]] 340 | name = "regex" 341 | version = "1.0.6" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "ee84f70c8c08744ea9641a731c7fadb475bf2ecc52d7f627feb833e0b3990467" 344 | dependencies = [ 345 | "aho-corasick", 346 | "memchr", 347 | "regex-syntax", 348 | "thread_local", 349 | "utf8-ranges", 350 | ] 351 | 352 | [[package]] 353 | name = "regex-syntax" 354 | version = "0.6.17" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 357 | 358 | [[package]] 359 | name = "rustc-serialize" 360 | version = "0.3.24" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 363 | 364 | [[package]] 365 | name = "ryu" 366 | version = "1.0.4" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" 369 | 370 | [[package]] 371 | name = "serde" 372 | version = "1.0.106" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" 375 | dependencies = [ 376 | "serde_derive", 377 | ] 378 | 379 | [[package]] 380 | name = "serde_derive" 381 | version = "1.0.106" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" 384 | dependencies = [ 385 | "proc-macro2", 386 | "quote", 387 | "syn", 388 | ] 389 | 390 | [[package]] 391 | name = "serde_json" 392 | version = "1.0.51" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" 395 | dependencies = [ 396 | "itoa", 397 | "ryu", 398 | "serde", 399 | ] 400 | 401 | [[package]] 402 | name = "syn" 403 | version = "1.0.18" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" 406 | dependencies = [ 407 | "proc-macro2", 408 | "quote", 409 | "unicode-xid 0.2.0", 410 | ] 411 | 412 | [[package]] 413 | name = "syntex" 414 | version = "0.42.2" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "0a30b08a6b383a22e5f6edc127d169670d48f905bb00ca79a00ea3e442ebe317" 417 | dependencies = [ 418 | "syntex_errors", 419 | "syntex_syntax", 420 | ] 421 | 422 | [[package]] 423 | name = "syntex_errors" 424 | version = "0.42.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "04c48f32867b6114449155b2a82114b86d4b09e1bddb21c47ff104ab9172b646" 427 | dependencies = [ 428 | "libc", 429 | "log 0.3.9", 430 | "rustc-serialize", 431 | "syntex_pos", 432 | "term", 433 | "unicode-xid 0.0.3", 434 | ] 435 | 436 | [[package]] 437 | name = "syntex_pos" 438 | version = "0.42.0" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "3fd49988e52451813c61fecbe9abb5cfd4e1b7bb6cdbb980a6fbcbab859171a6" 441 | dependencies = [ 442 | "rustc-serialize", 443 | ] 444 | 445 | [[package]] 446 | name = "syntex_syntax" 447 | version = "0.42.0" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "7628a0506e8f9666fdabb5f265d0059b059edac9a3f810bda077abb5d826bd8d" 450 | dependencies = [ 451 | "bitflags", 452 | "libc", 453 | "log 0.3.9", 454 | "rustc-serialize", 455 | "syntex_errors", 456 | "syntex_pos", 457 | "term", 458 | "unicode-xid 0.0.3", 459 | ] 460 | 461 | [[package]] 462 | name = "term" 463 | version = "0.4.6" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" 466 | dependencies = [ 467 | "kernel32-sys", 468 | "winapi 0.2.8", 469 | ] 470 | 471 | [[package]] 472 | name = "thread_local" 473 | version = "0.3.6" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 476 | dependencies = [ 477 | "lazy_static", 478 | ] 479 | 480 | [[package]] 481 | name = "time" 482 | version = "0.1.43" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 485 | dependencies = [ 486 | "libc", 487 | "winapi 0.3.8", 488 | ] 489 | 490 | [[package]] 491 | name = "unicode-xid" 492 | version = "0.0.3" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb" 495 | 496 | [[package]] 497 | name = "unicode-xid" 498 | version = "0.2.0" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 501 | 502 | [[package]] 503 | name = "utf8-ranges" 504 | version = "1.0.4" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" 507 | 508 | [[package]] 509 | name = "wasi" 510 | version = "0.9.0+wasi-snapshot-preview1" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 513 | 514 | [[package]] 515 | name = "winapi" 516 | version = "0.2.8" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 519 | 520 | [[package]] 521 | name = "winapi" 522 | version = "0.3.8" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 525 | dependencies = [ 526 | "winapi-i686-pc-windows-gnu", 527 | "winapi-x86_64-pc-windows-gnu", 528 | ] 529 | 530 | [[package]] 531 | name = "winapi-build" 532 | version = "0.1.1" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 535 | 536 | [[package]] 537 | name = "winapi-i686-pc-windows-gnu" 538 | version = "0.4.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 541 | 542 | [[package]] 543 | name = "winapi-x86_64-pc-windows-gnu" 544 | version = "0.4.0" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 547 | 548 | [[package]] 549 | name = "ws2_32-sys" 550 | version = "0.2.1" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 553 | dependencies = [ 554 | "winapi 0.2.8", 555 | "winapi-build", 556 | ] 557 | -------------------------------------------------------------------------------- /jumpingsumo-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jumpingsumo-rs" 3 | version = "0.0.2" 4 | authors = ["o0Ignition0o "] 5 | edition = "2018" 6 | description = "parrot jumping sumo in rust" 7 | license = "MIT/Apache-2.0" 8 | keywords = ["drone", "parrot", "jumping", "sumo"] 9 | 10 | [dependencies] 11 | anyhow = "1.0" 12 | arsdk-rs = { path = "../arsdk-rs" } 13 | chrono = "0.4" 14 | -------------------------------------------------------------------------------- /jumpingsumo-rs/LICENSE_APACHE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Jeremy Lempereur 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /jumpingsumo-rs/LICENSE_MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Jeremy Lempereur 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /jumpingsumo-rs/examples/jump.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use jumpingsumo_rs::prelude::*; 4 | 5 | fn main() -> Result<(), Box> { 6 | let drone_ip: std::net::IpAddr = "192.168.2.1".parse()?; 7 | 8 | let js = JumpingSumo::connect(drone_ip.into())?; 9 | 10 | std::thread::sleep(std::time::Duration::from_secs(2)); 11 | 12 | js.jump()?; 13 | 14 | std::thread::sleep(std::time::Duration::from_secs(3)); 15 | 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /jumpingsumo-rs/examples/new_sumo.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use jumpingsumo_rs::prelude::*; 4 | 5 | fn main() -> Result<(), Box> { 6 | let drone_ip: std::net::IpAddr = "192.168.2.1".parse()?; 7 | 8 | let js = JumpingSumo::connect(drone_ip.into())?; 9 | 10 | js.forward()?; 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /jumpingsumo-rs/examples/spin.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use jumpingsumo_rs::prelude::*; 4 | 5 | fn main() -> Result<(), Box> { 6 | let drone_ip: std::net::IpAddr = "192.168.2.1".parse()?; 7 | 8 | let js = JumpingSumo::connect(drone_ip.into())?; 9 | 10 | loop { 11 | std::thread::sleep(std::time::Duration::from_secs(2)); 12 | let turn_right = PilotState { 13 | flag: true, 14 | speed: 0, 15 | turn: i8::MAX, 16 | }; 17 | js.drive(turn_right)?; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /jumpingsumo-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | use arsdk_rs::{ 2 | command::Feature::JumpingSumo as JumpingSumoFeature, 3 | frame::{BufferID, Frame, Type as FrameType}, 4 | jumping_sumo::PilotState, 5 | jumping_sumo::{Anim, Class::*, PilotingID::*}, 6 | Config, ConnectionError, Drone, Error, 7 | }; 8 | 9 | pub mod prelude { 10 | pub use crate::JumpingSumo; 11 | pub use arsdk_rs::{jumping_sumo::PilotState, prelude::*}; 12 | } 13 | 14 | pub struct JumpingSumo { 15 | drone: Drone, 16 | } 17 | 18 | const TURN_ANGLE: i8 = 30; 19 | const FORWARD_SPEED: i8 = 100; 20 | 21 | impl JumpingSumo { 22 | pub fn connect(config: Config) -> Result { 23 | Ok(Self { 24 | drone: Drone::connect(config)?, 25 | }) 26 | } 27 | 28 | pub fn forward(&self) -> Result<(), Error> { 29 | self.drive(PilotState { 30 | flag: true, 31 | speed: FORWARD_SPEED, 32 | turn: 0, 33 | }) 34 | } 35 | 36 | pub fn backwards(&self) -> Result<(), Error> { 37 | self.drive(PilotState { 38 | flag: true, 39 | speed: -FORWARD_SPEED, 40 | turn: 0, 41 | }) 42 | } 43 | 44 | pub fn turn_left(&self) -> Result<(), Error> { 45 | self.drive(PilotState { 46 | flag: true, 47 | speed: 0, 48 | turn: -TURN_ANGLE, 49 | }) 50 | } 51 | 52 | pub fn turn_right(&self) -> Result<(), Error> { 53 | self.drive(PilotState { 54 | flag: true, 55 | speed: 0, 56 | turn: TURN_ANGLE, 57 | }) 58 | } 59 | 60 | pub fn stop(&self) -> Result<(), Error> { 61 | self.drive(PilotState { 62 | flag: false, 63 | speed: 0, 64 | turn: 0, 65 | }) 66 | } 67 | 68 | pub fn drive(&self, state: PilotState) -> Result<(), Error> { 69 | let feature = JumpingSumoFeature(Piloting(Pilot(state))); 70 | let frame = Frame::for_drone( 71 | &self.drone, 72 | FrameType::Data, 73 | BufferID::CDNonAck, 74 | Some(feature), 75 | ); 76 | 77 | self.drone.send_frame(frame) 78 | } 79 | 80 | pub fn jump(&self) -> Result<(), Error> { 81 | let feature = JumpingSumoFeature(Animations(Anim::Jump)); 82 | let frame = Frame::for_drone( 83 | &self.drone, 84 | FrameType::DataWithAck, 85 | BufferID::CDAck, 86 | Some(feature), 87 | ); 88 | 89 | self.drone.send_frame(frame) 90 | } 91 | } 92 | --------------------------------------------------------------------------------