11. Declarative Modeling Example¶
Declarative approach to modeling means that one could define or update a model using a fragment of structured text. A number of fragments could be “played” against a model in a sequence to build it up.
Enabling declarative modeling for Capella models enables a range of complex automations around modeling process that are explainable / transparent to human auditors.
This notebook will demonstrate a basic application of this approach to modeling on a coffee machine example. Please note that we will not model any specific modeling process but rather a “free-form” demo.
11.1. System Analysis of a Coffee Machine¶
Lets do a quick system analysis of a coffee machine. Lets assume that our meta-solution is an automated coffee machine for a household use. We may look into variant management scenario in a separate example.
11.1.1. 0. Initialize¶
But before we can model something lets first initialize the model. We will use an empty Capella 5.2 model as a starting point.
[1]:
import io
import capellambse
from capellambse import decl
model = capellambse.MelodyModel(
"../../../tests/data/decl/empty_project_52/empty_project_52.aird"
)
Cannot load PVMT extension: ValueError: Provided model does not have a PropertyValuePkg
Property values are not available in this model
to visualize the modeling results we’ll use context-diagrams extension, you may get one by uncommenting and running the command below
[ ]:
!pip install capellambse_context_diagrams
lets verify that the model is empty at SA layer:
[2]:
functions_allocated = model.sa.root_component.allocated_functions
functions_available = model.sa.root_function.functions
print(
f"At SA layer the model has {len(functions_available)},"
f" out of which {len(functions_allocated)} are allocated to Root Component"
)
At SA layer the model has 0, out of which 0 are allocated to Root Component
Also for this to work we’ll need “coordinates” of some key elements in the model:
[3]:
root_function = model.sa.root_function
root_component = model.sa.root_component
structure = model.sa.component_package
11.1.2. 1. Context¶
Lets start by renaming the root component from System to Coffee Machine, creating a human actor User and a component exchange between those two.
We can achieve this by applying the following YAML patch to an empty Capella model:
[4]:
model_update = f"""
- parent: !uuid {root_component.uuid}
modify:
name: Coffee Machine
- parent: !uuid {root_component.uuid}
extend:
ports:
- name: usr
direction: INOUT
promise_id: usr-port-promise
exchanges:
- name: user interactions
source: !promise usr-port-promise
target: !promise cm-port-promise
- parent: !uuid {structure.uuid}
extend:
components:
- name: User
is_actor: true
is_human: true
ports:
- name: cm
direction: INOUT
promise_id: cm-port-promise
"""
# the below line applies the model_update to the model
decl.apply(model, io.StringIO(model_update))
[4]:
{Promise(identifier='usr-port-promise'): <ComponentPort 'usr' (3a2ff9e0-adcc-4d2d-ae4f-7001a0c25475)>
.applied_property_value_groups = []
.applied_property_values = []
.constraints = []
.description = ''
.direction = <FPortDir.INOUT: 3>
.exchanges = ... # backreference to ComponentExchange - omitted: can be slow to compute
.filtering_criteria = []
.name = 'usr'
.owner = <SystemComponent 'Coffee Machine' (7af5971f-1a6c-47d3-b9a8-4e709444113e)>
.parent = <SystemComponent 'Coffee Machine' (7af5971f-1a6c-47d3-b9a8-4e709444113e)>
.progress_status = 'NOT_SET'
.property_value_groups = []
.property_values = []
.requirements = []
.summary = None
.traces = []
.uuid = '3a2ff9e0-adcc-4d2d-ae4f-7001a0c25475'
.xtype = 'org.polarsys.capella.core.data.fa:ComponentPort',
Promise(identifier='cm-port-promise'): <ComponentPort 'cm' (99f8db47-4771-4e4e-993a-b252398d8806)>
.applied_property_value_groups = []
.applied_property_values = []
.constraints = []
.description = ''
.direction = <FPortDir.INOUT: 3>
.exchanges = ... # backreference to ComponentExchange - omitted: can be slow to compute
.filtering_criteria = []
.name = 'cm'
.owner = <SystemComponent 'User' (d03d179b-5449-4193-94f8-61d614ea9156)>
.parent = <SystemComponent 'User' (d03d179b-5449-4193-94f8-61d614ea9156)>
.progress_status = 'NOT_SET'
.property_value_groups = []
.property_values = []
.requirements = []
.summary = None
.traces = []
.uuid = '99f8db47-4771-4e4e-993a-b252398d8806'
.xtype = 'org.polarsys.capella.core.data.fa:ComponentPort'}
and now we can verify the changes by visualizing the context of our system under analysis:
[5]:
root_component.context_diagram
[5]:
Please note: the changes we made are not yet stored - if you like those to be saved you may use model.save()
method. This will save the model back to where it was loaded from, for example by writing back into local files, or by creating a Git commit and pushing it back to the remote.