Thomas Vachuska

Added server-side code to create host-to-host intent. Still WIP.

Change-Id: I80626aa9ecb38802ddca4be3bce3def85ccfdb88
...@@ -296,7 +296,7 @@ public class FlowRuleManager ...@@ -296,7 +296,7 @@ public class FlowRuleManager
296 post(event); 296 post(event);
297 } 297 }
298 } else { 298 } else {
299 - log.info("Removing flow rules...."); 299 + log.debug("Removing flow rules....");
300 removeFlowRules(flowEntry); 300 removeFlowRules(flowEntry);
301 } 301 }
302 302
......
...@@ -20,12 +20,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -20,12 +20,17 @@ import com.fasterxml.jackson.databind.ObjectMapper;
20 import com.fasterxml.jackson.databind.node.ArrayNode; 20 import com.fasterxml.jackson.databind.node.ArrayNode;
21 import com.fasterxml.jackson.databind.node.ObjectNode; 21 import com.fasterxml.jackson.databind.node.ObjectNode;
22 import org.eclipse.jetty.websocket.WebSocket; 22 import org.eclipse.jetty.websocket.WebSocket;
23 +import org.onlab.onos.core.ApplicationId;
24 +import org.onlab.onos.core.CoreService;
23 import org.onlab.onos.mastership.MastershipEvent; 25 import org.onlab.onos.mastership.MastershipEvent;
24 import org.onlab.onos.mastership.MastershipListener; 26 import org.onlab.onos.mastership.MastershipListener;
25 import org.onlab.onos.mastership.MastershipService; 27 import org.onlab.onos.mastership.MastershipService;
26 import org.onlab.onos.net.Annotations; 28 import org.onlab.onos.net.Annotations;
29 +import org.onlab.onos.net.ConnectPoint;
30 +import org.onlab.onos.net.DefaultEdgeLink;
27 import org.onlab.onos.net.Device; 31 import org.onlab.onos.net.Device;
28 import org.onlab.onos.net.DeviceId; 32 import org.onlab.onos.net.DeviceId;
33 +import org.onlab.onos.net.ElementId;
29 import org.onlab.onos.net.Host; 34 import org.onlab.onos.net.Host;
30 import org.onlab.onos.net.HostId; 35 import org.onlab.onos.net.HostId;
31 import org.onlab.onos.net.HostLocation; 36 import org.onlab.onos.net.HostLocation;
...@@ -34,28 +39,41 @@ import org.onlab.onos.net.Path; ...@@ -34,28 +39,41 @@ import org.onlab.onos.net.Path;
34 import org.onlab.onos.net.device.DeviceEvent; 39 import org.onlab.onos.net.device.DeviceEvent;
35 import org.onlab.onos.net.device.DeviceListener; 40 import org.onlab.onos.net.device.DeviceListener;
36 import org.onlab.onos.net.device.DeviceService; 41 import org.onlab.onos.net.device.DeviceService;
42 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
43 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
37 import org.onlab.onos.net.host.HostEvent; 44 import org.onlab.onos.net.host.HostEvent;
38 import org.onlab.onos.net.host.HostListener; 45 import org.onlab.onos.net.host.HostListener;
39 import org.onlab.onos.net.host.HostService; 46 import org.onlab.onos.net.host.HostService;
47 +import org.onlab.onos.net.intent.HostToHostIntent;
48 +import org.onlab.onos.net.intent.Intent;
49 +import org.onlab.onos.net.intent.IntentEvent;
40 import org.onlab.onos.net.intent.IntentId; 50 import org.onlab.onos.net.intent.IntentId;
51 +import org.onlab.onos.net.intent.IntentListener;
52 +import org.onlab.onos.net.intent.IntentService;
53 +import org.onlab.onos.net.intent.PathIntent;
41 import org.onlab.onos.net.link.LinkEvent; 54 import org.onlab.onos.net.link.LinkEvent;
42 import org.onlab.onos.net.link.LinkListener; 55 import org.onlab.onos.net.link.LinkListener;
43 import org.onlab.onos.net.link.LinkService; 56 import org.onlab.onos.net.link.LinkService;
57 +import org.onlab.onos.net.provider.ProviderId;
44 import org.onlab.onos.net.topology.PathService; 58 import org.onlab.onos.net.topology.PathService;
45 import org.onlab.osgi.ServiceDirectory; 59 import org.onlab.osgi.ServiceDirectory;
46 import org.onlab.packet.IpAddress; 60 import org.onlab.packet.IpAddress;
47 61
48 import java.io.IOException; 62 import java.io.IOException;
49 -import java.util.HashMap;
50 import java.util.Iterator; 63 import java.util.Iterator;
64 +import java.util.List;
51 import java.util.Map; 65 import java.util.Map;
52 import java.util.Set; 66 import java.util.Set;
67 +import java.util.concurrent.ConcurrentHashMap;
53 68
54 import static com.google.common.base.Preconditions.checkNotNull; 69 import static com.google.common.base.Preconditions.checkNotNull;
55 import static org.onlab.onos.net.DeviceId.deviceId; 70 import static org.onlab.onos.net.DeviceId.deviceId;
56 import static org.onlab.onos.net.HostId.hostId; 71 import static org.onlab.onos.net.HostId.hostId;
72 +import static org.onlab.onos.net.PortNumber.portNumber;
57 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; 73 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
58 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED; 74 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED;
75 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
76 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
59 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED; 77 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
60 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED; 78 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
61 79
...@@ -64,6 +82,10 @@ import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED; ...@@ -64,6 +82,10 @@ import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
64 */ 82 */
65 public class TopologyWebSocket implements WebSocket.OnTextMessage { 83 public class TopologyWebSocket implements WebSocket.OnTextMessage {
66 84
85 + private static final String APP_ID = "org.onlab.onos.gui";
86 + private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
87 +
88 + private final ApplicationId appId;
67 private final ServiceDirectory directory; 89 private final ServiceDirectory directory;
68 90
69 private final ObjectMapper mapper = new ObjectMapper(); 91 private final ObjectMapper mapper = new ObjectMapper();
...@@ -74,14 +96,19 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage { ...@@ -74,14 +96,19 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage {
74 private final LinkService linkService; 96 private final LinkService linkService;
75 private final HostService hostService; 97 private final HostService hostService;
76 private final MastershipService mastershipService; 98 private final MastershipService mastershipService;
99 + private final IntentService intentService;
77 100
78 private final DeviceListener deviceListener = new InternalDeviceListener(); 101 private final DeviceListener deviceListener = new InternalDeviceListener();
79 private final LinkListener linkListener = new InternalLinkListener(); 102 private final LinkListener linkListener = new InternalLinkListener();
80 private final HostListener hostListener = new InternalHostListener(); 103 private final HostListener hostListener = new InternalHostListener();
81 private final MastershipListener mastershipListener = new InternalMastershipListener(); 104 private final MastershipListener mastershipListener = new InternalMastershipListener();
105 + private final IntentListener intentListener = new InternalIntentListener();
82 106
83 // TODO: extract into an external & durable state; good enough for now and demo 107 // TODO: extract into an external & durable state; good enough for now and demo
84 - private static Map<String, ObjectNode> metaUi = new HashMap<>(); 108 + private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
109 +
110 + // Intents that are being monitored for the GUI
111 + private static Map<IntentId, Long> intentsToMonitor = new ConcurrentHashMap<>();
85 112
86 private static final String COMPACT = "%s/%s-%s/%s"; 113 private static final String COMPACT = "%s/%s-%s/%s";
87 114
...@@ -97,6 +124,9 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage { ...@@ -97,6 +124,9 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage {
97 linkService = directory.get(LinkService.class); 124 linkService = directory.get(LinkService.class);
98 hostService = directory.get(HostService.class); 125 hostService = directory.get(HostService.class);
99 mastershipService = directory.get(MastershipService.class); 126 mastershipService = directory.get(MastershipService.class);
127 + intentService = directory.get(IntentService.class);
128 +
129 + appId = directory.get(CoreService.class).registerApplication(APP_ID);
100 } 130 }
101 131
102 @Override 132 @Override
...@@ -106,9 +136,17 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage { ...@@ -106,9 +136,17 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage {
106 linkService.addListener(linkListener); 136 linkService.addListener(linkListener);
107 hostService.addListener(hostListener); 137 hostService.addListener(hostListener);
108 mastershipService.addListener(mastershipListener); 138 mastershipService.addListener(mastershipListener);
139 + intentService.addListener(intentListener);
109 140
110 sendAllDevices(); 141 sendAllDevices();
111 sendAllLinks(); 142 sendAllLinks();
143 + sendAllHosts();
144 + }
145 +
146 + private void sendAllHosts() {
147 + for (Host host : hostService.getHosts()) {
148 + sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
149 + }
112 } 150 }
113 151
114 private void sendAllDevices() { 152 private void sendAllDevices() {
...@@ -141,14 +179,15 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage { ...@@ -141,14 +179,15 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage {
141 } else if (type.equals("updateMeta")) { 179 } else if (type.equals("updateMeta")) {
142 updateMetaInformation(event); 180 updateMetaInformation(event);
143 } else if (type.equals("requestPath")) { 181 } else if (type.equals("requestPath")) {
144 - sendPath(event); 182 + createHostIntent(event);
145 } else if (type.equals("requestTraffic")) { 183 } else if (type.equals("requestTraffic")) {
146 sendTraffic(event); 184 sendTraffic(event);
147 } else if (type.equals("cancelTraffic")) { 185 } else if (type.equals("cancelTraffic")) {
148 cancelTraffic(event); 186 cancelTraffic(event);
149 } 187 }
150 - } catch (IOException e) { 188 + } catch (Exception e) {
151 - System.out.println("Received: " + data); 189 + System.out.println("WTF?! " + data);
190 + e.printStackTrace();
152 } 191 }
153 } 192 }
154 193
...@@ -250,19 +289,19 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage { ...@@ -250,19 +289,19 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage {
250 metaUi.put(string(payload, "id"), payload); 289 metaUi.put(string(payload, "id"), payload);
251 } 290 }
252 291
253 - // Sends path message. 292 + // Creates host-to-host intent.
254 - private void sendPath(ObjectNode event) { 293 + private void createHostIntent(ObjectNode event) {
255 ObjectNode payload = payload(event); 294 ObjectNode payload = payload(event);
256 long id = number(event, "sid"); 295 long id = number(event, "sid");
257 - DeviceId one = deviceId(string(payload, "one")); 296 + // TODO: add protection against device ids and non-existent hosts.
258 - DeviceId two = deviceId(string(payload, "two")); 297 + HostId one = hostId(string(payload, "one"));
298 + HostId two = hostId(string(payload, "two"));
259 299
260 - ObjectNode response = findPath(one, two); 300 + HostToHostIntent hostIntent = new HostToHostIntent(appId, one, two,
261 - if (response != null) { 301 + DefaultTrafficSelector.builder().build(),
262 - sendMessage(envelope("showPath", id, response)); 302 + DefaultTrafficTreatment.builder().build());
263 - } else { 303 + intentsToMonitor.put(hostIntent.id(), number(event, "sid"));
264 - sendMessage(message("warn", id, "No path found")); 304 + intentService.submit(hostIntent);
265 - }
266 } 305 }
267 306
268 // Sends traffic message. 307 // Sends traffic message.
...@@ -314,8 +353,8 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage { ...@@ -314,8 +353,8 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage {
314 * @return formatted link string 353 * @return formatted link string
315 */ 354 */
316 public static String compactLinkString(Link link) { 355 public static String compactLinkString(Link link) {
317 - return String.format(COMPACT, link.src().deviceId(), link.src().port(), 356 + return String.format(COMPACT, link.src().elementId(), link.src().port(),
318 - link.dst().deviceId(), link.dst().port()); 357 + link.dst().elementId(), link.dst().port());
319 } 358 }
320 359
321 360
...@@ -337,11 +376,7 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage { ...@@ -337,11 +376,7 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage {
337 // Add labels, props and stuff the payload into envelope. 376 // Add labels, props and stuff the payload into envelope.
338 payload.set("labels", labels); 377 payload.set("labels", labels);
339 payload.set("props", props(device.annotations())); 378 payload.set("props", props(device.annotations()));
340 - 379 + addMetaUi(device.id(), payload);
341 - ObjectNode meta = metaUi.get(device.id().toString());
342 - if (meta != null) {
343 - payload.set("metaUi", meta);
344 - }
345 380
346 String type = (event.type() == DEVICE_ADDED) ? "addDevice" : 381 String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
347 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice"); 382 ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
...@@ -368,11 +403,30 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage { ...@@ -368,11 +403,30 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage {
368 private ObjectNode hostMessage(HostEvent event) { 403 private ObjectNode hostMessage(HostEvent event) {
369 Host host = event.subject(); 404 Host host = event.subject();
370 ObjectNode payload = mapper.createObjectNode() 405 ObjectNode payload = mapper.createObjectNode()
371 - .put("id", host.id().toString()); 406 + .put("id", host.id().toString())
407 + .put("ingress", compactLinkString(edgeLink(host, true)))
408 + .put("egress", compactLinkString(edgeLink(host, false)));
372 payload.set("cp", location(mapper, host.location())); 409 payload.set("cp", location(mapper, host.location()));
373 payload.set("labels", labels(mapper, ip(host.ipAddresses()), 410 payload.set("labels", labels(mapper, ip(host.ipAddresses()),
374 host.mac().toString())); 411 host.mac().toString()));
375 - return payload; 412 + payload.set("props", props(host.annotations()));
413 + addMetaUi(host.id(), payload);
414 +
415 + String type = (event.type() == HOST_ADDED) ? "addHost" :
416 + ((event.type() == HOST_REMOVED) ? "removeHost" : "updateHost");
417 + return envelope(type, 0, payload);
418 + }
419 +
420 + private DefaultEdgeLink edgeLink(Host host, boolean ingress) {
421 + return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
422 + host.location(), ingress);
423 + }
424 +
425 + private void addMetaUi(ElementId id, ObjectNode payload) {
426 + ObjectNode meta = metaUi.get(id.toString());
427 + if (meta != null) {
428 + payload.set("metaUi", meta);
429 + }
376 } 430 }
377 431
378 432
...@@ -468,5 +522,22 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage { ...@@ -468,5 +522,22 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage {
468 522
469 } 523 }
470 } 524 }
525 +
526 + private class InternalIntentListener implements IntentListener {
527 + @Override
528 + public void event(IntentEvent event) {
529 + Intent intent = event.subject();
530 + Long sid = intentsToMonitor.get(intent.id());
531 + if (sid != null) {
532 + List<Intent> installable = intentService.getInstallableIntents(intent.id());
533 + if (installable != null && !installable.isEmpty()) {
534 + PathIntent pathIntent = (PathIntent) installable.iterator().next();
535 + Path path = pathIntent.path();
536 + ObjectNode payload = pathMessage(path).put("intentId", intent.id().toString());
537 + sendMessage(envelope("showPath", sid, payload));
538 + }
539 + }
540 + }
541 + }
471 } 542 }
472 543
......
...@@ -71,6 +71,11 @@ ...@@ -71,6 +71,11 @@
71 opacity: .7; 71 opacity: .7;
72 } 72 }
73 73
74 +#topo svg .link.showPath {
75 + stroke: #f00;
76 + stroke-width: 4px;
77 +}
78 +
74 /* for debugging */ 79 /* for debugging */
75 #topo svg .node circle.debug { 80 #topo svg .node circle.debug {
76 fill: white; 81 fill: white;
......
...@@ -383,6 +383,7 @@ ...@@ -383,6 +383,7 @@
383 note('addLink', lnk.id); 383 note('addLink', lnk.id);
384 384
385 network.links.push(lnk); 385 network.links.push(lnk);
386 + network.lookup[lnk.id] = lnk;
386 updateLinks(); 387 updateLinks();
387 network.force.start(); 388 network.force.start();
388 } 389 }
...@@ -401,13 +402,29 @@ ...@@ -401,13 +402,29 @@
401 lnk = createHostLink(host); 402 lnk = createHostLink(host);
402 if (lnk) { 403 if (lnk) {
403 network.links.push(lnk); 404 network.links.push(lnk);
405 + network.lookup[host.ingress] = lnk;
406 + network.lookup[host.egress] = lnk;
404 updateLinks(); 407 updateLinks();
405 } 408 }
406 network.force.start(); 409 network.force.start();
407 } 410 }
408 411
409 function showPath(data) { 412 function showPath(data) {
410 - network.view.alert(data.event + "\n" + data.payload.links.length); 413 + var links = data.payload.links,
414 + s = [ data.event + "\n" + links.length ];
415 + links.forEach(function (d, i) {
416 + s.push(d);
417 + });
418 + network.view.alert(s.join('\n'));
419 +
420 + links.forEach(function (d, i) {
421 + var link = network.lookup[d];
422 + if (link) {
423 + d3.select('#' + link.svgId).classed('showPath', true);
424 + }
425 + });
426 +
427 + // TODO: add selection-highlite lines to links
411 } 428 }
412 429
413 // ............................... 430 // ...............................
...@@ -415,7 +432,7 @@ ...@@ -415,7 +432,7 @@
415 function stillToImplement(data) { 432 function stillToImplement(data) {
416 var p = data.payload; 433 var p = data.payload;
417 note(data.event, p.id); 434 note(data.event, p.id);
418 - network.view.alert('Not yet implemented: "' + data.event + '"'); 435 + //network.view.alert('Not yet implemented: "' + data.event + '"');
419 } 436 }
420 437
421 function unknownEvent(data) { 438 function unknownEvent(data) {
...@@ -437,6 +454,7 @@ ...@@ -437,6 +454,7 @@
437 function createHostLink(host) { 454 function createHostLink(host) {
438 var src = host.id, 455 var src = host.id,
439 dst = host.cp.device, 456 dst = host.cp.device,
457 + id = host.id,
440 srcNode = network.lookup[src], 458 srcNode = network.lookup[src],
441 dstNode = network.lookup[dst], 459 dstNode = network.lookup[dst],
442 lnk; 460 lnk;
...@@ -449,7 +467,8 @@ ...@@ -449,7 +467,8 @@
449 } 467 }
450 468
451 lnk = { 469 lnk = {
452 - id: safeId(src) + '~' + safeId(dst), 470 + svgId: safeId(src) + '-' + safeId(dst),
471 + id: id,
453 source: srcNode, 472 source: srcNode,
454 target: dstNode, 473 target: dstNode,
455 class: 'link', 474 class: 'link',
...@@ -467,6 +486,7 @@ ...@@ -467,6 +486,7 @@
467 var type = link.type, 486 var type = link.type,
468 src = link.src, 487 src = link.src,
469 dst = link.dst, 488 dst = link.dst,
489 + id = link.id,
470 w = link.linkWidth, 490 w = link.linkWidth,
471 srcNode = network.lookup[src], 491 srcNode = network.lookup[src],
472 dstNode = network.lookup[dst], 492 dstNode = network.lookup[dst],
...@@ -480,7 +500,8 @@ ...@@ -480,7 +500,8 @@
480 } 500 }
481 501
482 lnk = { 502 lnk = {
483 - id: safeId(src) + '~' + safeId(dst), 503 + svgId: safeId(src) + '-' + safeId(dst),
504 + id: id,
484 source: srcNode, 505 source: srcNode,
485 target: dstNode, 506 target: dstNode,
486 class: 'link', 507 class: 'link',
...@@ -511,7 +532,7 @@ ...@@ -511,7 +532,7 @@
511 var entering = link.enter() 532 var entering = link.enter()
512 .append('line') 533 .append('line')
513 .attr({ 534 .attr({
514 - id: function (d) { return d.id; }, 535 + id: function (d) { return d.svgId; },
515 class: function (d) { return d.svgClass; }, 536 class: function (d) { return d.svgClass; },
516 x1: function (d) { return d.x1; }, 537 x1: function (d) { return d.x1; },
517 y1: function (d) { return d.y1; }, 538 y1: function (d) { return d.y1; },
...@@ -529,6 +550,19 @@ ...@@ -529,6 +550,19 @@
529 // augment links 550 // augment links
530 // TODO: add src/dst port labels etc. 551 // TODO: add src/dst port labels etc.
531 552
553 +
554 + // operate on both existing and new links, if necessary
555 + //link .foo() .bar() ...
556 +
557 + // operate on exiting links:
558 + // TODO: figure out how to remove the node 'g' AND its children
559 + link.exit()
560 + .transition()
561 + .duration(750)
562 + .attr({
563 + opacity: 0
564 + })
565 + .remove();
532 } 566 }
533 567
534 function createDeviceNode(device) { 568 function createDeviceNode(device) {
...@@ -756,7 +790,6 @@ ...@@ -756,7 +790,6 @@
756 webSock.ws = new WebSocket(webSockUrl()); 790 webSock.ws = new WebSocket(webSockUrl());
757 791
758 webSock.ws.onopen = function() { 792 webSock.ws.onopen = function() {
759 - webSock._send("Hi there!");
760 }; 793 };
761 794
762 webSock.ws.onmessage = function(m) { 795 webSock.ws.onmessage = function(m) {
...@@ -932,10 +965,12 @@ ...@@ -932,10 +965,12 @@
932 node = nodeG.selectAll('.node'); 965 node = nodeG.selectAll('.node');
933 966
934 function ldist(d) { 967 function ldist(d) {
935 - return fcfg.linkDistance[d.class] || 150; 968 + return 2 * 30;
969 + //return fcfg.linkDistance[d.class] || 150;
936 } 970 }
937 function lstrg(d) { 971 function lstrg(d) {
938 - return fcfg.linkStrength[d.class] || 1; 972 + return 2 * 0.6;
973 + //return fcfg.linkStrength[d.class] || 1;
939 } 974 }
940 function lchrg(d) { 975 function lchrg(d) {
941 return fcfg.charge[d.class] || -200; 976 return fcfg.charge[d.class] || -200;
...@@ -968,11 +1003,23 @@ ...@@ -968,11 +1003,23 @@
968 .size(forceDim) 1003 .size(forceDim)
969 .nodes(network.nodes) 1004 .nodes(network.nodes)
970 .links(network.links) 1005 .links(network.links)
971 - .charge(lchrg) 1006 + .gravity(0.3)
1007 + .charge(-15000)
1008 + .friction(0.1)
1009 + //.charge(lchrg)
972 .linkDistance(ldist) 1010 .linkDistance(ldist)
973 .linkStrength(lstrg) 1011 .linkStrength(lstrg)
974 .on('tick', tick); 1012 .on('tick', tick);
975 1013
1014 + // TVUE
1015 + //.gravity(0.3)
1016 + //.charge(-15000)
1017 + //.friction(0.1)
1018 + //.linkDistance(function(d) { return d.value * 30; })
1019 + //.linkStrength(function(d) { return d.value * 0.6; })
1020 + //.size([w, h])
1021 + //.start();
1022 +
976 network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd); 1023 network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd);
977 } 1024 }
978 1025
......