locka99 / opcua

A client and server implementation of the OPC UA specification written in Rust
Mozilla Public License 2.0
496 stars 131 forks source link

Connect to siemens devices #93

Closed Dazul closed 3 years ago

Dazul commented 3 years ago

Hi,

I am currently trying to connect to a Siemens S7-1500 using your library.

First of all, I want to tell that I can connect to the device using this little tool: https://github.com/FreeOpcUa/opcua-client-gui

And regarding the code I am trying to run, this little code sample works properly with your simple-server sample:

use opcua_client::prelude::*;
use std::sync::{Arc, RwLock};

fn main() {
    let mut client = ClientBuilder::new()
        .application_name("Rusty-Mind")
        .application_uri("urn:rusty-mind")
        .create_sample_keypair(true)
        .trust_server_certs(true)
        .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://127.0.0.1:4855",
        "None",
        MessageSecurityMode::None,
        UserTokenPolicy::anonymous(),
    )
        .into();
    // Create the session
    let session = client
        .connect_to_endpoint(endpoint, IdentityToken::Anonymous)
        .unwrap();
    read_some_value(session.clone());
}

fn read_some_value(session: Arc<RwLock<Session>>) {
    let mut session = session.write().unwrap();
    let node_id = NodeId::new(2, "v1");
    let valueId = ReadValueId::from(node_id);
    let vals = match session.read(&[valueId]) {
        Err(e) => {
            println!("{}", e);
            return;
        }
        Ok(f) => f,
    };
    for val in vals {
        if let Some(ref value) = val.value {
            println!("Value is: {:?}", value)
        } else {
            println!("Item not found");
        }
    }
}

Now for my issue, I am trying to run this code:

use opcua_client::prelude::*;
use std::sync::{Arc, RwLock};

fn main() {
    let mut client = ClientBuilder::new()
        .application_name("Rusty-Mind")
        .application_uri("urn:rusty-mind")
        .create_sample_keypair(true)
        .trust_server_certs(true)
        .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://192.168.1.1:4840",
        "None",
        MessageSecurityMode::None,
        UserTokenPolicy::anonymous(),
    )
        .into();
    // Create the session
    let session = client
        .connect_to_endpoint(endpoint, IdentityToken::Anonymous)
        .unwrap();
    read_some_value(session.clone());
}

fn read_some_value(session: Arc<RwLock<Session>>) {
    let mut session = session.write().unwrap();
    let node_id = NodeId::new(3, "dbInfoPassagers");
    let valueId = ReadValueId::from(node_id);
    let vals = match session.read(&[valueId]) {
        Err(e) => {
            println!("{}", e);
            return;
        }
        Ok(f) => f,
    };
    for val in vals {
        if let Some(ref value) = val.value {
            println!("Value is: {:?}", value)
        } else {
            println!("Item not found");
        }
    }
}

The output of my program is this one:

    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/opcua-test`
BadTimeout

Process finished with exit code 0

And I tried to see what happened using wireshark. Here is what I found: Screenshot from 2021-02-27 19-45-39

Do you know if your lib should work with Siemens? Or some bugs could exist?

Thanks

locka99 commented 3 years ago

It looks to be an issue with message sizes since the response is BadResponseTooLarge.

Are you running from the tip of master, or the 0.9.0 release? Code was added to better support message chunking to break up large messages into chunks so if you are having an issue with 0.9.0 you might like to try the tip to see if it works better.

Dazul commented 3 years ago

I am running the 0.8.0 release. I did not see any 0.9.0.

I changed my configuration as follow:

[dependencies]
opcua-client = { path = "/home/user/git/opcua/client" }

With the repo freshly updated

[~/git/opcua]$ git branch
* master
[~/git/opcua]$ git log
commit 33494a1268e4a3d3fd7914b918f27b6af9842565 (HEAD -> master, origin/master, origin/HEAD)
Author: Adam Lock <locka99@gmail.com>
Date:   Sat Feb 20 20:32:20 2021 +0000

I observe the same behavior when running the code.

locka99 commented 3 years ago

Sorry my mistake we're on 0.9.0 right now. Can I ask if you send me the Hello and Acknowledge message you received. It would tell me what we're sending to them and what we're receiving back. In particular the max message size negotiation.

Dazul commented 3 years ago

Here is the pcap file on the zip. I created it with the last version of master of your lib. The pcab should contain communication from the hello message to the abort by the siemens device.

opc_abort_trace.zip

locka99 commented 3 years ago

I think what is happening is the client is saying "HELLO I can handle a max message size of 64Kb and a max chunk count of 1" and the server ACKs dropping the max message size to 8Kb and leaving the max chunk count alone at 1. So that means no message can exceed 8Kb in size and that is probably failing on the ReadResponse.

I've change the client code to send a max chunk count of 0 (i.e. the client is unlimited) and see what the server does with that. If it leaves it alone then hopefully it means the client will work.

Note there may be other issues with chunking to work through because although the code for multiple chunks has been there a long time but I've only used a max chunk count of 1. But try it and see if it solves your issue.

Dazul commented 3 years ago

With the last version, I did not got a timeout, but I got an "Item not found". I suspected my "dbInfoPassagers" to be empty. So I tried on another node, the manufacturer one. The result seems good on this one:

Value is: LocalizedText(LocalizedText { locale: UAString { value: None }, text: UAString { value: Some("Siemens AG") } })
locka99 commented 3 years ago

Does the node id actually exist on the server? As far as the client is concerned there should be no difference asking for the value of one node vs another. Providing the read request supplied the right node id and attribute you wish to read then the response should reflect the node existing or not.

It should be easy to verify by connecting to the same server as UA Expert to see if you can monitor the value.

Also just check if you're logging into the server with the same identity token. If the node is protected it might not show for an anonymous user.

Dazul commented 3 years ago

I am not sure atr 100% that the node exists. It should be, but I need to check with the person in charge with the Siemens project. That's why I check with a node I know it exists, so I can validate that the communication works.

Dazul commented 3 years ago

After checking, indeed my node value was wrong. I changed it in my code, and I am able to get values I need.

Thanks for your help.

locka99 commented 3 years ago

Thanks I'll close this then