From a67327e927760dd21c485424bf023679c8fff8a5 Mon Sep 17 00:00:00 2001 From: Adam Lock Date: Sat, 24 Jul 2021 08:54:34 +0100 Subject: [PATCH] Reformat docs --- docs/compatibility.md | 39 ++++++----------- docs/cross-compile.md | 20 +++------ docs/design.md | 93 +++++++++++++---------------------------- docs/developer.md | 6 +-- docs/opc_ua_overview.md | 59 +++++++------------------- docs/server.md | 56 ++++++++----------------- docs/setup.md | 37 +++++----------- docs/testing.md | 8 ++-- 8 files changed, 95 insertions(+), 223 deletions(-) diff --git a/docs/compatibility.md b/docs/compatibility.md index 4b9a02eac..e184ca58e 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -2,8 +2,7 @@ ## OPC UA Binary Transport Protocol -This implementation supports the `opc.tcp://` binary protocol. Binary over `https://` is not supported although it is -conceivable that it could be supported. +This implementation supports the `opc.tcp://` binary protocol. Binary over `https://` is not supported although it is conceivable that it could be supported. The implement will **never** implement OPC UA over XML. XML hasn't see much adoption so this is no great impediment. @@ -74,10 +73,8 @@ The following services are supported in the server: ### Address Space / Nodeset -The standard OPC UA address space is exposed. OPC UA for Rust uses a script to generate code to create and -populate the standard address space. This functionality is controlled by a server build feature -`generated-address-space` that defaults to on but can be disabled if the full address space is not required. -When disabled, the address space will be empty apart from some root objects. +The standard OPC UA address space is exposed. OPC UA for Rust uses a script to generate code to create and populate the standard address space. This functionality is controlled by a server build feature +`generated-address-space` that defaults to on but can be disabled if the full address space is not required. When disabled, the address space will be empty apart from some root objects. ### Current limitations @@ -85,18 +82,16 @@ Currently the following are not supported * Diagnostic info. OPC UA allows for you to ask for diagnostics with any request. None is supplied at this time * Session resumption. If your client disconnects, all information is discarded. -* Default node set is mostly static. Certain fields of server information will contain their default values - unless explicitly set. +* Default node set is mostly static. Certain fields of server information will contain their default values unless explicitly set. * Access control is limited to setting read/write permissions on nodes that apply to all sessions. +* Multiple created sessions in a single transport. ## Client The client API API is synchronous - i.e. you call a function that makes a request and it returns -when the response is received or a timeout occurs. Under the surface it is asynchronous so that functionality -may be exposed at some point. +when the response is received or a timeout occurs. Under the surface it is asynchronous so that functionality may be exposed at some point. -The client exposes functions that correspond to the current server supported profile, i.e. look above at the -server services and there will be client-side functions that are analogous to those services. +The client exposes functions that correspond to the current server supported profile, i.e. look above at the server services and there will be client-side functions that are analogous to those services. In addition to the server services above, the following are also supported. @@ -140,11 +135,9 @@ The server and client support the following user identity tokens ## Crypto -OPC UA for Rust uses cryptographic algorithms for signing, verifying, encrypting and decrypting data. In addition -it creates, loads and saves certificates and keys. +OPC UA for Rust uses cryptographic algorithms for signing, verifying, encrypting and decrypting data. In addition it creates, loads and saves certificates and keys. -OpenSSL is used for encryption although it would be nice to go to a pure Rust implementation assuming a crate -delivers everything required. The crypto+OpenSSL code is isolated in an `opcua-crypto` crate. +OpenSSL is used for encryption although it would be nice to go to a pure Rust implementation assuming a crate delivers everything required. The crypto+OpenSSL code is isolated in an `opcua-crypto` crate. You must read the [setup](./setup.md) to configure OpenSSL for your environment. @@ -166,16 +159,10 @@ pki/ For encrypted connections the following applies: -* The server will reject the first connection from an unrecognized client. It will create a file representing -the cert in its the `pki/rejected/` folder and you, the administrator must move the cert to the `trusted/` folder -to permit connections from that client in future. - * NOTE: Signed certificates are not supported at this time. Potentially a cert signed with a trusted CA could - be automatically moved to the `trusted/` folder. -* Likewise, the client shall reject unrecognized servers in the same fashion, and the cert must be moved from the -`rejected/` to `trusted/` folder for connection to succeed. -* Servers that register with a discovery server may find the discovery server rejects their registration attempts if the -cert is unrecognized. In that case you must move your server's cert from discovery server's `rejected` to its -``trusted` folder, wherever that may be. e.g. on Windows it is under `C:\ProgramData\OPC Foundation\UA\Discovery\pki` +* The server will reject the first connection from an unrecognized client. It will create a file representing the cert in its the `pki/rejected/` folder and you, the administrator must move the cert to the `trusted/` folder to permit connections from that client in future. + * NOTE: Signed certificates are not supported at this time. Potentially a cert signed with a trusted CA could be automatically moved to the `trusted/` folder. +* Likewise, the client shall reject unrecognized servers in the same fashion, and the cert must be moved from the `rejected/` to `trusted/` folder for connection to succeed. +* Servers that register with a discovery server may find the discovery server rejects their registration attempts if the cert is unrecognized. In that case you must move your server's cert from discovery server's `rejected` to its ``trusted` folder, wherever that may be. e.g. on Windows it is under `C:\ProgramData\OPC Foundation\UA\Discovery\pki` There are switches in config that can be used to change the folder that certs are stored and to modify the trust model. diff --git a/docs/cross-compile.md b/docs/cross-compile.md index d9d5dc9ef..97b00cbee 100644 --- a/docs/cross-compile.md +++ b/docs/cross-compile.md @@ -1,11 +1,8 @@ # Cross-compiling OPC UA for Rust -The Raspberry Pi will be used as the target device for this document. If you have -another target, e.g. some `bitbake` concoction, then you will have to adapt the instructions -accordingly. +The Raspberry Pi will be used as the target device for this document. If you have another target, e.g. some `bitbake` concoction, then you will have to adapt the instructions accordingly. -Cross compilation is described in two ways - one that uses the `cross` tool and one that is manual. Depending on your needs you may decide on one - or the other. +Cross compilation is described in two ways - one that uses the `cross` tool and one that is manual. Depending on your needs you may decide on one or the other. ## Build with Cross @@ -23,20 +20,17 @@ Install [cross](https://github.com/rust-embedded/cross) for Rust. $ cargo install cross ``` -Install the tool according its own instructions. Ensure your docker permissions are -set. Now you can use `cross` in place of `cargo`. e.g. +Install the tool according its own instructions. Ensure your docker permissions are set. Now you can use `cross` in place of `cargo`. e.g. ``` $ cross build --all --target armv7-unknown-linux-gnueabihf ``` -The additional argument `--target armv7-unknown-linux-gnueabihf` tells `cross` to set up a build environment - before invoking `cargo`. +The additional argument `--target armv7-unknown-linux-gnueabihf` tells `cross` to set up a build environment before invoking `cargo`. ### SELinux conflict -The `cross` tool may have an [issue](https://github.com/rust-embedded/cross/issues/112) running `cargo` on - Fedora / Red Hat dists due to a SELinux policy. Read the bug for a workaround. +The `cross` tool may have an [issue](https://github.com/rust-embedded/cross/issues/112) running `cargo` on Fedora / Red Hat dists due to a SELinux policy. Read the bug for a workaround. ## Manual build @@ -44,9 +38,7 @@ The manual process gives you complete control on the build process but requires ### Credit -A [bug](https://github.com/locka99/opcua/issues/24) was raised asking how to -cross-compile OPC UA for Rust and someone kindly answered with references. The links below were -used to produce a working solution: +A [bug](https://github.com/locka99/opcua/issues/24) was raised asking how to cross-compile OPC UA for Rust and someone kindly answered with references. The links below were used to produce a working solution: 1. Install cross-compile utilities as shown [here](https://github.com/sodiumoxide/sodiumoxide) 2. Follow malbarbo's answer [here](https://stackoverflow.com/questions/37375712/cross-compile-rust-openssl-for-raspberry-pi-2) diff --git a/docs/design.md b/docs/design.md index 92c1a295d..767514af0 100644 --- a/docs/design.md +++ b/docs/design.md @@ -2,11 +2,9 @@ ## OPC UA -OPC UA is a very large standard. The specification runs across THIRTEEN(!) parts that describe services, address space, -security, information model, mappings (communication protocol), alarms, history, discovery, aggregates and more. +OPC UA is a very large standard. The specification runs across THIRTEEN(!) parts that describe services, address space, security, information model, mappings (communication protocol), alarms, history, discovery, aggregates and more. -This implementation obviously does not implement all that. Instead it is equivalent to the OPC UA -Embedded profile, which allows for: +This implementation obviously does not implement all that. Instead it is equivalent to the OPC UA Embedded profile, which allows for: * Communication over opc.tcp:// * Encryption @@ -38,9 +36,7 @@ The workspace also contains some other folders: ## Testing -Unit and integration tests will cover all functional aspects of the project. In addition -the implementation will be tested with 3rd party OPC UA implementations so the client / server parts can be tested in -isolation. +Unit and integration tests will cover all functional aspects of the project. In addition the implementation will be tested with 3rd party OPC UA implementations so the client / server parts can be tested in isolation. See the [testing](./testing.md) document. @@ -68,11 +64,9 @@ for something that adds variables to the address space and changes their values. ## Types -OPC UA defines a lot of types. Some of those correspond to Rust primitives while others are types, structures or -enums which are used by the protocol. All types are defined in the [`opcua-types`](../types) crate. +OPC UA defines a lot of types. Some of those correspond to Rust primitives while others are types, structures or enums which are used by the protocol. All types are defined in the [`opcua-types`](../types) crate. -All types can be encoded / decoded to a stream according to the opc.tcp:// binary transport. They do so by implementing - a `BinaryEncoder` trait. The three functions on this trait allow a struct to be deserialized, serialized, or the byte size of it to be calculated. +All types can be encoded / decoded to a stream according to the opc.tcp:// binary transport. They do so by implementing a `BinaryEncoder` trait. The three functions on this trait allow a struct to be deserialized, serialized, or the byte size of it to be calculated. Typically encoding will begin with a structure, e.g. `CreateSubscriptionRequest` whose implementation will encode each member in turn. @@ -92,8 +86,7 @@ let operand = obj.decode_inner::(&decoding_options)?; ### Primitives -OPC UA primitive types are referred to by their Rust equivalents, i.e. if the specification says `Int32`, the signature - of the function / struct will use `i32`: +OPC UA primitive types are referred to by their Rust equivalents, i.e. if the specification says `Int32`, the signature of the function / struct will use `i32`: * `Boolean` to `bool` * `SByte` to `i8` @@ -109,13 +102,9 @@ OPC UA primitive types are referred to by their Rust equivalents, i.e. if the sp ### Strings -The OPC UA type `String` is not directly analogous to a Rust `String`. The OPC UA definition maintains a -distinction between being a null value and being an empty string. This affects how the string is encoded -and could impact on application logic too. +The OPC UA type `String` is not directly analogous to a Rust `String`. The OPC UA definition maintains a distinction between being a null value and being an empty string. This affects how the string is encoded and could impact on application logic too. -For this reason, `String` is mapped onto a new Rust type `UAString` type which captures this behaviour. Basically -it is a struct that holds an optional `String` where `None` means null. The name is `UAString` because `String` -is such a fundamental type that it is easier to disambiguate by calling it something else rather than through module prefixing. +For this reason, `String` is mapped onto a new Rust type `UAString` type which captures this behaviour. Basically it is a struct that holds an optional `String` where `None` means null. The name is `UAString` because `String` is such a fundamental type that it is easier to disambiguate by calling it something else rather than through module prefixing. ### Basic types @@ -133,13 +122,11 @@ All of the basic OPC UA types are implemented by hand. * `DataValue` * `Variant` -A `Variant` is a special catch-all enum which can hold any other primitive or basic type, including arrays of the same. -The implementation uses a `Box` (allocated memory) for larger kinds of type to keep the stack size down. +A `Variant` is a special catch-all enum which can hold any other primitive or basic type, including arrays of the same. The implementation uses a `Box` (allocated memory) for larger kinds of type to keep the stack size down. ### Machine generated types -Machine generated types reside in `types/src/service_types`. The `enums.rs` holds all of the enumerations. A special -`impls.rs` contains additional hand written functions that are associated with types. +Machine generated types reside in `types/src/service_types`. The `enums.rs` holds all of the enumerations. A special `impls.rs` contains additional hand written functions that are associated with types. The `tools/schema/` directory contains NodeJS scripts that will generate Rust code from from OPC UA schemas. @@ -151,9 +138,7 @@ The `tools/schema/` directory contains NodeJS scripts that will generate Rust co ## Handling OPC UA names in Rust -All OPC UA enums, structs, fields, constants etc. will conform to Rust lint rules where it makes sense. -i.e. OPC UA uses pascal case for field names but the impl will use snake case, for example `requestHeader` is defined -as `request_header`. +All OPC UA enums, structs, fields, constants etc. will conform to Rust lint rules where it makes sense. i.e. OPC UA uses pascal case for field names but the impl will use snake case, for example `requestHeader` is defined as `request_header`. ```rust struct OpenSecureChannelRequest { @@ -173,8 +158,7 @@ pub enum SecurityPolicy { The enum will be turned in and out of a scalar value during serialization via a match. -Wherever possible Rust idioms will be used - enums, options and other conveniences of the language will be used to -represent data in the most efficient and strict way possible. e.g. here is the ExtensionObject +Wherever possible Rust idioms will be used - enums, options and other conveniences of the language will be used to represent data in the most efficient and strict way possible. e.g. here is the ExtensionObject ```rust #[derive(PartialEq, Debug, Clone)] @@ -197,11 +181,9 @@ Rust enables the `body` payload to be `None`, `ByteString` or `XmlElement` and t ### Lint exceptions for OPC UA -OPC UA has some some really long PascalCase ids, many of which are further broken up by underscores. I've tried converting the -name to upper snake and they look terrible. I've tried removing underscores and they look terrible. +OPC UA has some some really long PascalCase ids, many of which are further broken up by underscores. I've tried converting the name to upper snake and they look terrible. I've tried removing underscores and they look terrible. -So the names and underscores are preserved as-in in generated code even though they generate lint errors. -The lint rules are disabled for generated code. +So the names and underscores are preserved as-in in generated code even though they generate lint errors. The lint rules are disabled for generated code. For example: @@ -218,21 +200,17 @@ pub enum VariableId { Most uses of a status code will be via a `StatusCode` enum. Values such as `Good`, `BadUnexpectedError` etc. -The enum will also implement `Copy` so that status codes are copy on assign. The enum provides helpers `is_good()`, -`is_bad()`, `name()` and `description()` for testing and debugging purposes. It also provides functions for turning the -code into and out of a UInt32 and masking status / info bits. +The enum will also implement `Copy` so that status codes are copy on assign. The enum provides helpers `is_good()`, `is_bad()`, `name()` and `description()` for testing and debugging purposes. It also provides functions for turning the code into and out of a UInt32 and masking status / info bits. ## Formatting -All code (with the exceptions noted for OPC UA) should be follow the most current Rust RFC coding guidelines for naming -conventions, layout etc. +All code (with the exceptions noted for OPC UA) should be follow the most current Rust RFC coding guidelines for naming conventions, layout etc. Code should be formatted with the IntelliJ rust plugin, or with rustfmt. ## Encryption -OPC UA for Rust uses OpenSSL for encryption. This decision was basically made for me since there is no Rust crate at this time -that satisfies the requirements for OPC UA. That includes: +OPC UA for Rust uses OpenSSL for encryption. This decision was basically made for me since there is no Rust crate at this time that satisfies the requirements for OPC UA. That includes: * Message digest - SHA1, SHA256 * Symmetric encryption algorithms - AES128 CBC, AES256 CBC @@ -246,8 +224,7 @@ that satisfies the requirements for OPC UA. That includes: The server maintains an address space. The `AddressSpace` struct manages the address space. -Each nodes in the address space is stored in a big hash map keyed by their `NodeId`. The value -is enum called a `NodeType` that is one of the standard OPC UA node types: +Each nodes in the address space is stored in a big hash map keyed by their `NodeId`. The value is enum called a `NodeType` that is one of the standard OPC UA node types: * DataType * Method @@ -258,15 +235,11 @@ is enum called a `NodeType` that is one of the standard OPC UA node types: * VariableType * View -References are managed by a `References` struct which has a map of vectors of outgoing references from a node. -Each `Reference` has a reference type id (a `NodeId`) indicating what the refeence is, -and the `NodeId` of the target node. `References` also maintains a reverse lookup map so it can tell if a target -is referenced by another node. +References are managed by a `References` struct which has a map of vectors of outgoing references from a node. Each `Reference` has a reference type id (a `NodeId`) indicating what the refeence is, and the `NodeId` of the target node. `References` also maintains a reverse lookup map so it can tell if a target is referenced by another node. ### Generated nodeset -Calling `Address::new()` automatically populates itself with the default nodeset. The population code is machine -generated and resides under `server/src/address_space/generated`. +Calling `Address::new()` automatically populates itself with the default nodeset. The population code is machine generated and resides under `server/src/address_space/generated`. ## Encryption @@ -276,8 +249,7 @@ Encryption is through functions that call onto OpenSSL. See this [document](cryp ### Synchronous I/O -Early versions of OPC UA for Rust used the standard `std::net::TcpStream` for I/O. Basically each session on the server -ran in a big loop on its own thread where it would wait to receive a message and send responses. +Early versions of OPC UA for Rust used the standard `std::net::TcpStream` for I/O. Basically each session on the server ran in a big loop on its own thread where it would wait to receive a message and send responses. This was easy to understand but caused some serious issues: @@ -297,20 +269,16 @@ It was necessary to go asynchronous, but hand-rolling a solution would probably ### Asynchronous I/O -So starting with `0.4`, the synchronous I/O was replaced with asynchronous I/O. But instead of using -a hand-rolled solution, I chose Tokio to solve the issues. +So starting with `0.4`, the synchronous I/O was replaced with asynchronous I/O. But instead of using a hand-rolled solution, I chose Tokio to solve the issues. * Futures based - actions are defined as promises which are executed asynchronously. * I/O is non-blocking. * Inherently multi-threaded via Tokio's executor. * Supports timers and other kinds of asynchronous operation. -The penalty for this is that asynchronous programming is _hard_. It's hard even in languages like JavaScript where -things like lifetimes and borrowing don't have to be thought about. In Rust, async means reference counting things, -frequently having to map the output of one future into another kind and so forth. +The penalty for this is that asynchronous programming is _hard_. It's hard even in languages like JavaScript where things like lifetimes and borrowing don't have to be thought about. In Rust, async means reference counting things, frequently having to map the output of one future into another kind and so forth. -In addition Tokio has been the cause of its own problems. The timers in tokio-timer 0.1 were broken for sub-100ms timers and not finegrained but this -was resolved in 0.2. Issues with closing streams after splitting a stream into reader / writer portions is also a problem that was only recently solved. +In addition Tokio has been the cause of its own problems. The timers in tokio-timer 0.1 were broken for sub-100ms timers and not finegrained but this was resolved in 0.2. Issues with closing streams after splitting a stream into reader / writer portions is also a problem that was only recently solved. In the new async world a session is a state machine: @@ -320,8 +288,7 @@ In the new async world a session is a state machine: * `Finished(StatusCode)` - Session is finished, sockets are closed. The `StatusCode` indicates why the session finished which might be `Good` if a normal termination occurred or another OPC UA error otherwise. -There are tasks running to monitor the health of the session and finish it if it goes into error. When it goes into error -the socket must close and all tasks terminate. +There are tasks running to monitor the health of the session and finish it if it goes into error. When it goes into error the socket must close and all tasks terminate. The main loop for a server is this: @@ -343,8 +310,7 @@ When a session ends in a Finished state it will hold a status code explaining th ## Implementation plan -Client and server will work their ways through OPC UA profiles to the point of usability. But presently they are working -towards. +Client and server will work their ways through OPC UA profiles to the point of usability. But presently they are working towards. * Nano Embedded Device Server Profile, which has these main points * UA-TCP binary @@ -366,8 +332,7 @@ towards. * GetMonitoredItems via call * ResendData via call -This [OPC UA link](http://opcfoundation-onlineapplications.org/ProfileReporting/index.htm) provides interactive and descriptive information about -profiles and relevant test cases. +This [OPC UA link](http://opcfoundation-onlineapplications.org/ProfileReporting/index.htm) provides interactive and descriptive information about profiles and relevant test cases. ## Major 3rd party dependencies @@ -389,9 +354,7 @@ There are also a couple of [node-opcua](https://github.com/node-opcua) scripts i 1. `client.js` - an OPC UA client that connects to a server and subscribes to v1, v2, v3, and v4. 2. `server.js` - an OPC UA server that exposes v1, v2, v3 and v4 and changes them from a timer. -These are functionally analogous to `simple-server` and `simple-client` so the Rust code can be tested against -an independently written implementation of OPC UA that works in the way it is expecting. This is useful for debugging -and isolating bugs / differences. +These are functionally analogous to `simple-server` and `simple-client` so the Rust code can be tested against an independently written implementation of OPC UA that works in the way it is expecting. This is useful for debugging and isolating bugs / differences. To use them: diff --git a/docs/developer.md b/docs/developer.md index 89ff75d72..63624d8f4 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -1,7 +1,6 @@ # Debugging / Development information -This is just a loose list of things that can come in useful for debugging and development. -This is on top of anything written in the [setup](./setup.md) documentation. +This is just a loose list of things that can come in useful for debugging and development. This is on top of anything written in the [setup](./setup.md) documentation. ## Use latest stable Rust @@ -37,8 +36,7 @@ TODO OpenSSL is the most painful build component so ensure you read [setup](./setup.md) for information. -It would be very nice if OpenSSL could be replaced by a native Rust crypto library but given the breadth -of things we use, this seems unlikely in the short term. See [crypto](./crypto.md) for more info. +It would be very nice if OpenSSL could be replaced by a native Rust crypto library but given the breadth of things we use, this seems unlikely in the short term. See [crypto](./crypto.md) for more info. ## Wireshark diff --git a/docs/opc_ua_overview.md b/docs/opc_ua_overview.md index 8d8d84133..b565e35eb 100644 --- a/docs/opc_ua_overview.md +++ b/docs/opc_ua_overview.md @@ -1,20 +1,12 @@ # OPC UA Overview -[OPC UA](https://opcfoundation.org/) is a communication protocol for industrial visualization and control systems. It -allows a client to connect to a server over a secure/insecure channel and call services on the server -in order to monitor variables, call methods and other activities. +[OPC UA](https://opcfoundation.org/) is a communication protocol for industrial visualization and control systems. It allows a client to connect to a server over a secure/insecure channel and call services on the server in order to monitor variables, call methods and other activities. -A server listens for connections on a port. A client first connects to that port. It -then queries what _endpoints_ the server supports. Endpoints describe different ways -of interacting with the server including their security policy. The client will then -connect to an endpoint, handshake and establish and active a session with an identity -token. Once activated the client can call services on the server. +A server listens for connections on a port. A client first connects to that port. It then queries what _endpoints_ the server supports. Endpoints describe different ways of interacting with the server including their security policy. The client will then connect to an endpoint, handshake and establish and active a session with an identity token. Once activated the client can call services on the server. -OPC UA is very complex to implement (spanning 14 volumes of specification) so it is broken down into a series of -profiles that build on each other and describe what features a server should implement. +OPC UA is very complex to implement (spanning 14 volumes of specification) so it is broken down into a series of profiles that build on each other and describe what features a server should implement. -There are a large number of implementations of the OPC UA protocol both open source and proprietary. All should be -interoperable with each other subject to their support for profiles and services. +There are a large number of implementations of the OPC UA protocol both open source and proprietary. All should be interoperable with each other subject to their support for profiles and services. ## OPC UA for Rust @@ -22,9 +14,7 @@ OPC UA for Rust describes its compatibility [here](./compatibility.md). ## Services -An OPC UA server provides _services_ and are grouped into sets. The client -calls services depending on what it wants to do. For example a client could -subscribe to a particular variable and receive notifications when the value changes. +An OPC UA server provides _services_ and are grouped into sets. The client calls services depending on what it wants to do. For example a client could subscribe to a particular variable and receive notifications when the value changes. Services include: @@ -35,11 +25,9 @@ Services include: * Methods - call methods on the server * View - browse the server address space -Not all servers support all services, and servers may enforce different limits -on what they can do, e.g. maximum number of subscriptions. +Not all servers support all services, and servers may enforce different limits on what they can do, e.g. maximum number of subscriptions. -A client is expected to know what it can call and what the limits are. A -server may drop a connection if an unsupported service is called. +A client is expected to know what it can call and what the limits are. A server may drop a connection if an unsupported service is called. ## Protocols @@ -53,23 +41,18 @@ Not all implementations support all 3 protocols. For example Rust OPC UA only su ## User identities -Sessions are created when the client presents an identity token to the server to identify itself. The identity -may be anonymous, username / password, or a security token. The server can use identity to affect what the -session is capable of doing. +Sessions are created when the client presents an identity token to the server to identify itself. The identity may be anonymous, username / password, or a security token. The server can use identity to affect what the session is capable of doing. ## Profiles -OPA UA classifies servers into profiles which are bundles of _facets_ that define the -services and minimum capabilities that a server should support. Each profile -implements everything preceding it. +OPA UA classifies servers into profiles which are bundles of _facets_ that define the services and minimum capabilities that a server should support. Each profile implements everything preceding it. * Nano - 1 session, no encryption, user/pass tokens, OPC UA TCP, read attributes * Micro - 2+ sessions, monitor items * Embedded - 2+ subscriptions, GetMonitoredItems / ResendData methods, 10+ monitored items, deadband filter * Standard - 50+ sessions, 5+ subscriptions, diagnostics, register with discovery server, X509 user tokens -These are the _basic_ characteristics of each profile, but the full requirements -are described [here](https://apps.opcfoundation.org/profilereporting/). +These are the _basic_ characteristics of each profile, but the full requirements are described [here](https://apps.opcfoundation.org/profilereporting/). Servers may implement further facets in addition to these that offer other functionality. @@ -82,27 +65,17 @@ a standard URL, for example `opc.tcp://servername:port/endpoint/path`. * `servername:port` is the host's name and port, e.g. "localhost:4855" * `/endpoint/path` is the _endpoint_ you wish to connect to, e.g. "/device/metrics". -Endpoints describe a security policy which may be none, or may describe -encryption and hash algorithms. If an endpoint is secure, communication -with it will be encrypted. +Endpoints describe a security policy which may be none, or may describe encryption and hash algorithms. If an endpoint is secure, communication with it will be encrypted. ## Security -Endpoints may be secure or insecure. Security is established when -the client connects to an endpoint. The endpoint describes the security -policy it must use which can be none for no encryption. For a security -policy other none and the client / server will present each other with -their respective X509 certificates to establish trust. +Endpoints may be secure or insecure. Security is established when the client connects to an endpoint. The endpoint describes the security policy it must use which can be none for no encryption. For a security policy other none and the client / server will present each other with their respective X509 certificates to establish trust. -Once each side trusts the other they will create an encrypted channel -with security policy's encryption algorithms to communicate with each -ther. The client / server can also choose to sign / verify packets in -addition to just encrypting them. +Once each side trusts the other they will create an encrypted channel with security policy's encryption algorithms to communicate with each other. The client / server can also choose to sign / verify packets in addition to just encrypting them. ## Sessions -After a client has connected to an endpoint, it now presents -an identity token to the server to identify itself and activate a session. +After a client has connected to an endpoint, it now presents an identity token to the server to identify itself and activate a session. Identity tokens may be one of the following: @@ -110,9 +83,7 @@ Identity tokens may be one of the following: * User/pass - a username and password. * X509 - a certificate associated with the user. -A server uses the identity in any way it chooses, e.g. allowing only -authorized users to write values while allowing anonymous users to -read values. +A server uses the identity in any way it chooses, e.g. allowing only authorized users to write values while allowing anonymous users to read values. ## More information diff --git a/docs/server.md b/docs/server.md index 649ba827b..5ef7b9cf0 100644 --- a/docs/server.md +++ b/docs/server.md @@ -7,11 +7,9 @@ This is a small tutorial for using the OPC UA server library. It will assume you ## Server API -The Rust OPC UA server API supports all of the OPC UA embedded profile services and a few of the standard profile -services. +The Rust OPC UA server API supports all of the OPC UA embedded profile services and a few of the standard profile services. -These are implemented for you so generally once you create a server configuration, set up an address space and register -some callbacks you are ready to run a server. +These are implemented for you so generally once you create a server configuration, set up an address space and register some callbacks you are ready to run a server. ## Lifecycle @@ -99,8 +97,7 @@ fn main() { } ``` -Alternatively, let's say you use a configuration file, but how do you create it when one isn't there? Well your code -logic could test if the file can load, and if it doesn't, could create the default one with a `ServerBuilder`. +Alternatively, let's say you use a configuration file, but how do you create it when one isn't there? Well your code logic could test if the file can load, and if it doesn't, could create the default one with a `ServerBuilder`. ```rust fn main() { @@ -125,30 +122,24 @@ fn main() { #### TCP Configuration The default TCP config uses an address / port of `127.0.0.1` and `4855`. If you intend for your server -to be remotely accessible then explicitly set the address to the assigned IP address or resolvable hostname for the network -adapter your server will listen on. +to be remotely accessible then explicitly set the address to the assigned IP address or resolvable hostname for the network adapter your server will listen on. Also ensure that your machine has a firewall rule to allow through the port number you use. ### Security -The server configuration determines what encryption it uses on its endpoints, and also what user identity tokens -it accepts. +The server configuration determines what encryption it uses on its endpoints, and also what user identity tokens it accepts. The client and server can communicate over an insecure or a secure channel. -1. An insecure channel is plaintext and is not encrypted in any way. This might be fine where trust is implicit and -controlled between the client and the server, e.g. when they reside on a private network, or even the same device. -2. A secure channel. The client presents a certificate to the server, the server presents a certificate to the client. -Each must trust the other, at which point the session proceeds over an encrypted channel. +1. An insecure channel is plaintext and is not encrypted in any way. This might be fine where trust is implicit and controlled between the client and the server, e.g. when they reside on a private network, or even the same device. +2. A secure channel. The client presents a certificate to the server, the server presents a certificate to the client. Each must trust the other, at which point the session proceeds over an encrypted channel. -Once the client establishes a session with the server, the next thing it will do is present its identity for activating -the session. The identity is the user's credentials which can be anonymous, user / password or X509 identity token. +Once the client establishes a session with the server, the next thing it will do is present its identity for activating the session. The identity is the user's credentials which can be anonymous, user / password or X509 identity token. ### Set up your address space -Your server has an address space that contains the default OPC UA node set. The default node set describes -all the standard types, server diagnostics variables and more besides. +Your server has an address space that contains the default OPC UA node set. The default node set describes all the standard types, server diagnostics variables and more besides. To this you may wish to add your own objects and variables. To make this easy, you can create new nodes with a builder, e.g: @@ -179,24 +170,17 @@ to other nodes before inserting it into the address space. ### Variables -Clients of servers will typically read values of variables, and may do so from -a subscription. A variable can reflect a value from a physical device that your -server will update either as it changes, or on a timer, or when a client requests it. +Clients of servers will typically read values of variables, and may do so from a subscription. A variable can reflect a value from a physical device that your server will update either as it changes, or on a timer, or when a client requests it. * Add hoc. Your code sets the variable when you deem the value to have changed. * Getter. The server invokes a getter in your code whenever the value is requested. -* Timer. The server invokes a call back on a timed interval allowing you the chance -to update the value. OPC UA for Rust provides a timer mechanism as a convenience +* Timer. The server invokes a call back on a timed interval allowing you the chance to update the value. OPC UA for Rust provides a timer mechanism as a convenience -In addition you may also register a setter callback which is called whenever -a client attempts to write a value to the variable. Your callback could ignore -the change, clamp it to some range or call the physical device with the change. +In addition you may also register a setter callback which is called whenever a client attempts to write a value to the variable. Your callback could ignore the change, clamp it to some range or call the physical device with the change. #### Setting variable values manually -For some values you may prefer to set them once when they change. How you do this is up to you - a timer, an event, a -separate thread receiving messages... Basically whatever mechanism you use, from your handler you will call something -like this: +For some values you may prefer to set them once when they change. How you do this is up to you - a timer, an event, a separate thread receiving messages... Basically whatever mechanism you use, from your handler you will call something like this: ```rust let now = DateTime::now(); @@ -209,8 +193,7 @@ In this example `now` is the current timestamp for when the value changed and th #### Create a variable Getter -Alternatively you might prefer to poll values when a client actually asks for it. In this case, you can set the getter function -whenever the variable is asked for and your function will be called. +Alternatively you might prefer to poll values when a client actually asks for it. In this case, you can set the getter function whenever the variable is asked for and your function will be called. This example will `123.456f`. @@ -226,8 +209,7 @@ This example will `123.456f`. } ``` -The difference with the dynamic getter is there are parameters that allow your code to conditionally decide how -they return a value. +The difference with the dynamic getter is there are parameters that allow your code to conditionally decide how they return a value. The parameters to the getter are: @@ -256,15 +238,11 @@ If you prefer to make it asynchronous, run it on a separate thread, or use `Serv ## Logging -OPC UA for Rust provides an extensive amount of logging at error, warn, info, debug and trace levels. All this is via -the standard [log](https://docs.rs/log/0.4.8/log/) facade so choose which logging implementation you want -to capture information. See the link for implementations that you can use. +OPC UA for Rust provides an extensive amount of logging at error, warn, info, debug and trace levels. All this is via the standard [log](https://docs.rs/log/0.4.8/log/) facade so choose which logging implementation you want to capture information. See the link for implementations that you can use. ### Console logging -For convenience OPC UA for Rust provides a simple `opcua-console-logging` crate that wraps [env_logger](https://docs.rs/env_logger/0.6.2/env_logger/) -and writes out logging information to stdout. To use it, set an `RUST_OPCUA_LOG` environment variable (not `RUST_LOG`), -otherwise following the documentation in `env_logger`. e.g. +For convenience OPC UA for Rust provides a simple `opcua-console-logging` crate that wraps [env_logger](https://docs.rs/env_logger/0.6.2/env_logger/) and writes out logging information to stdout. To use it, set an `RUST_OPCUA_LOG` environment variable (not `RUST_LOG`), otherwise following the documentation in `env_logger`. e.g. ```shell script export RUST_OPCUA_LOG=debug diff --git a/docs/setup.md b/docs/setup.md index d15dacab3..8fc7ada9a 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -2,16 +2,13 @@ This is the in-depth documentation about the OPC UA implementation in Rust. # Setup -OPC UA for Rust generally requires the most recent stable version of Rust to compile. -The recommendation is to install [rustup](https://rustup.rs/) to manage your toolchain and keep it -up to date. +Rust supports backends for gcc and MSVC so read the notes about this. Then use [rustup](https://rustup.rs/) to install your toolchain and keep it up to date. There are some [developer](./developer.md) related notes too for people actually modifying the source code. ## Windows -Rust supports two compiler backends - gcc or MSVC, the choice of which is up to you. If you choose the MSVC then you -must either build OpenSSL for yourself or use a prebuilt binary. +Rust supports two compiler backends - gcc or MSVC, the choice of which is up to you. If you choose the MSVC then you must either build OpenSSL for yourself or use a prebuilt binary. ### Visual Studio @@ -42,8 +39,7 @@ MinGW64 binaries are on your `PATH`. ## Linux -These instructions apply for `apt-get` but if you use DNF on a RedHat / Fedora system then substitute the equivalent packages -and syntax using `dnf`. +These instructions apply for `apt-get` but if you use DNF on a RedHat / Fedora system then substitute the equivalent packages and syntax using `dnf`. 1. Install gcc and OpenSSL development libs & headers, e.g. `sudo apt-get gcc libssl-dev`. 2. Use rustup to install the latest stable rust during setup. @@ -52,38 +48,27 @@ Package names may vary by dist but as you can see there isn't much to setup. ## OpenSSL -The major external dependency is OpenSSL. If you have trouble building the `openssl-*` crates or trouble running them -then refer to that project's [documentation](https://docs.rs/openssl/0.10.26/openssl/). +The major external dependency is OpenSSL. If you have trouble building the `openssl-*` crates or trouble running them then refer to that project's [documentation](https://docs.rs/openssl/0.10.26/openssl/). ## Vendored OpenSSL -The `openssl` crate has a `vendored` feature that can fetch, build and statically link to a copy of OpenSSL without it -being in your environment. +The `openssl` crate has a `vendored` feature that can fetch, build and statically link to a copy of OpenSSL without it being in your environment. -This might be useful for cross-compiling. OPC UA for Rust exposes the feature as `vendored-openssl` which is -on the `opcua-core`, `opcua-server` and `opcua-client` crates. i.e. when you specify `--features=vendored-openssl` it will -pass `vendored` through to the `openssl` crate. +This might be useful for cross-compiling. OPC UA for Rust exposes the feature as `vendored-openssl` which is on the `opcua-core`, `opcua-server` and `opcua-client` crates. i.e. when you specify `--features=vendored-openssl` it will pass `vendored` through to the `openssl` crate. -Note that Rust OPC UA is just passing through this feature so refer to the openssl documentation for any issues -and troubleshooting required to use it. +Note that Rust OPC UA is just passing through this feature so refer to the openssl documentation for any issues and troubleshooting required to use it. ## Conditional compilation The OPC UA server crate also provides some other features that you may or may not want to enable: -* `generated-address-space` - When enabled (default is enabled), the `AddressSpace::new()` will create and populate the address space - with the default OPC UA node set. When disabled, the address space will only contain a root node, thus saving - memory and also some disk footprint. -* `discovery-server-registration` - When enabled (default is disabled), the server will periodically attempt to register itself with - a local discovery server. The server will use the on the client crate which requires more memory. -* `http` - When enabled (default disabled), the server can start an HTTP server (see `demo-server`) providing diagnostic and metrics information about - how many active connections there are, what they're monitoring as well as the internal health of the server. This - is useful for development and debugging. Enabling the http server adds dependencies on `actix-web` and requires more memory. +* `generated-address-space` - When enabled (default is enabled), the `AddressSpace::new()` will create and populate the address space with the default OPC UA node set. When disabled, the address space will only contain a root node, thus saving memory and also some disk footprint. +* `discovery-server-registration` - When enabled (default is disabled), the server will periodically attempt to register itself with a local discovery server. The server will use the on the client crate which requires more memory. +* `http` - When enabled (default disabled), the server can start an HTTP server (see `demo-server`) providing diagnostic and metrics information about how many active connections there are, what they're monitoring as well as the internal health of the server. This is useful for development and debugging. Enabling the http server adds dependencies on `actix-web` and requires more memory. ## Workspace Layout -OPC UA for Rust follows the normal Rust conventions. There is a `Cargo.toml` per module that you may use to build the module -and all dependencies. e.g. +OPC UA for Rust follows the normal Rust conventions. There is a `Cargo.toml` per module that you may use to build the module and all dependencies. e.g. ```bash $ cd opcua/samples/demo-server diff --git a/docs/testing.md b/docs/testing.md index 2b7f6b21d..5e26470e7 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -47,8 +47,7 @@ The Criterion tool runs tests and requires `gnuplot` to generate reports of perf ## OPC UA test cases -The OPC UA foundation describes tests that servers/clients must pass to implement various profiles or facets. -Each is described under the test case links against the facets of each [OPC UA profile](http://opcfoundation-onlineapplications.org/ProfileReporting/index.htm). +The OPC UA foundation describes tests that servers/clients must pass to implement various profiles or facets. Each is described under the test case links against the facets of each [OPC UA profile](http://opcfoundation-onlineapplications.org/ProfileReporting/index.htm). These are not performed manually or automatically at present, however much of the functionality they describe is covered by unit / integration tests and of course interoperability testing. @@ -72,10 +71,9 @@ node client.js ``` The idea is to test the Rust `simple-client` against the Node OPC UA server to ensure it works. Or -test the Rust `simple-server` by connecting to it with the Nod OPC UA client. +test the Rust `simple-server` by connecting to it with the Node OPC UA client. -The Open62541 only has a very basic client implementation so far. It requires a C compiler -and CMake. Basic setup instructions: +The Open62541 only has a very basic client implementation so far. It requires a C compiler and CMake. Basic setup instructions: ``` cd opcua/open62541