Added server-side code to create host-to-host intent. Still WIP.
Change-Id: I80626aa9ecb38802ddca4be3bce3def85ccfdb88
Showing
4 changed files
with
157 additions
and
34 deletions
... | @@ -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 | ... | ... |
-
Please register or login to post a comment