From dd67d9858dd8dc9a376debf2a794dc3199d6d206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuzhan=20Emre=20=C3=96zdo=C4=9Fan?= Date: Tue, 26 Sep 2023 12:49:48 +0300 Subject: [PATCH] added necessary fields for event pulling edited readme added event example revision + added SourceType test fix --- README.md | 9 +++- onvif/Cargo.toml | 1 + onvif/examples/event.rs | 108 +++++++++++++++++++++++++++++++++++++ schema/src/tests.rs | 99 ++++++++++++++++++++++++++++++++++ wsdl_rs/b_2/src/lib.rs | 49 ++++++++++++++++- wsdl_rs/ws_addr/src/lib.rs | 2 +- 6 files changed, 264 insertions(+), 4 deletions(-) create mode 100644 onvif/examples/event.rs diff --git a/README.md b/README.md index d62244b..aac87f7 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Cargo.toml: onvif = { git = "https://github.com/lumeohq/onvif-rs" } ``` -## Troubleshooting +## Troubleshooting If you have an issue with OpenSSL build under Ubuntu, perform the following actions: @@ -41,6 +41,7 @@ cargo run --example discovery ``` To [inspect and control a camera](onvif/examples/camera.rs): + ```shell script cargo run --example camera -- help @@ -53,6 +54,12 @@ cargo run --example camera -- set-hostname \ cargo run --example camera -- get-stream-uris --uri=http://192.168.0.2:8000 ``` +To [pull events](onvif/examples/event.rs) from a camera: + +```shell script +cargo run --example event +``` + ## Dependencies - XSD -> Rust code generation: [xsd-parser-rs](https://github.com/lumeohq/xsd-parser-rs) diff --git a/onvif/Cargo.toml b/onvif/Cargo.toml index fbc742d..687ba73 100644 --- a/onvif/Cargo.toml +++ b/onvif/Cargo.toml @@ -43,3 +43,4 @@ futures-util = "0.3.8" structopt = "0.3.21" tokio = { version = "1.0.1", features = ["full"] } tracing-subscriber = "0.2.20" +b_2 = {path = "../wsdl_rs/b_2"} diff --git a/onvif/examples/event.rs b/onvif/examples/event.rs new file mode 100644 index 0000000..2c4bd2d --- /dev/null +++ b/onvif/examples/event.rs @@ -0,0 +1,108 @@ +// This example pulls messages related to the RuleEngine topic. +// RuleEngine topic consists of events related to motion detection. +// Tested with Dahua camera. +// Don't forget to set the camera's IP address, username and password. + +use onvif::soap::client::{ClientBuilder, Credentials}; +use schema::event::{ + self, CreatePullPointSubscription, CreatePullPointSubscriptionResponse, PullMessages, +}; +use url::Url; + +#[derive(Debug, Clone)] +pub struct Camera { + pub device_service_url: String, + pub username: String, + pub password: String, + pub event_service_url: String, +} + +impl Default for Camera { + fn default() -> Self { + Camera { + device_service_url: "http://192.168.1.100/onvif/device_service".to_string(), + username: "admin".to_string(), + password: "admin".to_string(), + event_service_url: "http://192.168.1.100/onvif/event_service".to_string(), + } + } +} + +#[tokio::main] +async fn main() { + let camera_ip = "192.168.1.50"; + let username = "admin"; + let password = "admin"; + + let camera: Camera = Camera { + device_service_url: format!("http://{}/onvif/device_service", camera_ip), + username: username.to_string(), + password: password.to_string(), + event_service_url: format!("http://{}/onvif/event_service", camera_ip), + }; + + let camera_sub: CreatePullPointSubscriptionResponse; + let creds: Credentials = Credentials { + username: camera.username.to_string(), + password: camera.password.to_string(), + }; + let event_client = ClientBuilder::new(&Url::parse(&camera.event_service_url).unwrap()) + .credentials(Some(creds)) + .build(); + let create_pull_sub_request = CreatePullPointSubscription { + initial_termination_time: None, + filter: Some(b_2::FilterType { + topic_expression: Some(b_2::TopicExpressionType { + dialect: "http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet".to_string(), + inner_text: "tns1:RuleEngine//.".to_string(), + }), + }), + subscription_policy: None, + }; + let create_pull_puint_sub_response = + event::create_pull_point_subscription(&event_client, &create_pull_sub_request).await; + camera_sub = match create_pull_puint_sub_response { + Ok(sub) => sub, + Err(e) => { + println!("Error: {:?}", e); + return; + } + }; + + let uri: Url = Url::parse(&camera_sub.subscription_reference.address).unwrap(); + let creds: Credentials = Credentials { + username: camera.username.to_string(), + password: camera.password.to_string(), + }; + let pull_msg_client = ClientBuilder::new(&uri) + .credentials(Some(creds)) + .auth_type(onvif::soap::client::AuthType::Digest) + .build(); + let pull_messages_request = PullMessages { + message_limit: 256, + timeout: xsd_types::types::Duration { + seconds: 1.0, + ..Default::default() + }, + }; + + // Main Loop + loop { + let pull_messages_response = + event::pull_messages(&pull_msg_client, &pull_messages_request).await; + let msg = match pull_messages_response { + Ok(msg) => msg, + Err(e) => { + println!("Error: {:?}", e); + continue; + } + }; + if msg.notification_message.len() > 0 { + println!("Notification Message: {:?}", msg.notification_message[0]); + } else { + println!("No new notification message"); + } + + tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; + } +} diff --git a/schema/src/tests.rs b/schema/src/tests.rs index 40da2da..391261b 100644 --- a/schema/src/tests.rs +++ b/schema/src/tests.rs @@ -620,3 +620,102 @@ fn extension_inside_extension() { let _ = yaserde::de::from_str::(ser).unwrap(); } + +#[tokio::test] +async fn operation_pull_messages() { + let req: event::PullMessages = Default::default(); + + let transport = FakeTransport { + response: r#" + + + 2023-09-26T07:55:11Z + + + 2023-09-26T07:56:05Z + + + + tns1:RuleEngine/CellMotionDetector/Motion + + + + + + + + + + + + + + + + "# + .into(), + }; + + let resp = event::pull_messages(&transport, &req).await.unwrap(); + + assert_eq!( + resp.notification_message[0].message.msg.source.simple_item[0].name, + "VideoSourceConfigurationToken" + ); + assert_eq!( + resp.notification_message[0].message.msg.source.simple_item[0].value, + "00000" + ); + assert_eq!( + resp.notification_message[0].message.msg.data.simple_item[0].name, + "IsMotion" + ); + assert_eq!( + resp.notification_message[0].message.msg.data.simple_item[0].value, + "true" + ); +} + +#[tokio::test] +async fn operation_create_pullpoint_subscription() { + let req: event::CreatePullPointSubscription = Default::default(); + + let transport = FakeTransport { + response: r#" + + + + http://192.168.88.108/onvif/Subscription?Idx=162 + + + + 2023-09-26T07:55:05Z + + + 2023-09-26T07:56:05Z + + + "# + .into(), + }; + + let resp = event::create_pull_point_subscription(&transport, &req) + .await + .unwrap(); + + assert_eq!( + resp.subscription_reference.address, + "http://192.168.88.108/onvif/Subscription?Idx=162" + ); +} diff --git a/wsdl_rs/b_2/src/lib.rs b/wsdl_rs/b_2/src/lib.rs index b312162..ba72cd8 100644 --- a/wsdl_rs/b_2/src/lib.rs +++ b/wsdl_rs/b_2/src/lib.rs @@ -27,6 +27,9 @@ impl Validate for QueryExpressionType {} pub struct TopicExpressionType { #[yaserde(attribute, rename = "Dialect")] pub dialect: String, + + #[yaserde(text)] + pub inner_text: String, } impl Validate for TopicExpressionType {} @@ -36,7 +39,10 @@ impl Validate for TopicExpressionType {} prefix = "wsnt", namespace = "wsnt: http://docs.oasis-open.org/wsn/b-2" )] -pub struct FilterType {} +pub struct FilterType { + #[yaserde(prefix = "wsnt", rename = "TopicExpression")] + pub topic_expression: Option, +} impl Validate for FilterType {} @@ -131,15 +137,54 @@ pub struct NotificationMessageHolderType { impl Validate for NotificationMessageHolderType {} +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +#[yaserde(prefix = "tt", namespace = "tt: http://www.onvif.org/ver10/schema")] +pub struct SimpleItemType { + // Item name. + #[yaserde(attribute, rename = "Name")] + pub name: String, + + // Item value. The type is defined in the corresponding description. + #[yaserde(attribute, rename = "Value")] + pub value: String, +} + pub mod notification_message_holder_type { use super::*; + #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] + #[yaserde(prefix = "tt", namespace = "tt: http://www.onvif.org/ver10/schema")] + pub struct DataType { + #[yaserde(prefix = "tt", rename = "SimpleItem")] + pub simple_item: Vec, + } + + #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] + #[yaserde(prefix = "tt", namespace = "tt: http://www.onvif.org/ver10/schema")] + pub struct SourceType { + #[yaserde(prefix = "tt", rename = "SimpleItem")] + pub simple_item: Vec, + } + + #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] + #[yaserde(prefix = "tt", namespace = "tt: http://www.onvif.org/ver10/schema")] + pub struct MessageTypeInner { + #[yaserde(prefix = "tt", rename = "Source")] + pub source: SourceType, + + #[yaserde(prefix = "tt", rename = "Data")] + pub data: DataType, + } + #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] #[yaserde( prefix = "wsnt", namespace = "wsnt: http://docs.oasis-open.org/wsn/b-2" )] - pub struct MessageType {} + pub struct MessageType { + #[yaserde(prefix = "tt", rename = "Message")] + pub msg: MessageTypeInner, + } impl Validate for MessageType {} } diff --git a/wsdl_rs/ws_addr/src/lib.rs b/wsdl_rs/ws_addr/src/lib.rs index 24546b8..074a901 100644 --- a/wsdl_rs/ws_addr/src/lib.rs +++ b/wsdl_rs/ws_addr/src/lib.rs @@ -14,7 +14,7 @@ pub type EndpointReference = EndpointReferenceType; )] pub struct EndpointReferenceType { #[yaserde(prefix = "tns", rename = "Address")] - pub address: AttributedURIType, + pub address: String, #[yaserde(prefix = "tns", rename = "ReferenceParameters")] pub reference_parameters: Option,