# 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
}
1
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;
      }
}
1
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);
1
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
Last Updated: 5/27/2025, 11:06:58 AM