In this page
OPC UA - IEC62541 library
This library is available for GreyCat Pro users
@library("opcua", "0.0.0");
OPC UA, or Open Platform Communications Unified Architecture, is a standardized communication protocol for industrial automation applications. It provides a platform-independent, service-oriented architecture that enables secure and reliable data exchange between various industrial devices and systems. OPC UA is used to facilitate interoperability and communication in industrial environments, allowing different types of machines, sensors, and control systems to seamlessly exchange data regardless of the underlying hardware or software platforms. It ensures compatibility, scalability, and security in distributed systems, making it a preferred choice for implementing Industrial Internet of Things (IIoT) and Industry 4.0 solutions. In that regard, we provide for users of GreyCat Pro, an OPC UA Client allowing the seamless connection to OPC UA devices to retrieve their values, without the need to leave their GCL code and use external tools!
OpcuaClient
The OpcuaClient
type is responsible for establishing a connection to an OPC UA device.
Properties
When instantiating this type, the following properties are available:
host: String
: Specifies the hostname or IP address of the OPC UA device.port: int
: Indicates the OPC UA port number (usually4840
).path: String?
: (Optional) Represents the optional path to the required endpoint. If not provided, it defaults to/
.security_mode: OpcuaSecurityMode
: Specifies the OPC UA security mode to be applied to the connection.security_policy: OpcuaSecurityPolicy?
: (Optional) Specifies the OPC UA security policy to be applied to the connection, if required.credentials: OpcuaCredentials?
: (Optional) Provides credentials for authentication, if necessary.certificate: OpcuaCertificate?
: (Optional) Optionally includes a client certificate for authentication purposes.
Methods
The following methods are available:
fn read(nodeId: String): any?
: Read the current value of an OPC UA nodefn read_all(nodeIds: Array<String>): Array<any?>
: Read the current value of a batch of OPC UA nodesfn read_with_time(nodeId: String):OpcuaValueDetails?
: Read the current value of a OPC UA node and returns an object withserver time
andsource time
fn read_all_with_time(nodeIds: Array<String>): Array<OpcuaValueDetails?>
: Read the current value of a batch of OPC UA nodes and returns a collection of objects withserver time
andsource time
fn read_history(nodeId: String, from: time?, to:time?):Array<OpcuaValueDetails>?
: Read the history of a node (if available, through an Historian for example), and returns a collection of objects withserver time
andsource time
fn write(nodeId: String, value:any)
: Write a value to a nodefn call(nodeId: String, parameters: Array<any?>): any?
: Perform a OPC UA RPC call with parameters
Addressing OPC UA nodes
To read the current value of a specified NodeID, it must be provided in its string representation, following the OPC UA encoding format: ns=<namespaceindex>;<type>=<value>
. For example, ns=0;i=2258
or ns=1;s=string.id
.
ns
: Indicates the namespace index of the NodeID.<type>
: Represents the type of the NodeID. It can bei
for numeric NodeIDs,s
for string NodeIDs,g
for Guid orb
for Opaque/ByteString identifier.<value>
: Specifies the value of the NodeID.
Read: Type mapping
The following table illustrates the OPC UA types supported by GreyCat and their corresponding mappings:
OPC UA Type | GreyCat mapping |
---|---|
Boolean | bool |
SByte | int |
Byte | int |
Int16 | int |
UInt16 | int |
Int32 | int |
UInt32 | int |
Int64 | int |
UInt64 | int |
Float | float |
Double | float |
String | String |
DateTime | time |
ByteString | String |
XmlElement | String |
Guid | String |
StatusCode | String |
NodeId | String |
ExpandedNodeId | String |
QualifiedName | String |
LocalizedText | String |
ExtensionObject | READING NOT SUPPORTED |
DataValue | READING NOT SUPPORTED |
Variant | READING NOT SUPPORTED |
DiagnosticInfo | READING NOT SUPPORTED |
OPC UA arrays are converted into GreyCat arrays, while OPC UA matrices are transformed into GreyCat arrays of arrays.
Writing values
In the context of Industry 4.0, the capability to write data back to a machine after data analytics or machine learning algorithms have been applied serves multiple essential purposes. Firstly, it enables proactive maintenance strategies by allowing machines to be stopped or alarms raised immediately upon detection of anomalies or potential malfunctions. Secondly, providing aggregated data or results from machine learning algorithms directly to the machine enables informed decision-making at the operational level, empowering machines to adjust their behavior autonomously based on insights derived from the data. Moreover, fine-tuning machine parameters based on the analyzed data ensures optimal performance and efficiency, as adjustments can be made in real-time to adapt to changing production conditions or quality requirements.
OpcuaClient
provides a write
method to write data on a OPC UA device. The prerequisites are that the node must be writable,
the OPC Ua user must have write
permissions on the device and the provided value
must match the node type.
write(nodeId: String, value:any)
The following types can be written on devices:
OPC UA Type | GreyCat mapping |
---|---|
Boolean | bool |
SByte | int |
Byte | int |
Int16 | int |
UInt16 | int |
Int32 | int |
UInt32 | int |
Int64 | int |
UInt64 | int |
Float | float |
Double | float |
String | String |
DateTime | time |
ByteString | WRITING NOT SUPPORTED |
XmlElement | WRITING NOT SUPPORTED |
Guid | WRITING NOT SUPPORTED |
StatusCode | WRITING NOT SUPPORTED |
NodeId | WRITING NOT SUPPORTED |
ExpandedNodeId | WRITING NOT SUPPORTED |
QualifiedName | WRITING NOT SUPPORTED |
LocalizedText | WRITING NOT SUPPORTED |
ExtensionObject | WRITING NOT SUPPORTED |
DataValue | WRITING NOT SUPPORTED |
Variant | WRITING NOT SUPPORTED |
DiagnosticInfo | WRITING NOT SUPPORTED |
Calling OPC-UA methods
The call
method is used to invoke methods on OPC UA nodes. This method requires two parameters: the OPC UA node identifier and an array of parameters to pass to the method.
OpcuaCredentials
The OpcuaCredentials
type represents credentials used for authentication in OPC UA connections.
login: String
: Specifies the login or username associated with the credentials.password: String
: Specifies the password associated with the credentials.
OpcuaSecurityMode
The OpcuaSecurityMode
is a GreyCat enum value representing the Security Mode to apply to the connection.
OpcuaSecurityMode::None
- In this mode, no encryption or digital signatures are applied to the communication.
- Data is transmitted in plaintext, making it vulnerable to interception and tampering.
- This mode is suitable for environments where security is not a concern, such as local testing or isolated networks. However, it should not be used in production environments where data confidentiality and integrity are paramount.
OpcuaSecurityMode::Sign
- In this mode, digital signatures are applied to the communication but encryption is not used.
- Data remains in plaintext, but each message is signed with a cryptographic signature to ensure its integrity and authenticity.
- While this mode provides some level of security by preventing tampering with data, it does not protect data confidentiality. It is suitable for scenarios where data confidentiality is not a requirement, but data integrity and authenticity are important.
OpcuaSecurityMode::SignAndEncrypt
- This mode offers the highest level of security by both signing and encrypting communication.
- Data is encrypted before transmission, ensuring confidentiality, and each message is signed to ensure its integrity and authenticity.
- SignAndEncrypt mode provides comprehensive protection against eavesdropping, tampering, and unauthorized access, making it suitable for environments where data security is critical, such as industrial control systems and IIoT applications.
OpcuaSecurityPolicy
The OpcuaSecurityPolicy
is a GreyCat enum value representing the Security Policy to apply to the connection.
OpcuaSecurityPolicy::Aes128_Sha256_RsaOaep
- This security policy utilizes AES (Advanced Encryption Standard) with a key length of 128 bits for encryption, SHA-256 (Secure Hash Algorithm 256) for hashing, and RSA-OAEP (RSA Optimal Asymmetric Encryption Padding) for asymmetric encryption.
- SHA-256 is a cryptographic hash function that produces a 256-bit (32-byte) hash value, ensuring data integrity.
- RSA-OAEP is an asymmetric encryption scheme based on RSA, providing secure encryption and decryption of data.
OpcuaSecurityPolicy::Aes256_Sha256_RsaPss
- This security policy is similar to Aes128_Sha256_RsaOaep but uses AES with a key length of 256 bits for encryption.
- AES-256 offers stronger encryption compared to AES-128, suitable for environments requiring higher security levels.
OpcuaSecurityPolicy::Basic128Rsa15
- This security policy utilizes Basic128 encryption and RSA with PKCS#1 v1.5 padding for asymmetric encryption.
- Basic128 refers to AES with a key length of 128 bits for encryption, providing basic security measures.
- RSA15 refers to RSA encryption with PKCS#1 v1.5 padding, an older encryption scheme that is less secure than RSA-OAEP.
OpcuaSecurityPolicy::Basic256
- This security policy uses AES with a key length of 256 bits for encryption without specifying the hash and asymmetric encryption algorithms.
- While it provides stronger encryption compared to Basic128, it lacks specificity regarding hashing and asymmetric encryption algorithms.
OpcuaSecurityPolicy::Basic256Sha256
- Similar to Basic256, this security policy uses AES with a key length of 256 bits for encryption.
- Additionally, it specifies SHA-256 for hashing, enhancing data integrity compared to Basic256.
OpcuaSecurityPolicy::None
- This security policy indicates that no security measures are applied to communication.
- It should only be used in environments where security is not a concern, such as local testing or isolated networks.
Security modes and Security policies compatibility
In OPC UA, not all combinations of security modes and security policies are compatible. Here’s a compatibility matrix indicating which security policies can be used with which security modes:
OpcuaSecurityPolicy / OpcuaSecurityMode | None | Sign | SignAndEncrypt |
---|---|---|---|
None | Y | N | N |
Basic128Rsa15 | N | Y | Y |
Basic256 | N | Y | Y |
Basic256Sha256 | N | Y | Y |
Aes128_Sha256_RsaOaep | N | Y | Y |
Aes256_Sha256_RsaPss | N | Y | Y |
Y
means the OpcuaSecurityPolicy is compatible with the OpcuaSecurityModeN
means the OpcuaSecurityPolicy is NOT compatible with the OpcuaSecurityMode
OpcuaCertificate
The OpcuaCertificate
type represents a certificate used for OPC UA connections.
path: String
: Specifies the file path to the certificate (containing the public key).private_key_path: String
: Specifies the file path to the private key associated with the certificate.application_uri: String?
: (Optional) Represents the application URI associated with the certificate.allow_self_signed: bool?
: (Optional) Indicates whether self-signed certificates are allowed. Iftrue
, self-signed certificates are permitted; iffalse
or not specified, they are not allowed.
Depending on the selected OpcuaSecurityPolicy
and OpcuaSecurityMode
, the use of certificate can be mandatory:
OpcuaSecurityPolicy / OpcuaSecurityMode | None | Sign | SignAndEncrypt |
---|---|---|---|
None | N | N | N |
Basic128Rsa15 | N | Y | Y |
Basic256 | N | Y | Y |
Basic256Sha256 | N | Y | Y |
Aes128_Sha256_RsaOaep | N | Y | Y |
Aes256_Sha256_RsaPss | N | Y | Y |
Y
means a certificate must be provided to perfom the connectionN
means no certificate is required to perform the connection
Examples
Read a single node
@library("opcua", "0.0.0");
use opcua;
fn main() {
var opcua = OpcuaClient {
host: "localhost",
port: 4842,
security_mode: OpcuaSecurityMode::SignAndEncrypt,
security_policy: OpcuaSecurityPolicy::Aes128_Sha256_RsaOaep,
certificate: OpcuaCertificate {
allow_self_signed: true,
path: "./test/key/client_cert.der",
private_key_path: "./test/key/client_key.der",
},
credentials: OpcuaCredentials { login: "root", password: "opcua" }
};
var result = opcua.read("ns=0;i=2258");
println(result);
println(result is time);
}
This code perform a OPC UA connection to a local server running on port 4842
. The connection is password protected and uses signature/encryption with the Aes128_Sha256_RsaOaep policy.
We want to read the node i=2258
on namespace 0
(this node is present on all OPC UA servers and store the server timestamp). We also assess the result is a core::time
.
Result:
'2024-05-30T09:49:38.074797+00:00'
true
Read a single node with times
In a similar way, we want to retrieve the content of node ns=0;i=2258
, alongside with the server
and source
times:
@library("opcua", "0.0.0");
use opcua;
fn main() {
var opcua = OpcuaClient {
host: "localhost",
port: 4842,
security_mode: OpcuaSecurityMode::SignAndEncrypt,
security_policy: OpcuaSecurityPolicy::Aes128_Sha256_RsaOaep,
certificate: OpcuaCertificate {
allow_self_signed: true,
path: "./test/key/client_cert.der",
private_key_path: "./test/key/client_key.der",
},
credentials: OpcuaCredentials { login: "root", password: "opcua" }
};
var result = opcua.read_with_time("ns=0;i=2258");
println(result);
}
Result:
OpcuaValueDetails{value:'2024-05-30T09:51:59.498332+00:00',source_time:'2024-05-30T09:51:59Z',server_time:'2024-05-30T09:51:59Z'}
Writing a value
Node ns=1;s=boolean
hosts a boolean value. We want to write a new value on this node:
@library("opcua", "0.0.0");
use opcua;
fn main() {
var opcua = OpcuaClient {
host: "localhost",
port: 4842,
security_mode: OpcuaSecurityMode::None,
security_policy: OpcuaSecurityPolicy::None,
};
println(opcua.read("ns=1;s=boolean"));
opcua.write("ns=1;s=boolean", true);
println(opcua.read("ns=1;s=boolean"));
opcua.write("ns=1;s=boolean", 42);
}
Result:
false //Original value
true //After writing
Error{code:ErrorCode::runtime_error,msg:"Unsupported GreyCat type (3) for OPCUA type: Boolean"}
at main (project.gcl:21:38)
Initially, the value on the node was false
. We replace it with true
and a subsequent call shows the value has been correctly written back to the server.
Writing an incompatible value (42
instead of a boolean value) raises a runtime_error
.
Writing an array
Node ns=1;s=bool.array
hosts a boolean array (size = 3). We want to write new values on this node:
@library("opcua", "0.0.0");
use opcua;
fn main() {
var opcua = OpcuaClient {
host: "localhost",
port: 4842,
security_mode: OpcuaSecurityMode::None,
security_policy: OpcuaSecurityPolicy::None,
};
println(opcua.read("ns=1;s=bool.array"));
opcua.write("ns=1;s=bool.array", [false, false, true]);
println(opcua.read("ns=1;s=bool.array"));
}
Result:
[true,false,true] //Original value
[false,false,true] //After writing
Calling a method
Our OPC UA server exposes two RPC methods:
ns=1;i=62541
: Take one string argument and returnHello <argument>
ns=1;s=IncInt32ArrayValues
: Take two arguments, a int array, and an increment. This method returns an array with elements increased by the increment value
@library("opcua", "0.0.0");
use opcua;
use util; //For the Assert methods
fn main() {
var opcua = OpcuaClient {
host: "localhost",
port: 4842,
security_mode: OpcuaSecurityMode::SignAndEncrypt,
security_policy: OpcuaSecurityPolicy::Aes128_Sha256_RsaOaep,
certificate: OpcuaCertificate {
allow_self_signed: true,
path: "./test/key/client_cert.der",
private_key_path: "./test/key/client_key.der",
},
credentials: OpcuaCredentials { login: "root", password: "opcua" }
};
Assert::equals("Hello DataThings", opcua.call("ns=1;i=62541", ["DataThings"]));
Assert::equals("${[6, 7, 8, 9, 10]}", "${opcua.call("ns=1;s=IncInt32ArrayValues", [[1, 2, 3, 4, 5], 5])}");
}
Third-party licenses
open62541 (https://www.open62541.org/)
This library uses internally open62541
, an Open Source OPC UA licensed under the MPL v2.0
openssl (https://www.openssl.org/)
open62541
is built with OpenSSL to perform the encryption. OpenSSL is licensed under Apache-2.0.