95c31d16b15adc1c10f611cd0a28014a9294e196
[sdc.git] /
1 /**
2  * Created by obarda on 11/9/2016.
3  */
4
5 /// <reference path="../../../../references"/>
6 module Sdc.Graph.Utils {
7
8     export class CompositionGraphNodesUtils {
9         constructor(private NodesFactory:Sdc.Utils.NodesFactory,  private $log:ng.ILogService,
10                     private GeneralGraphUtils:Graph.Utils.CompositionGraphGeneralUtils,
11                     private commonGraphUtils: Sdc.Graph.Utils.CommonGraphUtils,
12                     private eventListenerService: Services.EventListenerService,
13                     private loaderService:Services.LoaderService) {
14
15         }
16
17         /**
18          * Returns component instances for all nodes passed in
19          * @param nodes - Cy nodes
20          * @returns {any[]}
21          */
22         public getAllNodesData(nodes:Cy.CollectionNodes) {
23             return _.map(nodes, (node:Cy.CollectionFirstNode)=> {
24                 return node.data();
25             })
26         };
27
28         /**
29          * Deletes component instances on server and then removes it from the graph as well
30          * @param cy
31          * @param component
32          * @param nodeToDelete
33          */
34         public deleteNode(cy: Cy.Instance, component:Models.Components.Component, nodeToDelete:Cy.CollectionNodes):void {
35
36             this.loaderService.showLoader('composition-graph');
37             let onSuccess:(response:Models.ComponentsInstances.ComponentInstance) => void = (response:Models.ComponentsInstances.ComponentInstance) => {
38                 console.info('onSuccess', response);
39
40                 //if node to delete is a UCPE, remove all children (except UCPE-CPs) and remove their "hostedOn" links
41                 if (nodeToDelete.data().isUcpe){
42                     _.each(cy.nodes('[?isInsideGroup]'), (node)=>{
43                        this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, node, nodeToDelete);
44                     });
45                 }
46
47                 //check whether the node is connected to any VLs that only have one other connection. If so, delete that VL as well
48                 if(!(nodeToDelete.data() instanceof Sdc.Models.Graph.CompositionCiNodeVl)){
49                     let connectedVls:Array<Cy.CollectionFirstNode> = this.getConnectedVlToNode(nodeToDelete);
50                     this.handleConnectedVlsToDelete(connectedVls);
51                 }
52
53                 //update UI
54                 cy.remove(nodeToDelete);
55
56             };
57
58             let onFailed:(response:any) => void = (response:any) => {
59                 console.info('onFailed', response);
60             };
61
62
63             this.GeneralGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
64                 () => component.deleteComponentInstance(nodeToDelete.data().componentInstance.uniqueId).then(onSuccess, onFailed),
65                 () => this.loaderService.hideLoader('composition-graph')
66             );
67
68         };
69
70
71         /**
72          * Finds all VLs connected to a single node
73          * @param node
74          * @returns {Array<Cy.CollectionFirstNode>}
75          */
76         public getConnectedVlToNode = (node: Cy.CollectionNodes): Array<Cy.CollectionFirstNode> => {
77             let connectedVls: Array<Cy.CollectionFirstNode> = new Array<Cy.CollectionFirstNode>();
78             _.forEach(node.connectedEdges().connectedNodes(), (node: Cy.CollectionFirstNode) => {
79                 if (node.data() instanceof Models.Graph.CompositionCiNodeVl) {
80                     connectedVls.push(node);
81                 }
82             });
83             return connectedVls;
84         };
85
86
87         /**
88          * Delete all VLs that have only two connected nodes (this function is called when deleting a node)
89          * @param connectedVls
90          */
91         public handleConnectedVlsToDelete = (connectedVls: Array<Cy.CollectionFirstNode>) => {
92             _.forEach(connectedVls, (vlToDelete: Cy.CollectionNodes) => {
93
94                 if (vlToDelete.connectedEdges().length === 2) { // if vl connected only to 2 nodes need to delete the vl
95                     this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, vlToDelete.data().componentInstance);
96                 }
97             });
98         };
99
100
101         /**
102          * This function is called when moving a node in or out of UCPE.
103          * Deletes all connected VLs that have less than 2 valid connections remaining after the move
104          * Returns the collection of vls that are in the process of deletion (async) to prevent duplicate calls while deletion is in progress
105          * @param component
106          * @param cy
107          * @param node - node that was moved in/out of ucpe
108          */
109         public deleteNodeVLsUponMoveToOrFromUCPE = (component:Models.Components.Component, cy:Cy.Instance, node:Cy.CollectionNodes):Cy.CollectionNodes =>{
110             if(node.data() instanceof Models.Graph.CompositionCiNodeVl){ return;}
111
112             let connectedVLsToDelete:Cy.CollectionNodes = cy.collection();
113             _.forEach(node.neighborhood('node'), (connectedNode) => {
114
115                //Find all neighboring nodes that are VLs
116                if(connectedNode.data() instanceof  Models.Graph.CompositionCiNodeVl){
117
118                    //check VL's neighbors to see if it has 2 or more nodes whose location is compatible with VL (regardless of whether VL is in or out of UCPE)
119                    let compatibleNodeCount = 0;
120                    let vlNeighborhood = connectedNode.neighborhood('node');
121                    _.forEach(vlNeighborhood, (vlNeighborNode)=>{
122                        if(this.commonGraphUtils.nodeLocationsCompatible(cy, connectedNode, vlNeighborNode)) {
123                             compatibleNodeCount ++;
124                        }
125                    });
126
127                    if(compatibleNodeCount < 2) {
128                        connectedVLsToDelete = connectedVLsToDelete.add(connectedNode);
129                    }
130                }
131             });
132
133             connectedVLsToDelete.each((i, vlToDelete:Cy.CollectionNodes)=>{
134                 this.deleteNode(cy, component, vlToDelete);
135             });
136             return connectedVLsToDelete;
137         };
138
139         /**
140          * This function will update nodes position. if the new position is into or out of ucpe, the node will trigger the ucpe events
141          * @param cy
142          * @param component
143          * @param nodesMoved - the node/multiple nodes now moved by the user
144          */
145         public onNodesPositionChanged = (cy: Cy.Instance, component:Models.Components.Component, nodesMoved: Cy.CollectionNodes): void => {
146
147             if (nodesMoved.length === 0) {
148                 return;
149             }
150
151             let isValidMove:boolean = this.GeneralGraphUtils.isGroupValidDrop(cy, nodesMoved);
152             if (isValidMove) {
153
154                 this.$log.debug(`composition-graph::ValidDrop:: updating node position`);
155                 let instancesToUpdateInNonBlockingAction:Array<Models.ComponentsInstances.ComponentInstance> = new Array<Models.ComponentsInstances.ComponentInstance>();
156
157                 _.each(nodesMoved, (node:Cy.CollectionFirstNode)=> {  //update all nodes new position
158
159                     if(node.data().isUcpePart && !node.data().isUcpe){ return; }//No need to update UCPE-CPs
160
161                     //update position
162                     let newPosition:Cy.Position = this.commonGraphUtils.getNodePosition(node);
163                     node.data().componentInstance.updatePosition(newPosition.x, newPosition.y);
164
165                     //check if node moved to or from UCPE
166                     let ucpe = this.commonGraphUtils.isInUcpe(node.cy(), node.boundingbox());
167                     if(node.data().isInsideGroup || ucpe.length) {
168                         this.handleUcpeChildMove(node, ucpe, instancesToUpdateInNonBlockingAction);
169                     } else {
170                         instancesToUpdateInNonBlockingAction.push(node.data().componentInstance);
171                     }
172
173                 });
174
175                 if (instancesToUpdateInNonBlockingAction.length > 0) {
176                     this.GeneralGraphUtils.pushMultipleUpdateComponentInstancesRequestToQueue(false, instancesToUpdateInNonBlockingAction, component);
177                 }
178             } else {
179                 this.$log.debug(`composition-graph::notValidDrop:: node return to latest position`);
180                 //reset nodes position
181                 nodesMoved.positions((i, node) => {
182                     return {
183                         x: +node.data().componentInstance.posX,
184                         y: +node.data().componentInstance.posY
185                     };
186                 })
187             }
188
189             this.GeneralGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(() => {
190             }, () => {
191                 this.loaderService.hideLoader('composition-graph');
192             });
193
194         };
195
196         /**
197          * Checks whether the node has been added or removed from UCPE and triggers appropriate events
198          * @param node - node moved
199          * @param ucpeContainer - UCPE container that the node has been moved to. When moving a node out of ucpe, param will be empty
200          * @param instancesToUpdateInNonBlockingAction
201          */
202         public handleUcpeChildMove(node:Cy.CollectionFirstNode, ucpeContainer:Cy.CollectionElements, instancesToUpdateInNonBlockingAction:Array<Models.ComponentsInstances.ComponentInstance>){
203
204             if(node.data().isInsideGroup){
205                 if(ucpeContainer.length){ //moving node within UCPE. Simply update position
206                     this.commonGraphUtils.updateUcpeChildPosition(<Cy.CollectionNodes>node, ucpeContainer);
207                     instancesToUpdateInNonBlockingAction.push(node.data().componentInstance);
208                 } else { //removing node from UCPE. Notify observers
209                     this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, node, ucpeContainer);
210                 }
211             } else if(!node.data().isInsideGroup && ucpeContainer.length && !node.data().isUcpePart){ //adding node to UCPE
212                 this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, node, ucpeContainer, true);
213             }
214         }
215
216     }
217
218
219     CompositionGraphNodesUtils.$inject = ['NodesFactory', '$log', 'CompositionGraphGeneralUtils', 'CommonGraphUtils', 'EventListenerService', 'LoaderService'];
220 }