Commit 11aac01b authored by Johannes Löhnert's avatar Johannes Löhnert Committed by GitHub

load_data_type_definitions: Accept OptionSet / Enumeration defined via StructureDefinition (#1383)

* When reading Enumeration and OptionSet type definitions, also accept StructureDefinition instance.

This kind of type definition is "unclean" but not strictly forbidden by the standard.
Requires guessing of actual enum values:
- Count starting at 1 for Enums
- 0x1, 0x2, 0x4, ... for OptionSet

* add test for OptionSet defined via StructureDefinition
parent 88c0fc34
......@@ -526,11 +526,23 @@ class {name}({enum_type}):
'''
"""
for sfield in edef.Fields:
name = clean_name(sfield.Name)
value = sfield.Value if not option_set else (1 << sfield.Value)
code += f" {name} = {value}\n"
for n, sfield in enumerate(edef.Fields):
fieldname = clean_name(sfield.Name)
if hasattr(sfield, "Value"):
value = sfield.Value if not option_set else (1 << sfield.Value)
else:
# Some servers represent the datatype as StructureDefinition instead of EnumDefinition.
# In this case the Value attribute is missing and we must guess.
# XXX: Assuming that counting starts with 1 for enumerations, which is by no means guaranteed.
value = n+1 if not option_set else (1 << n)
if n==0:
_logger.warning(
"%s type %s: guessing field values since the server does not provide them.",
"OptionSet" if option_set else "Enumeration",
name
)
code += f" {fieldname} = {value}\n"
return code
......
......@@ -1280,6 +1280,27 @@ async def test_custom_option_set(opc):
val = await var.read_value()
assert val == (1 << 2) | (1 << 1)
async def test_custom_option_set_as_structure_definition(opc):
"""Option set defined via structure definition is understood.
This is arguably outside the standard but observed on some servers.
"""
idx = 4
server = opc.opc
await new_enum(opc.opc, idx, "MyOptionSet", ["tata", "titi", "toto", "None"], True)
sdef = ua.StructureDefinition()
for name in ["tata", "titi", "toto", "None"]:
field = ua.StructureField()
field.Name = name
sdef.Fields.append(field)
dtype = await server.nodes.option_set_type.add_data_type(idx, name)
await dtype.write_data_type_definition(sdef)
await opc.opc.load_data_type_definitions()
assert ua.MyOptionSet.toto | ua.MyOptionSet.titi == ua.MyOptionSet((1 << 2) | (1 << 1))
var = await opc.opc.nodes.objects.add_variable(idx, "my_option", ua.MyOptionSet.toto | ua.MyOptionSet.titi)
val = await var.read_value()
assert val == (1 << 2) | (1 << 1)
async def test_custom_struct_(opc):
idx = 4
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment