Skip to content

Commit

Permalink
model.base: implement constraint AASd-117
Browse files Browse the repository at this point in the history
  • Loading branch information
jkhsjdhjs committed Oct 2, 2023
1 parent b5a02e4 commit 340464a
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 12 deletions.
12 changes: 10 additions & 2 deletions basyx/aas/model/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,9 @@ def _set_id_short(self, id_short: Optional[NameType]):
)

if self.parent is not None:
if id_short is None:
raise AASConstraintViolation(117, f"id_short of {self!r} cannot be unset, since it is already "
f"contained in {self.parent!r}")
for set_ in self.parent.namespace_element_sets:
if set_.contains_id("id_short", id_short):
raise AASConstraintViolation(22, "Object with id_short '{}' is already present in the parent "
Expand Down Expand Up @@ -1813,7 +1816,8 @@ def __init__(self, parent: Union[UniqueIdShortNamespace, UniqueSemanticIdNamespa
:param item_add_hook: A function that is called for each item that is added to this NamespaceSet, even when
it is initialized. The first parameter is the item that is added while the second is
an iterator over all currently contained items. Useful for constraint checking.
:raises AASConstraintViolation: When `items` contains multiple objects with same unique attribute
:raises AASConstraintViolation: When `items` contains multiple objects with same unique attribute or when an
item doesn't has an identifying attribute
"""
self.parent = parent
parent.namespace_element_sets.append(self)
Expand Down Expand Up @@ -1869,6 +1873,9 @@ def add(self, value: _NSO):
for attr_name, (backend, case_sensitive) in set_._backend.items():
if hasattr(value, attr_name):
attr_value = self._get_attribute(value, attr_name, case_sensitive)
if attr_value is None:
raise AASConstraintViolation(117, f"{value!r} has attribute {attr_name}=None, which is not "
f"allowed within a {self.parent.__class__.__name__}!")
if attr_value in backend:
raise AASConstraintViolation(22, "Object with attribute (name='{}', value='{}') is already "
"present in {}"
Expand Down Expand Up @@ -2003,7 +2010,8 @@ def __init__(self, parent: Union[UniqueIdShortNamespace, UniqueSemanticIdNamespa
:attribute_names: Dict of attribute names, for which objects should be unique in the set. The bool flag
indicates if the attribute should be matched case-sensitive (true) or case-insensitive (false)
:param items: A given list of Referable items to be added to the set
:raises AASConstraintViolation: When `items` contains multiple objects with same id_short
:raises AASConstraintViolation: When `items` contains multiple objects with same unique attribute or when an
item doesn't has an identifying attribute
"""
self._order: List[_NSO] = []
super().__init__(parent, attribute_names, items, item_add_hook)
Expand Down
35 changes: 25 additions & 10 deletions test/model/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,12 +358,12 @@ def setUp(self):
self.prop6 = model.Property("Prop4", model.datatypes.Int, semantic_id=self.propSemanticID2)
self.prop7 = model.Property("Prop2", model.datatypes.Int, semantic_id=self.propSemanticID3)
self.prop8 = model.Property("ProP2", model.datatypes.Int, semantic_id=self.propSemanticID3)
self.prop1alt = model.Property("Prop1", model.datatypes.Int)
self.qualifier1 = model.Qualifier("type1", model.datatypes.Int, 1)
self.qualifier2 = model.Qualifier("type2", model.datatypes.Int, 1)
self.qualifier1alt = model.Qualifier("type1", model.datatypes.Int, 1)
self.extension1 = model.Extension("Ext1", model.datatypes.Int, 1)
self.extension2 = model.Extension("Ext2", model.datatypes.Int, 1)
self.prop1alt = model.Property("Prop1", model.datatypes.Int, semantic_id=self.propSemanticID)
self.qualifier1 = model.Qualifier("type1", model.datatypes.Int, 1, semantic_id=self.propSemanticID)
self.qualifier2 = model.Qualifier("type2", model.datatypes.Int, 1, semantic_id=self.propSemanticID2)
self.qualifier1alt = model.Qualifier("type1", model.datatypes.Int, 1, semantic_id=self.propSemanticID)
self.extension1 = model.Extension("Ext1", model.datatypes.Int, 1, semantic_id=self.propSemanticID)
self.extension2 = model.Extension("Ext2", model.datatypes.Int, 1, semantic_id=self.propSemanticID2)
self.namespace = self._namespace_class()
self.namespace3 = self._namespace_class_qualifier()

Expand Down Expand Up @@ -565,13 +565,13 @@ def test_Namespaceset_update_from(self) -> None:
# Prop2 is getting deleted since it does not exist in namespace2.set1
# Prop3 is getting added, since it does not exist in namespace1.set1 yet
namespace1 = self._namespace_class()
prop1 = model.Property("Prop1", model.datatypes.Int, 1)
prop2 = model.Property("Prop2", model.datatypes.Int, 0)
prop1 = model.Property("Prop1", model.datatypes.Int, 1, semantic_id=self.propSemanticID)
prop2 = model.Property("Prop2", model.datatypes.Int, 0, semantic_id=self.propSemanticID2)
namespace1.set2.add(prop1)
namespace1.set2.add(prop2)
namespace2 = self._namespace_class()
namespace2.set2.add(model.Property("Prop1", model.datatypes.Int, 0))
namespace2.set2.add(model.Property("Prop3", model.datatypes.Int, 2))
namespace2.set2.add(model.Property("Prop1", model.datatypes.Int, 0, semantic_id=self.propSemanticID))
namespace2.set2.add(model.Property("Prop3", model.datatypes.Int, 2, semantic_id=self.propSemanticID2))
namespace1.set2.update_nss_from(namespace2.set2)
# Check that Prop1 got updated correctly
self.assertIs(namespace1.get_referable("Prop1"), prop1)
Expand All @@ -596,6 +596,21 @@ def test_qualifiable_id_short_namespace(self) -> None:
self.assertIs(submodel_element_collection.get_referable("Prop1"), prop1)
self.assertIs(submodel_element_collection.get_qualifier_by_type("Qualifier1"), qualifier1)

def test_aasd_117(self) -> None:
property = model.Property(None, model.datatypes.Int, semantic_id=self.propSemanticID)
se_collection = model.SubmodelElementCollection("foo")
with self.assertRaises(model.AASConstraintViolation) as cm:
se_collection.add_referable(property)
self.assertEqual("Property has attribute id_short=None, which is not allowed within a "
"SubmodelElementCollection! (Constraint AASd-117)", str(cm.exception))
property.id_short = "property"
se_collection.add_referable(property)
with self.assertRaises(model.AASConstraintViolation) as cm:
property.id_short = None
self.assertEqual("id_short of Property[foo / property] cannot be unset, since it is already contained in "
"SubmodelElementCollection[foo] (Constraint AASd-117)", str(cm.exception))
property.id_short = "bar"


class ExampleOrderedNamespace(model.UniqueIdShortNamespace, model.UniqueSemanticIdNamespace):
def __init__(self, values=()):
Expand Down

0 comments on commit 340464a

Please sign in to comment.