# Cinco Transformation API
For every MGL in a Cinco Project Cinco Cloud provides an API which makes it easy to read and change models.
# MGL Example
Given a simple example for a simplified Petri Net (opens new window) with Arc weights of 1 and maximum capacity of 1 for Place elements:
stylePath "place-transition.style"
graphModel PlaceTransitionNet {
diagramExtension "ptnet"
attr string as modelName := "PTNet"
containableElements(Place, Transition)
}
node Token{
style tokenStyle
}
@Hook(PlaceHooks, PostDoubleClick, CanDoubleClick)
container Place {
style placeStyle
containableElements(Token[0,1])
incomingEdges(TransitionPlaceArc)
outgoingEdges(PlaceTransitionArc)
}
@Hook(TransitionHooks, PostDoubleClick, CanDoubleClick)
node Transition {
style transitionStyle
incomingEdges(PlaceTransitionArc)
outgoingEdges(TransitionPlaceArc)
}
edge PlaceTransitionArc {
style arcStyle
}
edge TransitionPlaceArc {
style arcStyle
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
we can create a simple execution logic for Transitions using the generated API.
# Example API for Place
The following shows an excerpt from the generated API representing the generated code for the Place element defined in the MGL above:
export class Place extends Container{
override readonly type: string = 'place-transition:place';
createToken(x: number, y: number): Token {
const node = Token.create({x:x,y:y});
node.index = this.index;
this.containments.push(node);
return node;
}
get containedTokenElements(): Token[] {
return this.containedElements.filter(element =>
Token.is(element)).map(element => Token.wrap(element));
}
createPlaceTransitionArc(target: Transition): PlaceTransitionArc{
let edge = PlaceTransitionArc.create(this.id,target.id);
edge.index = this.index;
his.getGraphModel().edges.push(edge);
return edge;
}
get outgoingPlaceTransitionArcEdges(): PlaceTransitionArc[]{
return this.outgoingEdges.filter(edge => PlaceTransitionArc.is(edge)).map(edge => PlaceTransitionArc.wrap(edge));
}
get incomingTransitionPlaceArcEdges(): TransitionPlaceArc[]{
return this.incomingEdges.filter(edge => TransitionPlaceArc.is(edge)).map(edge => TransitionPlaceArc.wrap(edge));
}
get TransitionSucessors(): Transition[] {
return this.successors.filter(node => Transition.is(node)).map(node => Transition.wrap(node));
}
get TransitionPredecessors(): Transition[] {
return this.predecessors.filter(node => Transition.is(node)).map(node => Transition.wrap(node));
}
delete(): boolean {
if(this.parent) {
const contained = this.parent.containments;
this.parent.containments = this.parent.containments.filter(containment => deletableValue(containment).id !== this.id);
const edges = this.getGraphModel().edges;
this.getGraphModel().edges = this.getGraphModel().edges.
filter(edge => !(deletableValue(edge).sourceID == this.id || deletableValue(edge).targetID == this.id));
return true;
}
return false;
}
moveTo(target: PlaceTransitionNet,x?: number,y?: number): void {
if(target.canContain(this.type)) {
if(this.parent) {
this.parent.containments = this.parent.containments.filter(element => deletableValue(element).id !== this.id);
target.containments.push(this);
}
this.position = {
x: x ?? this.position.x,
y: y ?? this.position.y
}
} else {
throw new Error(`Can not contain Element of type '${this.type}' in target '${target.id}'`)
}
}
}
export namespace Place {
/**
* Checks if an object has 'Place' as a type or supertype.
* If so, this method returns 'true' otherwise 'false'.
**/
export function is(object: any): object is Place {
return AnyObject.is(object) && ModelElement.checkType(object,'place-transition:place');
}
export function wrap(object: ModelElement): Place {
const wrapper: Place|undefined = new Place();
if(wrapper) {
return Object.assign(wrapper, object) as Place;
}
throw new Error(`'${object.type}' is not of type 'Place'`);
}
export function create(position: {x:number, y:number}): Place {
let node = new Place();
let specification = getNodeSpecOf('place-transition:place');
node.size = {
width: node.size.width ?? specification?.width ?? 100,
height: node.size.height ?? specification?.height ?? 100,
};
node.position = {
// center
x: position.x - node.size.width / 2,
y: position.y - node.size.height / 2
};
node.initializeProperties();
return node;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# Simple Execution Logic
For the execution logic we can create 2 simple Hooks that register a doubleclick on a Transition node. The first Hook (canDoubleClick) checks if the doubleclicked Transition can execute. Since our arcs have a weight of 1 and our Places have a capacitiy of 1, the following conditions need to be met:
- All Places preceding the doubleclicked Transition need to have exactly 1 Token
- All Places succeeding the doubleclicked Transition need to be empty
If the Conditions are met, the second Hook (postDoubleClick) will execute. We present the Code for the 2 Hooks in the following:
// Executes a doubleclicked Transition when preconditions are met
class TransitionHooks extends AbstractNodeHook {
// Check preconditions
canDoubleClick(node: Node): boolean {
// check if node is a Transition
// otherwise return false
if(!Transition.is(node)) {
return false;
}
// Wrap the node parameter into a Transition
const transition:Transition = Transition.wrap(node);
// Check that all Places preceding the clicked Transition contain exactly 1 Token
// and all Places succeeding the clicked Transition contain 0 Tokens
return transition.PlacePredecessors.every(place => place.containedTokenElements.length === 1 ) &&
transition.PlaceSucessors.every(place => place.containedTokenElements.length == 0 );
}
// Execute Transition
postDoubleClick(node: Node): void {
// We can assume the type is correct, since we check it in the canDoubleClick method
const transition:Transition = Transition.wrap(node);
// Delete the tokens in the preceding Places and create new ones in the succeeding Places
transition.PlacePredecessors.forEach(place => place.containedTokenElements.forEach(token => token.delete()));
transition.PlaceSucessors.forEach(place => place.createToken(20,20));
// Submit the Model to make the changes visible in the editor
this.submitModel();
// Save the current Model to file
this.saveModel();
}
}
// Register the Hooks, so the editor can find them when needed
LanguageFilesRegistry.register(TransitionHooks);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35