Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5ca38eb
[#350] WIP: Adding data constraints to the language
Yuri-Blankenstein-TNO Jun 24, 2026
1da5340
Merge remote-tracking branch 'https/main' into 350-define-or-extend-c…
Yuri-Blankenstein-TNO Jun 29, 2026
7ae57c3
[#350] Adding some validation
Yuri-Blankenstein-TNO Jul 8, 2026
093e968
[#350] Added data expressions to constraints language
Yuri-Blankenstein-TNO Jul 8, 2026
027e9a4
Merge remote-tracking branch 'https/main' into 350-define-or-extend-c…
Yuri-Blankenstein-TNO Jul 8, 2026
8f87ca2
[#350] Removed obsolete SCL language
Yuri-Blankenstein-TNO Jul 8, 2026
2c4d29a
[#350] Added language elements for prototyping constraints
Yuri-Blankenstein-TNO Jul 20, 2026
22bbc9d
[#350] Added option to show the where clauses in constraint atomata
Yuri-Blankenstein-TNO Jul 20, 2026
de71c9c
Added Assignment Action and Adjust of Existing Code
dbera Jul 23, 2026
a13ca2d
First Version of Support for Response Template Generation in PSpec
dbera Jul 24, 2026
292f3ba
Added License header to new XTend Class
dbera Jul 24, 2026
e971089
Added Code Comments
dbera Jul 24, 2026
603206e
Implementation of TSpec to PS and Dedicated Project Task
dbera Jul 27, 2026
c74cbdc
Added ESI Header
dbera Jul 27, 2026
ac2a4c3
Expression Helper for detecting Variable references to Steps
dbera Jul 30, 2026
3e6aec8
Created new package for CPN generators
dbera Jul 30, 2026
fb7f15b
Added ESI Header
dbera Jul 31, 2026
9421fa0
Made the constraints generator cpn modular
ritamraha Aug 4, 2026
66626cb
changed header in utils.java
ritamraha Aug 4, 2026
e3594cf
New project task for generating reachability graphs
dbera Aug 5, 2026
1b5992f
Fix unit test of products package to accept changed constructor
dbera Aug 5, 2026
90860da
updated test cases to add new info
dbera Aug 5, 2026
d7b5f5f
commenting test case needs to be investigated later
dbera Aug 5, 2026
d93e9c9
Merge branch 'main' into 350-define-or-extend-constraints-language-to…
dbera Aug 5, 2026
65da4e8
Added Feature to generate JSON Reachability Graph
dbera Aug 6, 2026
0d470af
git ignore update
dbera Aug 6, 2026
004c70d
1. Added support for generating pspec files per constraints, 2. Fixed
ritamraha Aug 7, 2026
fa6124d
added depth and state limits and support bpmn files
dbera Aug 7, 2026
f7b3fcd
Minor fixes in CPNTemplateGenerator
ritamraha Aug 7, 2026
e0ee409
1.Reimplemented computelabel function to be more generic, 2. Added all
ritamraha Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ __pycache__/
# Ignore plugin.xml_gen and feature.xml_gen files, when build Eclipse Plugin.
*.xml_gen

# Xtend #
xtend-gen/
src-gen/

# Eclipse #
.metadata
bin/
Expand Down
1 change: 0 additions & 1 deletion bundles/nl.asml.matala.doc.design/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ Require-Bundle: com.google.guava,
nl.asml.matala.product;resolution:=optional,
nl.esi.comma.abstracttestspecification;resolution:=optional,
nl.esi.comma.assertthat;resolution:=optional,
nl.esi.comma.behavior.scl;resolution:=optional,
nl.esi.comma.causalgraph;resolution:=optional,
nl.esi.comma.constraints;resolution:=optional,
nl.esi.comma.inputspecification;resolution:=optional,
Expand Down
1 change: 0 additions & 1 deletion bundles/nl.asml.matala.doc.design/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@
<inputFile>${project.build.directory}/meta-models/nl.esi.comma.assertthat/model/generated/AssertThat.ecore</inputFile>
<inputFile>${project.build.directory}/meta-models/nl.asml.matala.product/model/generated/Product.ecore</inputFile>
<inputFile>${project.build.directory}/meta-models/nl.esi.comma.abstracttestspecification/model/generated/AbstractTestspecification.ecore</inputFile>
<inputFile>${project.build.directory}/meta-models/nl.esi.comma.behavior.scl/model/generated/Scl.ecore</inputFile>
<inputFile>${project.build.directory}/meta-models/nl.esi.comma.causalgraph/model/generated/CausalGraph.ecore</inputFile>
<inputFile>${project.build.directory}/meta-models/nl.esi.comma.constraints/model/generated/Constraints.ecore</inputFile>
<inputFile>${project.build.directory}/meta-models/nl.esi.comma.inputspecification/model/generated/InputSpecification.ecore</inputFile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import nl.esi.xtext.expressions.expression.ExpressionConstantInt
import nl.esi.xtext.expressions.expression.ExpressionMinus
import nl.esi.xtext.expressions.expression.ExpressionPlus
import nl.esi.xtext.expressions.expression.Expression
import nl.esi.xtext.expressions.expression.ExpressionEnumLiteral

/**
* Parser for json elements, objects, and arrays
Expand Down Expand Up @@ -90,6 +91,7 @@ class JsonHelper {
ExpressionConstantBool: expr.value.toString
ExpressionConstantReal: expr.value.toString
ExpressionConstantInt: expr.value.toString
ExpressionEnumLiteral: "" // Added DB. 31.07.2026, attempted to add: expr.literal.value.toString but fails
ExpressionMinus: {
val sub = expr.sub
switch (sub){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,12 +458,128 @@ def generateSCN(self, level = 0, visitedT = None, visitedTP = None):
self.numTestCases = self.numTestCases + 1
return

def generateReachabilityGraph(self, writer, state_space = None, currIndex = 0, level = 0):
# Helpers to support JSON Reachability Graph #
def _reachabilityJsonValue(self, value):
"""Convert model data to values supported by the JSON format."""
if value is None or isinstance(value, (bool, int, float)):
return value

if isinstance(value, str):
try:
return json.loads(value)
except (json.JSONDecodeError, TypeError):
return value

if isinstance(value, dict):
return {
str(key): self._reachabilityJsonValue(item)
for key, item in value.items()
}

if isinstance(value, (list, tuple, set)):
return [self._reachabilityJsonValue(item) for item in value]

if hasattr(value, 'dict') and callable(value.dict):
try:
return self._reachabilityJsonValue(value.dict())
except (TypeError, ValueError):
pass

return str(value)

def _reachabilityMultisetToJson(self, multiset):
"""Convert a SNAKES multiset to a JSON array, preserving tokens."""
try:
tokens = multiset.items()
except (AttributeError, TypeError):
try:
tokens = list(multiset)
except TypeError:
tokens = [multiset]

return [self._reachabilityJsonValue(token) for token in tokens]

def _reachabilityMarkingToJson(self, marking):
"""Convert a SNAKES marking to place-name to token-array JSON."""
return {
str(place): self._reachabilityMultisetToJson(tokens)
for place, tokens in marking.items()
}

def _reachabilityModeToJson(self, mode):
"""Convert a transition substitution to structured input JSON."""
result = {}
for variable, value in mode.dict().items():
name = str(variable)
if name.startswith('v_'):
name = name[2:]
result[name] = self._reachabilityJsonValue(value)
return result

def _reachabilityEdgeToJson(
self, graph, source, target, transition, marked, mode,
produced_flow):
"""Create one LTSVisualizer edge without changing graph traversal."""
return {
'id': 'edge-%d' % len(graph['edges']),
'source': str(source),
'target': str(target),
'transition': transition,
'color': 'darkorange' if marked else None,
'inputs_raw': str(mode),
'inputs': self._reachabilityModeToJson(mode),
'outputs_raw': str(produced_flow),
'outputs': self._reachabilityFlowToJson(produced_flow)
}

def _writeReachabilityJson(self, writer, graph):
"""Write rg.json next to the PlantUML file when writer has a path."""
writer_name = getattr(writer, 'name', None)
if not isinstance(writer_name, (str, os.PathLike)):
print(' [RG-WARN] Cannot create JSON graph: writer has no file path.')
return None

puml_path = Path(writer_name)
json_path = puml_path.with_suffix('.json')
with open(json_path, 'w', encoding='utf-8') as json_writer:
json.dump(graph, json_writer, indent=2, ensure_ascii=False)
json_writer.write('\n')
print("[INFO] Created %s" % (json_path,))
return json_path

def _reachabilityFlowToJson(self, flow):
"""Convert a SNAKES produced flow to place-name to token-array JSON."""
return {
str(place): self._reachabilityMultisetToJson(tokens)
for place, tokens in flow.items()
}
# End of Helpers to support JSON Reachability Graph #

def generateReachabilityGraph(self, writer, state_space = None, currIndex = 0, level = 0, json_graph=None):
nrOfDependencies = 0
initial = not state_space
if initial:
writer.write('@startuml\n')
state_space = [self.n.get_marking()]
initial_marking = self.n.get_marking()
state_space = [initial_marking]
json_graph = {
'format': 'ltsvisualizer',
'version': 1,
'type': 'graph',
'metadata': {
'title': 'State space',
'stateCount': 0,
'transitionCount': 0
},
'nodes': [{
'id': '0',
'marking_raw': str(initial_marking),
'marking': self._reachabilityMarkingToJson(
initial_marking
)
}],
'edges': []
}
elif level > 300:
writer.write('(%s) #red\n' % (currIndex,))
print(' [RG-INFO] Depth limit reached! Terminating path.')
Expand All @@ -480,22 +596,59 @@ def generateReachabilityGraph(self, writer, state_space = None, currIndex = 0, l
for transition in enabledTransitions:
transitionLabel = transition.name.split('_default@')[0]
for mode in transition.modes():
writer.write("'Transition Inputs: %s\n" % (mode))
writer.write("'Marking (State): %s\n" % (self.n.get_marking()))
produced_flow = transition.flow(mode)[1]
transition.fire(mode)
nextMarking = self.n.get_marking()
if nextMarking in state_space:
nextIndex = state_space.index(nextMarking)
writer.write('(%s) -%s-> (%s): %s\n' % (currIndex, "[#darkorange]" if mark else "", nextIndex, transitionLabel))
json_graph['edges'].append(
self._reachabilityEdgeToJson(
json_graph,
currIndex,
nextIndex,
transitionLabel,
mark,
mode,
produced_flow
)
)
nrOfDependencies += 1
else:
nextIndex = len(state_space)
state_space.append(nextMarking)
writer.write('(%s) -%s-> (%s): %s\n' % (currIndex, "[#darkorange]" if mark else "", nextIndex, transitionLabel))
nrOfDependencies += 1 + self.generateReachabilityGraph(writer, state_space, nextIndex, level + 1)
json_graph['nodes'].append({
'id': str(nextIndex),
'marking_raw': str(nextMarking),
'marking': self._reachabilityMarkingToJson(
nextMarking
)
})
json_graph['edges'].append(
self._reachabilityEdgeToJson(
json_graph,
currIndex,
nextIndex,
transitionLabel,
mark,
mode,
produced_flow
)
)
nrOfDependencies += 1 + self.generateReachabilityGraph(writer, state_space, nextIndex, level + 1, json_graph)
self.n.set_marking(currMarking)

if initial:
writer.write('title State space: %d nodes and %d edges\n' % (len(state_space), nrOfDependencies))
writer.write('@enduml\n')
json_graph['metadata']['stateCount'] = len(json_graph['nodes'])
json_graph['metadata']['transitionCount'] = len(
json_graph['edges']
)
self._writeReachabilityJson(writer, json_graph)

return nrOfDependencies

Expand Down Expand Up @@ -652,7 +805,7 @@ def copy(self, name=None):
b = datetime.datetime.now()

# s.goto(0)

fname = p.plantuml_dir / "rg.plantuml"
with open(fname, 'w') as f:
pn.generateReachabilityGraph(f)
Expand All @@ -662,16 +815,15 @@ def copy(self, name=None):
print("[INFO] Starting Test Generation.")
pn.initializeTestGeneration()
pn.generateTestCases()

# print('[INFO] Number-of-generated-scenario files: ',len(pn.visitedTList))
print("[INFO] Test Generation Finished.")
d = datetime.datetime.now()

print("[INFO] Creating Structure and Behavior Views in PlantUML.")
map_block_uml_txt = {}
for t in pn.n.transition():
map_block_uml_txt[t.name.split('_')[0]] = '@startuml\n'

for t in pn.n.transition():
gtxt = map_block_uml_txt.get(t.name.split('_')[0])
if 'json.loads' in t.guard._str:
Expand All @@ -686,7 +838,6 @@ def copy(self, name=None):
gtxt += 'component %s\n' % (t.name)
gtxt += 'note right of [%s]\n %s\nendnote\n' % (t.name, t.guard)
map_block_uml_txt[t.name.split('_')[0]] = gtxt

for t in pn.n.transition():
for inp in pn.n.pre(t.name):
txt = map_block_uml_txt.get(t.name.split('_')[0])
Expand All @@ -702,31 +853,29 @@ def copy(self, name=None):
else:
txt += '[%s] --> %s\n' % (t.name, out)
map_block_uml_txt[t.name.split('_')[0]] = txt

for key in map_block_uml_txt:
txt = map_block_uml_txt.get(key)
txt += '@enduml\n'
map_block_uml_txt[key] = txt
fname = p.plantuml_dir / (key + ".plantuml")
with open(fname, 'w') as f:
f.write(txt)

print("[INFO] View Generation Finished.")
e = datetime.datetime.now()
print("[INFO] Time Statistics")
print("[INFO] * Reachability Computation: %s" % (b - a))
print("[INFO] * Reachability PUML Creation: %s" % (c - b))
print("[INFO] * Test Generation: %s" % (d - c))
print("[INFO] * PlantUML View Generation: %s" % (e - d))

# print("[INFO] Starting Command-Line Simulation.")
# simulate(pn.n)

#if not p.no_sim:
# print('[SIM] Start Simulation? (Y/N) :')
# value = input(" Enter Choice: ")
# if value == "Y" or value == "y":
# os.system('cls')
# simulate(pn.n)

print("[INFO] Exiting..")
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ def generateReachabilityGraph(self, writer, state_space = None, currIndex = 0, l
for transition in enabledTransitions:
transitionLabel = transition.name.split('_default@')[0]
for mode in transition.modes():
writer.write("'Transition Inputs: %s\n" % (mode))
writer.write("'Marking (State): %s\n" % (self.n.get_marking()))
transition.fire(mode)
nextMarking = self.n.get_marking()
if nextMarking in state_space:
Expand Down Expand Up @@ -362,7 +364,7 @@ def copy(self, name=None):
b = datetime.datetime.now()

# s.goto(0)

fname = p.plantuml_dir / "rg.plantuml"
with open(fname, 'w') as f:
pn.generateReachabilityGraph(f)
Expand All @@ -372,16 +374,15 @@ def copy(self, name=None):
print("[INFO] Starting Test Generation.")
pn.initializeTestGeneration()
pn.generateTestCases()

# print('[INFO] Number-of-generated-scenario files: ',len(pn.visitedTList))
print("[INFO] Test Generation Finished.")
d = datetime.datetime.now()

print("[INFO] Creating Structure and Behavior Views in PlantUML.")
map_block_uml_txt = {}
for t in pn.n.transition():
map_block_uml_txt[t.name.split('_')[0]] = '@startuml\n'

for t in pn.n.transition():
gtxt = map_block_uml_txt.get(t.name.split('_')[0])
if 'json.loads' in t.guard._str:
Expand All @@ -396,7 +397,6 @@ def copy(self, name=None):
gtxt += 'component %s\n' % (t.name)
gtxt += 'note right of [%s]\n %s\nendnote\n' % (t.name, t.guard)
map_block_uml_txt[t.name.split('_')[0]] = gtxt

for t in pn.n.transition():
for inp in pn.n.pre(t.name):
txt = map_block_uml_txt.get(t.name.split('_')[0])
Expand All @@ -412,31 +412,29 @@ def copy(self, name=None):
else:
txt += '[%s] --> %s\n' % (t.name, out)
map_block_uml_txt[t.name.split('_')[0]] = txt

for key in map_block_uml_txt:
txt = map_block_uml_txt.get(key)
txt += '@enduml\n'
map_block_uml_txt[key] = txt
fname = p.plantuml_dir / (key + ".plantuml")
with open(fname, 'w') as f:
f.write(txt)

print("[INFO] View Generation Finished.")
e = datetime.datetime.now()
print("[INFO] Time Statistics")
print("[INFO] * Reachability Computation: %s" % (b - a))
print("[INFO] * Reachability PUML Creation: %s" % (c - b))
print("[INFO] * Test Generation: %s" % (d - c))
print("[INFO] * PlantUML View Generation: %s" % (e - d))

# print("[INFO] Starting Command-Line Simulation.")
# simulate(pn.n)

#if not p.no_sim:
# print('[SIM] Start Simulation? (Y/N) :')
# value = input(" Enter Choice: ")
# if value == "Y" or value == "y":
# os.system('cls')
# simulate(pn.n)

print("[INFO] Exiting..")
Loading
Loading