Rust-Opcua: opcua — A pure rust OPC UA library.

Introduction

This is an OPC UA server / client API implementation for Rust.

Linux
Windows

OPC UA is an industry standard for monitoring of data. It's used extensively for embedded devices, industrial control, IoT, etc. - just about anything that has data that something else wants to monitor, control or visualize.

Rust is a systems programming language and is therefore a natural choice for implementing OPC UA. This implementation supports the embedded, micro and nano profiles but may grow to support features in time.

Read the compatibility page for how the implementation conforms with the OPC UA spec.

Read the change log for changes per version as well as aspirational / upcoming work.

License

The code is licenced under MPL-2.0. Like all open source code, you use this code at your own risk.

Setup

Read the setup for instructions on building OPCUA for Rust.

Read cross compilation for hints for cross compiling OPC UA for Rust to other platforms.

Design

Read the design for more in-depth description of implementation.

Tutorial

Tutorials / user guides are still work in progress.

Further Documentation

The API documentation is generated from the latest published crates. This may be some way behind current development.

Client Client side APIs to connect to an OPC UA server.
Server Server side APIs to host an OPC UA server, address space, create new nodes, subscriptions.
Crypto Security profiles, encryption, hashing, signing / verification, certificate management.
Core Core functionality shared by client and server - Secure channel, TCP encoding, TCP messages, chunking.
Types OPC UA core types and binary encoding implementations.

Samples

If you want to get stuck in, there are a number of samples in the samples/ folder. The simple-client and the simple-server projects are minimal client and server programs respectively.

# In one bash
cd opcua/samples/simple-server
cargo run
# In another bash
cd opcua/samples/simple-client
cargo run

The full list of samples:

  1. 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 - an OPC UA client that connects to a server and subscribes to the values of v1, v2, v3 and v4.
  3. discovery-client - an OPC UA client that connects to a discovery server and lists the servers registered on it.
  4. 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 - an OPC UA server that will implements more functionality than the simple server and may become a compliance server in time.
  6. mqtt-client - an OPC UA client that subscribes to some values and publishes them to an MQTT broker.
  7. web-client - an OPC UA client that subscribes to some values and streams them over a websocket.
  8. modbus-server - an OPC UA server that translates variables from MODBUS.

Comments

  • Refactor: zero length variant array decoding
    Refactor: zero length variant array decoding

    Dec 28, 2021

    Closes #145

    As I was just on the train with a few hours to kill, I took another look at this issue.

    In the discussion, @locka99 pointed out that there were problems inferring the type of 0-length arrays. Yet after reviewing https://reference.opcfoundation.org/v104/Core/docs/Part6/5.2.2/ it seems to me that, if the array bits are set, the type id should be there as well, even if the array length is 0. (the code being executed is in a branch where the array bit is set, so the encoding mask as a whole is not 0) So I think the most likely explanation would be for an encoder implementation having gone awry on that part. If this happens now, from_encoding_mask will return VariantTypeId::Empty, which, in my opinion, is okay.

    At the very least, this feels much cleaner than my initial idea of returning Variant::Empty from the encoder - which would probably also require some kind PartialEq implementation that can deal with comparisons of Variant::Empty and zero-length arrays.

    Reply
  • Simple-server failed to parse config
    Simple-server failed to parse config

    Jan 9, 2022

    When running the simple-server sample code, I get the following error:

    2022-01-09 18:08:18.062 - ERROR - opcua_core::config - Cannot deserialize configuration from ../server.conf
    

    By inspecting the source code, I noticed the following line

    https://github.com/locka99/opcua/blob/0.8.1/core/src/config.rs#L42

    This line reads the config from a YAML file and not from a config file as the one provided in the samples.

    rustup 1.24.3 (ce5817a94 2021-05-31) info: This is the version for the rustup toolchain manager, not the rustc compiler. info: The currently active rustc version is `rustc 1.57.0 (f1edd0429 2021-11-29)

    Library version: 0.8.1

    Reply
  • Fix module paths for BSD module generator
    Fix module paths for BSD module generator

    Jan 16, 2022

    In pull request #154 the core functionality from the existing gen_types.js was extracted into the separate types.js file so that gen_datatypes.js can also use it to generate types for other BSD files besides Opc.Ua.Types.bsd.xml. Unfortunately the use paths in the generated rust code still assumed that the generated code would be placed in the opcua-types crate.

    This pull request changes this so that the generated module can be used without having to adjust anything at all ideally (except for maybe sometimes changing CharArray to UAString or something like that). I also added some more documentation in tools/schema/README.md.

    Reply
  • fix: don't send CloseSession requests for unregistered sessions
    fix: don't send CloseSession requests for unregistered sessions

    Jan 17, 2022

    I noticed that some operations create a session object that isn't registered with the server but would result in CloseSession requests anyway. The best solution would have been to differentiate these two types of session but for now I just added a quick fix to check for the session id in order to determine whether to send CloseSessionRequest to the server...

    Reply
  • opcua client: panic when attempting to transfer subscriptions
    opcua client: panic when attempting to transfer subscriptions

    Jan 18, 2022

    After losing the conection to the OPC UA server, when reconnecting, the opcua client attempts to transfer an existing subscription to the new session but fails and panics

    opcua_client::session: session:2 Some or all of the existing subscriptions could not be transferred and must be created manually

    thread 'tokio-runtime-worker-0' panicked at 'range end index 69952 out of range for slice of length 66576', /home/valka/.cargo/registry/src/github.com-1ecc6299db9ec823/opcua-core-0.8.1/src/comms/secure_channel.rs:930:9

    opcua_client_error.log .

    Reply
  • Does this library support HistoryRead
    Does this library support HistoryRead

    Jan 18, 2022

    I tried toUaExpert to read a historical data using UaExpert, but fails 屏幕截图 2022-01-18 205619

    Reply
  • Allow the client to use the server time
    Allow the client to use the server time

    Mar 31, 2021

    In some situations, were the server setup is managed by a 3rd party, it can be needed to “follow” the server time in the client in order to be able to setup a connection.

    This solution is pretty much inline with what is documented here, except in this solution only the time used in the client is adjusted and the host clock is left untouched.

    And just to be clear, the offset is only used for the communication and security related parts between the client and the server. For the rest the actuall time will still be used.

    I tested this against several 3rd party server and the change resulted in stable connections without any errors, reconnects or constantly renewing security channels.

    Fixes #89

    Reply
  • do you have any plan to update to tokio 0.2?
    do you have any plan to update to tokio 0.2?

    Oct 10, 2020

    Using RUST'S new syntax async & Await, the code is much cleaner.

    ctx.read_holding_registers(address, count as u16)
                .map_err(move |err| {
                    println!("Read input registers error {:?}", err);
                    OutputRegister::end_read_output_registers(&runtime_for_err);
                })
                .and_then(move |values| {
                    store_values_in_registers(values, registers.clone());
                    OutputRegister::end_read_output_registers(&runtime);
                    Ok(())
                })
    

    it's hard to read code like this.

    Reply
  • core/comms/secure_channel: Always set remote nonce
    core/comms/secure_channel: Always set remote nonce

    Jun 23, 2021

    The set_remote_nonce_from_byte_string() performs some checks against the message security mode.

    In our tests, we use security policy = None and security mode = None, but the server sets Basic126Rsa15 for the user identity token policy meaning that we still need to use the server nonce for authentication.

    This patch removes all the checking around security policy and mode, which allows the client to connect to our OPC-UA server (Kepware) successfully.

    Reply
  • Possible performance issue / best practices
    Possible performance issue / best practices

    Sep 30, 2021

    After familiarizing myself enough with the library to get our PoC application working, I decided to look into why - using this library as a client - it seems to be a lot slower than my previous experience with, for example gopcua, even though the code is written in Rust.

    Right now I am looking at a TCP dump ( tcp_performance.csv) and there are noticeable delays between receiving the CreateSessionResponse and sending out the ActivateSessionRequest in Session::connect_and_activate whereas the time between CreateSessionRequest and CreateSessionResponse , i.e. the time it takes for the server to respond, is barely noticable. This suggests to me that the problem lies with the client code and not on the server.

    Looking at the whole dump, it is noticeable that the delays in between a request coming in and the next request going out is always more or less 0.05 seconds. But I chose Session::connect_and_activate as an example because nothing else is happening between these two calls and there is a noticeable delay nonetheless.

    Are there any best practices to follow to get rid of this? Maybe I have to tweak some Tokio runtime parameters or somesuch to make it faster? Is anyone else experiencing the same problem by any chance?

    Reply
  • Server connection loss is not propageted up to session
    Server connection loss is not propageted up to session

    Jan 25, 2019

    I'm currently testing edge cases against a small python server setup. When killing the server process and restarting it, the client does not seem to be able to reconnect. That said, I'm not quite sure, whether it notices that the server has been disconnected. Is there anyway to catch this? (I tried the connection state changed and session closed callbacks, but they're not being called though).

    Log output:

    2019-01-25 10:54:40.071 - INFO - opcua_client::comms::tcp_transport - ReadState has dropped
    2019-01-25 10:54:40.071 - INFO - opcua_client::comms::tcp_transport - Read loop finished
    2019-01-25 10:54:41.581 - ERROR - opcua_client::comms::tcp_transport - Write IO error Os { code: 32, kind: BrokenPipe, message: "Broken pipe" }
    2019-01-25 10:54:41.582 - ERROR - opcua_client::comms::tcp_transport - Write loop is finished with an error ()
    2019-01-25 10:54:41.582 - INFO - opcua_client::comms::tcp_transport - WriteState has dropped
    2019-01-25 10:55:09.579 - INFO - opcua_client::session_state - Making secure channel request
    2019-01-25 10:55:09.579 - INFO - opcua_client::session_state - security_mode = None
    2019-01-25 10:55:09.579 - INFO - opcua_client::session_state - security_policy = None
    2019-01-25 10:55:19.608 - INFO - opcua_client::session_state - Timeout waiting for response from server
    2019-01-25 10:55:19.609 - INFO - opcua_client::message_queue - Request 53 has timed out and any response will be ignored
    2019-01-25 10:55:19.609 - INFO - opcua_client::session_state - Making secure channel request
    2019-01-25 10:55:19.609 - INFO - opcua_client::session_state - security_mode = None
    2019-01-25 10:55:19.609 - INFO - opcua_client::session_state - security_policy = None
    

    The making secure channel request with sec mode and policy endlessly loop after that point.

    Reply
  • Does this library support CreateMonitoredItem of Alarm and Events
    Does this library support CreateMonitoredItem of Alarm and Events

    Mar 19, 2021

    I tried to create a monitored item for getting alarm data of OPCU Server, no response is coming.While monitored items of data changes of particular node id is giving result. I want to know whether this library support subscribing to alarms and events or this is due to my lack of knowledge to implement with this library. I am asking this because, one C++ library i used before only supported monitoring of data changes.

    Reply