xref: /aosp_15_r20/tools/netsim/rust/cli/src/args.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2022 Google LLC
2*cf78ab8cSAndroid Build Coastguard Worker //
3*cf78ab8cSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*cf78ab8cSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*cf78ab8cSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*cf78ab8cSAndroid Build Coastguard Worker //
7*cf78ab8cSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*cf78ab8cSAndroid Build Coastguard Worker //
9*cf78ab8cSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*cf78ab8cSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*cf78ab8cSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*cf78ab8cSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*cf78ab8cSAndroid Build Coastguard Worker // limitations under the License.
14*cf78ab8cSAndroid Build Coastguard Worker 
15*cf78ab8cSAndroid Build Coastguard Worker use crate::ffi::frontend_client_ffi::{FrontendClient, GrpcMethod};
16*cf78ab8cSAndroid Build Coastguard Worker use clap::builder::{PossibleValue, TypedValueParser};
17*cf78ab8cSAndroid Build Coastguard Worker use clap::{Args, Parser, Subcommand, ValueEnum};
18*cf78ab8cSAndroid Build Coastguard Worker use hex::{decode as hex_to_bytes, FromHexError};
19*cf78ab8cSAndroid Build Coastguard Worker use netsim_common::util::time_display::TimeDisplay;
20*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::common::ChipKind;
21*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::frontend;
22*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::frontend::patch_capture_request::PatchCapture as PatchCaptureProto;
23*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::frontend::patch_device_request::PatchDeviceFields as PatchDeviceFieldsProto;
24*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::model::chip::ble_beacon::advertise_settings::{
25*cf78ab8cSAndroid Build Coastguard Worker     AdvertiseMode as AdvertiseModeProto, AdvertiseTxPower as AdvertiseTxPowerProto,
26*cf78ab8cSAndroid Build Coastguard Worker     Interval as IntervalProto, Tx_power as TxPowerProto,
27*cf78ab8cSAndroid Build Coastguard Worker };
28*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::model::chip::ble_beacon::{
29*cf78ab8cSAndroid Build Coastguard Worker     AdvertiseData as AdvertiseDataProto, AdvertiseSettings as AdvertiseSettingsProto,
30*cf78ab8cSAndroid Build Coastguard Worker };
31*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::model::chip::{
32*cf78ab8cSAndroid Build Coastguard Worker     BleBeacon as Chip_Ble_Beacon, Bluetooth as Chip_Bluetooth, Chip as Chip_Type,
33*cf78ab8cSAndroid Build Coastguard Worker     Radio as Chip_Radio,
34*cf78ab8cSAndroid Build Coastguard Worker };
35*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::model::{
36*cf78ab8cSAndroid Build Coastguard Worker     self, chip_create, Chip, ChipCreate as ChipCreateProto, DeviceCreate as DeviceCreateProto,
37*cf78ab8cSAndroid Build Coastguard Worker     Position,
38*cf78ab8cSAndroid Build Coastguard Worker };
39*cf78ab8cSAndroid Build Coastguard Worker use protobuf::{Message, MessageField};
40*cf78ab8cSAndroid Build Coastguard Worker use std::fmt;
41*cf78ab8cSAndroid Build Coastguard Worker use std::iter;
42*cf78ab8cSAndroid Build Coastguard Worker use std::str::FromStr;
43*cf78ab8cSAndroid Build Coastguard Worker use tracing::error;
44*cf78ab8cSAndroid Build Coastguard Worker 
45*cf78ab8cSAndroid Build Coastguard Worker pub type BinaryProtobuf = Vec<u8>;
46*cf78ab8cSAndroid Build Coastguard Worker 
47*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Parser)]
48*cf78ab8cSAndroid Build Coastguard Worker pub struct NetsimArgs {
49*cf78ab8cSAndroid Build Coastguard Worker     #[command(subcommand)]
50*cf78ab8cSAndroid Build Coastguard Worker     pub command: Command,
51*cf78ab8cSAndroid Build Coastguard Worker     /// Set verbose mode
52*cf78ab8cSAndroid Build Coastguard Worker     #[arg(short, long)]
53*cf78ab8cSAndroid Build Coastguard Worker     pub verbose: bool,
54*cf78ab8cSAndroid Build Coastguard Worker     /// Set custom grpc port
55*cf78ab8cSAndroid Build Coastguard Worker     #[arg(short, long)]
56*cf78ab8cSAndroid Build Coastguard Worker     pub port: Option<i32>,
57*cf78ab8cSAndroid Build Coastguard Worker     /// Set netsimd instance number to connect
58*cf78ab8cSAndroid Build Coastguard Worker     #[arg(short, long)]
59*cf78ab8cSAndroid Build Coastguard Worker     pub instance: Option<u16>,
60*cf78ab8cSAndroid Build Coastguard Worker     /// Set vsock cid:port pair
61*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long)]
62*cf78ab8cSAndroid Build Coastguard Worker     pub vsock: Option<String>,
63*cf78ab8cSAndroid Build Coastguard Worker }
64*cf78ab8cSAndroid Build Coastguard Worker 
65*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Subcommand)]
66*cf78ab8cSAndroid Build Coastguard Worker #[command(infer_subcommands = true)]
67*cf78ab8cSAndroid Build Coastguard Worker pub enum Command {
68*cf78ab8cSAndroid Build Coastguard Worker     /// Print Netsim version information
69*cf78ab8cSAndroid Build Coastguard Worker     Version,
70*cf78ab8cSAndroid Build Coastguard Worker     /// Control the radio state of a device
71*cf78ab8cSAndroid Build Coastguard Worker     Radio(Radio),
72*cf78ab8cSAndroid Build Coastguard Worker     /// Set the device location
73*cf78ab8cSAndroid Build Coastguard Worker     Move(Move),
74*cf78ab8cSAndroid Build Coastguard Worker     /// Display device(s) information
75*cf78ab8cSAndroid Build Coastguard Worker     Devices(Devices),
76*cf78ab8cSAndroid Build Coastguard Worker     /// Reset Netsim device scene
77*cf78ab8cSAndroid Build Coastguard Worker     Reset,
78*cf78ab8cSAndroid Build Coastguard Worker     /// Open netsim Web UI
79*cf78ab8cSAndroid Build Coastguard Worker     Gui,
80*cf78ab8cSAndroid Build Coastguard Worker     /// Control the packet capture functionalities with commands: list, patch, get
81*cf78ab8cSAndroid Build Coastguard Worker     #[command(subcommand, visible_alias("pcap"))]
82*cf78ab8cSAndroid Build Coastguard Worker     Capture(Capture),
83*cf78ab8cSAndroid Build Coastguard Worker     /// Opens netsim artifacts directory (log, pcaps)
84*cf78ab8cSAndroid Build Coastguard Worker     Artifact,
85*cf78ab8cSAndroid Build Coastguard Worker     /// A chip that sends advertisements at a set interval
86*cf78ab8cSAndroid Build Coastguard Worker     #[command(subcommand)]
87*cf78ab8cSAndroid Build Coastguard Worker     Beacon(Beacon),
88*cf78ab8cSAndroid Build Coastguard Worker     /// Open Bumble Hive Web Page
89*cf78ab8cSAndroid Build Coastguard Worker     Bumble,
90*cf78ab8cSAndroid Build Coastguard Worker }
91*cf78ab8cSAndroid Build Coastguard Worker 
92*cf78ab8cSAndroid Build Coastguard Worker impl Command {
93*cf78ab8cSAndroid Build Coastguard Worker     /// Return the generated request protobuf as a byte vector
94*cf78ab8cSAndroid Build Coastguard Worker     /// The parsed command parameters are used to construct the request protobuf which is
95*cf78ab8cSAndroid Build Coastguard Worker     /// returned as a byte vector that can be sent to the server.
get_request_bytes(&self) -> BinaryProtobuf96*cf78ab8cSAndroid Build Coastguard Worker     pub fn get_request_bytes(&self) -> BinaryProtobuf {
97*cf78ab8cSAndroid Build Coastguard Worker         match self {
98*cf78ab8cSAndroid Build Coastguard Worker             Command::Version => Vec::new(),
99*cf78ab8cSAndroid Build Coastguard Worker             Command::Radio(cmd) => {
100*cf78ab8cSAndroid Build Coastguard Worker                 let mut chip = Chip { ..Default::default() };
101*cf78ab8cSAndroid Build Coastguard Worker                 let chip_state = match cmd.status {
102*cf78ab8cSAndroid Build Coastguard Worker                     UpDownStatus::Up => true,
103*cf78ab8cSAndroid Build Coastguard Worker                     UpDownStatus::Down => false,
104*cf78ab8cSAndroid Build Coastguard Worker                 };
105*cf78ab8cSAndroid Build Coastguard Worker                 if cmd.radio_type == RadioType::Wifi {
106*cf78ab8cSAndroid Build Coastguard Worker                     let mut wifi_chip = Chip_Radio::new();
107*cf78ab8cSAndroid Build Coastguard Worker                     wifi_chip.state = chip_state.into();
108*cf78ab8cSAndroid Build Coastguard Worker                     chip.set_wifi(wifi_chip);
109*cf78ab8cSAndroid Build Coastguard Worker                     chip.kind = ChipKind::WIFI.into();
110*cf78ab8cSAndroid Build Coastguard Worker                 } else if cmd.radio_type == RadioType::Uwb {
111*cf78ab8cSAndroid Build Coastguard Worker                     let mut uwb_chip = Chip_Radio::new();
112*cf78ab8cSAndroid Build Coastguard Worker                     uwb_chip.state = chip_state.into();
113*cf78ab8cSAndroid Build Coastguard Worker                     chip.set_uwb(uwb_chip);
114*cf78ab8cSAndroid Build Coastguard Worker                     chip.kind = ChipKind::UWB.into();
115*cf78ab8cSAndroid Build Coastguard Worker                 } else {
116*cf78ab8cSAndroid Build Coastguard Worker                     let mut bt_chip = Chip_Bluetooth::new();
117*cf78ab8cSAndroid Build Coastguard Worker                     let mut bt_chip_radio = Chip_Radio::new();
118*cf78ab8cSAndroid Build Coastguard Worker                     bt_chip_radio.state = chip_state.into();
119*cf78ab8cSAndroid Build Coastguard Worker                     if cmd.radio_type == RadioType::Ble {
120*cf78ab8cSAndroid Build Coastguard Worker                         bt_chip.low_energy = Some(bt_chip_radio).into();
121*cf78ab8cSAndroid Build Coastguard Worker                     } else {
122*cf78ab8cSAndroid Build Coastguard Worker                         bt_chip.classic = Some(bt_chip_radio).into();
123*cf78ab8cSAndroid Build Coastguard Worker                     }
124*cf78ab8cSAndroid Build Coastguard Worker                     chip.kind = ChipKind::BLUETOOTH.into();
125*cf78ab8cSAndroid Build Coastguard Worker                     chip.set_bt(bt_chip);
126*cf78ab8cSAndroid Build Coastguard Worker                 }
127*cf78ab8cSAndroid Build Coastguard Worker                 let mut result = frontend::PatchDeviceRequest::new();
128*cf78ab8cSAndroid Build Coastguard Worker                 let mut device = PatchDeviceFieldsProto::new();
129*cf78ab8cSAndroid Build Coastguard Worker                 device.name = Some(cmd.name.clone());
130*cf78ab8cSAndroid Build Coastguard Worker                 device.chips.push(chip);
131*cf78ab8cSAndroid Build Coastguard Worker                 result.device = Some(device).into();
132*cf78ab8cSAndroid Build Coastguard Worker                 result.write_to_bytes().unwrap()
133*cf78ab8cSAndroid Build Coastguard Worker             }
134*cf78ab8cSAndroid Build Coastguard Worker             Command::Move(cmd) => {
135*cf78ab8cSAndroid Build Coastguard Worker                 let mut result = frontend::PatchDeviceRequest::new();
136*cf78ab8cSAndroid Build Coastguard Worker                 let mut device = PatchDeviceFieldsProto::new();
137*cf78ab8cSAndroid Build Coastguard Worker                 let position = Position {
138*cf78ab8cSAndroid Build Coastguard Worker                     x: cmd.x,
139*cf78ab8cSAndroid Build Coastguard Worker                     y: cmd.y,
140*cf78ab8cSAndroid Build Coastguard Worker                     z: cmd.z.unwrap_or_default(),
141*cf78ab8cSAndroid Build Coastguard Worker                     ..Default::default()
142*cf78ab8cSAndroid Build Coastguard Worker                 };
143*cf78ab8cSAndroid Build Coastguard Worker                 device.name = Some(cmd.name.clone());
144*cf78ab8cSAndroid Build Coastguard Worker                 device.position = Some(position).into();
145*cf78ab8cSAndroid Build Coastguard Worker                 result.device = Some(device).into();
146*cf78ab8cSAndroid Build Coastguard Worker                 result.write_to_bytes().unwrap()
147*cf78ab8cSAndroid Build Coastguard Worker             }
148*cf78ab8cSAndroid Build Coastguard Worker             Command::Devices(_) => Vec::new(),
149*cf78ab8cSAndroid Build Coastguard Worker             Command::Reset => Vec::new(),
150*cf78ab8cSAndroid Build Coastguard Worker             Command::Gui => {
151*cf78ab8cSAndroid Build Coastguard Worker                 unimplemented!("get_request_bytes is not implemented for Gui Command.");
152*cf78ab8cSAndroid Build Coastguard Worker             }
153*cf78ab8cSAndroid Build Coastguard Worker             Command::Capture(cmd) => match cmd {
154*cf78ab8cSAndroid Build Coastguard Worker                 Capture::List(_) => Vec::new(),
155*cf78ab8cSAndroid Build Coastguard Worker                 Capture::Get(_) => {
156*cf78ab8cSAndroid Build Coastguard Worker                     unimplemented!("get_request_bytes not implemented for Capture Get command. Use get_requests instead.")
157*cf78ab8cSAndroid Build Coastguard Worker                 }
158*cf78ab8cSAndroid Build Coastguard Worker                 Capture::Patch(_) => {
159*cf78ab8cSAndroid Build Coastguard Worker                     unimplemented!("get_request_bytes not implemented for Capture Patch command. Use get_requests instead.")
160*cf78ab8cSAndroid Build Coastguard Worker                 }
161*cf78ab8cSAndroid Build Coastguard Worker             },
162*cf78ab8cSAndroid Build Coastguard Worker             Command::Artifact => {
163*cf78ab8cSAndroid Build Coastguard Worker                 unimplemented!("get_request_bytes is not implemented for Artifact Command.");
164*cf78ab8cSAndroid Build Coastguard Worker             }
165*cf78ab8cSAndroid Build Coastguard Worker             Command::Beacon(action) => match action {
166*cf78ab8cSAndroid Build Coastguard Worker                 Beacon::Create(kind) => match kind {
167*cf78ab8cSAndroid Build Coastguard Worker                     BeaconCreate::Ble(args) => {
168*cf78ab8cSAndroid Build Coastguard Worker                         let device = MessageField::some(DeviceCreateProto {
169*cf78ab8cSAndroid Build Coastguard Worker                             name: args.device_name.clone().unwrap_or_default(),
170*cf78ab8cSAndroid Build Coastguard Worker                             chips: vec![ChipCreateProto {
171*cf78ab8cSAndroid Build Coastguard Worker                                 name: args.chip_name.clone().unwrap_or_default(),
172*cf78ab8cSAndroid Build Coastguard Worker                                 kind: ChipKind::BLUETOOTH_BEACON.into(),
173*cf78ab8cSAndroid Build Coastguard Worker                                 chip: Some(chip_create::Chip::BleBeacon(
174*cf78ab8cSAndroid Build Coastguard Worker                                     chip_create::BleBeaconCreate {
175*cf78ab8cSAndroid Build Coastguard Worker                                         address: args.address.clone().unwrap_or_default(),
176*cf78ab8cSAndroid Build Coastguard Worker                                         settings: MessageField::some((&args.settings).into()),
177*cf78ab8cSAndroid Build Coastguard Worker                                         adv_data: MessageField::some((&args.advertise_data).into()),
178*cf78ab8cSAndroid Build Coastguard Worker                                         scan_response: MessageField::some(
179*cf78ab8cSAndroid Build Coastguard Worker                                             (&args.scan_response_data).into(),
180*cf78ab8cSAndroid Build Coastguard Worker                                         ),
181*cf78ab8cSAndroid Build Coastguard Worker                                         ..Default::default()
182*cf78ab8cSAndroid Build Coastguard Worker                                     },
183*cf78ab8cSAndroid Build Coastguard Worker                                 )),
184*cf78ab8cSAndroid Build Coastguard Worker                                 ..Default::default()
185*cf78ab8cSAndroid Build Coastguard Worker                             }],
186*cf78ab8cSAndroid Build Coastguard Worker                             ..Default::default()
187*cf78ab8cSAndroid Build Coastguard Worker                         });
188*cf78ab8cSAndroid Build Coastguard Worker 
189*cf78ab8cSAndroid Build Coastguard Worker                         let result = frontend::CreateDeviceRequest { device, ..Default::default() };
190*cf78ab8cSAndroid Build Coastguard Worker                         result.write_to_bytes().unwrap()
191*cf78ab8cSAndroid Build Coastguard Worker                     }
192*cf78ab8cSAndroid Build Coastguard Worker                 },
193*cf78ab8cSAndroid Build Coastguard Worker                 Beacon::Patch(kind) => match kind {
194*cf78ab8cSAndroid Build Coastguard Worker                     BeaconPatch::Ble(args) => {
195*cf78ab8cSAndroid Build Coastguard Worker                         let device = MessageField::some(PatchDeviceFieldsProto {
196*cf78ab8cSAndroid Build Coastguard Worker                             name: Some(args.device_name.clone()),
197*cf78ab8cSAndroid Build Coastguard Worker                             chips: vec![Chip {
198*cf78ab8cSAndroid Build Coastguard Worker                                 name: args.chip_name.clone(),
199*cf78ab8cSAndroid Build Coastguard Worker                                 kind: ChipKind::BLUETOOTH_BEACON.into(),
200*cf78ab8cSAndroid Build Coastguard Worker                                 chip: Some(Chip_Type::BleBeacon(Chip_Ble_Beacon {
201*cf78ab8cSAndroid Build Coastguard Worker                                     bt: MessageField::some(Chip_Bluetooth::new()),
202*cf78ab8cSAndroid Build Coastguard Worker                                     address: args.address.clone().unwrap_or_default(),
203*cf78ab8cSAndroid Build Coastguard Worker                                     settings: MessageField::some((&args.settings).into()),
204*cf78ab8cSAndroid Build Coastguard Worker                                     adv_data: MessageField::some((&args.advertise_data).into()),
205*cf78ab8cSAndroid Build Coastguard Worker                                     scan_response: MessageField::some(
206*cf78ab8cSAndroid Build Coastguard Worker                                         (&args.scan_response_data).into(),
207*cf78ab8cSAndroid Build Coastguard Worker                                     ),
208*cf78ab8cSAndroid Build Coastguard Worker                                     ..Default::default()
209*cf78ab8cSAndroid Build Coastguard Worker                                 })),
210*cf78ab8cSAndroid Build Coastguard Worker                                 ..Default::default()
211*cf78ab8cSAndroid Build Coastguard Worker                             }],
212*cf78ab8cSAndroid Build Coastguard Worker                             ..Default::default()
213*cf78ab8cSAndroid Build Coastguard Worker                         });
214*cf78ab8cSAndroid Build Coastguard Worker 
215*cf78ab8cSAndroid Build Coastguard Worker                         let result = frontend::PatchDeviceRequest { device, ..Default::default() };
216*cf78ab8cSAndroid Build Coastguard Worker                         result.write_to_bytes().unwrap()
217*cf78ab8cSAndroid Build Coastguard Worker                     }
218*cf78ab8cSAndroid Build Coastguard Worker                 },
219*cf78ab8cSAndroid Build Coastguard Worker                 Beacon::Remove(_) => Vec::new(),
220*cf78ab8cSAndroid Build Coastguard Worker             },
221*cf78ab8cSAndroid Build Coastguard Worker             Command::Bumble => {
222*cf78ab8cSAndroid Build Coastguard Worker                 unimplemented!("get_request_bytes is not implemented for Bumble Command.");
223*cf78ab8cSAndroid Build Coastguard Worker             }
224*cf78ab8cSAndroid Build Coastguard Worker         }
225*cf78ab8cSAndroid Build Coastguard Worker     }
226*cf78ab8cSAndroid Build Coastguard Worker 
227*cf78ab8cSAndroid Build Coastguard Worker     /// Create and return the request protobuf(s) for the command.
228*cf78ab8cSAndroid Build Coastguard Worker     /// In the case of a command with pattern argument(s) there may be multiple gRPC requests.
229*cf78ab8cSAndroid Build Coastguard Worker     /// The parsed command parameters are used to construct the request protobuf.
230*cf78ab8cSAndroid Build Coastguard Worker     /// The client is used to send gRPC call(s) to retrieve information needed for request protobufs.
get_requests(&mut self, client: &cxx::UniquePtr<FrontendClient>) -> Vec<BinaryProtobuf>231*cf78ab8cSAndroid Build Coastguard Worker     pub fn get_requests(&mut self, client: &cxx::UniquePtr<FrontendClient>) -> Vec<BinaryProtobuf> {
232*cf78ab8cSAndroid Build Coastguard Worker         match self {
233*cf78ab8cSAndroid Build Coastguard Worker             Command::Capture(Capture::Patch(cmd)) => {
234*cf78ab8cSAndroid Build Coastguard Worker                 let mut reqs = Vec::new();
235*cf78ab8cSAndroid Build Coastguard Worker                 let filtered_captures = Self::get_filtered_captures(client, &cmd.patterns);
236*cf78ab8cSAndroid Build Coastguard Worker                 // Create a request for each capture
237*cf78ab8cSAndroid Build Coastguard Worker                 for capture in &filtered_captures {
238*cf78ab8cSAndroid Build Coastguard Worker                     let mut result = frontend::PatchCaptureRequest::new();
239*cf78ab8cSAndroid Build Coastguard Worker                     result.id = capture.id;
240*cf78ab8cSAndroid Build Coastguard Worker                     let capture_state = match cmd.state {
241*cf78ab8cSAndroid Build Coastguard Worker                         OnOffState::On => true,
242*cf78ab8cSAndroid Build Coastguard Worker                         OnOffState::Off => false,
243*cf78ab8cSAndroid Build Coastguard Worker                     };
244*cf78ab8cSAndroid Build Coastguard Worker                     let mut patch_capture = PatchCaptureProto::new();
245*cf78ab8cSAndroid Build Coastguard Worker                     patch_capture.state = capture_state.into();
246*cf78ab8cSAndroid Build Coastguard Worker                     result.patch = Some(patch_capture).into();
247*cf78ab8cSAndroid Build Coastguard Worker                     reqs.push(result.write_to_bytes().unwrap())
248*cf78ab8cSAndroid Build Coastguard Worker                 }
249*cf78ab8cSAndroid Build Coastguard Worker                 reqs
250*cf78ab8cSAndroid Build Coastguard Worker             }
251*cf78ab8cSAndroid Build Coastguard Worker             Command::Capture(Capture::Get(cmd)) => {
252*cf78ab8cSAndroid Build Coastguard Worker                 let mut reqs = Vec::new();
253*cf78ab8cSAndroid Build Coastguard Worker                 let filtered_captures = Self::get_filtered_captures(client, &cmd.patterns);
254*cf78ab8cSAndroid Build Coastguard Worker                 // Create a request for each capture
255*cf78ab8cSAndroid Build Coastguard Worker                 for capture in &filtered_captures {
256*cf78ab8cSAndroid Build Coastguard Worker                     let mut result = frontend::GetCaptureRequest::new();
257*cf78ab8cSAndroid Build Coastguard Worker                     result.id = capture.id;
258*cf78ab8cSAndroid Build Coastguard Worker                     reqs.push(result.write_to_bytes().unwrap());
259*cf78ab8cSAndroid Build Coastguard Worker                     let time_display = TimeDisplay::new(
260*cf78ab8cSAndroid Build Coastguard Worker                         capture.timestamp.get_or_default().seconds,
261*cf78ab8cSAndroid Build Coastguard Worker                         capture.timestamp.get_or_default().nanos as u32,
262*cf78ab8cSAndroid Build Coastguard Worker                     );
263*cf78ab8cSAndroid Build Coastguard Worker                     let file_extension = "pcap";
264*cf78ab8cSAndroid Build Coastguard Worker                     cmd.filenames.push(format!(
265*cf78ab8cSAndroid Build Coastguard Worker                         "netsim-{:?}-{}-{}-{}.{}",
266*cf78ab8cSAndroid Build Coastguard Worker                         capture.id,
267*cf78ab8cSAndroid Build Coastguard Worker                         capture.device_name.to_owned().replace(' ', "_"),
268*cf78ab8cSAndroid Build Coastguard Worker                         Self::chip_kind_to_string(capture.chip_kind.enum_value_or_default()),
269*cf78ab8cSAndroid Build Coastguard Worker                         time_display.utc_display(),
270*cf78ab8cSAndroid Build Coastguard Worker                         file_extension
271*cf78ab8cSAndroid Build Coastguard Worker                     ));
272*cf78ab8cSAndroid Build Coastguard Worker                 }
273*cf78ab8cSAndroid Build Coastguard Worker                 reqs
274*cf78ab8cSAndroid Build Coastguard Worker             }
275*cf78ab8cSAndroid Build Coastguard Worker             _ => {
276*cf78ab8cSAndroid Build Coastguard Worker                 unimplemented!(
277*cf78ab8cSAndroid Build Coastguard Worker                     "get_requests not implemented for this command. Use get_request_bytes instead."
278*cf78ab8cSAndroid Build Coastguard Worker                 )
279*cf78ab8cSAndroid Build Coastguard Worker             }
280*cf78ab8cSAndroid Build Coastguard Worker         }
281*cf78ab8cSAndroid Build Coastguard Worker     }
282*cf78ab8cSAndroid Build Coastguard Worker 
get_filtered_captures( client: &cxx::UniquePtr<FrontendClient>, patterns: &[String], ) -> Vec<model::Capture>283*cf78ab8cSAndroid Build Coastguard Worker     fn get_filtered_captures(
284*cf78ab8cSAndroid Build Coastguard Worker         client: &cxx::UniquePtr<FrontendClient>,
285*cf78ab8cSAndroid Build Coastguard Worker         patterns: &[String],
286*cf78ab8cSAndroid Build Coastguard Worker     ) -> Vec<model::Capture> {
287*cf78ab8cSAndroid Build Coastguard Worker         // Get list of captures
288*cf78ab8cSAndroid Build Coastguard Worker         let result = client.send_grpc(&GrpcMethod::ListCapture, &Vec::new());
289*cf78ab8cSAndroid Build Coastguard Worker         if !result.is_ok() {
290*cf78ab8cSAndroid Build Coastguard Worker             error!("ListCapture Grpc call error: {}", result.err());
291*cf78ab8cSAndroid Build Coastguard Worker             return Vec::new();
292*cf78ab8cSAndroid Build Coastguard Worker         }
293*cf78ab8cSAndroid Build Coastguard Worker         let mut response =
294*cf78ab8cSAndroid Build Coastguard Worker             frontend::ListCaptureResponse::parse_from_bytes(result.byte_vec().as_slice()).unwrap();
295*cf78ab8cSAndroid Build Coastguard Worker         if !patterns.is_empty() {
296*cf78ab8cSAndroid Build Coastguard Worker             // Filter out list of captures with matching patterns
297*cf78ab8cSAndroid Build Coastguard Worker             Self::filter_captures(&mut response.captures, patterns)
298*cf78ab8cSAndroid Build Coastguard Worker         }
299*cf78ab8cSAndroid Build Coastguard Worker         response.captures
300*cf78ab8cSAndroid Build Coastguard Worker     }
301*cf78ab8cSAndroid Build Coastguard Worker }
302*cf78ab8cSAndroid Build Coastguard Worker 
303*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
304*cf78ab8cSAndroid Build Coastguard Worker pub struct Radio {
305*cf78ab8cSAndroid Build Coastguard Worker     /// Radio type
306*cf78ab8cSAndroid Build Coastguard Worker     #[arg(value_enum, ignore_case = true)]
307*cf78ab8cSAndroid Build Coastguard Worker     pub radio_type: RadioType,
308*cf78ab8cSAndroid Build Coastguard Worker     /// Radio status
309*cf78ab8cSAndroid Build Coastguard Worker     #[arg(value_enum, ignore_case = true)]
310*cf78ab8cSAndroid Build Coastguard Worker     pub status: UpDownStatus,
311*cf78ab8cSAndroid Build Coastguard Worker     /// Device name
312*cf78ab8cSAndroid Build Coastguard Worker     pub name: String,
313*cf78ab8cSAndroid Build Coastguard Worker }
314*cf78ab8cSAndroid Build Coastguard Worker 
315*cf78ab8cSAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
316*cf78ab8cSAndroid Build Coastguard Worker pub enum RadioType {
317*cf78ab8cSAndroid Build Coastguard Worker     Ble,
318*cf78ab8cSAndroid Build Coastguard Worker     Classic,
319*cf78ab8cSAndroid Build Coastguard Worker     Wifi,
320*cf78ab8cSAndroid Build Coastguard Worker     Uwb,
321*cf78ab8cSAndroid Build Coastguard Worker }
322*cf78ab8cSAndroid Build Coastguard Worker 
323*cf78ab8cSAndroid Build Coastguard Worker impl fmt::Display for RadioType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result324*cf78ab8cSAndroid Build Coastguard Worker     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325*cf78ab8cSAndroid Build Coastguard Worker         write!(f, "{:?}", self)
326*cf78ab8cSAndroid Build Coastguard Worker     }
327*cf78ab8cSAndroid Build Coastguard Worker }
328*cf78ab8cSAndroid Build Coastguard Worker 
329*cf78ab8cSAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
330*cf78ab8cSAndroid Build Coastguard Worker pub enum UpDownStatus {
331*cf78ab8cSAndroid Build Coastguard Worker     Up,
332*cf78ab8cSAndroid Build Coastguard Worker     Down,
333*cf78ab8cSAndroid Build Coastguard Worker }
334*cf78ab8cSAndroid Build Coastguard Worker 
335*cf78ab8cSAndroid Build Coastguard Worker impl fmt::Display for UpDownStatus {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result336*cf78ab8cSAndroid Build Coastguard Worker     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
337*cf78ab8cSAndroid Build Coastguard Worker         write!(f, "{:?}", self)
338*cf78ab8cSAndroid Build Coastguard Worker     }
339*cf78ab8cSAndroid Build Coastguard Worker }
340*cf78ab8cSAndroid Build Coastguard Worker 
341*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
342*cf78ab8cSAndroid Build Coastguard Worker pub struct Move {
343*cf78ab8cSAndroid Build Coastguard Worker     /// Device name
344*cf78ab8cSAndroid Build Coastguard Worker     pub name: String,
345*cf78ab8cSAndroid Build Coastguard Worker     /// x position of device
346*cf78ab8cSAndroid Build Coastguard Worker     pub x: f32,
347*cf78ab8cSAndroid Build Coastguard Worker     /// y position of device
348*cf78ab8cSAndroid Build Coastguard Worker     pub y: f32,
349*cf78ab8cSAndroid Build Coastguard Worker     /// Optional z position of device
350*cf78ab8cSAndroid Build Coastguard Worker     pub z: Option<f32>,
351*cf78ab8cSAndroid Build Coastguard Worker }
352*cf78ab8cSAndroid Build Coastguard Worker 
353*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
354*cf78ab8cSAndroid Build Coastguard Worker pub struct Devices {
355*cf78ab8cSAndroid Build Coastguard Worker     /// Continuously print device(s) information every second
356*cf78ab8cSAndroid Build Coastguard Worker     #[arg(short, long)]
357*cf78ab8cSAndroid Build Coastguard Worker     pub continuous: bool,
358*cf78ab8cSAndroid Build Coastguard Worker }
359*cf78ab8cSAndroid Build Coastguard Worker 
360*cf78ab8cSAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
361*cf78ab8cSAndroid Build Coastguard Worker pub enum OnOffState {
362*cf78ab8cSAndroid Build Coastguard Worker     On,
363*cf78ab8cSAndroid Build Coastguard Worker     Off,
364*cf78ab8cSAndroid Build Coastguard Worker }
365*cf78ab8cSAndroid Build Coastguard Worker 
366*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Subcommand)]
367*cf78ab8cSAndroid Build Coastguard Worker pub enum Beacon {
368*cf78ab8cSAndroid Build Coastguard Worker     /// Create a beacon chip
369*cf78ab8cSAndroid Build Coastguard Worker     #[command(subcommand)]
370*cf78ab8cSAndroid Build Coastguard Worker     Create(BeaconCreate),
371*cf78ab8cSAndroid Build Coastguard Worker     /// Modify a beacon chip
372*cf78ab8cSAndroid Build Coastguard Worker     #[command(subcommand)]
373*cf78ab8cSAndroid Build Coastguard Worker     Patch(BeaconPatch),
374*cf78ab8cSAndroid Build Coastguard Worker     /// Remove a beacon chip
375*cf78ab8cSAndroid Build Coastguard Worker     Remove(BeaconRemove),
376*cf78ab8cSAndroid Build Coastguard Worker }
377*cf78ab8cSAndroid Build Coastguard Worker 
378*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Subcommand)]
379*cf78ab8cSAndroid Build Coastguard Worker pub enum BeaconCreate {
380*cf78ab8cSAndroid Build Coastguard Worker     /// Create a Bluetooth low-energy beacon chip
381*cf78ab8cSAndroid Build Coastguard Worker     Ble(BeaconCreateBle),
382*cf78ab8cSAndroid Build Coastguard Worker }
383*cf78ab8cSAndroid Build Coastguard Worker 
384*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
385*cf78ab8cSAndroid Build Coastguard Worker pub struct BeaconCreateBle {
386*cf78ab8cSAndroid Build Coastguard Worker     /// Name of the device to create
387*cf78ab8cSAndroid Build Coastguard Worker     pub device_name: Option<String>,
388*cf78ab8cSAndroid Build Coastguard Worker     /// Name of the beacon chip to create within the new device. May only be specified if device_name is specified
389*cf78ab8cSAndroid Build Coastguard Worker     pub chip_name: Option<String>,
390*cf78ab8cSAndroid Build Coastguard Worker     /// Bluetooth address of the beacon. Must be a 6-byte hexadecimal string with each byte separated by a colon. Will be generated if not provided
391*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long)]
392*cf78ab8cSAndroid Build Coastguard Worker     pub address: Option<String>,
393*cf78ab8cSAndroid Build Coastguard Worker     #[command(flatten)]
394*cf78ab8cSAndroid Build Coastguard Worker     pub settings: BeaconBleSettings,
395*cf78ab8cSAndroid Build Coastguard Worker     #[command(flatten)]
396*cf78ab8cSAndroid Build Coastguard Worker     pub advertise_data: BeaconBleAdvertiseData,
397*cf78ab8cSAndroid Build Coastguard Worker     #[command(flatten)]
398*cf78ab8cSAndroid Build Coastguard Worker     pub scan_response_data: BeaconBleScanResponseData,
399*cf78ab8cSAndroid Build Coastguard Worker }
400*cf78ab8cSAndroid Build Coastguard Worker 
401*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Subcommand)]
402*cf78ab8cSAndroid Build Coastguard Worker pub enum BeaconPatch {
403*cf78ab8cSAndroid Build Coastguard Worker     /// Modify a Bluetooth low-energy beacon chip
404*cf78ab8cSAndroid Build Coastguard Worker     Ble(BeaconPatchBle),
405*cf78ab8cSAndroid Build Coastguard Worker }
406*cf78ab8cSAndroid Build Coastguard Worker 
407*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
408*cf78ab8cSAndroid Build Coastguard Worker pub struct BeaconPatchBle {
409*cf78ab8cSAndroid Build Coastguard Worker     /// Name of the device that contains the chip
410*cf78ab8cSAndroid Build Coastguard Worker     pub device_name: String,
411*cf78ab8cSAndroid Build Coastguard Worker     /// Name of the beacon chip to modify
412*cf78ab8cSAndroid Build Coastguard Worker     pub chip_name: String,
413*cf78ab8cSAndroid Build Coastguard Worker     /// Bluetooth address of the beacon. Must be a 6-byte hexadecimal string with each byte separated by a colon
414*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long)]
415*cf78ab8cSAndroid Build Coastguard Worker     pub address: Option<String>,
416*cf78ab8cSAndroid Build Coastguard Worker     #[command(flatten)]
417*cf78ab8cSAndroid Build Coastguard Worker     pub settings: BeaconBleSettings,
418*cf78ab8cSAndroid Build Coastguard Worker     #[command(flatten)]
419*cf78ab8cSAndroid Build Coastguard Worker     pub advertise_data: BeaconBleAdvertiseData,
420*cf78ab8cSAndroid Build Coastguard Worker     #[command(flatten)]
421*cf78ab8cSAndroid Build Coastguard Worker     pub scan_response_data: BeaconBleScanResponseData,
422*cf78ab8cSAndroid Build Coastguard Worker }
423*cf78ab8cSAndroid Build Coastguard Worker 
424*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
425*cf78ab8cSAndroid Build Coastguard Worker pub struct BeaconRemove {
426*cf78ab8cSAndroid Build Coastguard Worker     /// Name of the device to remove
427*cf78ab8cSAndroid Build Coastguard Worker     pub device_name: String,
428*cf78ab8cSAndroid Build Coastguard Worker     /// Name of the beacon chip to remove. Can be omitted if the device has exactly 1 chip
429*cf78ab8cSAndroid Build Coastguard Worker     pub chip_name: Option<String>,
430*cf78ab8cSAndroid Build Coastguard Worker }
431*cf78ab8cSAndroid Build Coastguard Worker 
432*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
433*cf78ab8cSAndroid Build Coastguard Worker pub struct BeaconBleAdvertiseData {
434*cf78ab8cSAndroid Build Coastguard Worker     /// Whether the device name should be included in the advertise packet
435*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long, required = false)]
436*cf78ab8cSAndroid Build Coastguard Worker     pub include_device_name: bool,
437*cf78ab8cSAndroid Build Coastguard Worker     /// Whether the transmission power level should be included in the advertise packet
438*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long, required = false)]
439*cf78ab8cSAndroid Build Coastguard Worker     pub include_tx_power_level: bool,
440*cf78ab8cSAndroid Build Coastguard Worker     /// Manufacturer-specific data given as bytes in hexadecimal
441*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long)]
442*cf78ab8cSAndroid Build Coastguard Worker     pub manufacturer_data: Option<ParsableBytes>,
443*cf78ab8cSAndroid Build Coastguard Worker }
444*cf78ab8cSAndroid Build Coastguard Worker 
445*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Clone)]
446*cf78ab8cSAndroid Build Coastguard Worker pub struct ParsableBytes(Vec<u8>);
447*cf78ab8cSAndroid Build Coastguard Worker 
448*cf78ab8cSAndroid Build Coastguard Worker impl ParsableBytes {
unwrap(self) -> Vec<u8>449*cf78ab8cSAndroid Build Coastguard Worker     fn unwrap(self) -> Vec<u8> {
450*cf78ab8cSAndroid Build Coastguard Worker         self.0
451*cf78ab8cSAndroid Build Coastguard Worker     }
452*cf78ab8cSAndroid Build Coastguard Worker }
453*cf78ab8cSAndroid Build Coastguard Worker 
454*cf78ab8cSAndroid Build Coastguard Worker impl FromStr for ParsableBytes {
455*cf78ab8cSAndroid Build Coastguard Worker     type Err = FromHexError;
from_str(s: &str) -> Result<Self, Self::Err>456*cf78ab8cSAndroid Build Coastguard Worker     fn from_str(s: &str) -> Result<Self, Self::Err> {
457*cf78ab8cSAndroid Build Coastguard Worker         hex_to_bytes(s.strip_prefix("0x").unwrap_or(s)).map(ParsableBytes)
458*cf78ab8cSAndroid Build Coastguard Worker     }
459*cf78ab8cSAndroid Build Coastguard Worker }
460*cf78ab8cSAndroid Build Coastguard Worker 
461*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
462*cf78ab8cSAndroid Build Coastguard Worker pub struct BeaconBleScanResponseData {
463*cf78ab8cSAndroid Build Coastguard Worker     /// Whether the device name should be included in the scan response packet
464*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long, required = false)]
465*cf78ab8cSAndroid Build Coastguard Worker     pub scan_response_include_device_name: bool,
466*cf78ab8cSAndroid Build Coastguard Worker     /// Whether the transmission power level should be included in the scan response packet
467*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long, required = false)]
468*cf78ab8cSAndroid Build Coastguard Worker     pub scan_response_include_tx_power_level: bool,
469*cf78ab8cSAndroid Build Coastguard Worker     /// Manufacturer-specific data to include in the scan response packet given as bytes in hexadecimal
470*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long, value_name = "MANUFACTURER_DATA")]
471*cf78ab8cSAndroid Build Coastguard Worker     pub scan_response_manufacturer_data: Option<ParsableBytes>,
472*cf78ab8cSAndroid Build Coastguard Worker }
473*cf78ab8cSAndroid Build Coastguard Worker 
474*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
475*cf78ab8cSAndroid Build Coastguard Worker pub struct BeaconBleSettings {
476*cf78ab8cSAndroid Build Coastguard Worker     /// Set advertise mode to control the advertising latency
477*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long, value_parser = IntervalParser)]
478*cf78ab8cSAndroid Build Coastguard Worker     pub advertise_mode: Option<Interval>,
479*cf78ab8cSAndroid Build Coastguard Worker     /// Set advertise TX power level to control the beacon's transmission power
480*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long, value_parser = TxPowerParser, allow_hyphen_values = true)]
481*cf78ab8cSAndroid Build Coastguard Worker     pub tx_power_level: Option<TxPower>,
482*cf78ab8cSAndroid Build Coastguard Worker     /// Set whether the beacon will respond to scan requests
483*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long)]
484*cf78ab8cSAndroid Build Coastguard Worker     pub scannable: bool,
485*cf78ab8cSAndroid Build Coastguard Worker     /// Limit advertising to an amount of time given in milliseconds
486*cf78ab8cSAndroid Build Coastguard Worker     #[arg(long, value_name = "MS")]
487*cf78ab8cSAndroid Build Coastguard Worker     pub timeout: Option<u64>,
488*cf78ab8cSAndroid Build Coastguard Worker }
489*cf78ab8cSAndroid Build Coastguard Worker 
490*cf78ab8cSAndroid Build Coastguard Worker #[derive(Clone, Debug)]
491*cf78ab8cSAndroid Build Coastguard Worker pub enum Interval {
492*cf78ab8cSAndroid Build Coastguard Worker     Mode(AdvertiseMode),
493*cf78ab8cSAndroid Build Coastguard Worker     Milliseconds(u64),
494*cf78ab8cSAndroid Build Coastguard Worker }
495*cf78ab8cSAndroid Build Coastguard Worker 
496*cf78ab8cSAndroid Build Coastguard Worker #[derive(Clone)]
497*cf78ab8cSAndroid Build Coastguard Worker struct IntervalParser;
498*cf78ab8cSAndroid Build Coastguard Worker 
499*cf78ab8cSAndroid Build Coastguard Worker impl TypedValueParser for IntervalParser {
500*cf78ab8cSAndroid Build Coastguard Worker     type Value = Interval;
501*cf78ab8cSAndroid Build Coastguard Worker 
parse_ref( &self, cmd: &clap::Command, arg: Option<&clap::Arg>, value: &std::ffi::OsStr, ) -> Result<Self::Value, clap::Error>502*cf78ab8cSAndroid Build Coastguard Worker     fn parse_ref(
503*cf78ab8cSAndroid Build Coastguard Worker         &self,
504*cf78ab8cSAndroid Build Coastguard Worker         cmd: &clap::Command,
505*cf78ab8cSAndroid Build Coastguard Worker         arg: Option<&clap::Arg>,
506*cf78ab8cSAndroid Build Coastguard Worker         value: &std::ffi::OsStr,
507*cf78ab8cSAndroid Build Coastguard Worker     ) -> Result<Self::Value, clap::Error> {
508*cf78ab8cSAndroid Build Coastguard Worker         let millis_parser = clap::value_parser!(u64);
509*cf78ab8cSAndroid Build Coastguard Worker         let mode_parser = clap::value_parser!(AdvertiseMode);
510*cf78ab8cSAndroid Build Coastguard Worker 
511*cf78ab8cSAndroid Build Coastguard Worker         mode_parser
512*cf78ab8cSAndroid Build Coastguard Worker             .parse_ref(cmd, arg, value)
513*cf78ab8cSAndroid Build Coastguard Worker             .map(Self::Value::Mode)
514*cf78ab8cSAndroid Build Coastguard Worker             .or(millis_parser.parse_ref(cmd, arg, value).map(Self::Value::Milliseconds))
515*cf78ab8cSAndroid Build Coastguard Worker     }
516*cf78ab8cSAndroid Build Coastguard Worker 
possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>>517*cf78ab8cSAndroid Build Coastguard Worker     fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
518*cf78ab8cSAndroid Build Coastguard Worker         Some(Box::new(
519*cf78ab8cSAndroid Build Coastguard Worker             AdvertiseMode::value_variants().iter().map(|v| v.to_possible_value().unwrap()).chain(
520*cf78ab8cSAndroid Build Coastguard Worker                 iter::once(
521*cf78ab8cSAndroid Build Coastguard Worker                     PossibleValue::new("<MS>").help("An exact advertise interval in milliseconds"),
522*cf78ab8cSAndroid Build Coastguard Worker                 ),
523*cf78ab8cSAndroid Build Coastguard Worker             ),
524*cf78ab8cSAndroid Build Coastguard Worker         ))
525*cf78ab8cSAndroid Build Coastguard Worker     }
526*cf78ab8cSAndroid Build Coastguard Worker }
527*cf78ab8cSAndroid Build Coastguard Worker 
528*cf78ab8cSAndroid Build Coastguard Worker #[derive(Clone, Debug)]
529*cf78ab8cSAndroid Build Coastguard Worker pub enum TxPower {
530*cf78ab8cSAndroid Build Coastguard Worker     Level(TxPowerLevel),
531*cf78ab8cSAndroid Build Coastguard Worker     Dbm(i8),
532*cf78ab8cSAndroid Build Coastguard Worker }
533*cf78ab8cSAndroid Build Coastguard Worker 
534*cf78ab8cSAndroid Build Coastguard Worker #[derive(Clone)]
535*cf78ab8cSAndroid Build Coastguard Worker struct TxPowerParser;
536*cf78ab8cSAndroid Build Coastguard Worker 
537*cf78ab8cSAndroid Build Coastguard Worker impl TypedValueParser for TxPowerParser {
538*cf78ab8cSAndroid Build Coastguard Worker     type Value = TxPower;
539*cf78ab8cSAndroid Build Coastguard Worker 
parse_ref( &self, cmd: &clap::Command, arg: Option<&clap::Arg>, value: &std::ffi::OsStr, ) -> Result<Self::Value, clap::Error>540*cf78ab8cSAndroid Build Coastguard Worker     fn parse_ref(
541*cf78ab8cSAndroid Build Coastguard Worker         &self,
542*cf78ab8cSAndroid Build Coastguard Worker         cmd: &clap::Command,
543*cf78ab8cSAndroid Build Coastguard Worker         arg: Option<&clap::Arg>,
544*cf78ab8cSAndroid Build Coastguard Worker         value: &std::ffi::OsStr,
545*cf78ab8cSAndroid Build Coastguard Worker     ) -> Result<Self::Value, clap::Error> {
546*cf78ab8cSAndroid Build Coastguard Worker         let dbm_parser = clap::value_parser!(i8);
547*cf78ab8cSAndroid Build Coastguard Worker         let level_parser = clap::value_parser!(TxPowerLevel);
548*cf78ab8cSAndroid Build Coastguard Worker 
549*cf78ab8cSAndroid Build Coastguard Worker         level_parser
550*cf78ab8cSAndroid Build Coastguard Worker             .parse_ref(cmd, arg, value)
551*cf78ab8cSAndroid Build Coastguard Worker             .map(Self::Value::Level)
552*cf78ab8cSAndroid Build Coastguard Worker             .or(dbm_parser.parse_ref(cmd, arg, value).map(Self::Value::Dbm))
553*cf78ab8cSAndroid Build Coastguard Worker     }
554*cf78ab8cSAndroid Build Coastguard Worker 
possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>>555*cf78ab8cSAndroid Build Coastguard Worker     fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
556*cf78ab8cSAndroid Build Coastguard Worker         Some(Box::new(
557*cf78ab8cSAndroid Build Coastguard Worker             TxPowerLevel::value_variants().iter().map(|v| v.to_possible_value().unwrap()).chain(
558*cf78ab8cSAndroid Build Coastguard Worker                 iter::once(
559*cf78ab8cSAndroid Build Coastguard Worker                     PossibleValue::new("<DBM>").help("An exact transmit power level in dBm"),
560*cf78ab8cSAndroid Build Coastguard Worker                 ),
561*cf78ab8cSAndroid Build Coastguard Worker             ),
562*cf78ab8cSAndroid Build Coastguard Worker         ))
563*cf78ab8cSAndroid Build Coastguard Worker     }
564*cf78ab8cSAndroid Build Coastguard Worker }
565*cf78ab8cSAndroid Build Coastguard Worker 
566*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Clone, ValueEnum)]
567*cf78ab8cSAndroid Build Coastguard Worker pub enum AdvertiseMode {
568*cf78ab8cSAndroid Build Coastguard Worker     /// Lowest power consumption, preferred advertising mode
569*cf78ab8cSAndroid Build Coastguard Worker     LowPower,
570*cf78ab8cSAndroid Build Coastguard Worker     /// Balanced between advertising frequency and power consumption
571*cf78ab8cSAndroid Build Coastguard Worker     Balanced,
572*cf78ab8cSAndroid Build Coastguard Worker     /// Highest power consumption
573*cf78ab8cSAndroid Build Coastguard Worker     LowLatency,
574*cf78ab8cSAndroid Build Coastguard Worker }
575*cf78ab8cSAndroid Build Coastguard Worker 
576*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Clone, ValueEnum)]
577*cf78ab8cSAndroid Build Coastguard Worker pub enum TxPowerLevel {
578*cf78ab8cSAndroid Build Coastguard Worker     /// Lowest transmission power level
579*cf78ab8cSAndroid Build Coastguard Worker     UltraLow,
580*cf78ab8cSAndroid Build Coastguard Worker     /// Low transmission power level
581*cf78ab8cSAndroid Build Coastguard Worker     Low,
582*cf78ab8cSAndroid Build Coastguard Worker     /// Medium transmission power level
583*cf78ab8cSAndroid Build Coastguard Worker     Medium,
584*cf78ab8cSAndroid Build Coastguard Worker     /// High transmission power level
585*cf78ab8cSAndroid Build Coastguard Worker     High,
586*cf78ab8cSAndroid Build Coastguard Worker }
587*cf78ab8cSAndroid Build Coastguard Worker 
588*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Subcommand)]
589*cf78ab8cSAndroid Build Coastguard Worker pub enum Capture {
590*cf78ab8cSAndroid Build Coastguard Worker     /// List currently available Captures (packet captures)
591*cf78ab8cSAndroid Build Coastguard Worker     List(ListCapture),
592*cf78ab8cSAndroid Build Coastguard Worker     /// Patch a Capture source to turn packet capture on/off
593*cf78ab8cSAndroid Build Coastguard Worker     Patch(PatchCapture),
594*cf78ab8cSAndroid Build Coastguard Worker     /// Download the packet capture content
595*cf78ab8cSAndroid Build Coastguard Worker     Get(GetCapture),
596*cf78ab8cSAndroid Build Coastguard Worker }
597*cf78ab8cSAndroid Build Coastguard Worker 
598*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
599*cf78ab8cSAndroid Build Coastguard Worker pub struct ListCapture {
600*cf78ab8cSAndroid Build Coastguard Worker     /// Optional strings of pattern for captures to list. Possible filter fields include Capture ID, Device Name, and Chip Kind
601*cf78ab8cSAndroid Build Coastguard Worker     pub patterns: Vec<String>,
602*cf78ab8cSAndroid Build Coastguard Worker     /// Continuously print Capture information every second
603*cf78ab8cSAndroid Build Coastguard Worker     #[arg(short, long)]
604*cf78ab8cSAndroid Build Coastguard Worker     pub continuous: bool,
605*cf78ab8cSAndroid Build Coastguard Worker }
606*cf78ab8cSAndroid Build Coastguard Worker 
607*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
608*cf78ab8cSAndroid Build Coastguard Worker pub struct PatchCapture {
609*cf78ab8cSAndroid Build Coastguard Worker     /// Packet capture state
610*cf78ab8cSAndroid Build Coastguard Worker     #[arg(value_enum, ignore_case = true)]
611*cf78ab8cSAndroid Build Coastguard Worker     pub state: OnOffState,
612*cf78ab8cSAndroid Build Coastguard Worker     /// Optional strings of pattern for captures to patch. Possible filter fields include Capture ID, Device Name, and Chip Kind
613*cf78ab8cSAndroid Build Coastguard Worker     pub patterns: Vec<String>,
614*cf78ab8cSAndroid Build Coastguard Worker }
615*cf78ab8cSAndroid Build Coastguard Worker 
616*cf78ab8cSAndroid Build Coastguard Worker #[derive(Debug, Args)]
617*cf78ab8cSAndroid Build Coastguard Worker pub struct GetCapture {
618*cf78ab8cSAndroid Build Coastguard Worker     /// Optional strings of pattern for captures to get. Possible filter fields include Capture ID, Device Name, and Chip Kind
619*cf78ab8cSAndroid Build Coastguard Worker     pub patterns: Vec<String>,
620*cf78ab8cSAndroid Build Coastguard Worker     /// Directory to store downloaded capture file(s)
621*cf78ab8cSAndroid Build Coastguard Worker     #[arg(short = 'o', long)]
622*cf78ab8cSAndroid Build Coastguard Worker     pub location: Option<String>,
623*cf78ab8cSAndroid Build Coastguard Worker     #[arg(skip)]
624*cf78ab8cSAndroid Build Coastguard Worker     pub filenames: Vec<String>,
625*cf78ab8cSAndroid Build Coastguard Worker     #[arg(skip)]
626*cf78ab8cSAndroid Build Coastguard Worker     pub current_file: String,
627*cf78ab8cSAndroid Build Coastguard Worker }
628*cf78ab8cSAndroid Build Coastguard Worker 
629*cf78ab8cSAndroid Build Coastguard Worker impl From<&BeaconBleSettings> for AdvertiseSettingsProto {
from(value: &BeaconBleSettings) -> Self630*cf78ab8cSAndroid Build Coastguard Worker     fn from(value: &BeaconBleSettings) -> Self {
631*cf78ab8cSAndroid Build Coastguard Worker         AdvertiseSettingsProto {
632*cf78ab8cSAndroid Build Coastguard Worker             interval: value.advertise_mode.as_ref().map(IntervalProto::from),
633*cf78ab8cSAndroid Build Coastguard Worker             tx_power: value.tx_power_level.as_ref().map(TxPowerProto::from),
634*cf78ab8cSAndroid Build Coastguard Worker             scannable: value.scannable,
635*cf78ab8cSAndroid Build Coastguard Worker             timeout: value.timeout.unwrap_or_default(),
636*cf78ab8cSAndroid Build Coastguard Worker             ..Default::default()
637*cf78ab8cSAndroid Build Coastguard Worker         }
638*cf78ab8cSAndroid Build Coastguard Worker     }
639*cf78ab8cSAndroid Build Coastguard Worker }
640*cf78ab8cSAndroid Build Coastguard Worker 
641*cf78ab8cSAndroid Build Coastguard Worker impl From<&Interval> for IntervalProto {
from(value: &Interval) -> Self642*cf78ab8cSAndroid Build Coastguard Worker     fn from(value: &Interval) -> Self {
643*cf78ab8cSAndroid Build Coastguard Worker         match value {
644*cf78ab8cSAndroid Build Coastguard Worker             Interval::Mode(mode) => IntervalProto::AdvertiseMode(
645*cf78ab8cSAndroid Build Coastguard Worker                 match mode {
646*cf78ab8cSAndroid Build Coastguard Worker                     AdvertiseMode::LowPower => AdvertiseModeProto::LOW_POWER,
647*cf78ab8cSAndroid Build Coastguard Worker                     AdvertiseMode::Balanced => AdvertiseModeProto::BALANCED,
648*cf78ab8cSAndroid Build Coastguard Worker                     AdvertiseMode::LowLatency => AdvertiseModeProto::LOW_LATENCY,
649*cf78ab8cSAndroid Build Coastguard Worker                 }
650*cf78ab8cSAndroid Build Coastguard Worker                 .into(),
651*cf78ab8cSAndroid Build Coastguard Worker             ),
652*cf78ab8cSAndroid Build Coastguard Worker             Interval::Milliseconds(ms) => IntervalProto::Milliseconds(*ms),
653*cf78ab8cSAndroid Build Coastguard Worker         }
654*cf78ab8cSAndroid Build Coastguard Worker     }
655*cf78ab8cSAndroid Build Coastguard Worker }
656*cf78ab8cSAndroid Build Coastguard Worker 
657*cf78ab8cSAndroid Build Coastguard Worker impl From<&TxPower> for TxPowerProto {
from(value: &TxPower) -> Self658*cf78ab8cSAndroid Build Coastguard Worker     fn from(value: &TxPower) -> Self {
659*cf78ab8cSAndroid Build Coastguard Worker         match value {
660*cf78ab8cSAndroid Build Coastguard Worker             TxPower::Level(level) => TxPowerProto::TxPowerLevel(
661*cf78ab8cSAndroid Build Coastguard Worker                 match level {
662*cf78ab8cSAndroid Build Coastguard Worker                     TxPowerLevel::UltraLow => AdvertiseTxPowerProto::ULTRA_LOW,
663*cf78ab8cSAndroid Build Coastguard Worker                     TxPowerLevel::Low => AdvertiseTxPowerProto::LOW,
664*cf78ab8cSAndroid Build Coastguard Worker                     TxPowerLevel::Medium => AdvertiseTxPowerProto::MEDIUM,
665*cf78ab8cSAndroid Build Coastguard Worker                     TxPowerLevel::High => AdvertiseTxPowerProto::HIGH,
666*cf78ab8cSAndroid Build Coastguard Worker                 }
667*cf78ab8cSAndroid Build Coastguard Worker                 .into(),
668*cf78ab8cSAndroid Build Coastguard Worker             ),
669*cf78ab8cSAndroid Build Coastguard Worker             TxPower::Dbm(dbm) => TxPowerProto::Dbm((*dbm).into()),
670*cf78ab8cSAndroid Build Coastguard Worker         }
671*cf78ab8cSAndroid Build Coastguard Worker     }
672*cf78ab8cSAndroid Build Coastguard Worker }
673*cf78ab8cSAndroid Build Coastguard Worker 
674*cf78ab8cSAndroid Build Coastguard Worker impl From<&BeaconBleAdvertiseData> for AdvertiseDataProto {
from(value: &BeaconBleAdvertiseData) -> Self675*cf78ab8cSAndroid Build Coastguard Worker     fn from(value: &BeaconBleAdvertiseData) -> Self {
676*cf78ab8cSAndroid Build Coastguard Worker         AdvertiseDataProto {
677*cf78ab8cSAndroid Build Coastguard Worker             include_device_name: value.include_device_name,
678*cf78ab8cSAndroid Build Coastguard Worker             include_tx_power_level: value.include_tx_power_level,
679*cf78ab8cSAndroid Build Coastguard Worker             manufacturer_data: value
680*cf78ab8cSAndroid Build Coastguard Worker                 .manufacturer_data
681*cf78ab8cSAndroid Build Coastguard Worker                 .clone()
682*cf78ab8cSAndroid Build Coastguard Worker                 .map(ParsableBytes::unwrap)
683*cf78ab8cSAndroid Build Coastguard Worker                 .unwrap_or_default(),
684*cf78ab8cSAndroid Build Coastguard Worker             ..Default::default()
685*cf78ab8cSAndroid Build Coastguard Worker         }
686*cf78ab8cSAndroid Build Coastguard Worker     }
687*cf78ab8cSAndroid Build Coastguard Worker }
688*cf78ab8cSAndroid Build Coastguard Worker 
689*cf78ab8cSAndroid Build Coastguard Worker impl From<&BeaconBleScanResponseData> for AdvertiseDataProto {
from(value: &BeaconBleScanResponseData) -> Self690*cf78ab8cSAndroid Build Coastguard Worker     fn from(value: &BeaconBleScanResponseData) -> Self {
691*cf78ab8cSAndroid Build Coastguard Worker         AdvertiseDataProto {
692*cf78ab8cSAndroid Build Coastguard Worker             include_device_name: value.scan_response_include_device_name,
693*cf78ab8cSAndroid Build Coastguard Worker             include_tx_power_level: value.scan_response_include_tx_power_level,
694*cf78ab8cSAndroid Build Coastguard Worker             manufacturer_data: value
695*cf78ab8cSAndroid Build Coastguard Worker                 .scan_response_manufacturer_data
696*cf78ab8cSAndroid Build Coastguard Worker                 .clone()
697*cf78ab8cSAndroid Build Coastguard Worker                 .map(ParsableBytes::unwrap)
698*cf78ab8cSAndroid Build Coastguard Worker                 .unwrap_or_default(),
699*cf78ab8cSAndroid Build Coastguard Worker             ..Default::default()
700*cf78ab8cSAndroid Build Coastguard Worker         }
701*cf78ab8cSAndroid Build Coastguard Worker     }
702*cf78ab8cSAndroid Build Coastguard Worker }
703*cf78ab8cSAndroid Build Coastguard Worker 
704*cf78ab8cSAndroid Build Coastguard Worker #[cfg(test)]
705*cf78ab8cSAndroid Build Coastguard Worker mod tests {
706*cf78ab8cSAndroid Build Coastguard Worker     use super::*;
707*cf78ab8cSAndroid Build Coastguard Worker 
708*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_hex_parser_succeeds()709*cf78ab8cSAndroid Build Coastguard Worker     fn test_hex_parser_succeeds() {
710*cf78ab8cSAndroid Build Coastguard Worker         let hex = ParsableBytes::from_str("beef1234");
711*cf78ab8cSAndroid Build Coastguard Worker         assert!(hex.is_ok(), "{}", hex.unwrap_err());
712*cf78ab8cSAndroid Build Coastguard Worker         let hex = hex.unwrap().unwrap();
713*cf78ab8cSAndroid Build Coastguard Worker 
714*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(vec![0xbeu8, 0xef, 0x12, 0x34], hex);
715*cf78ab8cSAndroid Build Coastguard Worker     }
716*cf78ab8cSAndroid Build Coastguard Worker 
717*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_hex_parser_prefix_succeeds()718*cf78ab8cSAndroid Build Coastguard Worker     fn test_hex_parser_prefix_succeeds() {
719*cf78ab8cSAndroid Build Coastguard Worker         let hex = ParsableBytes::from_str("0xabcd");
720*cf78ab8cSAndroid Build Coastguard Worker         assert!(hex.is_ok(), "{}", hex.unwrap_err());
721*cf78ab8cSAndroid Build Coastguard Worker         let hex = hex.unwrap().unwrap();
722*cf78ab8cSAndroid Build Coastguard Worker 
723*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(vec![0xabu8, 0xcd], hex);
724*cf78ab8cSAndroid Build Coastguard Worker     }
725*cf78ab8cSAndroid Build Coastguard Worker 
726*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_hex_parser_empty_str_succeeds()727*cf78ab8cSAndroid Build Coastguard Worker     fn test_hex_parser_empty_str_succeeds() {
728*cf78ab8cSAndroid Build Coastguard Worker         let hex = ParsableBytes::from_str("");
729*cf78ab8cSAndroid Build Coastguard Worker         assert!(hex.is_ok(), "{}", hex.unwrap_err());
730*cf78ab8cSAndroid Build Coastguard Worker         let hex = hex.unwrap().unwrap();
731*cf78ab8cSAndroid Build Coastguard Worker 
732*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(Vec::<u8>::new(), hex);
733*cf78ab8cSAndroid Build Coastguard Worker     }
734*cf78ab8cSAndroid Build Coastguard Worker 
735*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_hex_parser_bad_digit_fails()736*cf78ab8cSAndroid Build Coastguard Worker     fn test_hex_parser_bad_digit_fails() {
737*cf78ab8cSAndroid Build Coastguard Worker         assert!(ParsableBytes::from_str("0xabcdefg").is_err());
738*cf78ab8cSAndroid Build Coastguard Worker     }
739*cf78ab8cSAndroid Build Coastguard Worker }
740