Skip to content

Commit a9612c8

Browse files
committed
feat: Add discovery primitives for UDP search and server discovery
- Implemented UDP search and server discovery functions in `discovery.rs`. - Added Python bindings for auto-broadcast targets, search targets, and PV list retrieval. - Introduced `Packet` class in `packet.rs` for raw-frame handling with header accessors. - Created dynamic Python-defined `Source` support in `source.rs` for PVAccess sources. - Added tests for nested `pvRequest` field selection on MONITOR in `spvirit_monitor_fields.rs`.
1 parent 2cf27d3 commit a9612c8

44 files changed

Lines changed: 5149 additions & 289 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 108 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spvirit-client/examples/pvget.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,42 @@
11
use spvirit_client::PvaClient;
22

3+
/// Minimal `pvget` example.
4+
///
5+
/// Usage:
6+
///
7+
/// ```text
8+
/// cargo run --example pvget -- MY:PV
9+
/// cargo run --example pvget -- MY:PV --fields value,alarm.severity
10+
/// ```
311
#[tokio::main]
412
async fn main() -> Result<(), Box<dyn std::error::Error>> {
5-
let pv = std::env::args()
6-
.nth(1)
7-
.unwrap_or_else(|| "MY:PV:NAME".into());
13+
let mut args = std::env::args().skip(1);
14+
let mut pv: Option<String> = None;
15+
let mut fields: Vec<String> = Vec::new();
816

17+
while let Some(a) = args.next() {
18+
match a.as_str() {
19+
"-F" | "--fields" => {
20+
let raw = args.next().ok_or("--fields requires a value")?;
21+
fields.extend(
22+
raw.split(',')
23+
.map(|s| s.trim().to_string())
24+
.filter(|s| !s.is_empty()),
25+
);
26+
}
27+
_ if pv.is_none() => pv = Some(a),
28+
other => return Err(format!("unexpected argument: {other}").into()),
29+
}
30+
}
31+
32+
let pv = pv.unwrap_or_else(|| "MY:PV:NAME".into());
933
let client = PvaClient::builder().build();
10-
let result = client.pvget(&pv).await?;
34+
let result = if fields.is_empty() {
35+
client.pvget(&pv).await?
36+
} else {
37+
let refs: Vec<&str> = fields.iter().map(String::as_str).collect();
38+
client.pvget_fields(&pv, &refs).await?
39+
};
1140
println!("{}: {}", result.pv_name, result.value);
1241
Ok(())
1342
}
Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,46 @@
11
use spvirit_client::PvaClient;
22
use std::ops::ControlFlow;
33

4+
/// Minimal `pvmonitor` example.
5+
///
6+
/// Usage:
7+
///
8+
/// ```text
9+
/// cargo run --example pvmonitor -- MY:PV
10+
/// cargo run --example pvmonitor -- MY:PV --fields value,alarm.severity
11+
/// ```
412
#[tokio::main]
513
async fn main() -> Result<(), Box<dyn std::error::Error>> {
6-
let pv = std::env::args()
7-
.nth(1)
8-
.unwrap_or_else(|| "MY:PV:NAME".into());
14+
let mut args = std::env::args().skip(1);
15+
let mut pv: Option<String> = None;
16+
let mut fields: Vec<String> = Vec::new();
917

18+
while let Some(a) = args.next() {
19+
match a.as_str() {
20+
"-F" | "--fields" => {
21+
let raw = args.next().ok_or("--fields requires a value")?;
22+
fields.extend(
23+
raw.split(',')
24+
.map(|s| s.trim().to_string())
25+
.filter(|s| !s.is_empty()),
26+
);
27+
}
28+
_ if pv.is_none() => pv = Some(a),
29+
other => return Err(format!("unexpected argument: {other}").into()),
30+
}
31+
}
32+
33+
let pv = pv.unwrap_or_else(|| "MY:PV:NAME".into());
1034
let client = PvaClient::builder().build();
11-
client
12-
.pvmonitor(&pv, |value| {
13-
println!("{value}");
14-
ControlFlow::Continue(())
15-
})
16-
.await?;
35+
let cb = |value: &spvirit_codec::spvd_decode::DecodedValue| {
36+
println!("{value}");
37+
ControlFlow::Continue(())
38+
};
39+
if fields.is_empty() {
40+
client.pvmonitor(&pv, cb).await?;
41+
} else {
42+
let refs: Vec<&str> = fields.iter().map(String::as_str).collect();
43+
client.pvmonitor_fields(&pv, &refs, cb).await?;
44+
}
1745
Ok(())
1846
}

spvirit-client/examples/pvput.rs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,50 @@
11
use spvirit_client::PvaClient;
22

3+
/// Minimal `pvput` example.
4+
///
5+
/// Usage:
6+
///
7+
/// ```text
8+
/// cargo run --example pvput -- MY:PV 42.0
9+
/// cargo run --example pvput -- MY:PV 42.0 --fields value
10+
/// ```
311
#[tokio::main]
412
async fn main() -> Result<(), Box<dyn std::error::Error>> {
5-
let pv = std::env::args()
6-
.nth(1)
13+
let mut args = std::env::args().skip(1);
14+
let mut positional: Vec<String> = Vec::new();
15+
let mut fields: Vec<String> = Vec::new();
16+
17+
while let Some(a) = args.next() {
18+
match a.as_str() {
19+
"-F" | "--fields" => {
20+
let raw = args.next().ok_or("--fields requires a value")?;
21+
fields.extend(
22+
raw.split(',')
23+
.map(|s| s.trim().to_string())
24+
.filter(|s| !s.is_empty()),
25+
);
26+
}
27+
_ => positional.push(a),
28+
}
29+
}
30+
31+
let pv = positional
32+
.first()
33+
.cloned()
734
.unwrap_or_else(|| "MY:PV:NAME".into());
8-
let value: f64 = std::env::args()
9-
.nth(2)
35+
let value: f64 = positional
36+
.get(1)
37+
.cloned()
1038
.unwrap_or_else(|| "42.0".into())
1139
.parse()?;
1240

1341
let client = PvaClient::builder().build();
14-
client.pvput(&pv, value).await?;
42+
if fields.is_empty() {
43+
client.pvput(&pv, value).await?;
44+
} else {
45+
let refs: Vec<&str> = fields.iter().map(String::as_str).collect();
46+
client.pvput_fields(&pv, value, &refs).await?;
47+
}
1548
println!("OK");
1649
Ok(())
1750
}

spvirit-client/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ pub mod types;
4747

4848
// --- Re-exports: high-level API ---
4949
pub use pva_client::{
50-
PvaChannel, PvaClient, PvaClientBuilder, client_from_opts, pvinfo, pvmonitor, pvput,
50+
PvaChannel, PvaClient, PvaClientBuilder, client_from_opts, pvinfo, pvmonitor,
51+
pvmonitor_fields, pvput, pvput_fields,
5152
};
5253
pub use pvlist::PvListSource;
5354

0 commit comments

Comments
 (0)