NAME

OPCUA::Open62541 - Perl XS wrapper for open62541 OPC UA library

SYNOPSIS

use OPCUA::Open62541;

my $server = OPCUA::Open62541::Server->new();

my $client = OPCUA::Open62541::Client->new();

DESCRIPTION

The open62541 is a library implementing an OPC UA client and server. This module provides access to the C functionality from Perl programs.

EXPORT

Refer to OPCUA::Open62541::Constant module about the exported values.

METHODS

Refer to the open62541 documentation for the semantic of classes and methods.

Variant

$variant = OPCUA::Open62541::Variant->new()
$boolean = $variant->isEmpty()
$boolean = $variant->isScalar()
$boolean = $variant->hasScalarType($data_type)
$boolean = $variant->hasArrayType($data_type)
$variant->setScalar($p, $data_type)
$data_type = $variant->getType()
$p = $variant->getScalar()

Server

$server = OPCUA::Open62541::Server->new()
$server_config = $server->getConfig()
$status_code = $server->run($server, $running)

$running should be TRUE at startup. When set to FALSE during method invocation, the server stops magically.

$status_code = $server->run_startup($server)
$wait_ms = $server->run_iterate($server, $wait_internal)
$status_code = $server->run_shutdown($server)
\%dataValue = $server->read(\%item, $timestamps)
$status_code = $server->readAccessLevel(\%nodeId, \$outByte)
$status_code = $server->readArrayDimensions(\%nodeId, \$outVariant)
$status_code = $server->readBrowseName(\%nodeId, \$outQualifiedName)
$status_code = $server->readContainsNoLoops(\%nodeId, \$outBoolean)
$status_code = $server->readDataType(\%nodeId, \$outDataType)
$status_code = $server->readDescription(\%nodeId, \$outLocalizedText)
$status_code = $server->readDisplayName(\%nodeId, \$outLocalizedText)
$status_code = $server->readEventNotifier(\%nodeId, \$outByte)
$status_code = $server->readExecutable(\%nodeId, \$outBoolean)
$status_code = $server->readHistorizing(\%nodeId, \$outBoolean)
$status_code = $server->readInverseName(\%nodeId, \$outLocalizedText)
$status_code = $server->readIsAbstract(\%nodeId, \$outBoolean)
$status_code = $server->readMinimumSamplingInterval(\%nodeId, \$outDouble)
$status_code = $server->readNodeClass(\%nodeId, \$outNodeClass)
$status_code = $server->readNodeId(\%nodeId, \$outNodeId)
$status_code = $server->readObjectProperty(\%nodeId, \%propertyName, \$outVariant)
$status_code = $server->readSymmetric(\%nodeId, \$outBoolean)
$status_code = $server->readValue(\%nodeId, \$outVariant)
$status_code = $server->readValueRank(\%nodeId, \$outInt32)
$status_code = $server->readWriteMask(\%nodeId, \$outUInt32)
$status_code = $server->write(\%value)
$status_code = $server->writeAccessLevel(\%nodeId, $newByte)
$status_code = $server->writeArrayDimensions(\%nodeId, \%newVariant)
$status_code = $server->writeDataType(\%nodeId, $newDataType)
$status_code = $server->writeDescription(\%nodeId, \%newLocalizedText)
$status_code = $server->writeDisplayName(\%nodeId, \%newLocalizedText)
$status_code = $server->writeEventNotifier(\%nodeId, $newByte)
$status_code = $server->writeExecutable(\%nodeId, $newBoolean)
$status_code = $server->writeHistorizing(\%nodeId, $newBoolean)
$status_code = $server->writeInverseName(\%nodeId, \%newLocalizedText)
$status_code = $server->writeIsAbstract(\%nodeId, $newBoolean)
$status_code = $server->writeMinimumSamplingInterval(\%nodeId, $newDouble)
$status_code = $server->writeObjectProperty(\%nodeId, \%propertyName, \%newVariant)
$status_code = $server->writeValue(\%nodeId, \%newVariant)
$status_code = $server->writeValueRank(\%nodeId, $newInt32)
$status_code = $server->writeWriteMask(\%nodeId, $newUInt32)
\%browseResult = $server->browse($maxReferences, \%browseDescription)
\%browseResult = $server->browseNext($releaseContinuationPoint, $continuationPoint)
$server->setAdminSessionContext($context)

This method is only available if open62541 library supports it.

$status_code = $server->addVariableNode(\%requestedNewNodeId, \%parentNodeId, \%referenceTypeId, \%browseName, \%typeDefinition, \%attr, $nodeContext, \%outNewNodeId)
$status_code = $server->addVariableTypeNode(\%requestedNewNodeId, \%parentNodeId, \%referenceTypeId, \%browseName, \%typeDefinition, \%attr, $nodeContext, \%outNewNodeId)
$status_code = $server->addObjectNode(\%requestedNewNodeId, \%parentNodeId, \%referenceTypeId, \%browseName, \%typeDefinition, \%attr, $nodeContext, \%outNewNodeId)
$status_code = $server->addObjectTypeNode(\%requestedNewNodeId, \%parentNodeId, \%referenceTypeId, \%browseName, \%attr, $nodeContext, \%outNewNodeId)
$status_code = $server->addViewNode(\%requestedNewNodeId, \%parentNodeId, \%referenceTypeId, \%browseName, \%attr, $nodeContext, \%outNewNodeId)
$status_code = $server->addReferenceTypeNode(\%requestedNewNodeId, \%parentNodeId, \%referenceTypeId, \%browseName, \%attr, $nodeContext, \%outNewNodeId)
$status_code = $server->addDataTypeNode(\%requestedNewNodeId, \%parentNodeId, \%referenceTypeId, \%browseName, \%attr, $nodeContext, \%outNewNodeId)
$status_code = $server->deleteNode(\%nodeId, $deleteReferences)
$status_code = $server->addReference(\%sourceId, \%refTypeId, \%targetId, $isForward)
$status_code = $server->deleteReference(\%sourceNodeId, \%referenceTypeId, $isForward, \%targetNodeId, $deleteBidirectional)
$namespace_index = $server->addNamespace($namespace_name)

ServerConfig

$status_code = $server_config->setDefault()
$status_code = $server_config->setDefaultWithSecurityPolicies($port, $certificate, $privateKey, $trustList, $issuerList, $revocationList)

$trustList, $issuerList and $revocationList are currently not supported and have to be undef.

$status_code = $server_config->setMinimal($port, $certificate)
$server_config->setCustomHostname($custom_hostname)
$server_config->setGlobalNodeLifecycle(\%lifecycle)
$lifecycle{GlobalNodeLifecycle_constructor} = sub { my ($server, $sessionId, $sessionContext, $nodeId, \$nodeContext) = @_ }
$lifecycle{GlobalNodeLifecycle_destructor} = sub { my ($server, $sessionId, $sessionContext, $nodeId, $nodeContext) = @_ }
$lifecycle{GlobalNodeLifecycle_createOptionalChild} = sub { my ($server, $sessionId, $sessionContext, $sourceNodeId, $targetParentNodeId, $referenceTypeId) = @_ }
$lifecycle{GlobalNodeLifecycle_generateChildNodeId} = sub { my ($server, $sessionId, $sessionContext, $sourceNodeId, $targetParentNodeId, $referenceTypeId, \%targetNodeId) = @_ }

Call $server->setAdminSessionContext() to set $server and $sessionContext in the callback.

$logger = $server_config->getLogger()
$buildInfo = $server_config->getBuildInfo()
$server_config->setBuildInfo(\%buildInfo)
$applicationDescription = $server_config->getApplicationDescription()
$server_config->setApplicationDescription(\%applicationDescription)
$limit = $server_config->getMaxSecureChannels()
$server_config->setMaxSecureChannels($maxSecureChannels)
$limit = $server_config->getMaxSessions()
$server_config->setMaxSessions($maxSessions)
$limit = $server_config->getMaxSessionTimeout()
$server_config->setMaxSessionTimeout($maxSessionTimeout)
$limit = $server_config->getMaxNodesPerRead()
$server_config->setMaxNodesPerRead($maxNodesPerRead)
$limit = $server_config->getMaxNodesPerWrite()
$server_config->setMaxNodesPerWrite($maxNodesPerWrite)
$limit = $server_config->getMaxNodesPerMethodCall()
$server_config->setMaxNodesPerMethodCall($maxNodesPerMethodCall)
$limit = $server_config->getMaxNodesPerBrowse()
$server_config->setMaxNodesPerBrowse($maxNodesPerBrowse)
$limit = $server_config->getMaxNodesPerRegisterNodes()
$server_config->setMaxNodesPerRegisterNodes($maxNodesPerRegisterNodes)
$limit = $server_config->getMaxNodesPerTranslateBrowsePathsToNodeIds()
$server_config->setMaxNodesPerTranslateBrowsePathsToNodeIds($maxNodesPerTranslateBrowsePathsToNodeIds)
$limit = $server_config->getMaxNodesPerNodeManagement()
$server_config->setMaxNodesPerNodeManagement($maxNodesPerNodeManagement)
$limit = $server_config->getMaxMonitoredItemsPerCall()
$server_config->setMaxMonitoredItemsPerCall($maxMonitoredItemsPerCall)
$limit = $server_config->getMaxSubscriptions()
$server_config->setMaxSubscriptions($maxSubscriptions)
$limit = $server_config->getMaxSubscriptionsPerSession()
$server_config->setMaxSubscriptionsPerSession($maxSubscriptionsPerSession)
$limit = $server_config->getMaxNotificationsPerPublish()
$server_config->setMaxNotificationsPerPublish($maxNotificationsPerPublish)
$limit = $server_config->getEnableRetransmissionQueue()
$server_config->setEnableRetransmissionQueue($enableRetransmissionQueue)
$limit = $server_config->getMaxRetransmissionQueueSize()
$server_config->setMaxRetransmissionQueueSize($maxRetransmissionQueueSize)
$limit = $server_config->getMaxEventsPerNode()
$server_config->setMaxEventsPerNode($maxEventsPerNode)
$server_config->setUserRightsMaskReadonly($readonly)

If $readonly is set to true, only reading of attributes is allowed. If set to false, no additional restrictions on the UserWriteMask are made and attributes will also be writable (this is the default behaviour). Values of variable nodes are excluded from the UserWriteMask and are handled by the AccessLevel instead (see setUserAccessLevelReadonly()).

$server_config->setUserAccessLevelReadonly($readonly)

If $readonly is set to true, only reading of variable values is allowed (including reading historical data of values). If set to false, no additional restrictions on the AccessLevel are made and values will also be writable (this is the default behaviour).

$server_config->disableUserExecutable($disable)

If $disable is set to true, method nodes will not be shown as executable for users (UserExecutable attribute of the method node). If set to false, no addtional restrictions are made on the UserExecutable attribute of method nodes (this is the default).

$server_config->disableUserExecutableOnObject($disable)

If $disable is set to true, methods can not be executed. If set to false, no addtional restrictions are made on the execution of methods (this is the default).

$server_config->disableAddNode($disable)

If $disable is set to true, nodes can not be added. If set to false, nodes can be added (this is the default).

$server_config->disableAddReference($disable)

If $disable is set to true, references can not be added. If set to false, references can be added (this is the default).

$server_config->disableDeleteNode($disable)

If $disable is set to true, nodes can not be deleted. If set to false, nodes can be deleted (this is the default).

$server_config->disableDeleteReference($disable)

If $disable is set to true, references can not be deleted. If set to false, references can be deleted (this is the default).

$server_config->disableHistoryUpdateUpdateData($disable)

If $disable is set to true, historical data may not be inserted, replaced or updated. If set to false, no addtional restrictions are made on the modification of historical data (this is the default).

$server_config->disableHistoryUpdateDeleteRawModified($disable)

If $disable is set to true, historical data may not be deleted. If set to false, historical data can be deleted (this is the default).

Client

$client = OPCUA::Open62541::Client->new()
$client_config = $client->getConfig()
$status_code = $client->connect($url)
$status_code = $client->connectAsync($endpointUrl)
$callback = sub { my ($client, $userdata, $requestId, $status_code) = @_ }

There should be an interval of 100ms between the call to connectAsync() and run_iterate() or open62541 may try to operate on a non existent socket.

$status_code = $client->run_iterate($timeout)
$status_code = $client->disconnect()
$status_code = $client->disconnectAsync()
($channel_state, $session_state, $connect_status) = $client->getState()

1.1 API

In scalar context croak due to 1.0 API incompatibility.

$status_code = $client->sendAsyncBrowseRequest(\%request, \&callback, $data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, \%response) = @_ }
$status_code = $client->sendAsyncBrowseNextRequest(\%request, \&callback, $data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, \%response) = @_ }
$response = $client->Service_browse(\%request)
$status_code = $client->readAccessLevelAttribute(\%nodeId, \$outByte)
$status_code = $client->readBrowseNameAttribute(\%nodeId, \$outQualifiedName)
$status_code = $client->readContainsNoLoopsAttribute(\%nodeId, \$outBoolean)
$status_code = $client->readDataTypeAttribute(\%nodeId, \$outDataType)
$status_code = $client->readDescriptionAttribute(\%nodeId, \$outLocalizedText)
$status_code = $client->readDisplayNameAttribute(\%nodeId, \$outLocalizedText)
$status_code = $client->readEventNotifierAttribute(\%nodeId, \$outByte)
$status_code = $client->readExecutableAttribute(\%nodeId, \$outBoolean)
$status_code = $client->readHistorizingAttribute(\%nodeId, \$outBoolean)
$status_code = $client->readInverseNameAttribute(\%nodeId, \$outLocalizedText)
$status_code = $client->readIsAbstractAttribute(\%nodeId, \$outBoolean)
$status_code = $client->readMinimumSamplingIntervalAttribute(\%nodeId, \$outDouble)
$status_code = $client->readNodeClassAttribute(\%nodeId, \$outNodeClass)
$status_code = $client->readNodeIdAttribute(\%nodeId, \$outNodeId)
$status_code = $client->readSymmetricAttribute(\%nodeId, \$outBoolean)
$status_code = $client->readUserAccessLevelAttribute(\%nodeId, \$outByte)
$status_code = $client->readUserExecutableAttribute(\%nodeId, \$outBoolean)
$status_code = $client->readUserWriteMaskAttribute(\%nodeId, \$outUInt32)
$status_code = $client->readValueAttribute(\%nodeId, \$outVariant)
$status_code = $client->readValueRankAttribute(\%nodeId, \$outInt32)
$status_code = $client->readWriteMaskAttribute(\%nodeId, \$outUInt32)
$status_code = $client->sendAsyncReadRequest(\%request, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, \%response) = @_ }
$status_code = $client->readAccessLevelAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $byte) = @_ }
$status_code = $client->readBrowseNameAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, \%qualifiedName) = @_ }
$status_code = $client->readContainsNoLoopsAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $boolean) = @_ }
$status_code = $client->readDataTypeAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $dataType) = @_ }
$status_code = $client->readDescriptionAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, \%localizedText) = @_ }
$status_code = $client->readDisplayNameAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, \%localizedText) = @_ }
$status_code = $client->readEventNotifierAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $byte) = @_ }
$status_code = $client->readExecutableAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $boolean) = @_ }
$status_code = $client->readHistorizingAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $boolean) = @_ }
$status_code = $client->readInverseNameAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, \%localizedText) = @_ }
$status_code = $client->readIsAbstractAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $boolean) = @_ }
$status_code = $client->readMinimumSamplingIntervalAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $double) = @_ }
$status_code = $client->readNodeClassAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $nodeClass) = @_ }
$status_code = $client->readNodeIdAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, \%nodeId) = @_ }
$status_code = $client->readSymmetricAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $boolean) = @_ }
$status_code = $client->readUserAccessLevelAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $byte) = @_ }
$status_code = $client->readUserExecutableAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $boolean) = @_ }
$status_code = $client->readUserWriteMaskAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $uint32) = @_ }
$status_code = $client->readValueAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, \%variant) = @_ }
$status_code = $client->readValueRankAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $int32) = @_ }
$status_code = $client->readWriteMaskAttribute_async(\%nodeId, \&callback, \$data, \$reqId)
$callback = sub { my ($client, $userdata, $requestId, $uint32) = @_ }
$status_code = $client->writeAccessLevelAttribute(\%nodeId, $newByte)
$status_code = $client->writeBrowseNameAttribute(\%nodeId, \%newQualifiedName)
$status_code = $client->writeContainsNoLoopsAttribute(\%nodeId, $outBoolean)
$status_code = $client->writeDataTypeAttribute(\%nodeId, $newDataType)
$status_code = $client->writeDescriptionAttribute(\%nodeId, \%newLocalizedText)
$status_code = $client->writeDisplayNameAttribute(\%nodeId, \%newLocalizedText)
$status_code = $client->writeEventNotifierAttribute(\%nodeId, $newByte)
$status_code = $client->writeExecutableAttribute(\%nodeId, $newBoolean)
$status_code = $client->writeHistorizingAttribute(\%nodeId, $newBoolean)
$status_code = $client->writeInverseNameAttribute(\%nodeId, \%newLocalizedText)
$status_code = $client->writeIsAbstractAttribute(\%nodeId, $newBoolean)
$status_code = $client->writeMinimumSamplingIntervalAttribute(\%nodeId, $newDouble)
$status_code = $client->writeNodeClassAttribute(\%nodeId, $newNodeClass)
$status_code = $client->writeNodeIdAttribute(\%nodeId, \%newNodeId)
$status_code = $client->writeSymmetricAttribute(\%nodeId, $newBoolean)
$status_code = $client->writeUserAccessLevelAttribute(\%nodeId, $newByte)
$status_code = $client->writeUserExecutableAttribute(\%nodeId, $newBoolean)
$status_code = $client->writeUserWriteMaskAttribute(\%nodeId, $newUInt32)
$status_code = $client->writeValueAttribute(\%nodeId, \%newVariant)
$status_code = $client->writeValueRankAttribute(\%nodeId, $newInt32)
$status_code = $client->writeWriteMaskAttribute(\%nodeId, $newUInt32)
$request = OPCUA::Open62541::Client->CreateSubscriptionRequest_default()
$response = $client->Subscriptions_create(\%request, $subscriptionContext, \&statusChangeCallback, \&deleteCallback)
$statusChangeCallback = sub { my ($client, $subscriptionId, $subscriptionContext, $notification) = @_ }
$deleteCallback = sub { my ($client, $subscriptionId, $subscriptionContext) = @_ }
$response = $client->Subscriptions_modify(\%request)
$response = $client->Subscriptions_delete(\%request)
$status_code = $client->Subscriptions_deleteSingle($subscriptionId)
$response = $client->setPublishingMode(\%request)
$request = OPCUA::Open62541::Client->MonitoredItemCreateRequest_default(\%nodeId)
$response = $client->MonitoredItems_createDataChange($subscriptionId, $timestamps, \%request, $monitoredContext, \&dataChangeCallback, \&deleteCallback)
$dataChangeCallback = sub { my ($client, $subscriptionId, $subscriptionContext, $monitoredId, $monitoredContext, $value) = @_ }
$deleteCallback = sub { my ($client, $subscriptionId, $subscriptionContext, $monitoredId, $monitoredContext) = @_ }
$response = $client->MonitoredItems_createDataChanges(\%request, \@monitoredContexts, \@dataChangeCallbacks, \@deleteCallbacks)
$response = $client->MonitoredItems_delete(\%request)
$status_code = $client->MonitoredItems_deleteSingle($subscriptionId, $monitoredItemId)

ClientConfig

$status_code = $client_config->setDefault()
$status_code = $client_config->setDefaultEncryption($certificate, $privateKey, $trustList, $revocationList)

If no trust or revocation list is set, the client will accept all certificates.

$context = $client_config->getClientContext()
$client_config->setClientContext($context)
$securityMode = $client_config->getSecurityMode()
$client_config->setSecurityMode($securityMode)
$clientDescription = $client_config->getClientDescription()
$client_config->setClientDescription($clientDescription)
$client_config->setStateCallback($callback)
$logger = $client_config->getLogger()
$client_config->setUsernamePassword($userName, $password)

With this method a username and password can be set for the OPC UA connection. If $userName is an empty string or undef, username and password are cleared in the client configuration. Calling this method will also clear endpoint and userTokenPolicy data in the client configuration that may exist from previous connection. If a previous connection was made, the client will again try to get and match the endpoints and policies from the server.

Logger

The Logger uses the embedded logger of a client or server config. The scope of the logger object may extend the lifetime of the client or sever object. It contains Perl callbacks to the log and clear functions. The log functions are exported to Perl.

$logger->setCallback($log, $context, $clear);
$log = sub { my ($context, $level, $category, $message) = @_ }
$clear = sub { my ($context) = @_ }
$logger->logTrace($category, $msg, ...);
$logger->logDebug($category, $msg, ...);
$logger->logInfo($category, $msg, ...);
$logger->logWarning($category, $msg, ...);
$logger->logError($category, $msg, ...);
$logger->logFatal($category, $msg, ...);

SEE ALSO

OPC UA library, https://open62541.org/

OPC Foundation, https://opcfoundation.org/

OPCUA::Open62541::Constant

AUTHORS

Alexander Bluhm <bluhm@genua.de>, Anton Borowka, Arne Becker, Marvin Knoblauch <mknob@genua.de>

CAVEATS

This interface is far from complete.

The C types UA_Int64 and UA_UInt64 are implemented as Perl integers IV and UV respectively. This only works for Perl that is compiled on a 64 bit platform. 32 bit platforms are currently not supported.

COPYRIGHT AND LICENSE

Copyright (c) 2020-2023 Alexander Bluhm

Copyright (c) 2020-2023 Anton Borowka

Copyright (c) 2020 Arne Becker

Copyright (c) 2020 Marvin Knoblauch

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

Thanks to genua GmbH, https://www.genua.de/ for sponsoring this work.