def test_extension_object(): obj = ua.UserNameIdentityToken() obj.UserName = "******" obj.Password = b"pass" obj2 = extensionobject_from_binary(ua.utils.Buffer(extensionobject_to_binary(obj))) assert type(obj) == type(obj2) assert obj.UserName == obj2.UserName assert obj.Password == obj2.Password v1 = ua.Variant(obj) v2 = variant_from_binary(ua.utils.Buffer(variant_to_binary(v1))) assert type(v1) == type(v2) assert v1.VariantType == v2.VariantType
async def init(self, shelf_file=None): await self.iserver.init(shelf_file) # setup some expected values await self.set_application_uri(self._application_uri) sa_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_ServerArray)) await sa_node.write_value([self._application_uri]) #TODO: ServiceLevel is 255 default, should be calculated in later Versions sl_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_ServiceLevel)) await sl_node.write_value(ua.Variant(255, ua.VariantType.Byte)) await self.set_build_info(self.product_uri, self.manufacturer_name, self.name, "1.0pre", "0", datetime.now())
def _add_variable_value(self, obj): """ Returns the value for a Variable based on the objects value type. """ _logger.debug("Setting value with type %s and value %s", obj.valuetype, obj.value) if obj.valuetype == "ListOfExtensionObject": values = [] for ext in obj.value: extobj = self._make_ext_obj(ext) values.append(extobj) return ua.Variant(values, ua.VariantType.ExtensionObject) if obj.valuetype == "ListOfGuid": return ua.Variant([uuid.UUID(guid) for guid in obj.value], getattr(ua.VariantType, obj.valuetype[6:])) if obj.valuetype.startswith("ListOf"): vtype = obj.valuetype[6:] if hasattr(ua.ua_binary.Primitives, vtype): return ua.Variant(obj.value, getattr(ua.VariantType, vtype)) if vtype == "LocalizedText": return ua.Variant([ ua.LocalizedText(Text=item["Text"], Locale=item["Locale"]) for item in obj.value ]) return ua.Variant([getattr(ua, vtype)(v) for v in obj.value]) if obj.valuetype == "ExtensionObject": extobj = self._make_ext_obj(obj.value) return ua.Variant(extobj, getattr(ua.VariantType, obj.valuetype)) if obj.valuetype == "Guid": return ua.Variant(uuid.UUID(obj.value), getattr(ua.VariantType, obj.valuetype)) if obj.valuetype == "LocalizedText": myargs = dict(obj.value) if "Encoding" in myargs: del myargs["Encoding"] ltext = ua.LocalizedText(**dict(obj.value)) return ua.Variant(ltext, ua.VariantType.LocalizedText) if obj.valuetype == "NodeId": return ua.Variant(ua.NodeId.from_string(obj.value)) return ua.Variant(obj.value, getattr(ua.VariantType, obj.valuetype))
async def test_functional_advance(srv): basic_struct_name = 'basic_structure' basic_struct = await srv.dict_builder.create_data_type(basic_struct_name) basic_struct.add_field('ID', ua.VariantType.Int32) basic_struct.add_field('Gender', ua.VariantType.Boolean) basic_struct.add_field('Comments', ua.VariantType.String) nested_struct_name = 'nested_structure' nested_struct = await srv.dict_builder.create_data_type(nested_struct_name) nested_struct.add_field('Name', ua.VariantType.String) nested_struct.add_field('Surname', ua.VariantType.String) nested_struct.add_field('Stuff', basic_struct) await srv.dict_builder.set_dict_byte_string() await srv.srv.load_type_definitions() basic_var = await srv.srv.nodes.objects.add_variable(ua.NodeId(namespaceidx=srv.idx), 'BasicStruct', ua.Variant(None, ua.VariantType.Null), datatype=basic_struct.data_type) basic_msg = get_ua_class(basic_struct_name)() basic_msg.ID = 3 basic_msg.Gender = True basic_msg.Comments = 'Test string' await basic_var.write_value(basic_msg) nested_var = await srv.srv.nodes.objects.add_variable(ua.NodeId(namespaceidx=srv.idx), 'NestedStruct', ua.Variant(None, ua.VariantType.Null), datatype=nested_struct.data_type) nested_msg = get_ua_class(nested_struct_name)() nested_msg.Stuff = basic_msg nested_msg.Name = 'Max' nested_msg.Surname = 'Karl' await nested_var.write_value(nested_msg) basic_result = await basic_var.read_value() assert basic_result == basic_msg nested_result = await nested_var.read_value() assert nested_result == nested_msg
async def test_server_read_write_attribute_value(server: Server): node = await server.get_objects_node().add_variable( 0, "0:TestVar", 0, varianttype=ua.VariantType.Int64) dv = server.read_attribute_value(node.nodeid, attr=ua.AttributeIds.Value) assert dv.Value.Value == 0 dv = ua.DataValue( Value=ua.Variant(Value=5, VariantType=ua.VariantType.Int64)) await server.write_attribute_value(node.nodeid, dv, attr=ua.AttributeIds.Value) dv = server.read_attribute_value(node.nodeid, attr=ua.AttributeIds.Value) assert dv.Value.Value == 5 await server.delete_nodes([node])
def value_to_datavalue(val, varianttype=None): """ convert anyting to a DataValue using varianttype """ 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
async def main(): server = Server() await server.init() server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/") # setup our own namespace, not really necessary but should as spec uri = "http://examples.freeopcua.github.io" idx = await server.register_namespace(uri) # populating our address space myobj = await server.nodes.objects.add_object(idx, "MyObject") # Creating a custom event: Approach 1 # The custom event object automatically will have members from its parent (BaseEventType) etype = await server.create_custom_event_type( idx, 'MyFirstEvent', ua.ObjectIds.BaseEventType, [('MyNumericProperty', ua.VariantType.Float), ('MyStringProperty', ua.VariantType.String)]) myevgen = await server.get_event_generator(etype, myobj) # Creating a custom event: Approach 2 custom_etype = await server.nodes.base_event_type.add_object_type( 2, 'MySecondEvent') await custom_etype.add_property(2, 'MyIntProperty', ua.Variant(0, ua.VariantType.Int32)) await custom_etype.add_property(2, 'MyBoolProperty', ua.Variant(True, ua.VariantType.Boolean)) mysecondevgen = await server.get_event_generator(custom_etype, myobj) async with server: count = 0 while True: await asyncio.sleep(1) myevgen.event.Message = ua.LocalizedText("MyFirstEvent %d" % count) myevgen.event.Severity = count myevgen.event.MyNumericProperty = count myevgen.event.MyStringProperty = "Property %d" % count await myevgen.trigger() await mysecondevgen.trigger(message="MySecondEvent %d" % count) count += 1
async def main(): # setup our server server = Server() await server.init() server.set_endpoint('opc.tcp://0.0.0.0:4840/freeopcua/server/') # setup our own namespace, not really necessary but should as spec uri = 'http://examples.freeopcua.github.io' idx = await server.register_namespace(uri) snode1, _ = await new_struct(server, idx, "MyStruct", [ new_struct_field("MyBool", ua.VariantType.Boolean), new_struct_field("MyUInt32List", ua.VariantType.UInt32, array=True), ]) snode2, _ = await new_struct(server, idx, "MyOptionalStruct", [ new_struct_field("MyBool", ua.VariantType.Boolean), new_struct_field("MyUInt32List", ua.VariantType.UInt32, array=True), new_struct_field("MyInt64", ua.VariantType.Int64, optional=True), ]) enode = await new_enum(server, idx, "MyEnum", [ "titi", "toto", "tutu", ]) await server.load_data_type_definitions() valnode = await server.nodes.objects.add_variable(idx, "my_enum", ua.MyEnum.toto) await server.nodes.objects.add_variable(idx, "my_struct", ua.Variant(ua.MyStruct(), ua.VariantType.ExtensionObject)) my_struct_optional = ua.MyOptionalStruct() my_struct_optional.MyUInt32List = [45, 67] my_struct_optional.MyInt64 = -67 await server.nodes.objects.add_variable(idx, "my_struct_optional", ua.Variant(my_struct_optional, ua.VariantType.ExtensionObject)) await server.export_xml([server.nodes.objects, server.nodes.root, snode1, snode2, enode, valnode], "structs_and_enum.xml") async with server: while True: await asyncio.sleep(1)
def _add_node_attr(self, item, nodedata, name, vtype=None, add_timestamps=False): if item.SpecifiedAttributes & getattr(ua.NodeAttributesMask, name): dv = ua.DataValue(ua.Variant(getattr(item, name), vtype)) if add_timestamps: # dv.ServerTimestamp = datetime.utcnow() # Disabled until someone explains us it should be there dv.SourceTimestamp = datetime.utcnow() nodedata.attributes[getattr(ua.AttributeIds, name)] = AttributeValue(dv)
def __init__(self, id, name: str = None, number: int = None, node: Node = None): if id is not None: self.id = ua.Variant(id) else: self.id = id #in this case it needs to be added with add_state which takes the nodeid returen from add_state self.name = name self.number = number self.effectivedisplayname = ua.LocalizedText(name, "en-US") self.node = node #will be written from statemachine.add_state() or you need to overwrite it if the state is part of xml
def to_event_fields(self, select_clauses): """ return a field list using a select clause and the object properties """ fields = [] for sattr in select_clauses: if len(sattr.BrowsePath) == 0: name = ua.AttributeIds(sattr.AttributeId).name else: name = self.browse_path_to_attribute_name(sattr.BrowsePath) try: val = getattr(self, name) except AttributeError: field = ua.Variant(None) else: if val is None: field = ua.Variant(None) else: field = ua.Variant(copy.deepcopy(val), self.data_types[name]) fields.append(field) return fields
async def downstream_func_call(node_id, *args): try: output = await parent.call_method(func_name, *args) except UaStatusCodeError as e: status_code = e.code logging.warning( f"method {func_name} failed with status code {status_code}" ) return StatusCode(status_code) if not isinstance(output, list): return [ua.Variant(output)] else: return output
async def test_custom_struct_(opc): idx = 4 await new_struct(opc.opc, idx, "MyMyStruct", [ new_struct_field("MyBool", ua.VariantType.Boolean), new_struct_field("MyUInt32", ua.VariantType.UInt32, array=True), ]) await opc.opc.load_data_type_definitions() mystruct = ua.MyMyStruct() mystruct.MyUInt32 = [78, 79] var = await opc.opc.nodes.objects.add_variable(idx, "my_struct", ua.Variant(mystruct, ua.VariantType.ExtensionObject)) val = await var.read_value() assert val.MyUInt32 == [78, 79]
def _add_node_attr(self, item, nodedata, name, vtype=None, add_timestamps=False, is_array=False): if item.SpecifiedAttributes & getattr(ua.NodeAttributesMask, name): dv = ua.DataValue( ua.Variant(getattr(item, name), vtype, is_array=is_array), SourceTimestamp=datetime.utcnow() if add_timestamps else None, ) nodedata.attributes[getattr(ua.AttributeIds, name)] = AttributeValue(dv)
async def test_set_value(opc): o = opc.opc.get_objects_node() var = ua.Variant(1.98, ua.VariantType.Double) dvar = ua.DataValue(var) v = await o.add_variable(3, 'VariableValue', var) await v.set_value(var.Value) v1 = await v.get_value() assert v1 == var.Value await v.set_value(var) v2 = await v.get_value() assert v2 == var.Value await v.set_data_value(dvar) v3 = await v.get_data_value() assert v3.Value == dvar.Value
def __init__(self, id, name: str = None, number: int = None, node: Node = None): if id is not None: self.id = ua.Variant(id) else: self.id = id #in this case it needs to be added with add_transition which takes the nodeid returen from add_transition self.name = name self.number = number self._transitiontime = datetime.datetime.utcnow( ) #will be overwritten from _write_transition() self.node = node #will be written from statemachine.add_state() or you need to overwrite it if the state is part of xml
async def test_write_value(opc): o = opc.opc.nodes.objects var = ua.Variant(1.98, ua.VariantType.Double) dvar = ua.DataValue(var) v = await o.add_variable(3, 'VariableValue', var) await v.write_value(var.Value) v1 = await v.read_value() assert v1 == var.Value await v.write_value(var) v2 = await v.read_value() assert v2 == var.Value await v.write_value(dvar) v3 = await v.read_data_value() assert v3.Value == dvar.Value
def _add_variable_value(self, obj): """ Returns the value for a Variable based on the objects value type. """ self.logger.debug("Setting value with type %s and value %s", obj.valuetype, obj.value) if obj.valuetype == 'ListOfExtensionObject': values = [] for ext in obj.value: extobj = self._make_ext_obj(ext) values.append(extobj) return ua.Variant(values, ua.VariantType.ExtensionObject) elif obj.valuetype == 'ListOfGuid': return ua.Variant([uuid.UUID(guid) for guid in obj.value], getattr(ua.VariantType, obj.valuetype[6:])) elif obj.valuetype.startswith("ListOf"): vtype = obj.valuetype[6:] if hasattr(ua.ua_binary.Primitives, vtype): return ua.Variant(obj.value, getattr(ua.VariantType, vtype)) else: return ua.Variant([getattr(ua, vtype)(v) for v in obj.value]) elif obj.valuetype == 'ExtensionObject': extobj = self._make_ext_obj(obj.value) return ua.Variant(extobj, getattr(ua.VariantType, obj.valuetype)) elif obj.valuetype == 'Guid': return ua.Variant(uuid.UUID(obj.value), getattr(ua.VariantType, obj.valuetype)) elif obj.valuetype == 'LocalizedText': ltext = ua.LocalizedText() for name, val in obj.value: if name == "Text": ltext.Text = val else: self.logger.warning( "While parsing localizedText value, unkown element: %s with val: %s", name, val) return ua.Variant(ltext, ua.VariantType.LocalizedText) elif obj.valuetype == 'NodeId': return ua.Variant(ua.NodeId.from_string(obj.value)) else: return ua.Variant(obj.value, getattr(ua.VariantType, obj.valuetype))
def variant_from_binary(data): dimensions = None array = False encoding = ord(data.read(1)) int_type = encoding & 0b00111111 vtype = ua.datatype_to_varianttype(int_type) if test_bit(encoding, 7): value = unpack_uatype_array(vtype, data) array = True else: value = unpack_uatype(vtype, data) if test_bit(encoding, 6): dimensions = unpack_uatype_array(ua.VariantType.Int32, data) value = _reshape(value, dimensions) return ua.Variant(value, vtype, dimensions, is_array=array)
async def test_group_clients_by_health(self, ha_client, ha_servers): srv1, srv2 = ha_servers # srv2 service level is already 255 slevel = srv1.get_node(ua.NodeId(ua.ObjectIds.Server_ServiceLevel)) await ha_client.start() for c in ha_client.get_clients(): await wait_for_status_change(ha_client, c, 255) # if all clients are 255, group client should return them all healthy, unhealthy = await ha_client.group_clients_by_health() assert len(healthy) == 2 assert not unhealthy # the service level is considered (by default) unhealthy below 200, # so change to unhealthy for srv1 and client1 clients = ha_client.get_clients() await slevel.write_value(ua.Variant(199, ua.VariantType.Byte)) await wait_for_status_change(ha_client, clients[0], 199) healthy, unhealthy = await ha_client.group_clients_by_health() assert clients[0] == unhealthy[0] assert clients[1] == healthy[0] # now try with a custom HEALTHY_STATE value ha_client.HEALTHY_STATE = 4 healthy, unhealthy = await ha_client.group_clients_by_health() assert len(healthy) == 2 assert not unhealthy await slevel.write_value(ua.Variant(3, ua.VariantType.Byte)) await wait_for_status_change(ha_client, clients[0], 3) healthy, unhealthy = await ha_client.group_clients_by_health() assert clients[0] == unhealthy[0] assert clients[1] == healthy[0] # set back the value to 255 since the fixture as a wide scope await slevel.write_value(ua.Variant(255, ua.VariantType.Byte))
def _add_node_attr(self, attributes: __TYPE_ATTRIBUTES, nodedata: NodeData, name: str, vtype: ua.VariantType = None, add_timestamps: bool = False, is_array: bool = False): if attributes.SpecifiedAttributes & getattr(ua.NodeAttributesMask, name): dv = ua.DataValue( ua.Variant(getattr(attributes, name), vtype, is_array=is_array), SourceTimestamp=datetime.utcnow() if add_timestamps else None, ) nodedata.attributes[getattr(ua.AttributeIds, name)] = AttributeValue(dv)
async def create_property(parent, nodeid, bname, val, varianttype=None, datatype=None): """ create a child node property args are nodeid, browsename, value, [variant type] or idx, name, value, [variant type] """ nodeid, qname = _parse_nodeid_qname(nodeid, bname) var = ua.Variant(val, varianttype) if datatype and isinstance(datatype, int): datatype = ua.NodeId(datatype, 0) if datatype and not isinstance(datatype, ua.NodeId): raise RuntimeError("datatype argument must be a nodeid or an int refering to a nodeid") return make_node( parent.server, await _create_variable(parent.server, parent.nodeid, nodeid, qname, var, datatype=datatype, isproperty=True) )
async def setup_nodes(self): """ Set up some nodes as defined by spec """ uries = ['http://opcfoundation.org/UA/'] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) await ns_node.write_value(uries) params = ua.WriteParameters() for nodeid in ( ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerRead, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerHistoryReadData, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerHistoryReadEvents, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerWrite, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerHistoryUpdateData, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerHistoryUpdateEvents, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerMethodCall, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerBrowse, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerRegisterNodes, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerTranslateBrowsePathsToNodeIds, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxNodesPerNodeManagement, ua.ObjectIds. Server_ServerCapabilities_OperationLimits_MaxMonitoredItemsPerCall, ): attr = ua.WriteValue() attr.NodeId = ua.NodeId(nodeid) attr.AttributeId = ua.AttributeIds.Value attr.Value = ua.DataValue( ua.Variant(10000, ua.VariantType.UInt32), StatusCode_=ua.StatusCode(ua.StatusCodes.Good), ServerTimestamp=datetime.utcnow(), ) params.NodesToWrite.append(attr) result = await self.isession.write(params) result[0].check()
def trigger(self, time=None, message=None): """ Trigger the event. This will send a notification to all subscribed clients """ self.event.EventId = ua.Variant(uuid.uuid4().hex.encode('utf-8'), ua.VariantType.ByteString) if time: self.event.Time = time else: self.event.Time = datetime.utcnow() self.event.ReceiveTime = datetime.utcnow() # FIXME: LocalTime is wrong but currently know better. For description s. Part 5 page 18 self.event.LocalTime = datetime.utcnow() if message: self.event.Message = ua.LocalizedText(message) elif not self.event.Message: self.event.Message = ua.LocalizedText(Node(self.isession, self.event.SourceNode).get_browse_name().Text) self.isession.subscription_service.trigger_event(self.event)
async def create_srv(port, key, cert): srv = Server() srvs.append(srv) await srv.init() await srv.set_application_uri("urn:freeopcua:python:discovery") srv.set_endpoint(f"opc.tcp://127.0.0.1:{port}") srv.set_security_policy([ ua.SecurityPolicyType.NoSecurity, ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, ]) await srv.load_certificate(cert) await srv.load_private_key(key) await srv.start() # default the service level to 255 once started slevel = srv.get_node(ua.NodeId(ua.ObjectIds.Server_ServiceLevel)) await slevel.write_value(ua.Variant(255, ua.VariantType.Byte)) return srv
def test_where_clause(): cf = ua.ContentFilter() el = ua.ContentFilterElement() op = ua.SimpleAttributeOperand() op.BrowsePath.append(ua.QualifiedName("property", 2)) el.FilterOperands.append(op) for i in range(10): op = ua.LiteralOperand() op.Value = ua.Variant(i) el.FilterOperands.append(op) el.FilterOperator = ua.FilterOperator.InList cf.Elements.append(el) wce = WhereClauseEvaluator(logging.getLogger(__name__), None, cf) ev = BaseEvent() ev._freeze = False ev.property = 3 assert wce.eval(ev)
async def main(): # setup our server server = Server() # Configure server to use sqlite as history database (default is a simple memory dict) server.iserver.history_manager.set_storage( HistorySQLite("my_datavalue_history.sql")) # initialize server await server.init() server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/") # setup our own namespace, not really necessary but should as spec uri = "http://examples.freeopcua.github.io" idx = await server.register_namespace(uri) # get Objects node, this is where we should put our custom stuff objects = server.get_objects_node() # populating our address space myobj = await objects.add_object(idx, "MyObject") myvar = await myobj.add_variable(idx, "MyVariable", ua.Variant(0, ua.VariantType.Double)) await myvar.set_writable() # Set MyVariable to be writable by clients print(myvar) # starting! await server.start() # enable data change history for this particular node, must be called after start since it uses subscription await server.historize_node_data_change(myvar, period=None, count=100) try: count = 0 while True: await asyncio.sleep(1) count += 0.1 await myvar.set_value(math.sin(count)) finally: # close connection, remove subscriptions, etc await server.stop()
async def test_custom_struct_with_optional_fields(opc): idx = 4 await new_struct(opc.opc, idx, "MyOptionalStruct", [ new_struct_field("MyBool", ua.VariantType.Boolean), new_struct_field("MyUInt32", ua.VariantType.UInt32), new_struct_field("MyInt64", ua.VariantType.Int64, optional=True), ]) await opc.opc.load_data_type_definitions() my_struct_optional = ua.MyOptionalStruct() my_struct_optional.MyUInt32 = 45 my_struct_optional.MyInt64 = -67 var = await opc.opc.nodes.objects.add_variable(idx, "my_struct_optional", ua.Variant(my_struct_optional, ua.VariantType.ExtensionObject)) val = await var.read_value() assert val.MyUInt32 == 45 assert val.MyInt64 == -67
async def test_custom_list_of_struct(opc): idx = 4 dtype, encs = await new_struct(opc.opc, idx, "MySubStruct3", [ new_struct_field("MyBool", ua.VariantType.Boolean), new_struct_field("MyUInt32", ua.VariantType.UInt32), ]) await new_struct(opc.opc, idx, "MyMotherStruct3", [ new_struct_field("MyBool", ua.VariantType.Boolean), new_struct_field("MySubStruct", dtype, array=True), ]) await opc.opc.load_data_type_definitions() mystruct = ua.MyMotherStruct3() mystruct.MySubStruct = [ua.MySubStruct3()] mystruct.MySubStruct[0].MyUInt32 = 78 var = await opc.opc.nodes.objects.add_variable(idx, "my_mother_struct3", ua.Variant(mystruct, ua.VariantType.ExtensionObject)) val = await var.read_value() assert val.MySubStruct[0].MyUInt32 == 78
async def test_custom_struct_with_enum(opc): idx = 4 dtype = await new_enum(opc.opc, idx, "MyCustEnum2", [ "titi", "toto", "tutu", ]) await new_struct(opc.opc, idx, "MyStructEnum", [ new_struct_field("MyBool", ua.VariantType.Boolean), new_struct_field("MyEnum", dtype), ]) await opc.opc.load_data_type_definitions() mystruct = ua.MyStructEnum() mystruct.MyEnum = ua.MyCustEnum2.tutu var = await opc.opc.nodes.objects.add_variable(idx, "my_struct2", ua.Variant(mystruct, ua.VariantType.ExtensionObject)) val = await var.read_value() assert val.MyEnum == ua.MyCustEnum2.tutu