From 58186f22b3c8a8784bf16b89612a07439507ad8a Mon Sep 17 00:00:00 2001 From: Einar Omang Date: Thu, 23 Jan 2025 16:54:46 +0100 Subject: [PATCH] Add separate README files to get nicer pages on crates.io --- Cargo.lock | 5 +- README.md | 2 +- async-opcua-client/Cargo.toml | 5 +- async-opcua-client/README.md | 61 ++++++++++++ async-opcua-codegen/Cargo.toml | 6 +- async-opcua-codegen/README.md | 9 ++ async-opcua-core-namespace/Cargo.toml | 4 +- async-opcua-core-namespace/README.md | 7 ++ async-opcua-core/Cargo.toml | 4 +- async-opcua-core/README.md | 14 +++ async-opcua-crypto/Cargo.toml | 4 +- async-opcua-crypto/README.md | 17 ++++ async-opcua-macros/Cargo.toml | 4 +- async-opcua-macros/README.md | 21 +++++ async-opcua-nodes/Cargo.toml | 4 +- async-opcua-nodes/README.md | 13 +++ async-opcua-server/Cargo.toml | 11 ++- async-opcua-server/README.md | 93 +++++++++++++++++++ .../src/subscriptions/monitored_item.rs | 1 - async-opcua-types/Cargo.toml | 4 +- async-opcua-types/README.md | 65 +++++++++++++ async-opcua-xml/Cargo.toml | 4 +- async-opcua-xml/README.md | 11 +++ async-opcua/Cargo.toml | 10 +- async-opcua/README.md | 76 +++++++++++++++ docs/opc_ua_overview.md | 2 +- 26 files changed, 422 insertions(+), 35 deletions(-) create mode 100644 async-opcua-client/README.md create mode 100644 async-opcua-codegen/README.md create mode 100644 async-opcua-core-namespace/README.md create mode 100644 async-opcua-core/README.md create mode 100644 async-opcua-crypto/README.md create mode 100644 async-opcua-macros/README.md create mode 100644 async-opcua-nodes/README.md create mode 100644 async-opcua-server/README.md create mode 100644 async-opcua-types/README.md create mode 100644 async-opcua-xml/README.md create mode 100644 async-opcua/README.md diff --git a/Cargo.lock b/Cargo.lock index 479cb37b7..39b056697 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,7 +121,6 @@ dependencies = [ "async-opcua-crypto", "async-opcua-nodes", "async-opcua-types", - "async-opcua-xml", "async-trait", "chrono", "futures", @@ -136,7 +135,7 @@ dependencies = [ [[package]] name = "async-opcua-codegen" -version = "0.13.0" +version = "0.14.0" dependencies = [ "async-opcua-xml", "base64 0.22.1", @@ -282,6 +281,7 @@ dependencies = [ "async-opcua-core-namespace", "async-opcua-crypto", "async-opcua-nodes", + "async-opcua-server", "async-opcua-types", "async-trait", "bitflags", @@ -293,7 +293,6 @@ dependencies = [ "postcard", "regex", "serde", - "serde_json", "tokio", "tokio-util", ] diff --git a/README.md b/README.md index ae2e17ca4..46f58670f 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Tutorials / user guides are still work in progress. The API documentation is generated from the latest published crates. This may be some way behind current development. - + # Samples diff --git a/async-opcua-client/Cargo.toml b/async-opcua-client/Cargo.toml index 178420c0e..25be5fae0 100644 --- a/async-opcua-client/Cargo.toml +++ b/async-opcua-client/Cargo.toml @@ -8,8 +8,8 @@ repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] categories = ["embedded", "network-programming"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua-client/" edition = "2021" [lib] @@ -33,4 +33,3 @@ async-opcua-types = { path = "../async-opcua-types", version = "0.14.0" } async-opcua-core = { path = "../async-opcua-core", version = "0.14.0" } async-opcua-crypto = { path = "../async-opcua-crypto", version = "0.14.0" } async-opcua-nodes = { path = "../async-opcua-nodes", version = "0.14.0" } -async-opcua-xml = { path = "../async-opcua-xml", optional = true, version = "0.14.0" } diff --git a/async-opcua-client/README.md b/async-opcua-client/README.md new file mode 100644 index 000000000..dacba663f --- /dev/null +++ b/async-opcua-client/README.md @@ -0,0 +1,61 @@ +# Async OPC-UA Client + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This library defines a fully capable async OPC-UA client based on tokio. You will need a tokio runtime to use this client at all, as it depends on tokio for network and I/O. + +The OPC UA Client module contains the functionality necessary for a client to connect to an OPC UA server, authenticate itself, send messages, receive responses, get values, browse the address space and provide callbacks for things to be propagated to the client. + +Once the `Client` is created it can connect to a server by creating a `Session`. Multiple sessions can be created from the same client. + +To connect to a session, you can either use one of the `connect_*` methods on the `Client`, or the `SessionBuilder` which is more flexible. + +Once connected, you will get a `Session` object, and an `EventLoop`. The event loop must be continuously polled while you use the session, you can do this manually, to monitor the state of the connection, or you can just spawn it on a tokio task using `event_loop.spawn()`. + +The `Session` object contains methods for each OPC-UA service as of version 1.05 of the standard. Each service may be called directly with its corresponding method, i.e. + +```rust +session.read(...).await? +``` + +or by using the request builder: + +```rust +Read::new(&session).nodes_to_read(...).send(session.channel()).await? +``` + +By using the request builder, it is also possible to retry requests by using `Session::send_with_retry`. + +## Example + +```rust +#[tokio::main] +async fn main() { + let mut client = ClientBuilder::new() + .application_name("My First Client") + .application_uri("urn:MyFirstClient") + .create_sample_keypair(true) + .trust_server_certs(false) + .session_retry_limit(3) + .client().unwrap(); + // Create an endpoint. The EndpointDescription can be made from a tuple consisting of + // the endpoint url, security policy, message security mode and user token policy. + let endpoint: EndpointDescription = ( + "opc.tcp://localhost:4855/", + "None", + MessageSecurityMode::None, + UserTokenPolicy::anonymous() + ).into(); + // Create the session and event loop + let (session, event_loop) = client.connect_to_matching_endpoint(endpoint, IdentityToken::Anonymous).await.unwrap(); + let handle = event_loop.spawn(); + session.wait_for_connection().await; + // From here you can call services on the session... + // It is good practice to exit the session when you are done, since + // OPC-UA servers may keep clients that exit uncleanly alive for some time. + let _ = session_c.disconnect().await; + handle.await.unwrap(); +} +``` + +See [simple client](../samples/simple-client/) for a slightly more elaborate example. diff --git a/async-opcua-codegen/Cargo.toml b/async-opcua-codegen/Cargo.toml index 6d95c03e3..7d6b6c3ef 100644 --- a/async-opcua-codegen/Cargo.toml +++ b/async-opcua-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-opcua-codegen" -version = "0.13.0" +version = "0.14.0" edition = "2021" description = "OPC UA code generation library" authors = ["Einar Omang "] @@ -8,8 +8,8 @@ homepage = "https://github.com/freeopcua/async-opcua" repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua/" [lib] name = "opcua_codegen" diff --git a/async-opcua-codegen/README.md b/async-opcua-codegen/README.md new file mode 100644 index 000000000..c42aef2d9 --- /dev/null +++ b/async-opcua-codegen/README.md @@ -0,0 +1,9 @@ +# Async OPC-UA Core + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This is a command line tool to generate code for use with the async-opcua client and server libraries. + +To use, define a [YAML](https://yaml.org/) configuration file with a list of code gen targets, including OPC-UA BSD (Binary Schema Definition) files, XSD (XML Schema Definition) files, and NodeSet2.xml files. + +See the [custom-codegen](../samples/custom-codegen/) sample for an example of how this can be done. diff --git a/async-opcua-core-namespace/Cargo.toml b/async-opcua-core-namespace/Cargo.toml index c6ccfd217..60f107f0f 100644 --- a/async-opcua-core-namespace/Cargo.toml +++ b/async-opcua-core-namespace/Cargo.toml @@ -8,8 +8,8 @@ repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] categories = ["embedded", "network-programming"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua-core-namespace/" edition = "2021" [lib] diff --git a/async-opcua-core-namespace/README.md b/async-opcua-core-namespace/README.md new file mode 100644 index 000000000..c1f5338a4 --- /dev/null +++ b/async-opcua-core-namespace/README.md @@ -0,0 +1,7 @@ +# Async OPC-UA Core Namespace + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This library contains generated code for nodes and events defined in the core OPC-UA standard. It is intended to be used with [async-opcua-server](https://crates.io/crates/async-opcua-server), to define the core node manager. + +All OPC-UA servers must define the core namespace, which is primarily the core type hierarchy. Version 1.05 of the OPC-UA standard was used to generate this code, using `async-opcua-codegen`. diff --git a/async-opcua-core/Cargo.toml b/async-opcua-core/Cargo.toml index fbf5484c0..86b38d382 100644 --- a/async-opcua-core/Cargo.toml +++ b/async-opcua-core/Cargo.toml @@ -8,8 +8,8 @@ repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] categories = ["embedded", "network-programming"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua-core/" edition = "2021" [lib] diff --git a/async-opcua-core/README.md b/async-opcua-core/README.md new file mode 100644 index 000000000..359b92976 --- /dev/null +++ b/async-opcua-core/README.md @@ -0,0 +1,14 @@ +# Async OPC-UA Core + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This library contains common types used by the server and client parts of the `async-opcua` library. + +You will typically use either the client or server, and rarely use this library directly. + +`async-opcua-core` covers a few different areas of shared functionality. + + - The `RequestMessage` and `ResponseMessage` enums, which discriminate over the possible messages in an OPC-UA request and response. + - Core message types such as `HelloMessage`, `AcknowledgeMessage`, `MessageChunk`, etc. and components of these. + - The low-level implementation of the opc/tcp protocol. + - The `SecureChannel` type, which uses [async-opcua-crypto](https://crates.io/crates/async-opcua-crypto) to encrypt OPC-UA messages. diff --git a/async-opcua-crypto/Cargo.toml b/async-opcua-crypto/Cargo.toml index 6fb8d4c14..45ce8e3e6 100644 --- a/async-opcua-crypto/Cargo.toml +++ b/async-opcua-crypto/Cargo.toml @@ -8,8 +8,8 @@ repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] categories = ["embedded", "network-programming"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua-crypto/" edition = "2021" [lib] diff --git a/async-opcua-crypto/README.md b/async-opcua-crypto/README.md new file mode 100644 index 000000000..12936262d --- /dev/null +++ b/async-opcua-crypto/README.md @@ -0,0 +1,17 @@ +# Async OPC-UA Crypto + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This defines common cryptographic tooling for the OPC-UA protocol using libraries from [Rust Crypto](https://github.com/rustcrypto). + +Currently supported security policies: + + - `Basic256` (Deprecated) + - `Basic128Rsa15` (Deprecated) + - `Basic256Sha256` (Deprecated) + - `Aes256Sha256RsaPss` + - `Aes128Sha256Oaep` + +There's also some general tooling for working with and generating X509 Certificates, as well as support for the _legacy_ password encryption/decryption scheme in OPC-UA. + +You are unlikely to want to use this library directly, but it is used in both the server and client parts `async-opcua`. diff --git a/async-opcua-macros/Cargo.toml b/async-opcua-macros/Cargo.toml index d35f25a30..1b4b1ccd6 100644 --- a/async-opcua-macros/Cargo.toml +++ b/async-opcua-macros/Cargo.toml @@ -8,8 +8,8 @@ repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] categories = ["embedded", "network-programming"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua-macros/" edition = "2021" [features] diff --git a/async-opcua-macros/README.md b/async-opcua-macros/README.md new file mode 100644 index 000000000..96888b5f5 --- /dev/null +++ b/async-opcua-macros/README.md @@ -0,0 +1,21 @@ +# Async OPC-UA Macros + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This defines a number of utility macros for encoding, decoding, and defining types to help write server and client software. + +Currently defines: + + - `Event`, a macro for deriving the `Event` trait on custom event types. + - `EventField`, a macro for deriving the `EventField` trait, used for types that can be part of OPC-UA events. + - `FromXml`, with the `"xml"` feature. Derives conversion from XML objects in `NodeSet2` files. + - `JsonEncodable`, with the `"json"` feature. Derives streaming serialization using OPC-UA JSON. + - `JsonDecodable`, with the `"json"` feature. Derives streaming deserialization using OPC-UA JSON. + - `BinaryEncodable`, derives streaming serialization using OPC-UA Binary. + - `BinaryDecodable`, derives streaming deserialization using OPC-UA Binary. + - `UaEnum`, derives the `UaEnum` and a few other traits to make it easier to define custom OPC-UA enums. + + ## Features + + - `json`, adds the `JsonEncodable` and `JsonDecodable` macros. + - `xml`, adds the `FromXml` macro. diff --git a/async-opcua-nodes/Cargo.toml b/async-opcua-nodes/Cargo.toml index 62d4fcf6c..63de44ee3 100644 --- a/async-opcua-nodes/Cargo.toml +++ b/async-opcua-nodes/Cargo.toml @@ -8,8 +8,8 @@ repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] categories = ["embedded", "network-programming"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua-nodes/" edition = "2021" [lib] diff --git a/async-opcua-nodes/README.md b/async-opcua-nodes/README.md new file mode 100644 index 000000000..b9c15e221 --- /dev/null +++ b/async-opcua-nodes/README.md @@ -0,0 +1,13 @@ +# Async OPC-UA Nodes + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This library defines types used mainly in the [async-opcua-server](https://crates.io/crates/async-opcua-server) library as part of in-memory node managers, but also utilities for importing `NodeSet2` XML files to Rust. + +Primarily, this library defines a type for each OPC-UA NodeClass `Object`, `Variable`, `Method`, `View`, `ObjectType`, `VariableType`, `DataType`, and `ReferenceType`, as well as builders for all of these. There's also a common enum over all of these `NodeType`. + +A few other common types are also defined here, such as the `TypeTree` trait, used in the server to provide the server with a view of all the types defined on the server, and the `NodeSet2Import` type, used to import `NodeSet2` files into memory. + +## Features + + - `xml` adds support for reading NodeSet2 XML files into `NodeType`. diff --git a/async-opcua-server/Cargo.toml b/async-opcua-server/Cargo.toml index ad4db9848..b76e6f9d0 100644 --- a/async-opcua-server/Cargo.toml +++ b/async-opcua-server/Cargo.toml @@ -8,15 +8,15 @@ repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] categories = ["embedded", "network-programming"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua-server/" edition = "2021" [lib] name = "opcua_server" [features] -default = ["discovery-server-registration", "generated-address-space"] +default = ["generated-address-space"] # Includes all the code to populate the address space with the default node set. This is something that embedded # systems may or may not require. generated-address-space = ["async-opcua-core-namespace"] @@ -38,7 +38,6 @@ log = { workspace = true } parking_lot = { workspace = true } regex = { workspace = true } serde = { workspace = true } -serde_json = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true } postcard = { workspace = true } @@ -51,3 +50,7 @@ async-opcua-nodes = { path = "../async-opcua-nodes", version = "0.14.0" } async-opcua-client = { path = "../async-opcua-client", optional = true, version = "0.14.0" } async-opcua-core-namespace = { path = "../async-opcua-core-namespace", optional = true, version = "0.14.0" } + + +[dev-dependencies] +async-opcua-server = { path = ".", features = ["discovery-server-registration", "json"]} \ No newline at end of file diff --git a/async-opcua-server/README.md b/async-opcua-server/README.md new file mode 100644 index 000000000..b9d5657e3 --- /dev/null +++ b/async-opcua-server/README.md @@ -0,0 +1,93 @@ +# Async OPC-UA Server + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This library defines a general purpose OPC-UA server based on tokio. You will need a tokio runtime to run the server, as it depends on tokio for network and I/O. + +The OPC-UA Server module contains functionality for defining an OPC-UA server containing custom structures that build on the core namespace. Each connected client is spawned on its own tokio task, and each received message also spawns a task, this is similar in design to how webserver frameworks such as `Axum` work. + +To create a server, first build and configure one using the `ServerBuilder`, then run it using `Server::run`. The server, when constructed, returns a `Server` and a `ServerHandle`. The server handle contains references to core server types and can be used to manage the server externally, modifying its contents, stopping it, etc. + +## Features + + - `discovery-server-registration`, pulls in the `async-opcua-client` library to act as a client, attempting to register the server on a local discovery server. + - `generated-address-space`, enabled by default. This feature pulls in the `async-opcua-core-namespace` crate, which contains the entire core OPC-UA namespace. This is used to populate the core OPC-UA namespace. Without this, it is difficult to make a compliant OPC-UA server. + - `json`, adds support for deserializing and serializing OPC-UA types as JSON. + +## Example + +```rust +#[tokio::main] +async fn main() { + // Create an OPC UA server with sample configuration and default node set + let (server, handle) = ServerBuilder::new() + .build_info(BuildInfo { + product_uri: "my:server:uri".into(), + manufacturer_name: "Me".into(), + product_name: "My OPC-UA Server".into(), + // Here you could use something to inject the build time, version, number at compile time + software_version: "0.1.0".into(), + build_number: "1".into(), + build_date: DateTime::now(), + }) + .with_node_manager(simple_node_manager( + NamespaceMetadata { + namespace_uri: "urn:SimpleServer".to_owned(), + ..Default::default() + }, + "simple", + )) + .application_uri("urn:my-opcua-server") + .certificate_path("own/cert.der") + .private_key_path("private/private.pem") + .pki_dir("./pki") + // Add an endpoint. + .add_endpoint( + "none", + ( + endpoint_path, + SecurityPolicy::None, + MessageSecurityMode::None, + &[ANONYMOUS_USER_TOKEN_ID] as &[&str], + ), + ) + // Add a custom node manager + .with_node_manager(simple_node_manager( + NamespaceMetadata { + namespace_uri: "urn:my-opcua-server".to_owned(), + ..Default::default() + }, + "simple", + )) + .trust_client_certs(true) + .create_sample_keypair(true) + .build() + .unwrap(); + let node_manager = handle + .node_managers() + .get_of_type::() + .unwrap(); + let ns = handle.get_namespace_index("urn:my-opcua-server").unwrap(); + + // We can now edit the simple node manager by adding nodes to it... + + // If you don't register a ctrl-c handler, the server will close without + // informing clients. + let handle_c = handle.clone(); + tokio::spawn(async move { + if let Err(e) = tokio::signal::ctrl_c().await { + warn!("Failed to register CTRL-C handler: {e}"); + return; + } + handle_c.cancel(); + }); + + // Run the server. This does not ordinarily exit so you must Ctrl+C to terminate + server.run().await.unwrap(); +} + +``` + +For more detailed documentation on the server see [server.md](../docs/server.md) and [advanced_server.md](../docs/advanced_server.md). + +The server SDK is _very_ flexible. There are mechanisms to make simple usage easier, but writing an OPC-UA server is never going to be a simple task. diff --git a/async-opcua-server/src/subscriptions/monitored_item.rs b/async-opcua-server/src/subscriptions/monitored_item.rs index 0ab5397c4..5b42aadae 100644 --- a/async-opcua-server/src/subscriptions/monitored_item.rs +++ b/async-opcua-server/src/subscriptions/monitored_item.rs @@ -13,7 +13,6 @@ use opcua_types::{ }; #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "json", serde::Serialize)] pub enum Notification { MonitoredItemNotification(MonitoredItemNotification), Event(EventFieldList), diff --git a/async-opcua-types/Cargo.toml b/async-opcua-types/Cargo.toml index 14324699f..4808b60c3 100644 --- a/async-opcua-types/Cargo.toml +++ b/async-opcua-types/Cargo.toml @@ -8,8 +8,8 @@ repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] categories = ["embedded", "network-programming"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua-types/" edition = "2021" [features] diff --git a/async-opcua-types/README.md b/async-opcua-types/README.md new file mode 100644 index 000000000..688df1300 --- /dev/null +++ b/async-opcua-types/README.md @@ -0,0 +1,65 @@ +# Async OPC-UA Types + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This library contains a framework for encoding and decoding OPC-UA messages, as well as generated code for all types defined in the standard. + +This includes: + +1. All of the built-in data types described in OPC Part 6 Chapter 5 that are encodable. +2. All of the standard data types described in OPC Part 3 Chapter 8 (if not covered by 1.). +3. Autogenerated data types and request / responses as described in OPC Part 4. + +Notable types include + + - `Variant`, a discriminated union of a number of primitive types. + - `ExtensionObject` a wrapper around an OPC-UA structure identified by its encoding ID. + +## Features + + - `json`, enables OPC-UA JSON encoding and decoding. + - `xml`, enables OPC-UA XML decoding, notably this is _not_ yet full support for OPC-UA XML, only a limited subset intended for use with `NodeSet2` XML files. + +## Usage + +Usually this library is used as part of an OPC-UA client or server. + +Encoding is done by writing to a type implementing `std::io::Write`, and decoding +by reading from a type implementing `std::io::Read`: + +```rust +let context_owned = ContextOwned::default(); +let context = context_owned.context(); + +let my_opcua_value = Variant::from(123); + +// Get the byte length before encoding. +// This is not actually required, but can be useful. +let byte_len = my_opcua_value.byte_len(&context); +let mut stream = Cursor::new(vec![0u8; byte_len]); + +// Encode to a stream. +let start_pos = stream.position(); +value.encode(&mut stream, &context)?; + +stream.seek(SeekFrom::Start(0))?; +let decoded = Variant::decode(&mut stream, &context)?; + +assert_eq!(my_opcua_value, decoded); +``` + +### Custom types + +In order to make a custom OPC-UA structure, it must implement a number of traits depending on which features are enabled: + + - `BinaryEncodable` and `BinaryDecodable`, implementing encoding using the OPC-UA Binary protocol. + - `JsonEncodable` and `JsonDecodable`, implementing encoding using OPC-UA JSON, if the `"json"` feature is enabled. + - `FromXml`, loading the type from a NodeSet2 XML file, if the `"xml"` feature is enabled. + - `Clone`, `Send`, `Sync`, `Debug`, `PartialEq` are all required. + - `ExpandedMessageInfo`, which provides full encoding IDs. + +A type that satisfies these requirements can be stored in an `ExtensionObject` and sent to OPC-UA. In order to _receive_ the type, it must be added to a `TypeLoader`. + +`BinaryEncodable`, `BinaryDecodable`, `JsonEncodable`, `JsonDecodable`, and `FromXml` all have derive macros. + +Enums are simpler, the easiest way to make a custom OPC-UA enum (not a union) is to derive the `UaEnum` trait, which also implements a few other traits needed for numeric OPC-UA enums. diff --git a/async-opcua-xml/Cargo.toml b/async-opcua-xml/Cargo.toml index dc701fb87..d12bb2437 100644 --- a/async-opcua-xml/Cargo.toml +++ b/async-opcua-xml/Cargo.toml @@ -8,8 +8,8 @@ homepage = "https://github.com/freeopcua/async-opcua" repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua-xml/" [lib] name = "opcua_xml" diff --git a/async-opcua-xml/README.md b/async-opcua-xml/README.md new file mode 100644 index 000000000..3ffd9acab --- /dev/null +++ b/async-opcua-xml/README.md @@ -0,0 +1,11 @@ +# Async OPC-UA XML + +Part of [async-opcua](https://crates.io/crates/async-opcua), a general purpose OPC-UA library in rust. + +This implements parsing of some XML schemas needed for code generation and loading of `NodeSet2` XML files. + +Currently, we have support for: + + - XSD files, the subset needed for OPC-UA specs. + - OPC-UA BSD files. + - OPC-UA NodeSet2 files. diff --git a/async-opcua/Cargo.toml b/async-opcua/Cargo.toml index 22ccd8662..f91f46d22 100644 --- a/async-opcua/Cargo.toml +++ b/async-opcua/Cargo.toml @@ -8,19 +8,19 @@ repository = "https://github.com/freeopcua/async-opcua" license = "MPL-2.0" keywords = ["opcua", "opc", "ua"] categories = ["embedded", "network-programming"] -readme = "../README.md" -documentation = "https://docs.rs/opcua/" +readme = "README.md" +documentation = "https://docs.rs/async-opcua/" edition = "2021" [lib] name = "opcua" [features] -default = ["server", "client"] +default = [] all = ["server", "client", "console-logging"] # Server default settings server = ["base-server", "generated-address-space"] -# Base server, without the core address space and discovery server registration +# Base server, without the core address space. base-server = ["async-opcua-server", "async-opcua-nodes"] # Client default settings client = ["async-opcua-client"] @@ -68,4 +68,4 @@ tokio = { workspace = true } tokio-util = { workspace = true } # Include console-logging and json when building tests -async-opcua = { path = ".", features = ["console-logging", "json", "xml"] } +async-opcua = { path = ".", features = ["all", "json", "xml"] } diff --git a/async-opcua/README.md b/async-opcua/README.md new file mode 100644 index 000000000..5b8fa99a9 --- /dev/null +++ b/async-opcua/README.md @@ -0,0 +1,76 @@ +# Async OPC-UA + +This is an [OPC-UA](https://opcfoundation.org/about/opc-technologies/opc-ua/) server / client API implementation for Rust. + +OPC-UA is an industry standard for information modeling and communication. It is used for control systems, IoT, etc. + +The OPC-UA standard is very large and complex, and implementations are often flawed. The strictness of Rust makes it a good choice for implementing OPC-UA, and the performance characteristics are useful when creating OPC-UA tooling that will run in constrained environments. + +Read the [compatibility](../docs/compatibility.md) page for how the implementation conforms with the OPC UA spec. + +Read the [change log](../CHANGELOG.md) for changes per version as well as aspirational / upcoming work. + +## This is a fork + +This is a fork of [opcua](https://github.com/locka99/opcua) with a broader goal of a generic OPC-UA implementation and a number of different design decisions. See [fork.md](../docs/fork.md) for details on this decision and the differences between this library and the original. + +# MSRV Policy + +We target the latest `stable` rust compiler and make no promises of support for older rust versions. We have use for several recent and upcoming rust features so this is unlikely to change. + +# License + +The code is licenced under [MPL-2.0](https://opensource.org/licenses/MPL-2.0). Like all open source code, you use this code at your own risk. + +# Documentation + +Tutorials for using the server and client are available in the `async-opcua` github repo: + +* [Client Tutorial](../docs/client.md) +* [Server Tutorial](../docs/server.md) +* [General documentation](../docs/opc_ua_overview.md) +* [Library design](../docs/design.md) + +There are also generated API docs on crates.io. + +# Features + +* `all`, enables the `server`, `client`, and `console-logging` features. +* `server`, includes the server SDK. +* `base-server`, includes the server SDK, but without the core address space. Most users should use the `server` feature. +* `client`, includes the client SDK. +* `console-logging`, adds a method to install simple console logging. You do not have to use this, we use `log` for logging so you can include a library like [env_logger](https://docs.rs/env_logger/latest/env_logger/) yourself. +* `json`, adds support for OPC-UA JSON to generated types. +* `generated-address-space`, adds the core OPC-UA namespace. This is usually required for compliant OPC-UA servers. +* `discovery-server-registration`, allows the server to register itself with a local discovery server, by pulling in a client. +* `xml`, adds support for loading generated types from XML, and for loading `NodeSet2.xml` files. + +By default, no features are enabled, so only core types and functionality is pulled in. You will typically want to enable either the `client` or `server` features. + +# Crates + +Note that this library is split into multiple different crates. OPC-UA is a complex standard, and implementations typically involve a great deal of generated code. In order to allow good isolation of different components, and to speed up compile times, the `async-opcua` library is split into several crates. + +* `async-opcua`, the general crate that most users will use as the entry point. Contains a few utilities, but mostly just re-exports the other crates. I.e. `async-opcua-types` is re-exported under `opcua::types`. +* [async-opcua-client](https://crates.io/crates/async-opcua-client) contains a fully featured OPC-UA client. +* [async-opcua-server](https://crates.io/crates/async-opcua-server) contains a flexible SDK for building OPC-UA servers. +* [async-opcua-core](https://crates.io/crates/async-opcua-core) contains common primitives and tools for implementing the OPC-UA communication protocol. +* [async-opcua-core-namespace](https://crates.io/crates/async-opcua-core-namespace) contains generated code for the entire OPC-UA core namespace. +* [async-opcua-crypto](https://crates.io/crates/async-opcua-crypto) contains common cryptographic tooling for the OPC-UA protocol using libraries from [Rust Crypto](https://github.com/rustcrypto). +* [async-opcua-macros](https://crates.io/crates/async-opcua-macros) contains a few macros used to create custom extensions to the OPC-UA standard. +* [async-opcua-nodes](https://crates.io/crates/async-opcua-nodes) contains an in-memory representation of OPC-UA nodes, used by the server. +* [async-opcua-types](https://crates.io/crates/async-opcua-types) contains the framework used by serialization and deserialization to OPC-UA Binary and JSON, and deserialization from OPC-UA XML. It also contains generated code defining types from the OPC-UA standard, and manual implementations of a number of core types. +* [async-opcua-xml](https://crates.io/crates/async-opcua-xml) contains an implementation of XML decoding for certain XML schemas relevant to OPC-UA. + +# Samples + +The `async-opcua` github repo contains a number of samples that may be used as reference when writing your own clients and servers. + +1. [`simple-server`](../samples/simple-server) - an OPC-UA server that adds 4 variables v1, v2, v3 and v4 and updates them from a timer via push and pull mechanisms. +2. [`simple-client`](../samples/simple-client) - an OPC-UA client that connects to a server and subscribes to the values of v1, v2, v3 and v4. +3. [`discovery-client`](../samples/discovery-client) - an OPC-UA client that connects to a discovery server and lists the servers registered on it. +4. [`chess-server`](../samples/chess-server) - an OPC-UA server that connects to a chess engine as its back end and updates variables representing the state of the game. +5. [`demo-server`](../samples/demo-server) - an OPC-UA server that is more complex than the simple server and can be used for compliance testing. +6. [`mqtt-client`](../samples/mqtt-client) - an OPC-UA client that subscribes to some values and publishes them to an MQTT broker. +7. [`event-client`](../samples/event-client) - an OPC-UA client that will connect to a server and subscribe to alarms / events. +8. [`custom-codegen](../samples/custom-codegen) - an OPC-UA server that implements an OPC-UA companion standard generated using `async-opcua-codegen`. diff --git a/docs/opc_ua_overview.md b/docs/opc_ua_overview.md index 27aeafb42..6890e0ed8 100644 --- a/docs/opc_ua_overview.md +++ b/docs/opc_ua_overview.md @@ -88,4 +88,4 @@ A server uses the identity in any way it chooses, e.g. allowing only authorized ## More information * [OPC Foundation](https://opcfoundation.org/) - +* [The standard](https://reference.opcfoundation.org/)