Enable link functionality in GUI2 Topology View
Change-Id: I1b88080ecdf8c9b6f8a60af4832a12441186d508
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/force-directed-graph.spec.ts b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/force-directed-graph.spec.ts
index 767e094..bdcd5c5 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/force-directed-graph.spec.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/force-directed-graph.spec.ts
@@ -16,6 +16,8 @@
import {ForceDirectedGraph, Options} from './force-directed-graph';
import {Node} from './node';
import {Link} from './link';
+import {LogService} from 'gui2-fw-lib';
+import {TestBed} from '@angular/core/testing';
export class TestNode extends Node {
constructor(id: string) {
@@ -33,13 +35,15 @@
* ONOS GUI -- ForceDirectedGraph - Unit Tests
*/
describe('ForceDirectedGraph', () => {
+ let logServiceSpy: jasmine.SpyObj<LogService>;
let fdg: ForceDirectedGraph;
const options: Options = {width: 1000, height: 1000};
beforeEach(() => {
+ const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
const nodes: Node[] = [];
const links: Link[] = [];
- fdg = new ForceDirectedGraph(options);
+ fdg = new ForceDirectedGraph(options, logSpy);
for (let i = 0; i < 10; i++) {
const newNode: TestNode = new TestNode('id' + i);
@@ -53,7 +57,7 @@
fdg.links = links;
fdg.initSimulation(options);
fdg.initNodes();
-
+ logServiceSpy = TestBed.get(LogService);
});
afterEach(() => {
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/force-directed-graph.ts b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/force-directed-graph.ts
index 46d3ba7..b511886 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/force-directed-graph.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/force-directed-graph.ts
@@ -16,12 +16,35 @@
import { EventEmitter } from '@angular/core';
import { Link } from './link';
import { Node } from './node';
-import * as d3 from 'd3';
+import * as d3 from 'd3-force';
+import {LogService} from 'gui2-fw-lib';
const FORCES = {
LINKS: 1 / 50,
COLLISION: 1,
- CHARGE: -10
+ GRAVITY: 0.4,
+ FRICTION: 0.7
+};
+
+const CHARGES = {
+ device: -80,
+ host: -200,
+ region: -80,
+ _def_: -120
+};
+
+const LINK_DISTANCE = {
+ // note: key is link.type
+ direct: 100,
+ optical: 120,
+ UiEdgeLink: 100,
+ _def_: 50,
+};
+
+const LINK_STRENGTH = {
+ // note: key is link.type
+ // range: {0.0 ... 1.0}
+ _def_: 0.1
};
export interface Options {
@@ -36,7 +59,7 @@
public nodes: Node[] = [];
public links: Link[] = [];
- constructor(options: Options) {
+ constructor(options: Options, public log: LogService) {
this.initSimulation(options);
}
@@ -56,10 +79,26 @@
// Initializing the links force simulation
this.simulation.force('links',
d3.forceLink(this.links)
- .strength(FORCES.LINKS)
+ .strength(this.strength.bind(this))
+ .distance(this.distance.bind(this))
);
}
+ charges(node) {
+ const nodeType = node.nodeType;
+ return CHARGES[nodeType] || CHARGES._def_;
+ }
+
+ distance(node) {
+ const nodeType = node.nodeType;
+ return LINK_DISTANCE[nodeType] || LINK_DISTANCE._def_;
+ }
+
+ strength(node) {
+ const nodeType = node.nodeType;
+ return LINK_STRENGTH[nodeType] || LINK_STRENGTH._def_;
+ }
+
initSimulation(options: Options) {
if (!options || !options.width || !options.height) {
throw new Error('missing options when initializing simulation');
@@ -72,9 +111,12 @@
// Creating the force simulation and defining the charges
this.simulation = d3.forceSimulation()
.force('charge',
- d3.forceManyBody()
- .strength(FORCES.CHARGE)
- );
+ d3.forceManyBody().strength(this.charges.bind(this)))
+ // .distanceMin(100).distanceMax(500))
+ .force('gravity',
+ d3.forceManyBody().strength(FORCES.GRAVITY))
+ .force('friction',
+ d3.forceManyBody().strength(FORCES.FRICTION));
// Connecting the d3 ticker to an angular event emitter
this.simulation.on('tick', function () {
@@ -82,7 +124,7 @@
});
this.initNodes();
- this.initLinks();
+ // this.initLinks();
}
/** Updating the central force of the simulation */
@@ -94,5 +136,11 @@
stopSimulation() {
this.simulation.stop();
+ this.log.debug('Simulation stopped');
+ }
+
+ restartSimulation() {
+ this.simulation.restart();
+ this.log.debug('Simulation restarted');
}
}
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/link.ts b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/link.ts
index e4d7768..eb6d340 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/link.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/link.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Node } from './node';
+import {Node, UiElement} from './node';
import * as d3 from 'd3';
export enum LinkType {
@@ -38,9 +38,15 @@
/**
* Implementing SimulationLinkDatum interface into our custom Link class
*/
-export class Link implements d3.SimulationLinkDatum<Node> {
+export class Link implements UiElement, d3.SimulationLinkDatum<Node> {
// Optional - defining optional implementation properties - required for relevant typing assistance
index?: number;
+ id: string; // The id of the link in the format epA/portA~epB/portB
+ epA: string; // The name of the device or host at one end
+ epB: string; // The name of the device or host at the other end
+ portA: string; // The number of the port at one end
+ portB: string; // The number of the port at the other end
+ type: LinkType;
// Must - defining enforced implementation properties
source: Node;
@@ -50,21 +56,29 @@
this.source = source;
this.target = target;
}
+
+ linkTypeStr(): string {
+ return LinkType[this.type];
+ }
}
/**
- * model of the topo2CurrentRegion region link from Region below
+ * model of the topo2CurrentRegion region link from Region
*/
export class RegionLink extends Link {
- id: string; // The id of the link in the format epA/portA~epB/portB
- epA: string; // The name of the device or host at one end
- epB: string; // The name of the device or host at the other end
- portA: string; // The number of the port at one end
- portB: string; // The number of the port at the other end
rollup: RegionRollup[]; // Links in sub regions represented by this one link
- type: LinkType;
constructor(type: LinkType, nodeA: Node, nodeB: Node) {
super(nodeA, nodeB);
+ this.type = type;
}
}
+
+/**
+ * model of the highlights that are sent back from WebSocket when traffic is shown
+ */
+export interface LinkHighlight {
+ id: string;
+ css: string;
+ label: string;
+}
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/node.ts b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/node.ts
index a7a7cb1..a5b4f3a 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/node.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/node.ts
@@ -22,6 +22,11 @@
RegionProps
} from './regions';
+export interface UiElement {
+ index?: number;
+ id: string;
+}
+
/**
* Toggle state for how device labels should be displayed
*/
@@ -112,7 +117,7 @@
/**
* Implementing SimulationNodeDatum interface into our custom Node class
*/
-export abstract class Node implements d3.SimulationNodeDatum {
+export abstract class Node implements UiElement, d3.SimulationNodeDatum {
// Optional - defining optional implementation properties - required for relevant typing assistance
index?: number;
x: number;
@@ -121,7 +126,7 @@
vy?: number;
fx?: number | null;
fy?: number | null;
-
+ nodeType: NodeType;
id: string;
protected constructor(id) {
@@ -140,7 +145,6 @@
location: LocationType;
metaUi: MetaUi;
master: string;
- nodeType: NodeType;
online: boolean;
props: DeviceProps;
type: string;
@@ -150,12 +154,14 @@
}
}
+/**
+ * Model of the ONOS Host element in the topology
+ */
export class Host extends Node {
configured: boolean;
id: string;
ips: string[];
layer: LayerType;
- nodeType: NodeType;
props: HostProps;
constructor(id: string) {
@@ -173,10 +179,30 @@
nDevs: number;
nHosts: number;
name: string;
- nodeType: NodeType;
props: RegionProps;
constructor(id: string) {
super(id);
}
}
+
+/**
+ * Enumerated values for topology update event types
+ */
+export enum ModelEventType {
+ HOST_ADDED_OR_UPDATED,
+ LINK_ADDED_OR_UPDATED,
+ DEVICE_ADDED_OR_UPDATED,
+ DEVICE_REMOVED,
+ HOST_REMOVED
+}
+
+/**
+ * Enumerated values for topology update event memo field
+ */
+export enum ModelEventMemo {
+ ADDED = 'added',
+ REMOVED = 'removed',
+ UPDATED = 'updated'
+}
+