Skip to content

Commit

Permalink
Add support for handling of RegularExpression definitions for string …
Browse files Browse the repository at this point in the history
…based properties

Signed-off-by: Kostadin Ivanov (BD/TBC-BG) <[email protected]>
  • Loading branch information
Kostadin-Ivanov committed Oct 30, 2024
1 parent 2f1a984 commit c3269bd
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 11 deletions.
2 changes: 2 additions & 0 deletions src/vss_tools/exporters/samm/helpers/samm_concepts.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class SammConcepts(Enum):
DESCRIPTION = "description"
ENTITY = "Entity"
EVENTS = "events"
VALUE = "value"
EXAMPLE_VALUE = "exampleValue"
NAME = "name"
OPERATIONS = "operations"
Expand Down Expand Up @@ -92,6 +93,7 @@ class SammCConcepts(Enum):
MIN_VALUE = "minValue"
QUANTIFIABLE = "Quantifiable"
RANGE_CONSTRAINT = "RangeConstraint"
REG_EXP_CONSTRAINT = "RegularExpressionConstraint"
SINGLE_ENTITY = "SingleEntity"
STATE = "State"
TIMESTAMP = "Timestamp"
Expand Down
19 changes: 18 additions & 1 deletion src/vss_tools/exporters/samm/helpers/ttl_builder_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,14 @@ def add_node_leaf_constraint(graph: Graph, node_char_name: str, node_char_uri: U
constraint_name = str_to_uc_first_camel_case(vss_node.ttl_name + "Constraint")
constraint_node_uri = get_vspec_uri(constraint_name)

__add_node_tuple(graph, constraint_node_uri, RDF.type, SammCConcepts.RANGE_CONSTRAINT.uri)
# Default Constraint URI is for Range (min/max) constraints
constraint_uri = SammCConcepts.RANGE_CONSTRAINT.uri

if hasattr(vss_node.data, "pattern") and vss_node.data.pattern is not None:
# Pattern property is used for Regular Expression constraints of STRING based data nodes
constraint_uri = SammCConcepts.REG_EXP_CONSTRAINT.uri

__add_node_tuple(graph, constraint_node_uri, RDF.type, constraint_uri)
__add_node_tuple(graph, constraint_node_uri, SammConcepts.NAME.uri, Literal(constraint_name))

# Workaround since doubles are serialized as scientific numbers
Expand All @@ -354,6 +361,16 @@ def add_node_leaf_constraint(graph: Graph, node_char_name: str, node_char_uri: U
Literal(vss_node.data.min, datatype=data_type), # type: ignore
)

if vss_node.data.pattern is not None: # type: ignore
__add_node_tuple(
graph,
constraint_node_uri,
SammConcepts.VALUE.uri,
Literal(vss_node.data.pattern, datatype=data_type), # type: ignore
)

# Set the RegExp value for constraint_node_uri

base_c_name = str_to_uc_first_camel_case(vss_node.ttl_name + "BaseCharacteristic")
base_c_uri = get_vspec_uri(base_c_name)

Expand Down
13 changes: 3 additions & 10 deletions src/vss_tools/exporters/samm/helpers/vss_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from rdflib import URIRef
from vss_tools import log
from vss_tools.datatypes import Datatypes
from vss_tools.model import NodeType, VSSDataBranch
from vss_tools.model import NodeType, VSSDataBranch, VSSDataDatatype
from vss_tools.tree import VSSNode

from ..config import config as cfg
Expand Down Expand Up @@ -260,15 +260,8 @@ def get_node_description(vss_node: VSSNode) -> str:


def has_constraints(vss_node: VSSNode) -> bool:
return (
hasattr(vss_node.data, "type")
and vss_node.data.type in [NodeType.ACTUATOR, NodeType.SENSOR]
and (
hasattr(vss_node.data, "max")
and vss_node.data.max is not None
or hasattr(vss_node.data, "min")
and vss_node.data.min is not None
)
return bool(
isinstance(vss_node.data, VSSDataDatatype) and (vss_node.data.max or vss_node.data.min or vss_node.data.pattern)
)


Expand Down
29 changes: 29 additions & 0 deletions src/vss_tools/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ class VSSDataDatatype(VSSData):
arraysize: int | None = None
min: int | float | None = None
max: int | float | None = None
# Field, used to allow definition of Regular Expression constraints
# for string based property nodes.
# Example: VSS - VehicleIdentification.VIN property
pattern: str | None = None
unit: str | None = None
allowed: list[str | int | float | bool] | None = None
default: list[str | int | float | bool] | str | int | float | bool | None = None
Expand Down Expand Up @@ -277,6 +281,31 @@ def check_datatype_matching_allowed_unit_datatypes(self) -> Self:
), f"'{self.datatype}' is not allowed for unit '{self.unit}'"
return self

@model_validator(mode="after")
def check_datatype_pattern(self) -> Self:
"""
Checks that regular expression datatype 'pattern' field is:
1. string only
2. defined only for string typed nodes i.e., STRING and STRING_ARRAY
3. if default value is provided it is matching the specified pattern
"""
if self.pattern:
assert bool(type(self.pattern) is str), f"Specified pattern: '{self.pattern}' must be a string"

# Datatypes.TUPLE[0] is the string name of the type.
allowed_for = f"It is allowed only for '{Datatypes.STRING[0]}' and '{Datatypes.STRING_ARRAY[0]}' types."
assert bool(
Datatypes.get_type(self.datatype) in [Datatypes.STRING, Datatypes.STRING_ARRAY]
), f"Field 'pattern' is not allowed for '{self.fqn}' of type: '{self.datatype}'. {allowed_for}"

if self.default:
assert bool(
re.match(self.pattern, self.default) # type: ignore
), f"Specified default value: '{self.default}' must match defined pattern: '{self.pattern}'"

return self


class VSSDataProperty(VSSDataDatatype):
pass
Expand Down

0 comments on commit c3269bd

Please sign in to comment.