mirror of
https://github.com/FreeOpcUa/opcua-asyncio
synced 2025-10-29 17:07:18 +08:00
add Client.set_values for wrtting multiple node values at once
This commit is contained in:
committed by
Christian Bergmiller
parent
9cb6411c77
commit
a29444fb56
@@ -13,6 +13,7 @@ from ..common.subscription import Subscription
|
||||
from ..common.shortcuts import Shortcuts
|
||||
from ..common.structures import load_type_definitions, load_enums
|
||||
from ..common.utils import create_nonce
|
||||
from ..common.ua_utils import value_to_datavalue
|
||||
from ..crypto import uacrypto, security_policies
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -28,7 +29,6 @@ class Client:
|
||||
use UaClient object, available as self.uaclient
|
||||
which offers the raw OPC-UA services interface.
|
||||
"""
|
||||
|
||||
def __init__(self, url: str, timeout: int = 4, loop=None):
|
||||
"""
|
||||
:param url: url of the server.
|
||||
@@ -78,9 +78,7 @@ class Client:
|
||||
"""
|
||||
_logger.info("find_endpoint %r %r %r", endpoints, security_mode, policy_uri)
|
||||
for ep in endpoints:
|
||||
if (ep.EndpointUrl.startswith(ua.OPC_TCP_SCHEME) and
|
||||
ep.SecurityMode == security_mode and
|
||||
ep.SecurityPolicyUri == policy_uri):
|
||||
if (ep.EndpointUrl.startswith(ua.OPC_TCP_SCHEME) and ep.SecurityMode == security_mode and ep.SecurityPolicyUri == policy_uri):
|
||||
return ep
|
||||
raise ua.UaError("No matching endpoints: {0}, {1}".format(security_mode, policy_uri))
|
||||
|
||||
@@ -117,11 +115,12 @@ class Client:
|
||||
raise ua.UaError("Wrong format: `{}`, expected at least 4 comma-separated values".format(string))
|
||||
policy_class = getattr(security_policies, "SecurityPolicy{}".format(parts[0]))
|
||||
mode = getattr(ua.MessageSecurityMode, parts[1])
|
||||
return await self.set_security(
|
||||
policy_class, parts[2], parts[3], parts[4] if len(parts) >= 5 else None, mode
|
||||
)
|
||||
return await self.set_security(policy_class, parts[2], parts[3], parts[4] if len(parts) >= 5 else None, mode)
|
||||
|
||||
async def set_security(self, policy, certificate_path: str, private_key_path: str,
|
||||
async def set_security(self,
|
||||
policy,
|
||||
certificate_path: str,
|
||||
private_key_path: str,
|
||||
server_certificate_path: str = None,
|
||||
mode: ua.MessageSecurityMode = ua.MessageSecurityMode.SignAndEncrypt):
|
||||
"""
|
||||
@@ -580,8 +579,18 @@ class Client:
|
||||
|
||||
async def get_values(self, nodes):
|
||||
"""
|
||||
Read the value of multiple nodes in one roundtrip.
|
||||
Read the value of multiple nodes in one ua call.
|
||||
"""
|
||||
nodes = [node.nodeid for node in nodes]
|
||||
results = await self.uaclient.get_attribute(nodes, ua.AttributeIds.Value)
|
||||
nodeids = [node.nodeid for node in nodes]
|
||||
results = await self.uaclient.get_attributes(nodeids, ua.AttributeIds.Value)
|
||||
return [result.Value.Value for result in results]
|
||||
|
||||
async def set_values(self, nodes, values):
|
||||
"""
|
||||
Write values to multiple nodes in one ua call
|
||||
"""
|
||||
nodeids = [node.nodeid for node in nodes]
|
||||
dvs = [value_to_datavalue(val) for val in values]
|
||||
results = await self.uaclient.set_attributes(nodeids, dvs, ua.AttributeIds.Value)
|
||||
for result in results:
|
||||
result.check()
|
||||
|
||||
@@ -642,15 +642,35 @@ class UaClient:
|
||||
response.ResponseHeader.ServiceResult.check()
|
||||
# nothing to return for this service
|
||||
|
||||
async def get_attribute(self, nodes, attr):
|
||||
self.logger.info("get_attribute")
|
||||
async def get_attributes(self, nodeids, attr):
|
||||
self.logger.info("get_attributes of several nodes")
|
||||
request = ua.ReadRequest()
|
||||
for node in nodes:
|
||||
for nodeid in nodeids:
|
||||
rv = ua.ReadValueId()
|
||||
rv.NodeId = node
|
||||
rv.NodeId = nodeid
|
||||
rv.AttributeId = attr
|
||||
request.Parameters.NodesToRead.append(rv)
|
||||
data = await self.protocol.send_request(request)
|
||||
response = struct_from_binary(ua.ReadResponse, data)
|
||||
response.ResponseHeader.ServiceResult.check()
|
||||
return response.Results
|
||||
|
||||
async def set_attributes(self, nodeids, datavalues, attributeid=ua.AttributeIds.Value):
|
||||
"""
|
||||
Set an attribute of multiple nodes
|
||||
datavalue is a ua.DataValue object
|
||||
"""
|
||||
self.logger.info("set_attributes of several nodes")
|
||||
request = ua.WriteRequest()
|
||||
for idx, nodeid in enumerate(nodeids):
|
||||
attr = ua.WriteValue()
|
||||
attr.NodeId = nodeid
|
||||
attr.AttributeId = attributeid
|
||||
attr.Value = datavalues[idx]
|
||||
request.Parameters.NodesToWrite.append(attr)
|
||||
data = await self.protocol.send_request(request)
|
||||
response = struct_from_binary(ua.WriteResponse, data)
|
||||
response.ResponseHeader.ServiceResult.check()
|
||||
return response.Results
|
||||
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
High level node object, to access node attribute
|
||||
and browse address space
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
import logging
|
||||
|
||||
from asyncua import ua
|
||||
from .ua_utils import value_to_datavalue
|
||||
|
||||
from .events import Event, get_filter_from_event_type
|
||||
from .ua_utils import data_type_to_variant_type
|
||||
from .manage_nodes import create_folder, create_object, create_object_type, create_variable, create_variable_type, \
|
||||
@@ -214,16 +215,8 @@ class Node:
|
||||
and you modfy it afterward, then the object in db will be modified without any
|
||||
data change event generated
|
||||
"""
|
||||
datavalue = None
|
||||
if isinstance(value, ua.DataValue):
|
||||
datavalue = value
|
||||
elif isinstance(value, ua.Variant):
|
||||
datavalue = ua.DataValue(value)
|
||||
datavalue.SourceTimestamp = datetime.utcnow()
|
||||
else:
|
||||
datavalue = ua.DataValue(ua.Variant(value, varianttype))
|
||||
datavalue.SourceTimestamp = datetime.utcnow()
|
||||
await self.set_attribute(ua.AttributeIds.Value, datavalue)
|
||||
dv = value_to_datavalue(value, varianttype)
|
||||
await self.set_attribute(ua.AttributeIds.Value, dv)
|
||||
|
||||
set_data_value = set_value
|
||||
|
||||
|
||||
@@ -13,6 +13,22 @@ from asyncua import ua
|
||||
logger = logging.getLogger('__name__')
|
||||
|
||||
|
||||
def value_to_datavalue(val, varianttype=None):
|
||||
"""
|
||||
convert anyting to a DataValue using varianttype
|
||||
"""
|
||||
datavalue = None
|
||||
if isinstance(val, ua.DataValue):
|
||||
datavalue = val
|
||||
elif isinstance(val, ua.Variant):
|
||||
datavalue = ua.DataValue(val)
|
||||
datavalue.SourceTimestamp = datetime.utcnow()
|
||||
else:
|
||||
datavalue = ua.DataValue(ua.Variant(val, varianttype))
|
||||
datavalue.SourceTimestamp = datetime.utcnow()
|
||||
return datavalue
|
||||
|
||||
|
||||
def val_to_string(val, truncate=False):
|
||||
"""
|
||||
convert a python object or python-asyncua object to a string
|
||||
@@ -160,7 +176,7 @@ async def get_node_supertypes(node, includeitself=False, skipbase=True):
|
||||
:param node: can be a ua.Node or ua.NodeId
|
||||
:param includeitself: include also node to the list
|
||||
:param skipbase don't include the toplevel one
|
||||
:returns list of ua.Node, top parent first
|
||||
:returns list of ua.Node, top parent first
|
||||
"""
|
||||
parents = []
|
||||
if includeitself:
|
||||
@@ -202,7 +218,7 @@ async def is_child_present(node, browsename):
|
||||
return if a browsename is present a child from the provide node
|
||||
:param node: node wherein to find the browsename
|
||||
:param browsename: browsename to search
|
||||
:returns returne True if the browsename is present else False
|
||||
:returns returne True if the browsename is present else False
|
||||
"""
|
||||
child_descs = await node.get_children_descriptions()
|
||||
for child_desc in child_descs:
|
||||
@@ -229,7 +245,7 @@ async def get_base_data_type(datatype):
|
||||
Looks up the base datatype of the provided datatype Node
|
||||
The base datatype is either:
|
||||
A primitive type (ns=0, i<=21) or a complex one (ns=0 i>21 and i<=30) like Enum and Struct.
|
||||
|
||||
|
||||
Args:
|
||||
datatype: NodeId of a datype of a variable
|
||||
Returns:
|
||||
@@ -245,7 +261,7 @@ async def get_base_data_type(datatype):
|
||||
|
||||
async def get_nodes_of_namespace(server, namespaces=None):
|
||||
"""
|
||||
Get the nodes of one or more namespaces .
|
||||
Get the nodes of one or more namespaces .
|
||||
Args:
|
||||
server: opc ua server to use
|
||||
namespaces: list of string uri or int indexes of the namespace to export
|
||||
|
||||
@@ -87,3 +87,24 @@ async def test_custom_enum_struct(server, client):
|
||||
val = await myvar.get_value()
|
||||
assert 242 == val.IntVal1
|
||||
assert ua.ExampleEnum.EnumVal2 == val.EnumVal
|
||||
|
||||
|
||||
async def test_multiple_read_and_write(server, client):
|
||||
f = await server.nodes.objects.add_folder(3, 'Multiple_read_write_test')
|
||||
v1 = await f.add_variable(3, "a", 1)
|
||||
await v1.set_writable()
|
||||
v2 = await f.add_variable(3, "b", 2)
|
||||
await v2.set_writable()
|
||||
v3 = await f.add_variable(3, "c", 3)
|
||||
await v3.set_writable()
|
||||
v_ro = await f.add_variable(3, "ro", 3)
|
||||
|
||||
vals = await client.get_values([v1, v2, v3])
|
||||
assert vals == [1, 2, 3]
|
||||
await client.set_values([v1, v2, v3], [4, 5, 6])
|
||||
vals = await client.get_values([v1, v2, v3])
|
||||
assert vals == [4, 5, 6]
|
||||
with pytest.raises(ua.uaerrors.BadUserAccessDenied):
|
||||
await client.set_values([v1, v2, v_ro], [4, 5, 6])
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user