602e6b6def79b0ba5c1c0c71fcfbc7d6664b5431
[sdc.git] /
1 /**
2  * Created by obarda on 6/28/2016.
3  */
4 /// <reference path="../../../../references"/>
5 module Sdc.Graph.Utils {
6
7     import ImageCreatorService = Sdc.Utils.ImageCreatorService;
8     import Module = Sdc.Models.Module;
9     export class CompositionGraphLinkUtils {
10
11         private p2pVL:Models.Components.Component;
12         private mp2mpVL:Models.Components.Component;
13
14         constructor(private linksFactory:Sdc.Utils.LinksFactory,
15                     private loaderService:Services.LoaderService,
16                     private generalGraphUtils:Sdc.Graph.Utils.CompositionGraphGeneralUtils,
17                     private leftPaletteLoaderService:Services.Components.LeftPaletteLoaderService,
18                     private componentInstanceFactory:Sdc.Utils.ComponentInstanceFactory,
19                     private nodesFactory:Sdc.Utils.NodesFactory,
20                     private commonGraphUtils: Sdc.Graph.Utils.CommonGraphUtils,
21                     private matchCapabilitiesRequirementsUtils: Graph.Utils.MatchCapabilitiesRequirementsUtils) {
22
23             this.initScopeVls();
24
25         }
26
27
28         /**
29          * Delete the link on server and then remove it from graph
30          * @param component
31          * @param releaseLoading - true/false release the loader when finished
32          * @param link - the link to delete
33          */
34         public deleteLink = (cy:Cy.Instance, component:Models.Components.Component, releaseLoading:boolean, link:Cy.CollectionEdges) => {
35
36             this.loaderService.showLoader('composition-graph');
37             let onSuccessDeleteRelation = (response) => {
38                 cy.remove(link);
39             };
40
41             if (!releaseLoading) {
42                 this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
43                     () => component.deleteRelation(link.data().relation).then(onSuccessDeleteRelation)
44                 );
45             } else {
46                 this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
47                     () => component.deleteRelation(link.data().relation).then(onSuccessDeleteRelation),
48                     () => this.loaderService.hideLoader('composition-graph'));
49             }
50         };
51
52         /**
53          * create the link on server and than draw it on graph
54          * @param link - the link to create
55          * @param cy
56          * @param component
57          */
58         public createLink = (link:Models.CompositionCiLinkBase, cy:Cy.Instance, component:Models.Components.Component):void => {
59
60             this.loaderService.showLoader('composition-graph');
61
62             let onSuccess:(response:Models.RelationshipModel) => void = (relation:Models.RelationshipModel) => {
63                 link.setRelation(relation);
64                 this.commonGraphUtils.insertLinkToGraph(cy, link);
65             };
66
67             link.updateLinkDirection();
68
69             this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
70                 () => component.createRelation(link.relation).then(onSuccess),
71                 () => this.loaderService.hideLoader('composition-graph')
72             );
73         };
74
75
76         public initScopeVls = ():void => {
77
78             let vls = this.leftPaletteLoaderService.getFullDataComponentList(Sdc.Utils.Constants.ResourceType.VL);
79             vls.forEach((item) => {
80                 let key = _.find(Object.keys(item.capabilities), (key) => {
81                     return _.includes(key.toLowerCase(), 'linkable');
82                 });
83                 let linkable = item.capabilities[key];
84                 if (linkable) {
85                     if (linkable[0].maxOccurrences == '2') {
86                         this.p2pVL = _.find(vls, (component:Models.Components.Component) => {
87                             return component.uniqueId === item.uniqueId;
88                         });
89
90                     } else {//assuming unbounded occurrences
91                         this.mp2mpVL = _.find(vls, (component:Models.Components.Component) => {
92                             return component.uniqueId === item.uniqueId;
93                         });
94                     }
95                 }
96             });
97         };
98
99         private setVLlinks = (match:Models.MatchReqToReq, vl:Models.ComponentsInstances.ComponentInstance):Array<Models.RelationshipModel> => {
100
101             let relationship1 = new Models.Relationship();
102             let relationship2 = new Models.Relationship();
103             let newRelationshipModel1 = new Models.RelationshipModel();
104             let newRelationshipModel2 = new Models.RelationshipModel();
105
106             let capability:Models.Capability = vl.capabilities.findValueByKey('linkable')[0];
107             relationship1.setRelationProperties(capability, match.requirement);
108             relationship2.setRelationProperties(capability, match.secondRequirement);
109
110             newRelationshipModel1.setRelationshipModelParams(match.fromNode, vl.uniqueId, [relationship1]);
111             newRelationshipModel2.setRelationshipModelParams(match.toNode, vl.uniqueId, [relationship2]);
112
113             return [newRelationshipModel1, newRelationshipModel2];
114         };
115
116         private createVlinks = (cy:Cy.Instance, component:Models.Components.Component, matchReqToReq:Models.MatchReqToReq, vl:Models.Components.Component):void => {
117
118             let componentInstance:Models.ComponentsInstances.ComponentInstance = this.componentInstanceFactory.createComponentInstanceFromComponent(vl);
119             let fromNodePosition:Cy.Position = cy.getElementById(matchReqToReq.fromNode).relativePosition();
120             let toNodePosition:Cy.Position = cy.getElementById(matchReqToReq.toNode).relativePosition();
121             let location:Cy.Position = {
122                 x: 0.5 * (fromNodePosition.x + toNodePosition.x),
123                 y: 0.5 * (fromNodePosition.y + toNodePosition.y)
124             }
125
126             componentInstance.posX = location.x;
127             componentInstance.posY = location.y;
128
129             let onFailed:(error:any) => void = (error:any) => {
130                 this.loaderService.hideLoader('composition-graph');
131                 console.info('onFailed', error);
132             };
133
134             let onSuccess = (response:Models.ComponentsInstances.ComponentInstance):void => {
135
136                 console.info('onSuccses', response);
137                 response.requirements = new Models.RequirementsGroup(vl.requirements);
138                 response.capabilities = new Models.CapabilitiesGroup(vl.capabilities);
139                 response.componentVersion = vl.version;
140                 response.setInstanceRC();
141
142                 let newLinks = this.setVLlinks(matchReqToReq, response);
143                 let newNode = this.nodesFactory.createNode(response);
144
145                 this.commonGraphUtils.addComponentInstanceNodeToGraph(cy, newNode);
146
147                 _.forEach(newLinks, (link) => {
148                     let linkObg:Models.CompositionCiLinkBase = this.linksFactory.createGraphLink(cy, link, link.relationships[0]);
149                     this.createLink(linkObg, cy, component);
150                 });
151             };
152             component.createComponentInstance(componentInstance).then(onSuccess, onFailed);
153         };
154
155         private createSimpleLink = (match:Models.MatchReqToCapability, cy:Cy.Instance, component:Models.Components.Component):void => {
156             let newRelation:Models.RelationshipModel = match.matchToRelationModel();
157             let linkObg:Models.CompositionCiLinkBase = this.linksFactory.createGraphLink(cy,newRelation, newRelation.relationships[0]);
158             this.createLink(linkObg, cy, component);
159         };
160
161         public createLinkFromMenu = (cy:Cy.Instance, chosenMatch:Models.MatchBase, vl:Models.Components.Component, component:Models.Components.Component):void => {
162
163             if (chosenMatch) {
164                 if (chosenMatch && chosenMatch instanceof Models.MatchReqToReq) {
165                     this.createVlinks(cy, component, chosenMatch, vl); //TODO orit implement
166                 }
167                 if (chosenMatch && chosenMatch instanceof Models.MatchReqToCapability) {
168                     this.createSimpleLink(chosenMatch, cy, component);
169                 }
170             }
171         };
172
173
174         /**
175          * Filters the matches for UCPE links so that shown requirements and capabilites are only related to the selected ucpe-cp
176          * @param fromNode
177          * @param toNode
178          * @param matchesArray
179          * @returns {Array<Models.MatchBase>}
180          */
181         public filterUcpeLinks(fromNode: Models.Graph.CompositionCiNodeBase, toNode: Models.Graph.CompositionCiNodeBase, matchesArray: Array<Models.MatchBase>): any {
182
183             let matchLink: Array<Models.MatchBase>;
184
185             if (fromNode.isUcpePart) {
186                 matchLink = _.filter(matchesArray, (match: Models.MatchBase) => {
187                     return match.isOwner(fromNode.id);
188                 });
189             }
190
191             if (toNode.isUcpePart) {
192                 matchLink = _.filter(matchesArray, (match: Models.MatchBase) => {
193                     return match.isOwner(toNode.id);
194                 });
195             }
196             return matchLink ? matchLink : matchesArray;
197         }
198
199
200         /**
201          * open the connect link menu if the link drawn is valid - match  requirements & capabilities
202          * @param cy
203          * @param fromNode
204          * @param toNode
205          * @returns {any}
206          */
207         public onLinkDrawn(cy:Cy.Instance, fromNode:Cy.CollectionFirstNode, toNode:Cy.CollectionFirstNode):Models.RelationMenuDirectiveObj {
208
209             if(!this.commonGraphUtils.nodeLocationsCompatible(cy, fromNode, toNode)){ return null; }
210             let linkModel:Array<Models.CompositionCiLinkBase> = this.generalGraphUtils.getAllCompositionCiLinks(cy);
211
212             let possibleRelations:Array<Models.MatchBase> = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromNode.data().componentInstance,
213                 toNode.data().componentInstance, linkModel, this.mp2mpVL); //TODO orit - add p2p and mp2mp
214
215             //filter relations found to limit to specific ucpe-cp
216             possibleRelations = this.filterUcpeLinks(fromNode.data(), toNode.data(), possibleRelations);
217
218             //if found possibleRelations between the nodes we create relation menu directive and open the link menu
219             if (possibleRelations.length) {
220                 let menuPosition = this.generalGraphUtils.getLinkMenuPosition(cy, toNode.renderedPoint());
221                 return new Models.RelationMenuDirectiveObj(fromNode.data(), toNode.data(), this.mp2mpVL, this.p2pVL, menuPosition, possibleRelations);
222             }
223             return null;
224         };
225
226
227         /**
228          *  when we drag instance in to UCPE or out of UCPE  - get all links we need to delete - one node in ucpe and one node outside of ucpe
229          * @param node - the node we dragged into or out of the ucpe
230          */
231         public deleteLinksWhenNodeMovedFromOrToUCPE(component:Models.Components.Component, cy:Cy.Instance, nodeMoved:Cy.CollectionNodes, vlsPendingDeletion?:Cy.CollectionNodes):void {
232
233
234             let linksToDelete:Cy.CollectionElements = cy.collection();
235             _.forEach(nodeMoved.neighborhood('node'), (neighborNode)=>{
236
237                 if(neighborNode.data().isUcpePart){ //existing connections to ucpe or ucpe-cp - we want to delete even though nodeLocationsCompatible will technically return true
238                     linksToDelete = linksToDelete.add(nodeMoved.edgesWith(neighborNode)); // This will delete the ucpe-host-link, or the vl-ucpe-link if nodeMoved is vl
239                 } else if(!this.commonGraphUtils.nodeLocationsCompatible(cy, nodeMoved, neighborNode)){ //connection to regular node or vl - check if locations are compatible
240                    if(!vlsPendingDeletion || !vlsPendingDeletion.intersect(neighborNode).length){ //Check if this is a link to a VL pending deletion, to prevent double deletion of between the node moved and vl
241                        linksToDelete = linksToDelete.add(nodeMoved.edgesWith(neighborNode));
242                    }
243                 }
244             });
245
246
247
248             linksToDelete.each((i, link)=>{
249                 this.deleteLink(cy, component, false, link);
250             });
251
252         };
253
254
255         /**
256          * Creates a hostedOn link between a VF and UCPE
257          * @param component
258          * @param cy
259          * @param ucpeNode
260          * @param vfNode
261          */
262         public createVfToUcpeLink = (component: Models.Components.Component, cy:Cy.Instance, ucpeNode:Models.Graph.NodeUcpe, vfNode:Models.Graph.CompositionCiNodeVf):void => {
263             let hostedOnMatch:Models.MatchReqToCapability = this.generalGraphUtils.canBeHostedOn(cy, ucpeNode.componentInstance, vfNode.componentInstance);
264             /* create relation */
265             let newRelation = new Models.RelationshipModel();
266             newRelation.fromNode = ucpeNode.id;
267             newRelation.toNode = vfNode.id;
268
269             let link:Models.CompositionCiLinkBase = this.linksFactory.createUcpeHostLink(newRelation);
270             link.relation = hostedOnMatch.matchToRelationModel();
271             this.createLink(link, cy, component);
272         };
273
274
275         /**
276          * Handles click event on links.
277          * If one edge selected: do nothing.
278          /*Two edges selected - always select all
279          /* Three or more edges: first click - select all, secondary click - select single.
280          * @param cy
281          * @param event
282          */
283         public handleLinkClick(cy:Cy.Instance, event : Cy.EventObject) {
284             if(cy.$('edge:selected').length > 2 && event.cyTarget[0].selected()) {
285                 cy.$(':selected').unselect();
286             } else {
287
288                 let vl: Cy.CollectionNodes = event.cyTarget[0].target('.vl-node');
289                 let connectedEdges:Cy.CollectionEdges = vl.connectedEdges();
290                 if (vl.length && connectedEdges.length > 1) {
291
292                     setTimeout(() => {
293                         vl.select();
294                         connectedEdges.select();
295                     }, 0);
296                 }
297             }
298
299         }
300
301
302         /**
303          * Calculates the position for the menu that modifies an existing link
304          * @param event
305          * @param elementWidth
306          * @param elementHeight
307          * @returns {Sdc.Models.Graph.Point}
308          */
309         public calculateLinkMenuPosition(event, elementWidth, elementHeight): Sdc.Models.Graph.Point {
310             let point: Sdc.Models.Graph.Point = new Sdc.Models.Graph.Point(event.originalEvent.x,event.originalEvent.y);
311             if(event.originalEvent.view.screen.height-elementHeight<point.y){
312                 point.y = event.originalEvent.view.screen.height-elementHeight;
313             }
314             if(event.originalEvent.view.screen.width-elementWidth<point.x){
315                 point.x = event.originalEvent.view.screen.width-elementWidth;
316             }
317             return point;
318         };
319
320
321         /**
322          * Gets the menu that is displayed when you click an existing link.
323          * @param link
324          * @param event
325          * @returns {Models.LinkMenu}
326          */
327         public getModifyLinkMenu(link:Cy.CollectionFirstEdge, event:Cy.EventObject):Models.LinkMenu{
328             let point:Sdc.Models.Graph.Point = this.calculateLinkMenuPosition(event,Sdc.Utils.Constants.GraphUIObjects.MENU_LINK_VL_WIDTH_OFFSET,Sdc.Utils.Constants.GraphUIObjects.MENU_LINK_VL_HEIGHT_OFFSET);
329             let menu:Models.LinkMenu = new Models.LinkMenu(point, true, link);
330             return menu;
331         };
332
333     }
334
335
336
337     CompositionGraphLinkUtils.$inject = [
338         'LinksFactory',
339         'LoaderService',
340         'CompositionGraphGeneralUtils',
341         'LeftPaletteLoaderService',
342         'ComponentInstanceFactory',
343         'NodesFactory',
344         'CommonGraphUtils',
345         'MatchCapabilitiesRequirementsUtils'
346     ];
347 }