1use testioc::{Part, build};
2
3const USAGE: &str = "usage: bobview-testioc [--only ea|motor|cam]...
4
5Serves the example-services BL01T PVs over Channel Access and PV Access.
6Honours EPICS_CA_SERVER_PORT and the other EPICS_CA(S)_* variables, and
7EPICS_PVAS_SERVER_PORT / EPICS_PVAS_BROADCAST_PORT and the other
8EPICS_PVA(S)_* variables.";
9
10fn parse_args() -> Result<Vec<Part>, String> {
11 let mut parts = Vec::new();
12 let mut args = std::env::args().skip(1);
13 while let Some(arg) = args.next() {
14 match arg.as_str() {
15 "-h" | "--help" => {
16 println!("{USAGE}");
17 std::process::exit(0);
18 }
19 "--only" => {
20 let v = args.next().ok_or("--only needs a value")?;
21 let p = Part::parse(&v).ok_or(format!("unknown part {v:?}"))?;
22 if !parts.contains(&p) {
23 parts.push(p);
24 }
25 }
26 _ => return Err(format!("unexpected argument {arg:?}")),
27 }
28 }
29 if parts.is_empty() {
30 parts = Part::ALL.to_vec();
31 }
32 Ok(parts)
33}
34
35#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
36async fn main() {
37 let parts = match parse_args() {
38 Ok(p) => p,
39 Err(e) => {
40 eprintln!("bobview-testioc: {e}\n{USAGE}");
41 std::process::exit(2);
42 }
43 };
44 let ioc = match build(&parts, None, None).await {
45 Ok(ioc) => ioc,
46 Err(e) => {
47 eprintln!("bobview-testioc: startup failed: {e}");
48 std::process::exit(1);
49 }
50 };
51 let served: Vec<_> = parts.iter().map(|p| p.describe()).collect();
52 let (pva_tcp, pva_udp) = ioc.pva_ports();
53 println!(
54 "bobview-testioc: CA port {}, serving {}",
55 ioc.server.tcp_port(),
56 served.join("; ")
57 );
58 println!("bobview-testioc: PVA served on TCP port {pva_tcp}, UDP search port {pva_udp}");
59 if let Err(e) = ioc.run().await {
60 eprintln!("bobview-testioc: {e}");
61 std::process::exit(1);
62 }
63}