1 // Copyright 2022 Google LLC
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 // https://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.
14
15 use anyhow::Result;
16 use clap::Parser;
17 use env_logger::Env;
18 use pica::{Pica, PicaCommand};
19 use std::net::{Ipv4Addr, SocketAddrV4};
20 use std::path::PathBuf;
21 use tokio::net::TcpListener;
22 use tokio::sync::mpsc;
23 use tokio::try_join;
24
25 const DEFAULT_UCI_PORT: u16 = 7000;
26
accept_incoming(cmd_tx: mpsc::Sender<PicaCommand>, uci_port: u16) -> Result<()>27 async fn accept_incoming(cmd_tx: mpsc::Sender<PicaCommand>, uci_port: u16) -> Result<()> {
28 let uci_socket = SocketAddrV4::new(Ipv4Addr::LOCALHOST, uci_port);
29 let uci_listener = TcpListener::bind(uci_socket).await?;
30 log::info!("Pica: Listening on: {}", uci_port);
31
32 loop {
33 let (socket, addr) = uci_listener.accept().await?;
34 log::info!("Uwb host addr: {}", addr);
35
36 let (read_half, write_half) = socket.into_split();
37 let stream = Box::pin(futures::stream::unfold(read_half, pica::packets::uci::read));
38 let sink = Box::pin(futures::sink::unfold(write_half, pica::packets::uci::write));
39
40 cmd_tx
41 .send(PicaCommand::Connect(stream, sink))
42 .await
43 .map_err(|_| anyhow::anyhow!("pica command stream closed"))?
44 }
45 }
46
47 #[derive(Parser, Debug)]
48 #[command(name = "pica", about = "Virtual UWB subsystem")]
49 struct Args {
50 /// Output directory for storing .pcapng traces.
51 /// If provided, .pcapng traces of client connections are automatically
52 /// saved under the name `device-{handle}.pcapng`.
53 #[arg(short, long, value_name = "PCAPNG_DIR")]
54 pcapng_dir: Option<PathBuf>,
55 /// Configure the TCP port for the UCI server.
56 #[arg(short, long, value_name = "UCI_PORT", default_value_t = DEFAULT_UCI_PORT)]
57 uci_port: u16,
58 }
59
60 struct MockRangingEstimator();
61
62 /// The position cannot be communicated to the pica environment when
63 /// using the default binary (HTTP interface not available).
64 /// Thus the ranging estimator cannot produce any result.
65 impl pica::RangingEstimator for MockRangingEstimator {
estimate( &self, _left: &pica::Handle, _right: &pica::Handle, ) -> Option<pica::RangingMeasurement>66 fn estimate(
67 &self,
68 _left: &pica::Handle,
69 _right: &pica::Handle,
70 ) -> Option<pica::RangingMeasurement> {
71 Some(Default::default())
72 }
73 }
74
75 #[tokio::main]
main() -> Result<()>76 async fn main() -> Result<()> {
77 env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init();
78
79 let args = Args::parse();
80
81 let pica = Pica::new(Box::new(MockRangingEstimator()), args.pcapng_dir);
82 let commands = pica.commands();
83
84 try_join!(accept_incoming(commands.clone(), args.uci_port), pica.run(),)?;
85
86 Ok(())
87 }
88