Merge tag 'v0.1.1' into HEAD am: b2e8fdebd7 am: bb8118c127
Original change: https://android-review.googlesource.com/c/platform/external/rust/pica/+/2488375
Change-Id: Iaef527b4ef51e23f5ea81a2a692c78f42e59c38a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..e422ea1
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,34 @@
+# Build, test and check the code against the linter and clippy
+name: Build, Test, Format and Clippy
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ build_and_test:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [macos-latest, ubuntu-latest, windows-latest]
+ steps:
+ - uses: actions/checkout@v3
+ - name: Install Rust 1.67.1
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: 1.67.1
+ override: true
+ components: rustfmt, clippy
+ - name: Build
+ run: cargo build
+ - name: Test
+ run: cargo test -- --skip uci_packets
+ - name: Fmt
+ run: cargo fmt --check --quiet
+ - name: Clippy
+ run: cargo clippy --no-deps -- --deny warnings
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bdbc135
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+__pycache__
+target/
diff --git a/Cargo.lock b/Cargo.lock
index 37936d2..4e1828a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,32 +3,12 @@
version = 3
[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
-[[package]]
name = "anyhow"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi",
- "libc",
- "winapi",
-]
-
-[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -47,6 +27,12 @@
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -54,17 +40,60 @@
[[package]]
name = "clap"
-version = "2.34.0"
+version = "4.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
dependencies = [
- "ansi_term",
- "atty",
"bitflags",
+ "clap_derive",
+ "clap_lex",
+ "is-terminal",
+ "once_cell",
"strsim",
- "textwrap",
- "unicode-width",
- "vec_map",
+ "termcolor",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
]
[[package]]
@@ -120,12 +149,9 @@
[[package]]
name = "heck"
-version = "0.3.3"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
@@ -137,6 +163,12 @@
]
[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -200,6 +232,28 @@
]
[[package]]
+name = "io-lifetimes"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
+dependencies = [
+ "libc",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -213,9 +267,15 @@
[[package]]
name = "libc"
-version = "0.2.121"
+version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "lock_api"
@@ -299,15 +359,21 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.1.19",
"libc",
]
[[package]]
name = "once_cell"
-version = "1.10.0"
+version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "parking_lot"
@@ -329,7 +395,7 @@
"libc",
"redox_syscall",
"smallvec",
- "windows-sys",
+ "windows-sys 0.32.0",
]
[[package]]
@@ -338,6 +404,7 @@
dependencies = [
"anyhow",
"bytes",
+ "clap",
"glam",
"hex",
"hyper",
@@ -345,7 +412,6 @@
"num-traits",
"serde",
"serde_json",
- "structopt",
"thiserror",
"tokio",
"tokio-stream",
@@ -389,11 +455,11 @@
[[package]]
name = "proc-macro2"
-version = "1.0.36"
+version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
dependencies = [
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
@@ -415,6 +481,20 @@
]
[[package]]
+name = "rustix"
+version = "0.36.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -484,33 +564,9 @@
[[package]]
name = "strsim"
-version = "0.8.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
-
-[[package]]
-name = "structopt"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
-dependencies = [
- "clap",
- "lazy_static",
- "structopt-derive",
-]
-
-[[package]]
-name = "structopt-derive"
-version = "0.4.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
-dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
@@ -524,12 +580,12 @@
]
[[package]]
-name = "textwrap"
-version = "0.11.0"
+name = "termcolor"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
- "unicode-width",
+ "winapi-util",
]
[[package]]
@@ -554,16 +610,15 @@
[[package]]
name = "tokio"
-version = "1.17.0"
+version = "1.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
+checksum = "0e050c618355082ae5a89ec63bbf897225d5ffe84c7c4e036874e4d185a5044e"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
- "once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
@@ -642,16 +697,10 @@
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
-name = "unicode-segmentation"
-version = "1.9.0"
+name = "unicode-ident"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-xid"
@@ -660,12 +709,6 @@
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
-
-[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -704,6 +747,15 @@
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -715,39 +767,105 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
dependencies = [
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_msvc",
+ "windows_aarch64_msvc 0.32.0",
+ "windows_i686_gnu 0.32.0",
+ "windows_i686_msvc 0.32.0",
+ "windows_x86_64_gnu 0.32.0",
+ "windows_x86_64_msvc 0.32.0",
]
[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.42.1",
+ "windows_i686_gnu 0.42.1",
+ "windows_i686_msvc 0.42.1",
+ "windows_x86_64_gnu 0.42.1",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.42.1",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
+
+[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
+
+[[package]]
name = "windows_i686_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
[[package]]
+name = "windows_i686_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
+
+[[package]]
name = "windows_i686_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
[[package]]
+name = "windows_i686_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
+
+[[package]]
name = "windows_x86_64_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
+
+[[package]]
name = "windows_x86_64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
diff --git a/Cargo.toml b/Cargo.toml
index da3611e..7b2582b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,13 +6,25 @@
"David Duarte",
"Henri Chataing",
]
-version = "0.1.0"
+version = "0.1.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[lib]
+name = "pica"
+path = "src/lib.rs"
+
+[[bin]]
+name = "server"
+path = "src/bin/server/mod.rs"
+
+[features]
+default = ["web"]
+web = ["hyper"]
+
[dependencies]
-tokio = { version = "1.17.0", features = ["full"] }
+tokio = { version = "1.18.5", features = ["full"] }
tokio-stream = { version = "0.1.8", features = ["sync"] }
bytes = "1"
anyhow = "1.0.56"
@@ -20,8 +32,8 @@
num-traits = "*"
thiserror = "*"
glam = "0.20.3"
-hyper = { version = "0.14", features = ["server", "stream", "http1", "tcp"] }
+hyper = { version = "0.14", features = ["server", "stream", "http1", "tcp"], optional = true }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
-structopt = "0.3.23"
hex = "0.4.3"
+clap = { version = "4.1.8", features = ["derive"] }
diff --git a/README.md b/README.md
index 8a0ca22..aeee11e 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
- Pica provides HTTP commands to interact with the scene directly such as create and destroy
virtual anchors.
-# Build and run
+# Build and Run
```bash
$> git clone https://github.com/google/pica.git
@@ -20,7 +20,7 @@
$> cargo run
```
-You should have the following output:
+You should receive the following output:
```
Pica: Listening on: 7000
diff --git a/scripts/__pycache__/uci_packets.cpython-310.pyc b/scripts/__pycache__/uci_packets.cpython-310.pyc
deleted file mode 100644
index 9487b58..0000000
--- a/scripts/__pycache__/uci_packets.cpython-310.pyc
+++ /dev/null
Binary files differ
diff --git a/scripts/console.py b/scripts/console.py
index 644b6ff..c06400a 100755
--- a/scripts/console.py
+++ b/scripts/console.py
@@ -28,9 +28,6 @@
from concurrent.futures import ThreadPoolExecutor
import uci_packets
-MAX_PAYLOAD_SIZE = 1024
-
-
def encode_position(x: int, y: int, z: int, yaw: int, pitch: int, roll: int) -> bytes:
return (struct.pack('<h', x)
+ struct.pack('<h', y)
diff --git a/src/bin/server/mod.rs b/src/bin/server/mod.rs
new file mode 100644
index 0000000..bfa0bdb
--- /dev/null
+++ b/src/bin/server/mod.rs
@@ -0,0 +1,86 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+extern crate bytes;
+extern crate num_derive;
+extern crate num_traits;
+extern crate thiserror;
+
+#[cfg(feature = "web")]
+mod web;
+
+use anyhow::Result;
+use clap::Parser;
+use pica::{Pica, PicaCommand};
+use std::net::{Ipv4Addr, SocketAddrV4};
+use std::path::PathBuf;
+use tokio::net::TcpListener;
+use tokio::sync::{broadcast, mpsc};
+use tokio::try_join;
+
+const DEFAULT_UCI_PORT: u16 = 7000;
+const DEFAULT_WEB_PORT: u16 = 3000;
+
+async fn accept_incoming(tx: mpsc::Sender<PicaCommand>, uci_port: u16) -> Result<()> {
+ let uci_socket = SocketAddrV4::new(Ipv4Addr::LOCALHOST, uci_port);
+ let uci_listener = TcpListener::bind(uci_socket).await?;
+ println!("Pica: Listening on: {}", uci_port);
+
+ loop {
+ let (socket, addr) = uci_listener.accept().await?;
+ println!("Uwb host addr: {}", addr);
+ tx.send(PicaCommand::Connect(socket)).await?
+ }
+}
+
+#[derive(Parser, Debug)]
+#[command(name = "pica", about = "Virtual UWB subsystem")]
+struct Args {
+ /// Output directory for storing .pcapng traces.
+ /// If provided, .pcapng traces of client connections are automatically
+ /// saved under the name `device-{handle}.pcapng`.
+ #[arg(short, long, value_name = "PCAPNG_DIR")]
+ pcapng_dir: Option<PathBuf>,
+ /// Configure the TCP port for the UCI server.
+ #[arg(short, long, value_name = "UCI_PORT", default_value_t = DEFAULT_UCI_PORT)]
+ uci_port: u16,
+ /// Configure the HTTP port for the web interface.
+ #[arg(short, long, value_name = "WEB_PORT", default_value_t = DEFAULT_WEB_PORT)]
+ web_port: u16,
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+ let args = Args::parse();
+ assert_ne!(
+ args.uci_port, args.web_port,
+ "UCI port and Web port shall be different."
+ );
+ let (event_tx, _) = broadcast::channel(16);
+
+ let mut pica = Pica::new(event_tx.clone(), args.pcapng_dir);
+ let pica_tx = pica.tx();
+
+ #[cfg(feature = "web")]
+ try_join!(
+ accept_incoming(pica_tx.clone(), args.uci_port),
+ pica.run(),
+ web::serve(pica_tx, event_tx, args.web_port)
+ )?;
+
+ #[cfg(not(feature = "web"))]
+ try_join!(accept_incoming(pica_tx.clone(), args.uci_port), pica.run(),)?;
+
+ Ok(())
+}
diff --git a/src/web.rs b/src/bin/server/web.rs
similarity index 79%
rename from src/web.rs
rename to src/bin/server/web.rs
index 5744c8b..0af876f 100644
--- a/src/web.rs
+++ b/src/bin/server/web.rs
@@ -23,38 +23,37 @@
use tokio::sync::{broadcast, mpsc, oneshot};
use tokio_stream::{wrappers::BroadcastStream, StreamExt};
-use crate::position::Position;
-use crate::{Anchor, MacAddress, PicaCommand, PicaCommandError, PicaCommandStatus, PicaEvent};
+use pica::{
+ Category, MacAddress, PicaCommand, PicaCommandError, PicaCommandStatus, PicaEvent, Position,
+};
use PicaEvent::{DeviceAdded, DeviceRemoved, DeviceUpdated, NeighborUpdated};
-const WEB_PORT: u16 = 3000;
-
const STATIC_FILES: &[(&str, &str, &str)] = &[
- ("/", "text/html", include_str!("../static/index.html")),
+ ("/", "text/html", include_str!("../../../static/index.html")),
(
"/openapi",
"text/html",
- include_str!("../static/openapi.html"),
+ include_str!("../../../static/openapi.html"),
),
(
"/openapi.yaml",
"text/yaml",
- include_str!("../static/openapi.yaml"),
+ include_str!("../../../static/openapi.yaml"),
),
(
"/src/components/Map.js",
"application/javascript",
- include_str!("../static/src/components/Map.js"),
+ include_str!("../../../static/src/components/Map.js"),
),
(
"/src/components/DeviceInfo.js",
"application/javascript",
- include_str!("../static/src/components/DeviceInfo.js"),
+ include_str!("../../../static/src/components/DeviceInfo.js"),
),
(
"/src/components/Orientation.js",
"application/javascript",
- include_str!("../static/src/components/Orientation.js"),
+ include_str!("../../../static/src/components/Orientation.js"),
),
];
@@ -101,40 +100,14 @@
};
}
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum Category {
- Uci,
- Anchor,
-}
-
#[derive(Debug, Serialize, Clone)]
-pub struct Device {
+struct Device {
pub category: Category,
pub mac_address: String,
#[serde(flatten)]
pub position: Position,
}
-impl Device {
- pub fn new(category: Category, mac_address: MacAddress, position: Position) -> Self {
- Self {
- category,
- mac_address: mac_address.to_string(),
- position,
- }
- }
-}
-
-impl From<Anchor> for Device {
- fn from(anchor: Anchor) -> Self {
- Self {
- category: Category::Anchor,
- mac_address: anchor.mac_address.to_string(),
- position: anchor.position,
- }
- }
-}
-
fn event_name(event: &PicaEvent) -> &'static str {
match event {
DeviceAdded { .. } => "device-added",
@@ -168,16 +141,23 @@
tx.send(pica_cmd).await.unwrap();
let (status, description) = match pica_cmd_rsp_rx.await {
Ok(Ok(_)) => (HttpStatusCode::OK, "success".into()),
- Ok(Err(err)) => (match err {
- PicaCommandError::DeviceAlreadyExists(_) => HttpStatusCode::CONFLICT,
- PicaCommandError::DeviceNotFound(_) => HttpStatusCode::NOT_FOUND,
- }, format!("{}", err)),
- Err(err) =>
- (HttpStatusCode::INTERNAL_SERVER_ERROR,
- format!("Error getting command response: {}", err))
+ Ok(Err(err)) => (
+ match err {
+ PicaCommandError::DeviceAlreadyExists(_) => HttpStatusCode::CONFLICT,
+ PicaCommandError::DeviceNotFound(_) => HttpStatusCode::NOT_FOUND,
+ },
+ format!("{}", err),
+ ),
+ Err(err) => (
+ HttpStatusCode::INTERNAL_SERVER_ERROR,
+ format!("Error getting command response: {}", err),
+ ),
};
println!(" status: {}, {}", status, description);
- Response::builder().status(status).body(description.into()).unwrap()
+ Response::builder()
+ .status(status)
+ .body(description.into())
+ .unwrap()
};
match req
@@ -239,10 +219,19 @@
devices: Vec<Device>,
}
println!("PicaCommand: GetState");
- let (state_tx, state_rx) = oneshot::channel::<Vec<Device>>();
+ let (state_tx, state_rx) = oneshot::channel::<Vec<_>>();
tx.send(PicaCommand::GetState(state_tx)).await.unwrap();
let devices = match state_rx.await {
- Ok(devices) => GetStateResponse { devices },
+ Ok(devices) => GetStateResponse {
+ devices: devices
+ .into_iter()
+ .map(|(category, mac_address, position)| Device {
+ category,
+ mac_address: mac_address.into(),
+ position,
+ })
+ .collect(),
+ },
Err(_) => GetStateResponse { devices: vec![] },
};
let body = serde_json::to_string(&devices).unwrap();
@@ -258,8 +247,9 @@
pub async fn serve(
tx: mpsc::Sender<PicaCommand>,
events: broadcast::Sender<PicaEvent>,
+ web_port: u16,
) -> Result<()> {
- let addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, WEB_PORT);
+ let addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, web_port);
let make_svc = make_service_fn(move |_conn| {
let tx = tx.clone();
@@ -273,7 +263,7 @@
let server = Server::bind(&addr.into()).serve(make_svc);
- println!("Pica: Web server started on http://0.0.0.0:{}", WEB_PORT);
+ println!("Pica: Web server started on http://0.0.0.0:{}", web_port);
server.await.context("Web Server Error")
}
diff --git a/src/lib.rs b/src/lib.rs
index 66e07f7..e1ed9b7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,7 +14,7 @@
use anyhow::Result;
use bytes::{Bytes, BytesMut};
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::Display;
use std::path::PathBuf;
@@ -28,7 +28,7 @@
mod pcapng;
mod position;
-use position::Position;
+pub use position::Position;
mod uci_packets;
use uci_packets::StatusCode as UciStatusCode;
@@ -40,13 +40,13 @@
mod session;
use session::MAX_SESSION;
-pub mod web;
-use web::Category;
+mod mac_address;
+pub use mac_address::MacAddress;
-pub mod mac_address;
-use mac_address::MacAddress;
-
-const MAX_PAYLOAD_SIZE: usize = 4096;
+// UCI Generic Specification v1.1.0 § 4.4
+const HEADER_SIZE: usize = 4;
+const MAX_PAYLOAD_SIZE: usize = 255;
+const MAX_PACKET_SIZE: usize = HEADER_SIZE + MAX_PAYLOAD_SIZE;
struct Connection {
socket: TcpStream,
@@ -58,7 +58,7 @@
fn new(socket: TcpStream, pcapng_file: Option<pcapng::File>) -> Self {
Connection {
socket,
- buffer: BytesMut::with_capacity(MAX_PAYLOAD_SIZE),
+ buffer: BytesMut::with_capacity(MAX_PACKET_SIZE),
pcapng_file,
}
}
@@ -118,7 +118,7 @@
// Destroy Anchor
DestroyAnchor(MacAddress, oneshot::Sender<PicaCommandStatus>),
// Get State
- GetState(oneshot::Sender<Vec<web::Device>>),
+ GetState(oneshot::Sender<Vec<(Category, MacAddress, Position)>>),
}
impl Display for PicaCommand {
@@ -143,25 +143,40 @@
pub enum PicaEvent {
// A Device was added
DeviceAdded {
- device: web::Device,
+ category: Category,
+ mac_address: MacAddress,
+ #[serde(flatten)]
+ position: Position,
},
// A Device was removed
DeviceRemoved {
- device: web::Device,
+ category: Category,
+ mac_address: MacAddress,
},
// A Device position has changed
DeviceUpdated {
- device: web::Device,
+ category: Category,
+ mac_address: MacAddress,
+ #[serde(flatten)]
+ position: Position,
},
NeighborUpdated {
- source_device: web::Device,
- destination_device: web::Device,
+ source_category: Category,
+ source_mac_address: MacAddress,
+ destination_category: Category,
+ destination_mac_address: MacAddress,
distance: u16,
azimuth: i16,
elevation: i8,
},
}
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+pub enum Category {
+ Uci,
+ Anchor,
+}
+
#[derive(Debug, Clone, Copy)]
struct Anchor {
mac_address: MacAddress,
@@ -298,7 +313,9 @@
device.init();
self.send_event(PicaEvent::DeviceAdded {
- device: web::Device::new(Category::Uci, device.mac_address, device.position),
+ category: Category::Uci,
+ mac_address: device.mac_address,
+ position: device.position,
});
self.devices.insert(device_handle, device);
@@ -357,7 +374,8 @@
{
Ok(device) => {
self.send_event(PicaEvent::DeviceRemoved {
- device: web::Device::new(Category::Uci, device.mac_address, device.position),
+ category: Category::Uci,
+ mac_address: device.mac_address,
});
self.devices.remove(&device_handle);
}
@@ -417,7 +435,7 @@
// TODO: support extended address
ShortMacTwoWayRangeDataNtfBuilder {
sequence_number: session.sequence_number,
- session_id: session_id as u32,
+ session_id,
rcr_indicator: 0, //TODO
current_ranging_interval: 0, //TODO
two_way_ranging_measurements: measurements,
@@ -448,9 +466,11 @@
{
Ok(device) => {
let response = device.command(cmd).into();
- device.tx.send(response).await.unwrap_or_else(|err| {
- println!("Failed to send UCI command response: {}", err)
- });
+ device
+ .tx
+ .send(response)
+ .await
+ .unwrap_or_else(|err| println!("Failed to send UCI command response: {}", err));
}
Err(err) => println!("{}", err),
}
@@ -549,13 +569,15 @@
}
};
self.send_event(PicaEvent::DeviceUpdated {
- device: web::Device::new(category, mac_address, position),
+ category,
+ mac_address,
+ position,
});
let devices = self.devices.values().map(|d| (d.mac_address, d.position));
let anchors = self.anchors.values().map(|b| (b.mac_address, b.position));
- let update_neighbors = |category, device_mac_address, device_position| {
+ let update_neighbors = |device_category, device_mac_address, device_position| {
if mac_address != device_mac_address {
let local = position.compute_range_azimuth_elevation(&device_position);
let remote = device_position.compute_range_azimuth_elevation(&position);
@@ -563,20 +585,20 @@
assert!(local.0 == remote.0);
self.send_event(PicaEvent::NeighborUpdated {
- source_device: web::Device::new(category, mac_address, position),
- destination_device: web::Device::new(
- category,
- device_mac_address,
- device_position,
- ),
+ source_category: category,
+ source_mac_address: mac_address,
+ destination_category: device_category,
+ destination_mac_address: device_mac_address,
distance: local.0,
azimuth: local.1,
elevation: local.2,
});
self.send_event(PicaEvent::NeighborUpdated {
- source_device: web::Device::new(category, device_mac_address, device_position),
- destination_device: web::Device::new(category, mac_address, position),
+ source_category: device_category,
+ source_mac_address: device_mac_address,
+ destination_category: category,
+ destination_mac_address: mac_address,
distance: remote.0,
azimuth: remote.1,
elevation: remote.2,
@@ -601,7 +623,9 @@
Err(PicaCommandError::DeviceAlreadyExists(mac_address))
} else {
self.send_event(PicaEvent::DeviceAdded {
- device: web::Device::new(Category::Anchor, mac_address, position),
+ category: Category::Anchor,
+ mac_address,
+ position,
});
assert!(self
.anchors
@@ -633,7 +657,8 @@
Err(PicaCommandError::DeviceNotFound(mac_address))
} else {
self.send_event(PicaEvent::DeviceRemoved {
- device: web::Device::new(Category::Anchor, mac_address, Position::default()),
+ category: Category::Anchor,
+ mac_address,
});
Ok(())
};
@@ -642,17 +667,21 @@
})
}
- fn get_state(&self, state_tx: oneshot::Sender<Vec<web::Device>>) {
+ fn get_state(&self, state_tx: oneshot::Sender<Vec<(Category, MacAddress, Position)>>) {
println!("[_] Get State");
- let web_devices: Vec<web::Device> = self
- .anchors
- .iter()
- .map(|(_, anchor)| web::Device::from(*anchor))
- .chain(self.devices.iter().map(|(_, uci_device)| {
- web::Device::new(Category::Uci, uci_device.mac_address, uci_device.position)
- }))
- .collect();
- state_tx.send(web_devices).unwrap();
+ state_tx
+ .send(
+ self.anchors
+ .values()
+ .map(|anchor| (Category::Anchor, anchor.mac_address, anchor.position))
+ .chain(
+ self.devices
+ .values()
+ .map(|device| (Category::Uci, device.mac_address, device.position)),
+ )
+ .collect(),
+ )
+ .unwrap();
}
}
diff --git a/src/mac_address.rs b/src/mac_address.rs
index 768bb6d..cc9d2c4 100644
--- a/src/mac_address.rs
+++ b/src/mac_address.rs
@@ -15,6 +15,7 @@
use std::fmt::Display;
use hex::FromHex;
+use serde::{Deserialize, Serialize};
use thiserror::Error;
const SHORT_MAC_ADDRESS_SIZE: usize = 2;
@@ -28,7 +29,8 @@
#[error("MacAddress has the wrong format: 0")]
MacAddressWrongFormat(String),
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[serde(try_from = "String", into = "String")]
pub enum MacAddress {
Short([u8; SHORT_MAC_ADDRESS_SIZE]),
Extend([u8; EXTEND_MAC_ADDRESS_SIZE]),
@@ -36,6 +38,19 @@
impl MacAddress {
pub fn new(mac_address: String) -> Result<Self, Error> {
+ mac_address.try_into()
+ }
+}
+
+impl From<usize> for MacAddress {
+ fn from(device_handle: usize) -> Self {
+ MacAddress::Extend(device_handle.to_be_bytes())
+ }
+}
+
+impl TryFrom<String> for MacAddress {
+ type Error = Error;
+ fn try_from(mac_address: String) -> std::result::Result<Self, Error> {
let mac_address = mac_address.replace(':', "");
let mac_address = mac_address.replace("%3A", "");
let uwb_mac_address = match mac_address.len() {
@@ -53,14 +68,8 @@
}
}
-impl From<usize> for MacAddress {
- fn from(device_handle: usize) -> Self {
- MacAddress::Extend(device_handle.to_be_bytes())
- }
-}
-
-impl Display for MacAddress {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl From<&MacAddress> for String {
+ fn from(mac_address: &MacAddress) -> Self {
let to_string = |addr: &[u8]| -> String {
let mac_address: Vec<_> = addr.iter().map(|byte| format!("{:02X}:", byte)).collect();
let s = mac_address
@@ -69,13 +78,25 @@
.collect::<String>();
s.trim_end_matches(':').into()
};
- match *self {
- MacAddress::Short(address) => write!(f, "{}", to_string(&address)),
- MacAddress::Extend(address) => write!(f, "{}", to_string(&address)),
+ match mac_address {
+ MacAddress::Short(address) => to_string(address),
+ MacAddress::Extend(address) => to_string(address),
}
}
}
+impl From<MacAddress> for String {
+ fn from(mac_address: MacAddress) -> Self {
+ String::from(&mac_address)
+ }
+}
+
+impl Display for MacAddress {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", String::from(self))
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index f789d57..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-extern crate bytes;
-extern crate num_derive;
-extern crate num_traits;
-extern crate thiserror;
-
-use pica::{web, Pica, PicaCommand};
-use std::path::PathBuf;
-
-use anyhow::Result;
-use std::net::{Ipv4Addr, SocketAddrV4};
-use structopt::StructOpt;
-use tokio::net::TcpListener;
-use tokio::sync::{broadcast, mpsc};
-use tokio::try_join;
-
-const UCI_PORT: u16 = 7000;
-
-async fn accept_incoming(tx: mpsc::Sender<PicaCommand>) -> Result<()> {
- let uci_socket = SocketAddrV4::new(Ipv4Addr::LOCALHOST, UCI_PORT);
- let uci_listener = TcpListener::bind(uci_socket).await?;
- println!("Pica: Listening on: {}", UCI_PORT);
-
- loop {
- let (socket, addr) = uci_listener.accept().await?;
- println!("Uwb host addr: {}", addr);
- tx.send(PicaCommand::Connect(socket)).await?
- }
-}
-
-#[derive(Debug, StructOpt)]
-#[structopt(name = "pica", about = "Virtual UWB subsystem")]
-struct Opts {
- /// Output directory for storing .pcapng traces.
- /// If provided, .pcapng traces of client connections are automatically
- /// saved under the name `device-{handle}.pcapng`.
- #[structopt(short, long, parse(from_os_str))]
- pcapng_dir: Option<PathBuf>,
-}
-
-#[tokio::main]
-async fn main() -> Result<()> {
- let opts = Opts::from_args();
- let (event_tx, _) = broadcast::channel(16);
-
- let mut pica = Pica::new(event_tx.clone(), opts.pcapng_dir);
- let pica_tx = pica.tx();
-
- try_join!(
- accept_incoming(pica_tx.clone()),
- pica.run(),
- web::serve(pica_tx, event_tx)
- )?;
-
- Ok(())
-}
diff --git a/static/index.html b/static/index.html
index f33c5f1..84f83c2 100644
--- a/static/index.html
+++ b/static/index.html
@@ -123,7 +123,7 @@
console.log("Device Added", data);
const {
- device: { mac_address, x, y, z, yaw, pitch, roll },
+ mac_address, x, y, z, yaw, pitch, roll,
} = data;
map.devices = [
...map.devices,
@@ -143,7 +143,7 @@
console.log("Device Removed", data);
const {
- device: { mac_address },
+ mac_address,
} = data;
if (info.device?.mac_address === mac_address) {
info.device = null;
@@ -165,7 +165,7 @@
console.log("Position updated", data);
const {
- device: { mac_address, x, y, z, yaw, pitch, roll },
+ mac_address, x, y, z, yaw, pitch, roll,
} = data;
const device = map.devices.find(
@@ -186,20 +186,20 @@
console.log("Neighbor updated", data);
const {
- source_device: { mac_address: src_mac_address },
- destination_device: { mac_address: dest_mac_address },
+ source_mac_address,
+ destination_mac_address,
distance,
azimuth,
elevation,
} = data;
const device = map.devices.find(
- (device) => device.mac_address === src_mac_address
+ (device) => device.mac_address === source_mac_address
);
const neighbor = device.neighbors.find(
- (device) => device.mac_address == dest_mac_address
- ) || { mac_address: dest_mac_address };
+ (device) => device.mac_address == destination_mac_address
+ ) || { mac_address: destination_mac_address };
neighbor.distance = distance;
neighbor.azimuth = azimuth;
diff --git a/static/openapi.yaml b/static/openapi.yaml
index e1af749..7193836 100644
--- a/static/openapi.yaml
+++ b/static/openapi.yaml
@@ -54,19 +54,23 @@
or an UCI Device as described in the Fira UCI Specification, noted `uci`.
type: object
properties:
- Category:
- description: Represents the device's category, uci or anchor.
- type: string
- enum: [uci, anchor]
- MacAddress:
- description: |
- Valid UWB mac addresses must follow the above format
- * Short Mode: "XX:XX"
- * Extend Mode: "XX:XX:XX:XX:XX:XX:XX:XX"
- where X is an hexadecimal number.
- type: string
- Position:
+ category:
+ $ref: "#/components/schemas/Category"
+ mac_address:
+ $ref: "#/components/schemas/MacAddress"
+ position:
$ref: "#/components/schemas/Position"
+ Category:
+ description: Represents the device's category, uci or anchor.
+ type: string
+ enum: [uci, anchor]
+ MacAddress:
+ description: |
+ Valid UWB mac addresses must follow the above format
+ * Short Mode: "XX:XX"
+ * Extend Mode: "XX:XX:XX:XX:XX:XX:XX:XX"
+ where X is an hexadecimal number.
+ type: string
Position:
description:
The position includes the Cartesian coordinates in cm, and the yaw, pitch, roll angles in degrees.
@@ -214,10 +218,7 @@
const: device-added
description: Device added to the scene
data:
- type: object
- properties:
- device:
- $ref: "#/components/schemas/Device"
+ $ref: "#/components/schemas/Device"
- type: object
properties:
event:
@@ -226,18 +227,17 @@
data:
type: object
properties:
- device:
- $ref: "#/components/schemas/Device"
+ category:
+ $ref: "#/components/schemas/Category"
+ mac_address:
+ $ref: "#/components/schemas/MacAddress"
- type: object
properties:
event:
const: device-updated
description: Device position updated
data:
- type: object
- properties:
- device:
- $ref: "#/components/schemas/Device"
+ $ref: "#/components/schemas/Device"
- type: object
properties:
event:
@@ -246,10 +246,14 @@
data:
type: object
properties:
- source-device:
- $ref: "#/components/schemas/Device"
- destination-device:
- $ref: "#/components/schemas/Device"
+ source_category:
+ $ref: "#/components/schemas/Category"
+ source_mac_address:
+ $ref: "#/components/schemas/MacAddress"
+ destination_category:
+ $ref: "#/components/schemas/Category"
+ destination_mac_address:
+ $ref: "#/components/schemas/MacAddress"
distance:
description: Distance in cm.
type: integer # u16
@@ -258,7 +262,7 @@
azimuth:
description: Azimuth in degrees
type: integer
- minium: -180
+ minimum: -180
maximum: 180
elevation:
description: Elevation is degrees
diff --git a/static/src/components/Map.js b/static/src/components/Map.js
index d1e86c4..e239414 100644
--- a/static/src/components/Map.js
+++ b/static/src/components/Map.js
@@ -66,7 +66,7 @@
if (event.target.classList?.contains("handle")) {
this.changingElevation = true;
} else {
- const element = event.path.find((el) => el.classList?.contains("marker"));
+ const element = event.composedPath().find((el) => el.classList?.contains("marker"));
if (element) {
const key = element.getAttribute("key");
this.selected = this.devices[key];