Committed by
Gerrit Code Review
ONOS-1479 -- GUI - augmenting topology view for extensibility: WIP.
Change-Id: I11820a9ff8f446c0d10a0311cee5ce448c15f402
Showing
22 changed files
with
816 additions
and
451 deletions
... | @@ -17,29 +17,22 @@ | ... | @@ -17,29 +17,22 @@ |
17 | 17 | ||
18 | package org.onosproject.ui.topo; | 18 | package org.onosproject.ui.topo; |
19 | 19 | ||
20 | +import com.google.common.base.MoreObjects; | ||
21 | + | ||
20 | /** | 22 | /** |
21 | - * Designates a descriptor for a button on the topology view panels. | 23 | + * Designates the identity of a button on the topology view panels. |
22 | */ | 24 | */ |
23 | -public class ButtonDescriptor { | 25 | +public class ButtonId { |
24 | 26 | ||
25 | private final String id; | 27 | private final String id; |
26 | - private final String glyphId; | ||
27 | - private final String tooltip; | ||
28 | 28 | ||
29 | /** | 29 | /** |
30 | - * Creates a button descriptor with the given identifier, glyph ID, and | 30 | + * Creates a button ID with the given identifier. |
31 | - * tooltip text. To reference a custom glyph defined in the overlay itself, | ||
32 | - * prefix its ID with an asterisk, (e.g. {@code "*myGlyph"}). Alternatively, | ||
33 | - * use one of the {@link TopoConstants.Glyphs predefined constant}. | ||
34 | * | 31 | * |
35 | * @param id identifier for the button | 32 | * @param id identifier for the button |
36 | - * @param glyphId identifier for the glyph | ||
37 | - * @param tooltip tooltip text | ||
38 | */ | 33 | */ |
39 | - public ButtonDescriptor(String id, String glyphId, String tooltip) { | 34 | + public ButtonId(String id) { |
40 | this.id = id; | 35 | this.id = id; |
41 | - this.glyphId = glyphId; | ||
42 | - this.tooltip = tooltip; | ||
43 | } | 36 | } |
44 | 37 | ||
45 | /** | 38 | /** |
... | @@ -51,22 +44,10 @@ public class ButtonDescriptor { | ... | @@ -51,22 +44,10 @@ public class ButtonDescriptor { |
51 | return id; | 44 | return id; |
52 | } | 45 | } |
53 | 46 | ||
54 | - /** | 47 | + @Override |
55 | - * Returns the glyph identifier for this button. | 48 | + public String toString() { |
56 | - * | 49 | + return MoreObjects.toStringHelper(getClass()) |
57 | - * @return glyph identifier | 50 | + .add("id", id()).toString(); |
58 | - */ | ||
59 | - public String glyphId() { | ||
60 | - return glyphId; | ||
61 | - } | ||
62 | - | ||
63 | - /** | ||
64 | - * Returns the tooltip text for this button. | ||
65 | - * | ||
66 | - * @return tooltip text | ||
67 | - */ | ||
68 | - public String tooltip() { | ||
69 | - return tooltip; | ||
70 | } | 51 | } |
71 | 52 | ||
72 | @Override | 53 | @Override |
... | @@ -78,9 +59,8 @@ public class ButtonDescriptor { | ... | @@ -78,9 +59,8 @@ public class ButtonDescriptor { |
78 | return false; | 59 | return false; |
79 | } | 60 | } |
80 | 61 | ||
81 | - ButtonDescriptor that = (ButtonDescriptor) o; | 62 | + ButtonId that = (ButtonId) o; |
82 | return id.equals(that.id); | 63 | return id.equals(that.id); |
83 | - | ||
84 | } | 64 | } |
85 | 65 | ||
86 | @Override | 66 | @Override | ... | ... |
... | @@ -35,7 +35,7 @@ public class PropertyPanel { | ... | @@ -35,7 +35,7 @@ public class PropertyPanel { |
35 | private String typeId; | 35 | private String typeId; |
36 | private String id; | 36 | private String id; |
37 | private List<Prop> properties = new ArrayList<>(); | 37 | private List<Prop> properties = new ArrayList<>(); |
38 | - private List<ButtonDescriptor> buttons = new ArrayList<>(); | 38 | + private List<ButtonId> buttons = new ArrayList<>(); |
39 | 39 | ||
40 | /** | 40 | /** |
41 | * Constructs a property panel model with the given title and | 41 | * Constructs a property panel model with the given title and |
... | @@ -181,7 +181,7 @@ public class PropertyPanel { | ... | @@ -181,7 +181,7 @@ public class PropertyPanel { |
181 | * @return the button list | 181 | * @return the button list |
182 | */ | 182 | */ |
183 | // TODO: consider protecting this? | 183 | // TODO: consider protecting this? |
184 | - public List<ButtonDescriptor> buttons() { | 184 | + public List<ButtonId> buttons() { |
185 | return buttons; | 185 | return buttons; |
186 | } | 186 | } |
187 | 187 | ||
... | @@ -243,7 +243,7 @@ public class PropertyPanel { | ... | @@ -243,7 +243,7 @@ public class PropertyPanel { |
243 | * @param button button descriptor | 243 | * @param button button descriptor |
244 | * @return self, for chaining | 244 | * @return self, for chaining |
245 | */ | 245 | */ |
246 | - public PropertyPanel addButton(ButtonDescriptor button) { | 246 | + public PropertyPanel addButton(ButtonId button) { |
247 | buttons.add(button); | 247 | buttons.add(button); |
248 | return this; | 248 | return this; |
249 | } | 249 | } |
... | @@ -254,10 +254,10 @@ public class PropertyPanel { | ... | @@ -254,10 +254,10 @@ public class PropertyPanel { |
254 | * @param descriptors descriptors to remove | 254 | * @param descriptors descriptors to remove |
255 | * @return self, for chaining | 255 | * @return self, for chaining |
256 | */ | 256 | */ |
257 | - public PropertyPanel removeButtons(ButtonDescriptor... descriptors) { | 257 | + public PropertyPanel removeButtons(ButtonId... descriptors) { |
258 | - Set<ButtonDescriptor> forRemoval = Sets.newHashSet(descriptors); | 258 | + Set<ButtonId> forRemoval = Sets.newHashSet(descriptors); |
259 | - List<ButtonDescriptor> toKeep = new ArrayList<>(); | 259 | + List<ButtonId> toKeep = new ArrayList<>(); |
260 | - for (ButtonDescriptor bd: buttons) { | 260 | + for (ButtonId bd: buttons) { |
261 | if (!forRemoval.contains(bd)) { | 261 | if (!forRemoval.contains(bd)) { |
262 | toKeep.add(bd); | 262 | toKeep.add(bd); |
263 | } | 263 | } | ... | ... |
... | @@ -108,30 +108,22 @@ public final class TopoConstants { | ... | @@ -108,30 +108,22 @@ public final class TopoConstants { |
108 | public static final String VLAN = "VLAN"; | 108 | public static final String VLAN = "VLAN"; |
109 | } | 109 | } |
110 | 110 | ||
111 | - private static final class CoreButton extends ButtonDescriptor { | ||
112 | - private CoreButton(String tag, String glyphId, boolean extra) { | ||
113 | - super("show" + tag + "View", | ||
114 | - glyphId, | ||
115 | - "Show " + tag + " View" + (extra ? " for this Device" : "")); | ||
116 | - } | ||
117 | - } | ||
118 | - | ||
119 | /** | 111 | /** |
120 | - * Defines constants for core buttons that appear on the topology | 112 | + * Defines identities of core buttons that appear on the topology |
121 | * details panel. | 113 | * details panel. |
122 | */ | 114 | */ |
123 | public static final class CoreButtons { | 115 | public static final class CoreButtons { |
124 | - public static final ButtonDescriptor SHOW_DEVICE_VIEW = | 116 | + public static final ButtonId SHOW_DEVICE_VIEW = |
125 | - new CoreButton("Device", Glyphs.SWITCH, false); | 117 | + new ButtonId("showDeviceView"); |
126 | 118 | ||
127 | - public static final ButtonDescriptor SHOW_FLOW_VIEW = | 119 | + public static final ButtonId SHOW_FLOW_VIEW = |
128 | - new CoreButton("Flow", Glyphs.FLOW_TABLE, true); | 120 | + new ButtonId("showFlowView"); |
129 | 121 | ||
130 | - public static final ButtonDescriptor SHOW_PORT_VIEW = | 122 | + public static final ButtonId SHOW_PORT_VIEW = |
131 | - new CoreButton("Port", Glyphs.PORT_TABLE, true); | 123 | + new ButtonId("showPortView"); |
132 | 124 | ||
133 | - public static final ButtonDescriptor SHOW_GROUP_VIEW = | 125 | + public static final ButtonId SHOW_GROUP_VIEW = |
134 | - new CoreButton("Group", Glyphs.GROUP_TABLE, true); | 126 | + new ButtonId("showGroupView"); |
135 | } | 127 | } |
136 | 128 | ||
137 | } | 129 | } | ... | ... |
... | @@ -19,27 +19,38 @@ package org.onosproject.ui.topo; | ... | @@ -19,27 +19,38 @@ package org.onosproject.ui.topo; |
19 | 19 | ||
20 | import org.junit.Test; | 20 | import org.junit.Test; |
21 | 21 | ||
22 | -import static org.junit.Assert.assertEquals; | 22 | +import static org.junit.Assert.assertFalse; |
23 | +import static org.junit.Assert.assertTrue; | ||
23 | 24 | ||
24 | /** | 25 | /** |
25 | - * Unit tests for {@link ButtonDescriptor}. | 26 | + * Unit tests for {@link ButtonId}. |
26 | */ | 27 | */ |
27 | -public class ButtonDescriptorTest { | 28 | +public class ButtonIdTest { |
28 | 29 | ||
29 | - private static final String ID = "my-id"; | 30 | + private static final String ID1 = "id-1"; |
30 | - private static final String GID = "my-glyphId"; | 31 | + private static final String ID2 = "id-2"; |
31 | - private static final String TT = "my-tewltyp"; | ||
32 | 32 | ||
33 | - private ButtonDescriptor bd; | 33 | + private ButtonId b1, b2; |
34 | 34 | ||
35 | 35 | ||
36 | @Test | 36 | @Test |
37 | public void basic() { | 37 | public void basic() { |
38 | - bd = new ButtonDescriptor(ID, GID, TT); | 38 | + b1 = new ButtonId(ID1); |
39 | + } | ||
39 | 40 | ||
40 | - assertEquals("bad id", ID, bd.id()); | 41 | + @Test |
41 | - assertEquals("bad gid", GID, bd.glyphId()); | 42 | + public void same() { |
42 | - assertEquals("bad tt", TT, bd.tooltip()); | 43 | + b1 = new ButtonId(ID1); |
44 | + b2 = new ButtonId(ID1); | ||
45 | + assertFalse("same ref?", b1 == b2); | ||
46 | + assertTrue("not equal?", b1.equals(b2)); | ||
43 | } | 47 | } |
44 | 48 | ||
49 | + @Test | ||
50 | + public void notSame() { | ||
51 | + b1 = new ButtonId(ID1); | ||
52 | + b2 = new ButtonId(ID2); | ||
53 | + assertFalse("same ref?", b1 == b2); | ||
54 | + assertFalse("equal?", b1.equals(b2)); | ||
55 | + } | ||
45 | } | 56 | } | ... | ... |
... | @@ -47,14 +47,6 @@ public class PropertyPanelTest { | ... | @@ -47,14 +47,6 @@ public class PropertyPanelTest { |
47 | private static final String VALUE_B = "Bee"; | 47 | private static final String VALUE_B = "Bee"; |
48 | private static final String VALUE_C = "Sea"; | 48 | private static final String VALUE_C = "Sea"; |
49 | private static final String VALUE_Z = "Zed"; | 49 | private static final String VALUE_Z = "Zed"; |
50 | - private static final String GID_A = "gid-A"; | ||
51 | - private static final String GID_B = "gid-B"; | ||
52 | - private static final String GID_C = "gid-C"; | ||
53 | - private static final String GID_Z = "gid-Z"; | ||
54 | - private static final String TT_A = "toolTip-A"; | ||
55 | - private static final String TT_B = "toolTip-B"; | ||
56 | - private static final String TT_C = "toolTip-C"; | ||
57 | - private static final String TT_Z = "toolTip-Z"; | ||
58 | 50 | ||
59 | private static final Map<String, Prop> PROP_MAP = new HashMap<>(); | 51 | private static final Map<String, Prop> PROP_MAP = new HashMap<>(); |
60 | 52 | ||
... | @@ -211,17 +203,13 @@ public class PropertyPanelTest { | ... | @@ -211,17 +203,13 @@ public class PropertyPanelTest { |
211 | validateProp(KEY_B, ">byyy<"); | 203 | validateProp(KEY_B, ">byyy<"); |
212 | } | 204 | } |
213 | 205 | ||
214 | - private static final ButtonDescriptor BD_A = | 206 | + private static final ButtonId BD_A = new ButtonId(KEY_A); |
215 | - new ButtonDescriptor(KEY_A, GID_A, TT_A); | 207 | + private static final ButtonId BD_B = new ButtonId(KEY_B); |
216 | - private static final ButtonDescriptor BD_B = | 208 | + private static final ButtonId BD_C = new ButtonId(KEY_C); |
217 | - new ButtonDescriptor(KEY_B, GID_B, TT_B); | 209 | + private static final ButtonId BD_Z = new ButtonId(KEY_Z); |
218 | - private static final ButtonDescriptor BD_C = | ||
219 | - new ButtonDescriptor(KEY_C, GID_C, TT_C); | ||
220 | - private static final ButtonDescriptor BD_Z = | ||
221 | - new ButtonDescriptor(KEY_Z, GID_Z, TT_Z); | ||
222 | 210 | ||
223 | private void verifyButtons(String... keys) { | 211 | private void verifyButtons(String... keys) { |
224 | - Iterator<ButtonDescriptor> iter = pp.buttons().iterator(); | 212 | + Iterator<ButtonId> iter = pp.buttons().iterator(); |
225 | for (String k: keys) { | 213 | for (String k: keys) { |
226 | assertEquals("wrong button", k, iter.next().id()); | 214 | assertEquals("wrong button", k, iter.next().id()); |
227 | } | 215 | } | ... | ... |
... | @@ -112,7 +112,10 @@ public abstract class AbstractAccumulator<T> implements Accumulator<T> { | ... | @@ -112,7 +112,10 @@ public abstract class AbstractAccumulator<T> implements Accumulator<T> { |
112 | if (isReady()) { | 112 | if (isReady()) { |
113 | try { | 113 | try { |
114 | maxTask = cancelIfActive(maxTask); | 114 | maxTask = cancelIfActive(maxTask); |
115 | - processItems(finalizeCurrentBatch()); | 115 | + List<T> items = finalizeCurrentBatch(); |
116 | + if (!items.isEmpty()) { | ||
117 | + processItems(items); | ||
118 | + } | ||
116 | } catch (Exception e) { | 119 | } catch (Exception e) { |
117 | log.warn("Unable to process batch due to {}", e); | 120 | log.warn("Unable to process batch due to {}", e); |
118 | } | 121 | } | ... | ... |
... | @@ -214,6 +214,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -214,6 +214,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
214 | new UpdateMeta(), | 214 | new UpdateMeta(), |
215 | new EqMasters(), | 215 | new EqMasters(), |
216 | 216 | ||
217 | + // TODO: implement "showHighlights" event (replaces "showTraffic") | ||
218 | + | ||
217 | // TODO: migrate traffic related to separate app | 219 | // TODO: migrate traffic related to separate app |
218 | new AddHostIntent(), | 220 | new AddHostIntent(), |
219 | new AddMultiSourceIntent(), | 221 | new AddMultiSourceIntent(), |
... | @@ -965,6 +967,14 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -965,6 +967,14 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
965 | 967 | ||
966 | @Override | 968 | @Override |
967 | public void processItems(List<Event> items) { | 969 | public void processItems(List<Event> items) { |
970 | + // Start-of-Debugging | ||
971 | + long now = System.currentTimeMillis(); | ||
972 | + String me = this.toString(); | ||
973 | + String miniMe = me.replaceAll("^.*@", "me@"); | ||
974 | + log.debug("Time: {}; this: {}, processing items ({} events)", | ||
975 | + now, miniMe, items.size()); | ||
976 | + // End-of-Debugging | ||
977 | + | ||
968 | try { | 978 | try { |
969 | if (summaryRunning) { | 979 | if (summaryRunning) { |
970 | msgSender.execute(() -> requestSummary(0)); | 980 | msgSender.execute(() -> requestSummary(0)); | ... | ... |
... | @@ -72,7 +72,7 @@ import org.onosproject.net.topology.TopologyService; | ... | @@ -72,7 +72,7 @@ import org.onosproject.net.topology.TopologyService; |
72 | import org.onosproject.ui.JsonUtils; | 72 | import org.onosproject.ui.JsonUtils; |
73 | import org.onosproject.ui.UiConnection; | 73 | import org.onosproject.ui.UiConnection; |
74 | import org.onosproject.ui.UiMessageHandler; | 74 | import org.onosproject.ui.UiMessageHandler; |
75 | -import org.onosproject.ui.topo.ButtonDescriptor; | 75 | +import org.onosproject.ui.topo.ButtonId; |
76 | import org.onosproject.ui.topo.PropertyPanel; | 76 | import org.onosproject.ui.topo.PropertyPanel; |
77 | import org.slf4j.Logger; | 77 | import org.slf4j.Logger; |
78 | import org.slf4j.LoggerFactory; | 78 | import org.slf4j.LoggerFactory; |
... | @@ -123,6 +123,8 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -123,6 +123,8 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
123 | new ProviderId("core", "org.onosproject.core", true); | 123 | new ProviderId("core", "org.onosproject.core", true); |
124 | private static final String COMPACT = "%s/%s-%s/%s"; | 124 | private static final String COMPACT = "%s/%s-%s/%s"; |
125 | 125 | ||
126 | + private static final String SHOW_HIGHLIGHTS = "showHighlights"; | ||
127 | + | ||
126 | private static final double KILO = 1024; | 128 | private static final double KILO = 1024; |
127 | private static final double MEGA = 1024 * KILO; | 129 | private static final double MEGA = 1024 * KILO; |
128 | private static final double GIGA = 1024 * MEGA; | 130 | private static final double GIGA = 1024 * MEGA; |
... | @@ -642,7 +644,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -642,7 +644,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
642 | labelsN.add(""); | 644 | labelsN.add(""); |
643 | } | 645 | } |
644 | } | 646 | } |
645 | - return JsonUtils.envelope("showTraffic", 0, payload); | 647 | + return JsonUtils.envelope(SHOW_HIGHLIGHTS, 0, payload); |
646 | } | 648 | } |
647 | 649 | ||
648 | private Load getLinkLoad(Link link) { | 650 | private Load getLinkLoad(Link link) { |
... | @@ -679,7 +681,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -679,7 +681,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
679 | addLinkFlows(link, paths, counts.get(link)); | 681 | addLinkFlows(link, paths, counts.get(link)); |
680 | } | 682 | } |
681 | } | 683 | } |
682 | - return JsonUtils.envelope("showTraffic", 0, payload); | 684 | + return JsonUtils.envelope(SHOW_HIGHLIGHTS, 0, payload); |
683 | } | 685 | } |
684 | 686 | ||
685 | private void addLinkFlows(Link link, ArrayNode paths, Integer count) { | 687 | private void addLinkFlows(Link link, ArrayNode paths, Integer count) { |
... | @@ -723,7 +725,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -723,7 +725,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
723 | ((ArrayNode) pathNode.path("labels")).add(hasTraffic ? formatBytes(biLink.bytes) : ""); | 725 | ((ArrayNode) pathNode.path("labels")).add(hasTraffic ? formatBytes(biLink.bytes) : ""); |
724 | } | 726 | } |
725 | 727 | ||
726 | - return JsonUtils.envelope("showTraffic", 0, payload); | 728 | + return JsonUtils.envelope(SHOW_HIGHLIGHTS, 0, payload); |
727 | } | 729 | } |
728 | 730 | ||
729 | // Classifies the link traffic according to the specified classes. | 731 | // Classifies the link traffic according to the specified classes. |
... | @@ -870,21 +872,13 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -870,21 +872,13 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
870 | result.set("props", pnode); | 872 | result.set("props", pnode); |
871 | 873 | ||
872 | ArrayNode buttons = arrayNode(); | 874 | ArrayNode buttons = arrayNode(); |
873 | - for (ButtonDescriptor b : pp.buttons()) { | 875 | + for (ButtonId b : pp.buttons()) { |
874 | - buttons.add(json(b)); | 876 | + buttons.add(b.id()); |
875 | } | 877 | } |
876 | result.set("buttons", buttons); | 878 | result.set("buttons", buttons); |
877 | return result; | 879 | return result; |
878 | } | 880 | } |
879 | 881 | ||
880 | - // translates the button descriptor into JSON | ||
881 | - private ObjectNode json(ButtonDescriptor bdesc) { | ||
882 | - return objectNode() | ||
883 | - .put("id", bdesc.id()) | ||
884 | - .put("gid", bdesc.glyphId()) | ||
885 | - .put("tt", bdesc.tooltip()); | ||
886 | - } | ||
887 | - | ||
888 | 882 | ||
889 | // Produces canonical link key, i.e. one that will match link and its inverse. | 883 | // Produces canonical link key, i.e. one that will match link and its inverse. |
890 | static LinkKey canonicalLinkKey(Link link) { | 884 | static LinkKey canonicalLinkKey(Link link) { | ... | ... |
... | @@ -18,6 +18,8 @@ | ... | @@ -18,6 +18,8 @@ |
18 | package org.onosproject.ui.impl; | 18 | package org.onosproject.ui.impl; |
19 | 19 | ||
20 | import org.onosproject.ui.UiTopoOverlay; | 20 | import org.onosproject.ui.UiTopoOverlay; |
21 | +import org.onosproject.ui.topo.ButtonId; | ||
22 | +import org.onosproject.ui.topo.PropertyPanel; | ||
21 | 23 | ||
22 | /** | 24 | /** |
23 | * Topology Overlay for network traffic. | 25 | * Topology Overlay for network traffic. |
... | @@ -25,12 +27,21 @@ import org.onosproject.ui.UiTopoOverlay; | ... | @@ -25,12 +27,21 @@ import org.onosproject.ui.UiTopoOverlay; |
25 | public class TrafficOverlay extends UiTopoOverlay { | 27 | public class TrafficOverlay extends UiTopoOverlay { |
26 | private static final String TRAFFIC_ID = "traffic"; | 28 | private static final String TRAFFIC_ID = "traffic"; |
27 | 29 | ||
28 | - /** | 30 | + private static final String SDF_ID = "showDeviceFlows"; |
29 | - * Constructs the traffic overlay. | 31 | + private static final String SRT_ID = "showRelatedTraffic"; |
30 | - */ | 32 | + |
33 | + private static final ButtonId SHOW_DEVICE_FLOWS = new ButtonId(SDF_ID); | ||
34 | + private static final ButtonId SHOW_RELATED_TRAFFIC = new ButtonId(SRT_ID); | ||
35 | + | ||
36 | + | ||
31 | public TrafficOverlay() { | 37 | public TrafficOverlay() { |
32 | super(TRAFFIC_ID); | 38 | super(TRAFFIC_ID); |
33 | } | 39 | } |
34 | 40 | ||
35 | - // TODO : override init(), activate(), deactivate(), destroy() | 41 | + @Override |
42 | + public void modifyDeviceDetails(PropertyPanel pp) { | ||
43 | + pp.addButton(SHOW_DEVICE_FLOWS) | ||
44 | + .addButton(SHOW_RELATED_TRAFFIC); | ||
45 | + } | ||
46 | + | ||
36 | } | 47 | } | ... | ... |
... | @@ -18,7 +18,7 @@ | ... | @@ -18,7 +18,7 @@ |
18 | package org.meowster.over; | 18 | package org.meowster.over; |
19 | 19 | ||
20 | import org.onosproject.ui.UiTopoOverlay; | 20 | import org.onosproject.ui.UiTopoOverlay; |
21 | -import org.onosproject.ui.topo.ButtonDescriptor; | 21 | +import org.onosproject.ui.topo.ButtonId; |
22 | import org.onosproject.ui.topo.PropertyPanel; | 22 | import org.onosproject.ui.topo.PropertyPanel; |
23 | import org.onosproject.ui.topo.TopoConstants.CoreButtons; | 23 | import org.onosproject.ui.topo.TopoConstants.CoreButtons; |
24 | import org.onosproject.ui.topo.TopoConstants.Glyphs; | 24 | import org.onosproject.ui.topo.TopoConstants.Glyphs; |
... | @@ -36,12 +36,8 @@ public class AppUiTopoOverlay extends UiTopoOverlay { | ... | @@ -36,12 +36,8 @@ public class AppUiTopoOverlay extends UiTopoOverlay { |
36 | private static final String MY_TITLE = "I changed the title"; | 36 | private static final String MY_TITLE = "I changed the title"; |
37 | private static final String MY_VERSION = "Beta-1.0.0042"; | 37 | private static final String MY_VERSION = "Beta-1.0.0042"; |
38 | 38 | ||
39 | - private static final ButtonDescriptor FOO_DESCRIPTOR = | 39 | + private static final ButtonId FOO_BUTTON = new ButtonId("foo"); |
40 | - new ButtonDescriptor("foo", "chain", "A FOO action"); | 40 | + private static final ButtonId BAR_BUTTON = new ButtonId("bar"); |
41 | - | ||
42 | - private static final ButtonDescriptor BAR_DESCRIPTOR = | ||
43 | - new ButtonDescriptor("bar", "*banner", "A BAR action"); | ||
44 | - | ||
45 | 41 | ||
46 | public AppUiTopoOverlay() { | 42 | public AppUiTopoOverlay() { |
47 | super(OVERLAY_ID); | 43 | super(OVERLAY_ID); |
... | @@ -68,8 +64,8 @@ public class AppUiTopoOverlay extends UiTopoOverlay { | ... | @@ -68,8 +64,8 @@ public class AppUiTopoOverlay extends UiTopoOverlay { |
68 | pp.title(MY_TITLE); | 64 | pp.title(MY_TITLE); |
69 | pp.removeProps(LATITUDE, LONGITUDE); | 65 | pp.removeProps(LATITUDE, LONGITUDE); |
70 | 66 | ||
71 | - pp.addButton(FOO_DESCRIPTOR) | 67 | + pp.addButton(FOO_BUTTON) |
72 | - .addButton(BAR_DESCRIPTOR); | 68 | + .addButton(BAR_BUTTON); |
73 | 69 | ||
74 | pp.removeButtons(CoreButtons.SHOW_PORT_VIEW) | 70 | pp.removeButtons(CoreButtons.SHOW_PORT_VIEW) |
75 | .removeButtons(CoreButtons.SHOW_GROUP_VIEW); | 71 | .removeButtons(CoreButtons.SHOW_GROUP_VIEW); | ... | ... |
... | @@ -3,7 +3,10 @@ | ... | @@ -3,7 +3,10 @@ |
3 | 'use strict'; | 3 | 'use strict'; |
4 | 4 | ||
5 | // injected refs | 5 | // injected refs |
6 | - var $log; | 6 | + var $log, tov; |
7 | + | ||
8 | + // internal state | ||
9 | + var someStateValue = true; | ||
7 | 10 | ||
8 | // our overlay definition | 11 | // our overlay definition |
9 | var overlay = { | 12 | var overlay = { |
... | @@ -27,40 +30,127 @@ | ... | @@ -27,40 +30,127 @@ |
27 | } | 30 | } |
28 | }, | 31 | }, |
29 | 32 | ||
30 | - activate: activateOverlay, | 33 | + activate: function () { |
31 | - deactivate: deactivateOverlay, | 34 | + $log.debug("sample topology overlay ACTIVATED"); |
35 | + }, | ||
36 | + deactivate: function () { | ||
37 | + $log.debug("sample topology overlay DEACTIVATED"); | ||
38 | + }, | ||
39 | + | ||
40 | + | ||
41 | + | ||
32 | 42 | ||
33 | - // button callbacks matching button identifiers | ||
34 | - buttonActions: { | ||
35 | - foo: fooCb, | ||
36 | - bar: barCb | ||
37 | - } | ||
38 | - }; | ||
39 | 43 | ||
40 | - function fooCb(data) { | 44 | + // detail panel button definitions |
41 | - $log.debug('FOO callback with data:', data); | 45 | + buttons: { |
46 | + foo: { | ||
47 | + gid: 'chain', | ||
48 | + tt: 'A FOO action', | ||
49 | + cb: function (data) { | ||
50 | + $log.debug('FOO action invoked with data:', data); | ||
42 | } | 51 | } |
52 | + }, | ||
53 | + bar: { | ||
54 | + gid: '*banner', | ||
55 | + tt: 'A BAR action', | ||
56 | + cb: function (data) { | ||
57 | + $log.debug('BAR action invoked with data:', data); | ||
58 | + } | ||
59 | + } | ||
60 | + }, | ||
43 | 61 | ||
44 | - function barCb(data) { | 62 | + // Key bindings for traffic overlay buttons |
45 | - $log.debug('BAR callback with data:', data); | 63 | + // NOTE: fully qual. button ID is derived from overlay-id and key-name |
64 | + keyBindings: { | ||
65 | + V: { | ||
66 | + cb: buttonCallback, | ||
67 | + tt: 'Uses the V key', | ||
68 | + gid: '*banner' | ||
69 | + }, | ||
70 | + F: { | ||
71 | + cb: buttonCallback, | ||
72 | + tt: 'Uses the F key', | ||
73 | + gid: 'chain' | ||
74 | + }, | ||
75 | + G: { | ||
76 | + cb: buttonCallback, | ||
77 | + tt: 'Uses the G key', | ||
78 | + gid: 'crown' | ||
79 | + }, | ||
80 | + | ||
81 | + T: { | ||
82 | + cb: buttonCallback, | ||
83 | + tt: 'Uses the T key', | ||
84 | + gid: 'switch' | ||
85 | + }, | ||
86 | + | ||
87 | + R: { | ||
88 | + cb: buttonCallback, | ||
89 | + tt: 'Uses the R key', | ||
90 | + gid: 'endstation' | ||
91 | + }, | ||
92 | + | ||
93 | + 0: { | ||
94 | + cb: buttonCallback, | ||
95 | + tt: 'Uses the ZERO key', | ||
96 | + gid: 'xMark' | ||
97 | + }, | ||
98 | + | ||
99 | + _keyOrder: [ | ||
100 | + '0', 'V', 'F', 'G', 'T', 'R' | ||
101 | + ] | ||
102 | + | ||
103 | + // NOTE: T and R should be rejected (not installed) | ||
104 | + // T is reserved for 'toggle Theme' | ||
105 | + // R is reserved for 'Reset pan and zoom' | ||
106 | + }, | ||
107 | + | ||
108 | + hooks: { | ||
109 | + // hook for handling escape key | ||
110 | + // Must return true to consume ESC, false otherwise. | ||
111 | + escape: cancelState, | ||
112 | + | ||
113 | + // hooks for when the selection changes... | ||
114 | + empty: function () { | ||
115 | + selectionCallback('empty'); | ||
116 | + }, | ||
117 | + single: function (data) { | ||
118 | + selectionCallback('single', data); | ||
119 | + }, | ||
120 | + multi: function (selectOrder) { | ||
121 | + selectionCallback('multi', selectOrder); | ||
122 | + tov.addDetailButton('foo'); | ||
123 | + tov.addDetailButton('bar'); | ||
124 | + } | ||
46 | } | 125 | } |
47 | 126 | ||
48 | - // === implementation of overlay API (essentially callbacks) | 127 | + }; |
49 | - function activateOverlay() { | 128 | + |
50 | - $log.debug("sample topology overlay ACTIVATED"); | 129 | + // invoked when the escape key is pressed |
130 | + function cancelState() { | ||
131 | + if (someStateValue) { | ||
132 | + someStateValue = false; | ||
133 | + // we consumed the ESC event | ||
134 | + return true; | ||
135 | + } | ||
136 | + return false; | ||
51 | } | 137 | } |
52 | 138 | ||
53 | - function deactivateOverlay() { | 139 | + function buttonCallback(x) { |
54 | - $log.debug("sample topology overlay DEACTIVATED"); | 140 | + $log.debug('Toolbar-button callback', x); |
55 | } | 141 | } |
56 | 142 | ||
143 | + function selectionCallback(x, d) { | ||
144 | + $log.debug('Selection callback', x, d); | ||
145 | + } | ||
57 | 146 | ||
58 | // invoke code to register with the overlay service | 147 | // invoke code to register with the overlay service |
59 | angular.module('ovSample') | 148 | angular.module('ovSample') |
60 | .run(['$log', 'TopoOverlayService', | 149 | .run(['$log', 'TopoOverlayService', |
61 | 150 | ||
62 | - function (_$log_, tov) { | 151 | + function (_$log_, _tov_) { |
63 | $log = _$log_; | 152 | $log = _$log_; |
153 | + tov = _tov_; | ||
64 | tov.register(overlay); | 154 | tov.register(overlay); |
65 | }]); | 155 | }]); |
66 | 156 | ... | ... |
... | @@ -45,6 +45,10 @@ | ... | @@ -45,6 +45,10 @@ |
45 | "C429.9,285.5,426.7,293.2,427.7,300.4z" | 45 | "C429.9,285.5,426.7,293.2,427.7,300.4z" |
46 | }, | 46 | }, |
47 | 47 | ||
48 | + // TODO: ONOS-2566 glyphs for device types: | ||
49 | + // otn, roadm_otn, firewall, balancer, ips, ids, | ||
50 | + // controller, virtual, fiber_switch, other | ||
51 | + | ||
48 | glyphDataSet = { | 52 | glyphDataSet = { |
49 | _viewbox: "0 0 110 110", | 53 | _viewbox: "0 0 110 110", |
50 | 54 | ... | ... |
... | @@ -18,6 +18,7 @@ | ... | @@ -18,6 +18,7 @@ |
18 | ONOS GUI -- Widget -- Toolbar Service | 18 | ONOS GUI -- Widget -- Toolbar Service |
19 | */ | 19 | */ |
20 | // TODO: Augment service to allow toolbars to exist on right edge of screen | 20 | // TODO: Augment service to allow toolbars to exist on right edge of screen |
21 | +// TODO: also - make toolbar more object aware (rows etc.) | ||
21 | 22 | ||
22 | 23 | ||
23 | (function () { | 24 | (function () { |
... | @@ -80,6 +81,7 @@ | ... | @@ -80,6 +81,7 @@ |
80 | panel = ps.createPanel(tbid, settings), | 81 | panel = ps.createPanel(tbid, settings), |
81 | arrowDiv = createArrow(panel), | 82 | arrowDiv = createArrow(panel), |
82 | currentRow = panel.append('div').classed('tbar-row', true), | 83 | currentRow = panel.append('div').classed('tbar-row', true), |
84 | + rowButtonIds = [], // for removable buttons | ||
83 | tbWidth = arrowSize + 2, // empty toolbar width | 85 | tbWidth = arrowSize + 2, // empty toolbar width |
84 | maxWidth = panel.width(); | 86 | maxWidth = panel.width(); |
85 | 87 | ||
... | @@ -162,7 +164,41 @@ | ... | @@ -162,7 +164,41 @@ |
162 | } else { | 164 | } else { |
163 | panel.append('br'); | 165 | panel.append('br'); |
164 | currentRow = panel.append('div').classed('tbar-row', true); | 166 | currentRow = panel.append('div').classed('tbar-row', true); |
167 | + | ||
168 | + // return API to allow caller more access to the row | ||
169 | + return { | ||
170 | + clear: rowClear, | ||
171 | + setText: rowSetText, | ||
172 | + addButton: rowAddButton, | ||
173 | + classed: rowClassed | ||
174 | + }; | ||
175 | + } | ||
176 | + } | ||
177 | + | ||
178 | + function rowClear() { | ||
179 | + currentRow.selectAll('*').remove(); | ||
180 | + rowButtonIds.forEach(function (bid) { | ||
181 | + delete items[bid]; | ||
182 | + }); | ||
183 | + rowButtonIds = []; | ||
165 | } | 184 | } |
185 | + | ||
186 | + // installs a div with text into the button row | ||
187 | + function rowSetText(text) { | ||
188 | + rowClear(); | ||
189 | + currentRow.append('div').classed('tbar-row-text', true) | ||
190 | + .html(text); | ||
191 | + } | ||
192 | + | ||
193 | + function rowAddButton(id, gid, cb, tooltip) { | ||
194 | + var b = addButton(id, gid, cb, tooltip); | ||
195 | + if (b) { | ||
196 | + rowButtonIds.push(id); | ||
197 | + } | ||
198 | + } | ||
199 | + | ||
200 | + function rowClassed(classes, bool) { | ||
201 | + currentRow.classed(classes, bool); | ||
166 | } | 202 | } |
167 | 203 | ||
168 | function show(cb) { | 204 | function show(cb) { | ... | ... |
... | @@ -305,6 +305,21 @@ html[data-platform='iPad'] #topo-p-detail { | ... | @@ -305,6 +305,21 @@ html[data-platform='iPad'] #topo-p-detail { |
305 | filter: url("data:image/svg+xml;utf8, <svg xmlns = \'http://www.w3.org/2000/svg\'><filter x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\" id=\"yellow-glow\"><feColorMatrix type=\"matrix\" values=\"0 0 0 0 1.0 0 0 0 0 1.0 0 0 0 0 0.3 0 0 0 1 0 \"></feColorMatrix><feGaussianBlur stdDeviation=\"3\" result=\"coloredBlur\"></feGaussianBlur><feMerge><feMergeNode in=\"coloredBlur\"></feMergeNode><feMergeNode in=\"SourceGraphic\"></feMergeNode></feMerge></filter></svg>#yellow-glow"); | 305 | filter: url("data:image/svg+xml;utf8, <svg xmlns = \'http://www.w3.org/2000/svg\'><filter x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\" id=\"yellow-glow\"><feColorMatrix type=\"matrix\" values=\"0 0 0 0 1.0 0 0 0 0 1.0 0 0 0 0 0.3 0 0 0 1 0 \"></feColorMatrix><feGaussianBlur stdDeviation=\"3\" result=\"coloredBlur\"></feGaussianBlur><feMerge><feMergeNode in=\"coloredBlur\"></feMergeNode><feMergeNode in=\"SourceGraphic\"></feMergeNode></feMerge></filter></svg>#yellow-glow"); |
306 | } | 306 | } |
307 | 307 | ||
308 | + | ||
309 | +/* --- Toolbar --- */ | ||
310 | + | ||
311 | +#toolbar-topo-tbar .tbar-row.right { | ||
312 | + width: 100%; | ||
313 | +} | ||
314 | + | ||
315 | +#toolbar-topo-tbar .tbar-row-text { | ||
316 | + height: 21px; | ||
317 | + text-align: right; | ||
318 | + padding: 8px 60px 0 0; | ||
319 | + font-style: italic; | ||
320 | +} | ||
321 | + | ||
322 | + | ||
308 | /* --- Topo Nodes --- */ | 323 | /* --- Topo Nodes --- */ |
309 | 324 | ||
310 | #ov-topo svg .suppressed { | 325 | #ov-topo svg .suppressed { | ... | ... |
... | @@ -40,7 +40,7 @@ | ... | @@ -40,7 +40,7 @@ |
40 | 40 | ||
41 | // --- Short Cut Keys ------------------------------------------------ | 41 | // --- Short Cut Keys ------------------------------------------------ |
42 | 42 | ||
43 | - function setUpKeys() { | 43 | + function setUpKeys(overlayKeys) { |
44 | // key bindings need to be made after the services have been injected | 44 | // key bindings need to be made after the services have been injected |
45 | // thus, deferred to here... | 45 | // thus, deferred to here... |
46 | actionMap = { | 46 | actionMap = { |
... | @@ -63,14 +63,6 @@ | ... | @@ -63,14 +63,6 @@ |
63 | R: [resetZoom, 'Reset pan / zoom'], | 63 | R: [resetZoom, 'Reset pan / zoom'], |
64 | dot: [ttbs.toggleToolbar, 'Toggle Toolbar'], | 64 | dot: [ttbs.toggleToolbar, 'Toggle Toolbar'], |
65 | 65 | ||
66 | - V: [tts.showRelatedIntentsAction, 'Show all related intents'], | ||
67 | - rightArrow: [tts.showNextIntentAction, 'Show next related intent'], | ||
68 | - leftArrow: [tts.showPrevIntentAction, 'Show previous related intent'], | ||
69 | - W: [tts.showSelectedIntentTrafficAction, 'Monitor traffic of selected intent'], | ||
70 | - A: [tts.showAllFlowTrafficAction, 'Monitor all traffic using flow stats'], | ||
71 | - Q: [tts.showAllPortTrafficAction, 'Monitor all traffic using port stats'], | ||
72 | - F: [tts.showDeviceLinkFlowsAction, 'Show device link flows'], | ||
73 | - | ||
74 | E: [equalizeMasters, 'Equalize mastership roles'], | 66 | E: [equalizeMasters, 'Equalize mastership roles'], |
75 | 67 | ||
76 | esc: handleEscape, | 68 | esc: handleEscape, |
... | @@ -78,12 +70,16 @@ | ... | @@ -78,12 +70,16 @@ |
78 | _keyListener: ttbs.keyListener, | 70 | _keyListener: ttbs.keyListener, |
79 | 71 | ||
80 | _helpFormat: [ | 72 | _helpFormat: [ |
81 | - ['I', 'O', 'D', '-', 'H', 'M', 'P', 'dash', 'B' ], | 73 | + ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B', 'S' ], |
82 | - ['X', 'Z', 'N', 'L', 'U', 'R', '-', 'dot'], | 74 | + ['X', 'Z', 'N', 'L', 'U', 'R', '-', 'E', '-', 'dot'], |
83 | - ['V', 'rightArrow', 'leftArrow', 'W', 'A', 'F', '-', 'E' ] | 75 | + [] // this column reserved for overlay actions |
84 | ] | 76 | ] |
85 | }; | 77 | }; |
86 | 78 | ||
79 | + if (fs.isO(overlayKeys)) { | ||
80 | + mergeKeys(overlayKeys); | ||
81 | + } | ||
82 | + | ||
87 | ks.keyBindings(actionMap); | 83 | ks.keyBindings(actionMap); |
88 | 84 | ||
89 | ks.gestureNotes([ | 85 | ks.gestureNotes([ |
... | @@ -95,6 +91,22 @@ | ... | @@ -95,6 +91,22 @@ |
95 | ]); | 91 | ]); |
96 | } | 92 | } |
97 | 93 | ||
94 | + // when a topology overlay is activated, we need to bind their keystrokes | ||
95 | + // and include them in the quick-help panel | ||
96 | + function mergeKeys(extra) { | ||
97 | + var _hf = actionMap._helpFormat[2]; | ||
98 | + extra._keyOrder.forEach(function (k) { | ||
99 | + var d = extra[k], | ||
100 | + cb = d && d.cb, | ||
101 | + tt = d && d.tt; | ||
102 | + // NOTE: ignore keys that are already defined | ||
103 | + if (d && !actionMap[k]) { | ||
104 | + actionMap[k] = [cb, tt]; | ||
105 | + _hf.push(k); | ||
106 | + } | ||
107 | + }); | ||
108 | + } | ||
109 | + | ||
98 | // --- Keystroke functions ------------------------------------------- | 110 | // --- Keystroke functions ------------------------------------------- |
99 | 111 | ||
100 | function toggleInstances(x) { | 112 | function toggleInstances(x) { |
... | @@ -153,6 +165,10 @@ | ... | @@ -153,6 +165,10 @@ |
153 | // if an instance is selected, cancel the affinity mapping | 165 | // if an instance is selected, cancel the affinity mapping |
154 | tis.cancelAffinity() | 166 | tis.cancelAffinity() |
155 | 167 | ||
168 | + } else if (tov.hooks.escape()) { | ||
169 | + // else if the overlay consumed the ESC event... | ||
170 | + // (work already done) | ||
171 | + | ||
156 | } else if (tss.deselectAll()) { | 172 | } else if (tss.deselectAll()) { |
157 | // else if we have node selections, deselect them all | 173 | // else if we have node selections, deselect them all |
158 | // (work already done) | 174 | // (work already done) |
... | @@ -169,19 +185,15 @@ | ... | @@ -169,19 +185,15 @@ |
169 | } else if (tps.summaryVisible()) { | 185 | } else if (tps.summaryVisible()) { |
170 | // else if the Summary Panel is visible, hide it | 186 | // else if the Summary Panel is visible, hide it |
171 | tps.hideSummaryPanel(); | 187 | tps.hideSummaryPanel(); |
172 | - | ||
173 | - } else { | ||
174 | - // TODO: set hover mode to hoverModeNone | ||
175 | - // talk to Thomas about this: shouldn't it be done | ||
176 | - // when we deselect the node (if tss.haveDetails()...) | ||
177 | } | 188 | } |
178 | } | 189 | } |
179 | 190 | ||
180 | // --- Toolbar Functions --------------------------------------------- | 191 | // --- Toolbar Functions --------------------------------------------- |
181 | 192 | ||
182 | function notValid(what) { | 193 | function notValid(what) { |
183 | - $log.warn('Topo.js getActionEntry(): Not a valid ' + what); | 194 | + $log.warn('topo.js getActionEntry(): Not a valid ' + what); |
184 | } | 195 | } |
196 | + | ||
185 | function getActionEntry(key) { | 197 | function getActionEntry(key) { |
186 | var entry; | 198 | var entry; |
187 | 199 | ||
... | @@ -201,7 +213,8 @@ | ... | @@ -201,7 +213,8 @@ |
201 | 213 | ||
202 | function setUpToolbar() { | 214 | function setUpToolbar() { |
203 | ttbs.init({ | 215 | ttbs.init({ |
204 | - getActionEntry: getActionEntry | 216 | + getActionEntry: getActionEntry, |
217 | + setUpKeys: setUpKeys | ||
205 | }); | 218 | }); |
206 | ttbs.createToolbar(); | 219 | ttbs.createToolbar(); |
207 | } | 220 | } |
... | @@ -503,7 +516,6 @@ | ... | @@ -503,7 +516,6 @@ |
503 | restoreConfigFromPrefs(); | 516 | restoreConfigFromPrefs(); |
504 | 517 | ||
505 | $log.debug('registered overlays...', tov.list()); | 518 | $log.debug('registered overlays...', tov.list()); |
506 | - | ||
507 | $log.log('OvTopoCtrl has been created'); | 519 | $log.log('OvTopoCtrl has been created'); |
508 | }]); | 520 | }]); |
509 | }()); | 521 | }()); | ... | ... |
... | @@ -27,14 +27,14 @@ | ... | @@ -27,14 +27,14 @@ |
27 | 'use strict'; | 27 | 'use strict'; |
28 | 28 | ||
29 | // injected refs | 29 | // injected refs |
30 | - var $log, $interval, wss, tps, tis, tfs, tss, tts, tspr; | 30 | + var $log, $interval, wss, tps, tis, tfs, tss, tov, tspr; |
31 | 31 | ||
32 | // internal state | 32 | // internal state |
33 | var handlerMap, | 33 | var handlerMap, |
34 | openListener, | 34 | openListener, |
35 | heartbeatTimer; | 35 | heartbeatTimer; |
36 | 36 | ||
37 | - var heartbeatPeriod = 5000; // 5 seconds | 37 | + var heartbeatPeriod = 9000; // 9 seconds |
38 | 38 | ||
39 | // ========================== | 39 | // ========================== |
40 | 40 | ||
... | @@ -44,7 +44,7 @@ | ... | @@ -44,7 +44,7 @@ |
44 | 44 | ||
45 | showDetails: tss, | 45 | showDetails: tss, |
46 | 46 | ||
47 | - showTraffic: tts, | 47 | + showHighlights: tov, |
48 | 48 | ||
49 | addInstance: tis, | 49 | addInstance: tis, |
50 | updateInstance: tis, | 50 | updateInstance: tis, |
... | @@ -90,10 +90,10 @@ | ... | @@ -90,10 +90,10 @@ |
90 | .factory('TopoEventService', | 90 | .factory('TopoEventService', |
91 | ['$log', '$interval', 'WebSocketService', | 91 | ['$log', '$interval', 'WebSocketService', |
92 | 'TopoPanelService', 'TopoInstService', 'TopoForceService', | 92 | 'TopoPanelService', 'TopoInstService', 'TopoForceService', |
93 | - 'TopoSelectService', 'TopoTrafficService', 'TopoSpriteService', | 93 | + 'TopoSelectService', 'TopoOverlayService', 'TopoSpriteService', |
94 | 94 | ||
95 | function (_$log_, _$interval_, _wss_, | 95 | function (_$log_, _$interval_, _wss_, |
96 | - _tps_, _tis_, _tfs_, _tss_, _tts_, _tspr_) { | 96 | + _tps_, _tis_, _tfs_, _tss_, _tov_, _tspr_) { |
97 | $log = _$log_; | 97 | $log = _$log_; |
98 | $interval = _$interval_; | 98 | $interval = _$interval_; |
99 | wss = _wss_; | 99 | wss = _wss_; |
... | @@ -101,7 +101,7 @@ | ... | @@ -101,7 +101,7 @@ |
101 | tis = _tis_; | 101 | tis = _tis_; |
102 | tfs = _tfs_; | 102 | tfs = _tfs_; |
103 | tss = _tss_; | 103 | tss = _tss_; |
104 | - tts = _tts_; | 104 | + tov = _tov_; |
105 | tspr = _tspr_; | 105 | tspr = _tspr_; |
106 | 106 | ||
107 | createHandlerMap(); | 107 | createHandlerMap(); | ... | ... |
... | @@ -23,7 +23,7 @@ | ... | @@ -23,7 +23,7 @@ |
23 | 'use strict'; | 23 | 'use strict'; |
24 | 24 | ||
25 | // injected refs | 25 | // injected refs |
26 | - var $log, $timeout, fs, sus, is, ts, flash, wss, | 26 | + var $log, $timeout, fs, sus, ts, flash, wss, tov, |
27 | tis, tms, td3, tss, tts, tos, fltr, tls, uplink, svg; | 27 | tis, tms, td3, tss, tts, tos, fltr, tls, uplink, svg; |
28 | 28 | ||
29 | // configuration | 29 | // configuration |
... | @@ -797,9 +797,10 @@ | ... | @@ -797,9 +797,10 @@ |
797 | return true; | 797 | return true; |
798 | } | 798 | } |
799 | 799 | ||
800 | - // ========================== | 800 | + // ============================================= |
801 | - // function entry points for traffic module | 801 | + // function entry points for overlay module |
802 | 802 | ||
803 | + // TODO: find an automatic way of tracking via the "showHighlights" events | ||
803 | var allTrafficClasses = 'primary secondary optical animated ' + | 804 | var allTrafficClasses = 'primary secondary optical animated ' + |
804 | 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' + | 805 | 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' + |
805 | 'port-traffic-Gbps-choked'; | 806 | 'port-traffic-Gbps-choked'; |
... | @@ -845,7 +846,7 @@ | ... | @@ -845,7 +846,7 @@ |
845 | }; | 846 | }; |
846 | } | 847 | } |
847 | 848 | ||
848 | - function mkD3Api(uplink) { | 849 | + function mkD3Api() { |
849 | return { | 850 | return { |
850 | node: function () { return node; }, | 851 | node: function () { return node; }, |
851 | link: function () { return link; }, | 852 | link: function () { return link; }, |
... | @@ -859,7 +860,7 @@ | ... | @@ -859,7 +860,7 @@ |
859 | }; | 860 | }; |
860 | } | 861 | } |
861 | 862 | ||
862 | - function mkSelectApi(uplink) { | 863 | + function mkSelectApi() { |
863 | return { | 864 | return { |
864 | node: function () { return node; }, | 865 | node: function () { return node; }, |
865 | zoomingOrPanning: zoomingOrPanning, | 866 | zoomingOrPanning: zoomingOrPanning, |
... | @@ -868,15 +869,20 @@ | ... | @@ -868,15 +869,20 @@ |
868 | }; | 869 | }; |
869 | } | 870 | } |
870 | 871 | ||
871 | - function mkTrafficApi(uplink) { | 872 | + function mkTrafficApi() { |
873 | + return { | ||
874 | + hovered: tss.hovered, | ||
875 | + somethingSelected: tss.somethingSelected, | ||
876 | + selectOrder: tss.selectOrder | ||
877 | + }; | ||
878 | + } | ||
879 | + | ||
880 | + function mkOverlayApi() { | ||
872 | return { | 881 | return { |
873 | clearLinkTrafficStyle: clearLinkTrafficStyle, | 882 | clearLinkTrafficStyle: clearLinkTrafficStyle, |
874 | removeLinkLabels: removeLinkLabels, | 883 | removeLinkLabels: removeLinkLabels, |
875 | updateLinks: updateLinks, | 884 | updateLinks: updateLinks, |
876 | - findLinkById: tms.findLinkById, | 885 | + findLinkById: tms.findLinkById |
877 | - hovered: tss.hovered, | ||
878 | - validateSelectionContext: tss.validateSelectionContext, | ||
879 | - selectOrder: tss.selectOrder | ||
880 | }; | 886 | }; |
881 | } | 887 | } |
882 | 888 | ||
... | @@ -904,7 +910,7 @@ | ... | @@ -904,7 +910,7 @@ |
904 | }; | 910 | }; |
905 | } | 911 | } |
906 | 912 | ||
907 | - function mkFilterApi(uplink) { | 913 | + function mkFilterApi() { |
908 | return { | 914 | return { |
909 | node: function () { return node; }, | 915 | node: function () { return node; }, |
910 | link: function () { return link; } | 916 | link: function () { return link; } |
... | @@ -925,11 +931,11 @@ | ... | @@ -925,11 +931,11 @@ |
925 | .factory('TopoForceService', | 931 | .factory('TopoForceService', |
926 | ['$log', '$timeout', 'FnService', 'SvgUtilService', | 932 | ['$log', '$timeout', 'FnService', 'SvgUtilService', |
927 | 'ThemeService', 'FlashService', 'WebSocketService', | 933 | 'ThemeService', 'FlashService', 'WebSocketService', |
928 | - 'TopoInstService', 'TopoModelService', | 934 | + 'TopoOverlayService', 'TopoInstService', 'TopoModelService', |
929 | 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService', | 935 | 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService', |
930 | 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService', | 936 | 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService', |
931 | 937 | ||
932 | - function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, | 938 | + function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_, |
933 | _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) { | 939 | _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) { |
934 | $log = _$log_; | 940 | $log = _$log_; |
935 | $timeout = _$timeout_; | 941 | $timeout = _$timeout_; |
... | @@ -938,6 +944,7 @@ | ... | @@ -938,6 +944,7 @@ |
938 | ts = _ts_; | 944 | ts = _ts_; |
939 | flash = _flash_; | 945 | flash = _flash_; |
940 | wss = _wss_; | 946 | wss = _wss_; |
947 | + tov = _tov_; | ||
941 | tis = _tis_; | 948 | tis = _tis_; |
942 | tms = _tms_; | 949 | tms = _tms_; |
943 | td3 = _td3_; | 950 | td3 = _td3_; |
... | @@ -966,12 +973,13 @@ | ... | @@ -966,12 +973,13 @@ |
966 | 973 | ||
967 | $log.debug('initForce().. dim = ' + dim); | 974 | $log.debug('initForce().. dim = ' + dim); |
968 | 975 | ||
976 | + tov.setApi(mkOverlayApi(), tss); | ||
969 | tms.initModel(mkModelApi(uplink), dim); | 977 | tms.initModel(mkModelApi(uplink), dim); |
970 | - td3.initD3(mkD3Api(uplink)); | 978 | + td3.initD3(mkD3Api()); |
971 | - tss.initSelect(mkSelectApi(uplink)); | 979 | + tss.initSelect(mkSelectApi()); |
972 | - tts.initTraffic(mkTrafficApi(uplink)); | 980 | + tts.initTraffic(mkTrafficApi()); |
973 | tos.initOblique(mkObliqueApi(uplink, fltr)); | 981 | tos.initOblique(mkObliqueApi(uplink, fltr)); |
974 | - fltr.initFilter(mkFilterApi(uplink)); | 982 | + fltr.initFilter(mkFilterApi()); |
975 | tls.initLink(mkLinkApi(svg, uplink), td3); | 983 | tls.initLink(mkLinkApi(svg, uplink), td3); |
976 | 984 | ||
977 | settings = angular.extend({}, defaultSettings, opts); | 985 | settings = angular.extend({}, defaultSettings, opts); |
... | @@ -1016,6 +1024,7 @@ | ... | @@ -1016,6 +1024,7 @@ |
1016 | tss.destroySelect(); | 1024 | tss.destroySelect(); |
1017 | td3.destroyD3(); | 1025 | td3.destroyD3(); |
1018 | tms.destroyModel(); | 1026 | tms.destroyModel(); |
1027 | + // note: no need to destroy overlay service | ||
1019 | ts.removeListener(themeListener); | 1028 | ts.removeListener(themeListener); |
1020 | themeListener = null; | 1029 | themeListener = null; |
1021 | 1030 | ... | ... |
... | @@ -30,7 +30,7 @@ | ... | @@ -30,7 +30,7 @@ |
30 | var tos = 'TopoOverlayService: '; | 30 | var tos = 'TopoOverlayService: '; |
31 | 31 | ||
32 | // injected refs | 32 | // injected refs |
33 | - var $log, fs, gs, wss, ns; | 33 | + var $log, fs, gs, wss, ns, tss, tps, api; |
34 | 34 | ||
35 | // internal state | 35 | // internal state |
36 | var overlays = {}, | 36 | var overlays = {}, |
... | @@ -80,6 +80,7 @@ | ... | @@ -80,6 +80,7 @@ |
80 | function register(overlay) { | 80 | function register(overlay) { |
81 | var r = 'register', | 81 | var r = 'register', |
82 | over = fs.isO(overlay), | 82 | over = fs.isO(overlay), |
83 | + kb = over ? fs.isO(overlay.keyBindings) : null, | ||
83 | id = over ? over.overlayId : ''; | 84 | id = over ? over.overlayId : ''; |
84 | 85 | ||
85 | if (!id) { | 86 | if (!id) { |
... | @@ -90,11 +91,26 @@ | ... | @@ -90,11 +91,26 @@ |
90 | } | 91 | } |
91 | overlays[id] = overlay; | 92 | overlays[id] = overlay; |
92 | handleGlyphs(overlay); | 93 | handleGlyphs(overlay); |
94 | + | ||
95 | + if (kb) { | ||
96 | + if (!fs.isA(kb._keyOrder)) { | ||
97 | + warn(r, 'no _keyOrder array defined on keyBindings'); | ||
98 | + } else { | ||
99 | + kb._keyOrder.forEach(function (k) { | ||
100 | + if (k !== '-' && !kb[k]) { | ||
101 | + warn(r, 'no "' + k + '" property defined on keyBindings'); | ||
102 | + } | ||
103 | + }); | ||
104 | + } | ||
105 | + } | ||
106 | + | ||
93 | $log.debug(tos + 'registered overlay: ' + id, overlay); | 107 | $log.debug(tos + 'registered overlay: ' + id, overlay); |
94 | } | 108 | } |
95 | 109 | ||
110 | + // TODO: remove this redundant code....... | ||
96 | // NOTE: unregister needs to be called if an app is ever | 111 | // NOTE: unregister needs to be called if an app is ever |
97 | // deactivated/uninstalled via the applications view | 112 | // deactivated/uninstalled via the applications view |
113 | +/* | ||
98 | function unregister(overlay) { | 114 | function unregister(overlay) { |
99 | var u = 'unregister', | 115 | var u = 'unregister', |
100 | over = fs.isO(overlay), | 116 | over = fs.isO(overlay), |
... | @@ -108,21 +124,33 @@ | ... | @@ -108,21 +124,33 @@ |
108 | } | 124 | } |
109 | delete overlays[id]; | 125 | delete overlays[id]; |
110 | $log.debug(tos + 'unregistered overlay: ' + id); | 126 | $log.debug(tos + 'unregistered overlay: ' + id); |
111 | - // TODO: rebuild the toolbar overlay radio button set | ||
112 | } | 127 | } |
128 | +*/ | ||
113 | 129 | ||
130 | + | ||
131 | + // returns the list of overlay identifiers | ||
114 | function list() { | 132 | function list() { |
115 | return d3.map(overlays).keys(); | 133 | return d3.map(overlays).keys(); |
116 | } | 134 | } |
117 | 135 | ||
118 | - function overlay(id) { | 136 | + // add a radio button for each registered overlay |
119 | - return overlays[id]; | 137 | + function augmentRbset(rset, switchFn) { |
138 | + angular.forEach(overlays, function (ov) { | ||
139 | + rset.push({ | ||
140 | + gid: ov._glyphId, | ||
141 | + tooltip: (ov.tooltip || '(no tooltip)'), | ||
142 | + cb: function () { | ||
143 | + tbSelection(ov.overlayId, switchFn); | ||
144 | + } | ||
145 | + }); | ||
146 | + }); | ||
120 | } | 147 | } |
121 | 148 | ||
122 | // an overlay was selected via toolbar radio button press from user | 149 | // an overlay was selected via toolbar radio button press from user |
123 | - function tbSelection(id) { | 150 | + function tbSelection(id, switchFn) { |
124 | var same = current && current.overlayId === id, | 151 | var same = current && current.overlayId === id, |
125 | - payload = {}; | 152 | + payload = {}, |
153 | + actions; | ||
126 | 154 | ||
127 | function doop(op) { | 155 | function doop(op) { |
128 | var oid = current.overlayId; | 156 | var oid = current.overlayId; |
... | @@ -133,70 +161,211 @@ | ... | @@ -133,70 +161,211 @@ |
133 | 161 | ||
134 | if (!same) { | 162 | if (!same) { |
135 | current && doop('deactivate'); | 163 | current && doop('deactivate'); |
136 | - current = overlay(id); | 164 | + current = overlays[id]; |
137 | current && doop('activate'); | 165 | current && doop('activate'); |
166 | + actions = current && fs.isO(current.keyBindings); | ||
167 | + switchFn(id, actions); | ||
168 | + | ||
138 | wss.sendEvent('topoSelectOverlay', payload); | 169 | wss.sendEvent('topoSelectOverlay', payload); |
139 | 170 | ||
140 | - // TODO: refactor to emit "flush on overlay change" messages | 171 | + // Ensure summary and details panels are updated immediately.. |
141 | wss.sendEvent('requestSummary'); | 172 | wss.sendEvent('requestSummary'); |
173 | + tss.updateDetail(); | ||
142 | } | 174 | } |
143 | } | 175 | } |
144 | 176 | ||
145 | - var coreButtonPath = { | 177 | + var coreButtons = { |
146 | - showDeviceView: 'device', | 178 | + showDeviceView: { |
147 | - showFlowView: 'flow', | 179 | + gid: 'switch', |
148 | - showPortView: 'port', | 180 | + tt: 'Show Device View', |
149 | - showGroupView: 'group' | 181 | + path: 'device' |
182 | + }, | ||
183 | + showFlowView: { | ||
184 | + gid: 'flowTable', | ||
185 | + tt: 'Show Flow View for this Device', | ||
186 | + path: 'flow' | ||
187 | + }, | ||
188 | + showPortView: { | ||
189 | + gid: 'portTable', | ||
190 | + tt: 'Show Port View for this Device', | ||
191 | + path: 'port' | ||
192 | + }, | ||
193 | + showGroupView: { | ||
194 | + gid: 'groupTable', | ||
195 | + tt: 'Show Group View for this Device', | ||
196 | + path: 'group' | ||
197 | + } | ||
150 | }; | 198 | }; |
151 | 199 | ||
200 | + // retrieves a button definition from the current overlay and generates | ||
201 | + // a button descriptor to be added to the panel, with the data baked in | ||
202 | + function _getButtonDef(id, data) { | ||
203 | + var btns = current && current.buttons, | ||
204 | + b = btns && btns[id], | ||
205 | + cb = fs.isF(b.cb), | ||
206 | + f = cb ? function () { cb(data); } : function () {}; | ||
207 | + | ||
208 | + return b ? { | ||
209 | + id: current.mkId(id), | ||
210 | + gid: current.mkGid(b.gid), | ||
211 | + tt: b.tt, | ||
212 | + cb: f | ||
213 | + } : null; | ||
214 | + } | ||
215 | + | ||
152 | // install core buttons, and include any additional from the current overlay | 216 | // install core buttons, and include any additional from the current overlay |
153 | - function installButtons(buttons, addFn, data, devId) { | 217 | + function installButtons(buttons, data, devId) { |
154 | - | 218 | + buttons.forEach(function (id) { |
155 | - angular.forEach(buttons, function (btn) { | 219 | + var btn = coreButtons[id], |
156 | - var path = coreButtonPath[btn.id], | 220 | + gid = btn && btn.gid, |
157 | - _id, | 221 | + tt = btn && btn.tt, |
158 | - _gid, | 222 | + path = btn && btn.path; |
159 | - _cb, | 223 | + |
160 | - action; | 224 | + if (btn) { |
161 | - | 225 | + tps.addAction({ |
162 | - if (path) { | 226 | + id: 'core-' + id, |
163 | - // core callback function | 227 | + gid: gid, |
164 | - _id = btn.id; | 228 | + tt: tt, |
165 | - _gid = btn.gid; | 229 | + cb: function () { ns.navTo(path, {devId: devId }); } |
166 | - action = function () { | 230 | + }); |
167 | - ns.navTo(path, { devId: devId }); | 231 | + } else if (btn = _getButtonDef(id, data)) { |
168 | - }; | 232 | + tps.addAction(btn); |
169 | - } else if (current) { | 233 | + } |
170 | - _id = current.mkId(btn.id); | 234 | + }); |
171 | - _gid = current.mkGid(btn.gid); | ||
172 | - action = current.buttonActions[btn.id] || function () {}; | ||
173 | } | 235 | } |
174 | 236 | ||
175 | - _cb = function () { action(data); }; | 237 | + function addDetailButton(id) { |
238 | + var b = _getButtonDef(id); | ||
239 | + if (b) { | ||
240 | + tps.addAction({ | ||
241 | + id: current.mkId(id), | ||
242 | + gid: current.mkGid(b.gid), | ||
243 | + cb: b.cb, | ||
244 | + tt: b.tt | ||
245 | + }); | ||
246 | + } | ||
247 | + } | ||
248 | + | ||
249 | + | ||
250 | + // === ----------------------------------------------------- | ||
251 | + // Hooks for overlays | ||
252 | + | ||
253 | + function _hook(x) { | ||
254 | + var h = current && current.hooks; | ||
255 | + return h && fs.isF(h[x]); | ||
256 | + } | ||
176 | 257 | ||
177 | - addFn({ id: _id, gid: _gid, cb: _cb, tt: btn.tt}); | 258 | + function escapeHook() { |
259 | + var eh = _hook('escape'); | ||
260 | + return eh ? eh() : false; | ||
261 | + } | ||
262 | + | ||
263 | + function emptySelectHook() { | ||
264 | + var cb = _hook('empty'); | ||
265 | + cb && cb(); | ||
266 | + } | ||
267 | + | ||
268 | + function singleSelectHook(data) { | ||
269 | + var cb = _hook('single'); | ||
270 | + cb && cb(data); | ||
271 | + } | ||
272 | + | ||
273 | + function multiSelectHook(selectOrder) { | ||
274 | + var cb = _hook('multi'); | ||
275 | + cb && cb(selectOrder); | ||
276 | + } | ||
277 | + | ||
278 | + // === ----------------------------------------------------- | ||
279 | + // Event (from server) Handlers | ||
280 | + | ||
281 | + function setApi(_api_, _tss_) { | ||
282 | + api = _api_; | ||
283 | + tss = _tss_; | ||
284 | + } | ||
285 | + | ||
286 | + // TODO: refactor this (currently using showTraffic data structure) | ||
287 | + function showHighlights(data) { | ||
288 | + /* | ||
289 | + API to topoForce | ||
290 | + clearLinkTrafficStyle() | ||
291 | + removeLinkLabels() | ||
292 | + updateLinks() | ||
293 | + findLinkById( id ) | ||
294 | + */ | ||
295 | + | ||
296 | + var paths = data.paths; | ||
297 | + | ||
298 | + api.clearLinkTrafficStyle(); | ||
299 | + api.removeLinkLabels(); | ||
300 | + | ||
301 | + // Now highlight all links in the paths payload, and attach | ||
302 | + // labels to them, if they are defined. | ||
303 | + paths.forEach(function (p) { | ||
304 | + var n = p.links.length, | ||
305 | + i, ldata, lab, units, magnitude, portcls; | ||
306 | + | ||
307 | + for (i=0; i<n; i++) { | ||
308 | + ldata = api.findLinkById(p.links[i]); | ||
309 | + lab = p.labels[i]; | ||
310 | + | ||
311 | + if (ldata && !ldata.el.empty()) { | ||
312 | + ldata.el.classed(p.class, true); | ||
313 | + ldata.label = lab; | ||
314 | + | ||
315 | + if (fs.endsWith(lab, 'bps')) { | ||
316 | + // inject additional styling for port-based traffic | ||
317 | + units = lab.substring(lab.length-4); | ||
318 | + portcls = 'port-traffic-' + units; | ||
319 | + | ||
320 | + // for GBps | ||
321 | + if (units.substring(0,1) === 'G') { | ||
322 | + magnitude = fs.parseBitRate(lab); | ||
323 | + if (magnitude >= 9) { | ||
324 | + portcls += '-choked' | ||
325 | + } | ||
326 | + } | ||
327 | + ldata.el.classed(portcls, true); | ||
328 | + } | ||
329 | + } | ||
330 | + } | ||
178 | }); | 331 | }); |
179 | 332 | ||
333 | + api.updateLinks(); | ||
180 | } | 334 | } |
181 | 335 | ||
336 | + // ======================================================================== | ||
337 | + | ||
182 | angular.module('ovTopo') | 338 | angular.module('ovTopo') |
183 | .factory('TopoOverlayService', | 339 | .factory('TopoOverlayService', |
184 | ['$log', 'FnService', 'GlyphService', 'WebSocketService', 'NavService', | 340 | ['$log', 'FnService', 'GlyphService', 'WebSocketService', 'NavService', |
341 | + 'TopoPanelService', | ||
185 | 342 | ||
186 | - function (_$log_, _fs_, _gs_, _wss_, _ns_) { | 343 | + function (_$log_, _fs_, _gs_, _wss_, _ns_, _tps_) { |
187 | $log = _$log_; | 344 | $log = _$log_; |
188 | fs = _fs_; | 345 | fs = _fs_; |
189 | gs = _gs_; | 346 | gs = _gs_; |
190 | wss = _wss_; | 347 | wss = _wss_; |
191 | ns = _ns_; | 348 | ns = _ns_; |
349 | + tps = _tps_; | ||
192 | 350 | ||
193 | return { | 351 | return { |
194 | register: register, | 352 | register: register, |
195 | - unregister: unregister, | 353 | + //unregister: unregister, |
354 | + setApi: setApi, | ||
196 | list: list, | 355 | list: list, |
197 | - overlay: overlay, | 356 | + augmentRbset: augmentRbset, |
357 | + mkGlyphId: mkGlyphId, | ||
198 | tbSelection: tbSelection, | 358 | tbSelection: tbSelection, |
199 | - installButtons: installButtons | 359 | + installButtons: installButtons, |
360 | + addDetailButton: addDetailButton, | ||
361 | + hooks: { | ||
362 | + escape: escapeHook, | ||
363 | + emptySelect: emptySelectHook, | ||
364 | + singleSelect: singleSelectHook, | ||
365 | + multiSelect: multiSelectHook | ||
366 | + }, | ||
367 | + | ||
368 | + showHighlights: showHighlights | ||
200 | } | 369 | } |
201 | }]); | 370 | }]); |
202 | 371 | ... | ... |
... | @@ -40,12 +40,6 @@ | ... | @@ -40,12 +40,6 @@ |
40 | selectOrder = [], // the order in which we made selections | 40 | selectOrder = [], // the order in which we made selections |
41 | consumeClick = false; // used to coordinate with SVG click handler | 41 | consumeClick = false; // used to coordinate with SVG click handler |
42 | 42 | ||
43 | - // constants | ||
44 | - var devPath = 'device', | ||
45 | - flowPath = 'flow', | ||
46 | - portPath ='port', | ||
47 | - groupPath = 'group'; | ||
48 | - | ||
49 | // ========================== | 43 | // ========================== |
50 | 44 | ||
51 | function nSel() { | 45 | function nSel() { |
... | @@ -157,8 +151,7 @@ | ... | @@ -157,8 +151,7 @@ |
157 | 151 | ||
158 | // === ----------------------------------------------------- | 152 | // === ----------------------------------------------------- |
159 | 153 | ||
160 | - function requestDetails() { | 154 | + function requestDetails(data) { |
161 | - var data = getSel(0).obj; | ||
162 | wss.sendEvent('requestDetails', { | 155 | wss.sendEvent('requestDetails', { |
163 | id: data.id, | 156 | id: data.id, |
164 | class: data.class | 157 | class: data.class |
... | @@ -179,91 +172,62 @@ | ... | @@ -179,91 +172,62 @@ |
179 | } | 172 | } |
180 | 173 | ||
181 | function emptySelect() { | 174 | function emptySelect() { |
182 | - tts.cancelTraffic(); | 175 | + tov.hooks.emptySelect(); |
183 | tps.displayNothing(); | 176 | tps.displayNothing(); |
184 | } | 177 | } |
185 | 178 | ||
186 | function singleSelect() { | 179 | function singleSelect() { |
187 | - // NOTE: detail is shown from 'showDetails' event callback | 180 | + var data = getSel(0).obj; |
188 | - requestDetails(); | 181 | + requestDetails(data); |
189 | - tts.cancelTraffic(); | 182 | + // NOTE: detail panel is shown as a response to receiving |
190 | - tts.requestTrafficForMode(); | 183 | + // a 'showDetails' event from the server. See 'showDetails' |
184 | + // callback function below... | ||
191 | } | 185 | } |
192 | 186 | ||
193 | function multiSelect() { | 187 | function multiSelect() { |
194 | // display the selected nodes in the detail panel | 188 | // display the selected nodes in the detail panel |
195 | tps.displayMulti(selectOrder); | 189 | tps.displayMulti(selectOrder); |
190 | + addHostSelectionActions(); | ||
191 | + tov.hooks.multiSelect(selectOrder); | ||
192 | + tps.displaySomething(); | ||
193 | + } | ||
196 | 194 | ||
197 | - // always add the 'show traffic' action | 195 | + function addHostSelectionActions() { |
198 | - tps.addAction({ | 196 | + if (allSelectionsClass('host')) { |
199 | - id: '-mult-rel-traf-btn', | 197 | + if (nSel() === 2) { |
200 | - gid: 'allTraffic', | ||
201 | - cb: tts.showRelatedIntentsAction, | ||
202 | - tt: 'Show Related Traffic' | ||
203 | - }); | ||
204 | - | ||
205 | - // add other actions, based on what is selected... | ||
206 | - if (nSel() === 2 && allSelectionsClass('host')) { | ||
207 | tps.addAction({ | 198 | tps.addAction({ |
208 | id: 'host-flow-btn', | 199 | id: 'host-flow-btn', |
209 | gid: 'endstation', | 200 | gid: 'endstation', |
210 | - cb: tts.addHostIntentAction, | 201 | + cb: tts.addHostIntent, |
211 | tt: 'Create Host-to-Host Flow' | 202 | tt: 'Create Host-to-Host Flow' |
212 | }); | 203 | }); |
213 | - } else if (nSel() >= 2 && allSelectionsClass('host')) { | 204 | + } else if (nSel() >= 2) { |
214 | tps.addAction({ | 205 | tps.addAction({ |
215 | id: 'mult-src-flow-btn', | 206 | id: 'mult-src-flow-btn', |
216 | gid: 'flows', | 207 | gid: 'flows', |
217 | - cb: tts.addMultiSourceIntentAction, | 208 | + cb: tts.addMultiSourceIntent, |
218 | tt: 'Create Multi-Source Flow' | 209 | tt: 'Create Multi-Source Flow' |
219 | }); | 210 | }); |
220 | } | 211 | } |
221 | - | 212 | + } |
222 | - tts.cancelTraffic(); | ||
223 | - tts.requestTrafficForMode(); | ||
224 | - tps.displaySomething(); | ||
225 | } | 213 | } |
226 | 214 | ||
227 | 215 | ||
228 | // === ----------------------------------------------------- | 216 | // === ----------------------------------------------------- |
229 | // Event Handlers | 217 | // Event Handlers |
230 | 218 | ||
219 | + // display the data for the single selected node | ||
231 | function showDetails(data) { | 220 | function showDetails(data) { |
232 | var buttons = fs.isA(data.buttons) || []; | 221 | var buttons = fs.isA(data.buttons) || []; |
233 | - | ||
234 | - // display the data for the single selected node | ||
235 | tps.displaySingle(data); | 222 | tps.displaySingle(data); |
236 | - | 223 | + tov.installButtons(buttons, data, data.props['URI']); |
237 | - tov.installButtons(buttons, tps.addAction, data, data.props['URI']); | 224 | + tov.hooks.singleSelect(data); |
238 | - | ||
239 | - // TODO: MOVE traffic buttons to the traffic overlay | ||
240 | - // always add the 'show traffic' action | ||
241 | - tps.addAction({ | ||
242 | - id: '-sin-rel-traf-btn', | ||
243 | - gid: 'intentTraffic', | ||
244 | - cb: tts.showRelatedIntentsAction, | ||
245 | - tt: 'Show Related Traffic' | ||
246 | - }); | ||
247 | - | ||
248 | - // add other actions, based on what is selected... | ||
249 | - if (data.type === 'switch') { | ||
250 | - tps.addAction({ | ||
251 | - id: 'sin-dev-flows-btn', | ||
252 | - gid: 'flows', | ||
253 | - cb: tts.showDeviceLinkFlowsAction, | ||
254 | - tt: 'Show Device Flows' | ||
255 | - }); | ||
256 | - } | ||
257 | - | ||
258 | tps.displaySomething(); | 225 | tps.displaySomething(); |
259 | } | 226 | } |
260 | 227 | ||
261 | - function validateSelectionContext() { | 228 | + // returns true if we are hovering over a node, or any nodes are selected |
262 | - if (!hovered && !nSel()) { | 229 | + function somethingSelected() { |
263 | - tts.cancelTraffic(); | 230 | + return hovered || nSel(); |
264 | - return false; | ||
265 | - } | ||
266 | - return true; | ||
267 | } | 231 | } |
268 | 232 | ||
269 | function clickConsumed(x) { | 233 | function clickConsumed(x) { |
... | @@ -306,10 +270,11 @@ | ... | @@ -306,10 +270,11 @@ |
306 | selectObject: selectObject, | 270 | selectObject: selectObject, |
307 | deselectObject: deselectObject, | 271 | deselectObject: deselectObject, |
308 | deselectAll: deselectAll, | 272 | deselectAll: deselectAll, |
273 | + updateDetail: updateDetail, | ||
309 | 274 | ||
310 | hovered: function () { return hovered; }, | 275 | hovered: function () { return hovered; }, |
311 | selectOrder: function () { return selectOrder; }, | 276 | selectOrder: function () { return selectOrder; }, |
312 | - validateSelectionContext: validateSelectionContext, | 277 | + somethingSelected: somethingSelected, |
313 | 278 | ||
314 | clickConsumed: clickConsumed | 279 | clickConsumed: clickConsumed |
315 | }; | 280 | }; | ... | ... |
... | @@ -25,19 +25,25 @@ | ... | @@ -25,19 +25,25 @@ |
25 | // injected references | 25 | // injected references |
26 | var $log, fs, tbs, ps, tov, api; | 26 | var $log, fs, tbs, ps, tov, api; |
27 | 27 | ||
28 | + // API: | ||
29 | + // getActionEntry | ||
30 | + // setUpKeys | ||
31 | + | ||
28 | // internal state | 32 | // internal state |
29 | - var toolbar, keyData, cachedState; | 33 | + var toolbar, keyData, cachedState, thirdRow; |
30 | 34 | ||
31 | // constants | 35 | // constants |
32 | var name = 'topo-tbar', | 36 | var name = 'topo-tbar', |
33 | - cooktag = 'topo_prefs'; | 37 | + cooktag = 'topo_prefs', |
38 | + soa = 'switchOverlayActions: ', | ||
39 | + selOver = 'Select overlay here ⇧'; | ||
40 | + | ||
34 | 41 | ||
35 | // key to button mapping data | 42 | // key to button mapping data |
36 | var k2b = { | 43 | var k2b = { |
37 | O: { id: 'summary-tog', gid: 'summary', isel: true}, | 44 | O: { id: 'summary-tog', gid: 'summary', isel: true}, |
38 | I: { id: 'instance-tog', gid: 'uiAttached', isel: true }, | 45 | I: { id: 'instance-tog', gid: 'uiAttached', isel: true }, |
39 | D: { id: 'details-tog', gid: 'details', isel: true }, | 46 | D: { id: 'details-tog', gid: 'details', isel: true }, |
40 | - | ||
41 | H: { id: 'hosts-tog', gid: 'endstation', isel: false }, | 47 | H: { id: 'hosts-tog', gid: 'endstation', isel: false }, |
42 | M: { id: 'offline-tog', gid: 'switch', isel: true }, | 48 | M: { id: 'offline-tog', gid: 'switch', isel: true }, |
43 | P: { id: 'ports-tog', gid: 'ports', isel: true }, | 49 | P: { id: 'ports-tog', gid: 'ports', isel: true }, |
... | @@ -50,16 +56,16 @@ | ... | @@ -50,16 +56,16 @@ |
50 | L: { id: 'cycleLabels-btn', gid: 'cycleLabels' }, | 56 | L: { id: 'cycleLabels-btn', gid: 'cycleLabels' }, |
51 | R: { id: 'resetZoom-btn', gid: 'resetZoom' }, | 57 | R: { id: 'resetZoom-btn', gid: 'resetZoom' }, |
52 | 58 | ||
53 | - E: { id: 'eqMaster-btn', gid: 'eqMaster' }, | 59 | + E: { id: 'eqMaster-btn', gid: 'eqMaster' } |
54 | - | ||
55 | - V: { id: 'relatedIntents-btn', gid: 'relatedIntents' }, | ||
56 | - leftArrow: { id: 'prevIntent-btn', gid: 'prevIntent' }, | ||
57 | - rightArrow: { id: 'nextIntent-btn', gid: 'nextIntent' }, | ||
58 | - W: { id: 'intentTraffic-btn', gid: 'intentTraffic' }, | ||
59 | - A: { id: 'allTraffic-btn', gid: 'allTraffic' }, | ||
60 | - F: { id: 'flows-btn', gid: 'flows' } | ||
61 | }; | 60 | }; |
62 | 61 | ||
62 | + var prohibited = [ | ||
63 | + 'T', 'backSlash', 'slash', | ||
64 | + 'X' // needed until we re-instate X above. | ||
65 | + ]; | ||
66 | + prohibited = prohibited.concat(d3.map(k2b).keys()); | ||
67 | + | ||
68 | + | ||
63 | // initial toggle state: default settings and tag to key mapping | 69 | // initial toggle state: default settings and tag to key mapping |
64 | var defaultPrefsState = { | 70 | var defaultPrefsState = { |
65 | summary: 1, | 71 | summary: 1, |
... | @@ -112,6 +118,7 @@ | ... | @@ -112,6 +118,7 @@ |
112 | } | 118 | } |
113 | 119 | ||
114 | function initKeyData() { | 120 | function initKeyData() { |
121 | + // TODO: use angular forEach instead of d3.map | ||
115 | keyData = d3.map(k2b); | 122 | keyData = d3.map(k2b); |
116 | keyData.forEach(function(key, value) { | 123 | keyData.forEach(function(key, value) { |
117 | var data = api.getActionEntry(key); | 124 | var data = api.getActionEntry(key); |
... | @@ -124,6 +131,7 @@ | ... | @@ -124,6 +131,7 @@ |
124 | var v = keyData.get(key); | 131 | var v = keyData.get(key); |
125 | v.btn = toolbar.addButton(v.id, v.gid, v.cb, v.tt); | 132 | v.btn = toolbar.addButton(v.id, v.gid, v.cb, v.tt); |
126 | } | 133 | } |
134 | + | ||
127 | function addToggle(key, suppressIfMobile) { | 135 | function addToggle(key, suppressIfMobile) { |
128 | var v = keyData.get(key); | 136 | var v = keyData.get(key); |
129 | if (suppressIfMobile && fs.isMobile()) { return; } | 137 | if (suppressIfMobile && fs.isMobile()) { return; } |
... | @@ -158,36 +166,60 @@ | ... | @@ -158,36 +166,60 @@ |
158 | 166 | ||
159 | // generate radio button set for overlays; start with 'none' | 167 | // generate radio button set for overlays; start with 'none' |
160 | var rset = [{ | 168 | var rset = [{ |
161 | - gid: 'unknown', | 169 | + gid: 'topo', |
162 | tooltip: 'No Overlay', | 170 | tooltip: 'No Overlay', |
163 | cb: function () { | 171 | cb: function () { |
164 | - tov.tbSelection(null); | 172 | + tov.tbSelection(null, switchOverlayActions); |
165 | } | 173 | } |
166 | }]; | 174 | }]; |
167 | - | 175 | + tov.augmentRbset(rset, switchOverlayActions); |
168 | - tov.list().forEach(function (key) { | 176 | + toolbar.addRadioSet('topo-overlays', rset); |
169 | - var ov = tov.overlay(key); | ||
170 | - rset.push({ | ||
171 | - gid: ov._glyphId, | ||
172 | - tooltip: (ov.tooltip || '(no tooltip)'), | ||
173 | - cb: function () { | ||
174 | - tov.tbSelection(ov.overlayId); | ||
175 | } | 177 | } |
178 | + | ||
179 | + // invoked by overlay service to switch out old buttons and switch in new | ||
180 | + function switchOverlayActions(oid, keyBindings) { | ||
181 | + var prohibits = [], | ||
182 | + kb = fs.isO(keyBindings) || {}, | ||
183 | + order = fs.isA(kb._keyOrder) || []; | ||
184 | + | ||
185 | + if (keyBindings && !keyBindings._keyOrder) { | ||
186 | + $log.warn(soa + 'no _keyOrder property defined'); | ||
187 | + } else { | ||
188 | + // sanity removal of reserved property names | ||
189 | + ['esc', '_keyListener', '_helpFormat'].forEach(function (k) { | ||
190 | + fs.removeFromArray(k, order); | ||
176 | }); | 191 | }); |
177 | - }); | 192 | + } |
178 | 193 | ||
179 | - toolbar.addRadioSet('topo-overlays', rset); | 194 | + thirdRow.clear(); |
195 | + | ||
196 | + if (!order.length) { | ||
197 | + thirdRow.setText(selOver); | ||
198 | + thirdRow.classed('right', true); | ||
199 | + api.setUpKeys(); // clear previous overlay key bindings | ||
200 | + | ||
201 | + } else { | ||
202 | + thirdRow.classed('right', false); | ||
203 | + angular.forEach(order, function (key) { | ||
204 | + var value, bid, gid, tt; | ||
205 | + | ||
206 | + if (prohibited.indexOf(key) > -1) { | ||
207 | + prohibits.push(key); | ||
208 | + | ||
209 | + } else { | ||
210 | + value = keyBindings[key]; | ||
211 | + bid = oid + '-' + key; | ||
212 | + gid = tov.mkGlyphId(oid, value.gid); | ||
213 | + tt = value.tt + ' (' + key + ')'; | ||
214 | + thirdRow.addButton(bid, gid, value.cb, tt); | ||
215 | + } | ||
216 | + }); | ||
217 | + api.setUpKeys(keyBindings); // add overlay key bindings | ||
180 | } | 218 | } |
181 | 219 | ||
182 | - // TODO: 3rd row needs to be swapped in/out based on selected overlay | 220 | + if (prohibits.length) { |
183 | - // NOTE: This particular row of buttons is for the traffic overlay | 221 | + $log.warn(soa + 'Prohibited key bindings ignored:', prohibits); |
184 | - function addThirdRow() { | 222 | + } |
185 | - addButton('V'); | ||
186 | - addButton('leftArrow'); | ||
187 | - addButton('rightArrow'); | ||
188 | - addButton('W'); | ||
189 | - addButton('A'); | ||
190 | - addButton('F'); | ||
191 | } | 223 | } |
192 | 224 | ||
193 | function createToolbar() { | 225 | function createToolbar() { |
... | @@ -197,8 +229,9 @@ | ... | @@ -197,8 +229,9 @@ |
197 | toolbar.addRow(); | 229 | toolbar.addRow(); |
198 | addSecondRow(); | 230 | addSecondRow(); |
199 | addOverlays(); | 231 | addOverlays(); |
200 | - toolbar.addRow(); | 232 | + thirdRow = toolbar.addRow(); |
201 | - addThirdRow(); | 233 | + thirdRow.setText(selOver); |
234 | + thirdRow.classed('right', true); | ||
202 | 235 | ||
203 | if (cachedState.toolbar) { | 236 | if (cachedState.toolbar) { |
204 | toolbar.show(); | 237 | toolbar.show(); | ... | ... |
... | @@ -23,85 +23,44 @@ | ... | @@ -23,85 +23,44 @@ |
23 | 'use strict'; | 23 | 'use strict'; |
24 | 24 | ||
25 | // injected refs | 25 | // injected refs |
26 | - var $log, fs, flash, wss; | 26 | + var $log, fs, flash, wss, api; |
27 | 27 | ||
28 | - // api to topoForce | ||
29 | - var api; | ||
30 | /* | 28 | /* |
31 | - clearLinkTrafficStyle() | 29 | + API to topoForce |
32 | - removeLinkLabels() | ||
33 | - updateLinks() | ||
34 | - findLinkById( id ) | ||
35 | hovered() | 30 | hovered() |
36 | - validateSelectionContext() | 31 | + somethingSelected() |
32 | + selectOrder() | ||
37 | */ | 33 | */ |
38 | 34 | ||
39 | - // constants | ||
40 | - var hoverModeNone = 0, | ||
41 | - hoverModeAll = 1, | ||
42 | - hoverModeFlows = 2, | ||
43 | - hoverModeIntents = 3; | ||
44 | - | ||
45 | // internal state | 35 | // internal state |
46 | - var hoverMode = hoverModeNone; | 36 | + var trafficMode = null, |
37 | + hoverMode = null; | ||
47 | 38 | ||
48 | 39 | ||
49 | // === ----------------------------------------------------- | 40 | // === ----------------------------------------------------- |
50 | - // Event Handlers | 41 | + // Helper functions |
51 | - | ||
52 | - function showTraffic(data) { | ||
53 | - var paths = data.paths; | ||
54 | - | ||
55 | - api.clearLinkTrafficStyle(); | ||
56 | - api.removeLinkLabels(); | ||
57 | - | ||
58 | - // Now highlight all links in the paths payload, and attach | ||
59 | - // labels to them, if they are defined. | ||
60 | - paths.forEach(function (p) { | ||
61 | - var n = p.links.length, | ||
62 | - i, ldata, lab, units, magnitude, portcls; | ||
63 | - | ||
64 | - for (i=0; i<n; i++) { | ||
65 | - ldata = api.findLinkById(p.links[i]); | ||
66 | - lab = p.labels[i]; | ||
67 | - | ||
68 | - if (ldata && !ldata.el.empty()) { | ||
69 | - ldata.el.classed(p.class, true); | ||
70 | - ldata.label = lab; | ||
71 | - | ||
72 | - if (fs.endsWith(lab, 'bps')) { | ||
73 | - // inject additional styling for port-based traffic | ||
74 | - units = lab.substring(lab.length-4); | ||
75 | - portcls = 'port-traffic-' + units; | ||
76 | - | ||
77 | - // for GBps | ||
78 | - if (units.substring(0,1) === 'G') { | ||
79 | - magnitude = fs.parseBitRate(lab); | ||
80 | - if (magnitude >= 9) { | ||
81 | - portcls += '-choked' | ||
82 | - } | ||
83 | - } | ||
84 | - ldata.el.classed(portcls, true); | ||
85 | - } | ||
86 | - } | ||
87 | - } | ||
88 | - }); | ||
89 | 42 | ||
90 | - api.updateLinks(); | 43 | + // invoked in response to change in selection and/or mouseover/out: |
44 | + function requestTrafficForMode() { | ||
45 | + if (hoverMode === 'flows') { | ||
46 | + requestDeviceLinkFlows(); | ||
47 | + } else if (hoverMode === 'intents') { | ||
48 | + requestRelatedIntents(); | ||
49 | + } else { | ||
50 | + cancelTraffic(); | ||
51 | + } | ||
91 | } | 52 | } |
92 | - | ||
93 | - // === ----------------------------------------------------- | ||
94 | - // Helper functions | ||
95 | 53 | ||
96 | function requestDeviceLinkFlows() { | 54 | function requestDeviceLinkFlows() { |
55 | + // generates payload based on current hover-state | ||
97 | var hov = api.hovered(); | 56 | var hov = api.hovered(); |
98 | 57 | ||
99 | function hoverValid() { | 58 | function hoverValid() { |
100 | - return hoverMode === hoverModeFlows && | 59 | + return hoverMode === 'flows' && |
101 | hov && (hov.class === 'device'); | 60 | hov && (hov.class === 'device'); |
102 | } | 61 | } |
103 | 62 | ||
104 | - if (api.validateSelectionContext()) { | 63 | + if (api.somethingSelected()) { |
105 | wss.sendEvent('requestDeviceLinkFlows', { | 64 | wss.sendEvent('requestDeviceLinkFlows', { |
106 | ids: api.selectOrder(), | 65 | ids: api.selectOrder(), |
107 | hover: hoverValid() ? hov.id : '' | 66 | hover: hoverValid() ? hov.id : '' |
... | @@ -110,14 +69,15 @@ | ... | @@ -110,14 +69,15 @@ |
110 | } | 69 | } |
111 | 70 | ||
112 | function requestRelatedIntents() { | 71 | function requestRelatedIntents() { |
72 | + // generates payload based on current hover-state | ||
113 | var hov = api.hovered(); | 73 | var hov = api.hovered(); |
114 | 74 | ||
115 | function hoverValid() { | 75 | function hoverValid() { |
116 | - return hoverMode === hoverModeIntents && | 76 | + return hoverMode === 'intents' && |
117 | hov && (hov.class === 'host' || hov.class === 'device'); | 77 | hov && (hov.class === 'host' || hov.class === 'device'); |
118 | } | 78 | } |
119 | 79 | ||
120 | - if (api.validateSelectionContext()) { | 80 | + if (api.somethingSelected()) { |
121 | wss.sendEvent('requestRelatedIntents', { | 81 | wss.sendEvent('requestRelatedIntents', { |
122 | ids: api.selectOrder(), | 82 | ids: api.selectOrder(), |
123 | hover: hoverValid() ? hov.id : '' | 83 | hover: hoverValid() ? hov.id : '' |
... | @@ -126,71 +86,75 @@ | ... | @@ -126,71 +86,75 @@ |
126 | } | 86 | } |
127 | 87 | ||
128 | 88 | ||
129 | - // === ----------------------------------------------------- | 89 | + // === ------------------------------------------------------------- |
130 | - // Traffic requests | 90 | + // Traffic requests invoked from keystrokes or toolbar buttons... |
131 | 91 | ||
132 | function cancelTraffic() { | 92 | function cancelTraffic() { |
93 | + if (!trafficMode) { | ||
94 | + return false; | ||
95 | + } | ||
96 | + | ||
97 | + trafficMode = hoverMode = null; | ||
133 | wss.sendEvent('cancelTraffic'); | 98 | wss.sendEvent('cancelTraffic'); |
99 | + flash.flash('Traffic monitoring canceled'); | ||
100 | + return true; | ||
134 | } | 101 | } |
135 | 102 | ||
136 | - // invoked in response to change in selection and/or mouseover/out: | 103 | + function showAllFlowTraffic() { |
137 | - function requestTrafficForMode() { | 104 | + trafficMode = 'allFlow'; |
138 | - if (hoverMode === hoverModeFlows) { | 105 | + hoverMode = 'all'; |
139 | - requestDeviceLinkFlows(); | 106 | + wss.sendEvent('requestAllFlowTraffic'); |
140 | - } else if (hoverMode === hoverModeIntents) { | 107 | + flash.flash('All Flow Traffic'); |
141 | - requestRelatedIntents(); | ||
142 | } | 108 | } |
109 | + | ||
110 | + function showAllPortTraffic() { | ||
111 | + trafficMode = 'allPort'; | ||
112 | + hoverMode = 'all'; | ||
113 | + wss.sendEvent('requestAllPortTraffic'); | ||
114 | + flash.flash('All Port Traffic'); | ||
143 | } | 115 | } |
144 | 116 | ||
145 | - // === ----------------------------- | 117 | + function showDeviceLinkFlows () { |
146 | - // keystroke commands | 118 | + trafficMode = hoverMode = 'flows'; |
119 | + requestDeviceLinkFlows(); | ||
120 | + flash.flash('Device Flows'); | ||
121 | + } | ||
147 | 122 | ||
148 | - // keystroke-right-arrow (see topo.js) | 123 | + function showRelatedIntents () { |
149 | - function showNextIntentAction() { | 124 | + trafficMode = hoverMode = 'intents'; |
150 | - hoverMode = hoverModeNone; | 125 | + requestRelatedIntents(); |
151 | - wss.sendEvent('requestNextRelatedIntent'); | 126 | + flash.flash('Related Paths'); |
152 | - flash.flash('Next related intent'); | ||
153 | } | 127 | } |
154 | 128 | ||
155 | - // keystroke-left-arrow (see topo.js) | 129 | + function showPrevIntent() { |
156 | - function showPrevIntentAction() { | 130 | + if (trafficMode === 'intents') { |
157 | - hoverMode = hoverModeNone; | 131 | + hoverMode = null; |
158 | wss.sendEvent('requestPrevRelatedIntent'); | 132 | wss.sendEvent('requestPrevRelatedIntent'); |
159 | flash.flash('Previous related intent'); | 133 | flash.flash('Previous related intent'); |
160 | } | 134 | } |
161 | - | ||
162 | - // keystroke-W (see topo.js) | ||
163 | - function showSelectedIntentTrafficAction() { | ||
164 | - hoverMode = hoverModeNone; | ||
165 | - wss.sendEvent('requestSelectedIntentTraffic'); | ||
166 | - flash.flash('Traffic on Selected Path'); | ||
167 | } | 135 | } |
168 | 136 | ||
169 | - // keystroke-A (see topo.js) | 137 | + function showNextIntent() { |
170 | - function showAllFlowTrafficAction() { | 138 | + if (trafficMode === 'intents') { |
171 | - hoverMode = hoverModeAll; | 139 | + hoverMode = null; |
172 | - wss.sendEvent('requestAllFlowTraffic'); | 140 | + wss.sendEvent('requestNextRelatedIntent'); |
173 | - flash.flash('All Flow Traffic'); | 141 | + flash.flash('Next related intent'); |
142 | + } | ||
174 | } | 143 | } |
175 | 144 | ||
176 | - // keystroke-A (see topo.js) | 145 | + function showSelectedIntentTraffic() { |
177 | - function showAllPortTrafficAction() { | 146 | + if (trafficMode === 'intents') { |
178 | - hoverMode = hoverModeAll; | 147 | + hoverMode = null; |
179 | - wss.sendEvent('requestAllPortTraffic'); | 148 | + wss.sendEvent('requestSelectedIntentTraffic'); |
180 | - flash.flash('All Port Traffic'); | 149 | + flash.flash('Traffic on Selected Path'); |
150 | + } | ||
181 | } | 151 | } |
182 | 152 | ||
183 | - // === ----------------------------- | ||
184 | - // action buttons on detail panel | ||
185 | 153 | ||
186 | - // also, keystroke-V (see topo.js) | 154 | + // === ------------------------------------------------------ |
187 | - function showRelatedIntentsAction () { | 155 | + // action buttons on detail panel (multiple selection) |
188 | - hoverMode = hoverModeIntents; | ||
189 | - requestRelatedIntents(); | ||
190 | - flash.flash('Related Paths'); | ||
191 | - } | ||
192 | 156 | ||
193 | - function addHostIntentAction () { | 157 | + function addHostIntent () { |
194 | var so = api.selectOrder(); | 158 | var so = api.selectOrder(); |
195 | wss.sendEvent('addHostIntent', { | 159 | wss.sendEvent('addHostIntent', { |
196 | one: so[0], | 160 | one: so[0], |
... | @@ -200,7 +164,7 @@ | ... | @@ -200,7 +164,7 @@ |
200 | flash.flash('Host-to-Host flow added'); | 164 | flash.flash('Host-to-Host flow added'); |
201 | } | 165 | } |
202 | 166 | ||
203 | - function addMultiSourceIntentAction () { | 167 | + function addMultiSourceIntent () { |
204 | var so = api.selectOrder(); | 168 | var so = api.selectOrder(); |
205 | wss.sendEvent('addMultiSourceIntent', { | 169 | wss.sendEvent('addMultiSourceIntent', { |
206 | src: so.slice(0, so.length - 1), | 170 | src: so.slice(0, so.length - 1), |
... | @@ -210,12 +174,6 @@ | ... | @@ -210,12 +174,6 @@ |
210 | flash.flash('Multi-Source flow added'); | 174 | flash.flash('Multi-Source flow added'); |
211 | } | 175 | } |
212 | 176 | ||
213 | - // also, keystroke-F (see topo.js) | ||
214 | - function showDeviceLinkFlowsAction () { | ||
215 | - hoverMode = hoverModeFlows; | ||
216 | - requestDeviceLinkFlows(); | ||
217 | - flash.flash('Device Flows'); | ||
218 | - } | ||
219 | 177 | ||
220 | 178 | ||
221 | // === ----------------------------------------------------- | 179 | // === ----------------------------------------------------- |
... | @@ -231,29 +189,26 @@ | ... | @@ -231,29 +189,26 @@ |
231 | flash = _flash_; | 189 | flash = _flash_; |
232 | wss = _wss_; | 190 | wss = _wss_; |
233 | 191 | ||
234 | - function initTraffic(_api_) { | ||
235 | - api = _api_; | ||
236 | - } | ||
237 | - | ||
238 | - function destroyTraffic() { } | ||
239 | - | ||
240 | return { | 192 | return { |
241 | - initTraffic: initTraffic, | 193 | + initTraffic: function (_api_) { api = _api_; }, |
242 | - destroyTraffic: destroyTraffic, | 194 | + destroyTraffic: function () { }, |
243 | - | ||
244 | - showTraffic: showTraffic, | ||
245 | 195 | ||
196 | + // invoked from toolbar overlay buttons or keystrokes | ||
246 | cancelTraffic: cancelTraffic, | 197 | cancelTraffic: cancelTraffic, |
198 | + showAllFlowTraffic: showAllFlowTraffic, | ||
199 | + showAllPortTraffic: showAllPortTraffic, | ||
200 | + showDeviceLinkFlows: showDeviceLinkFlows, | ||
201 | + showRelatedIntents: showRelatedIntents, | ||
202 | + showPrevIntent: showPrevIntent, | ||
203 | + showNextIntent: showNextIntent, | ||
204 | + showSelectedIntentTraffic: showSelectedIntentTraffic, | ||
205 | + | ||
206 | + // invoked from mouseover/mouseout and selection change | ||
247 | requestTrafficForMode: requestTrafficForMode, | 207 | requestTrafficForMode: requestTrafficForMode, |
248 | - showRelatedIntentsAction: showRelatedIntentsAction, | 208 | + |
249 | - addHostIntentAction: addHostIntentAction, | 209 | + // invoked from buttons on detail (multi-select) panel |
250 | - addMultiSourceIntentAction: addMultiSourceIntentAction, | 210 | + addHostIntent: addHostIntent, |
251 | - showDeviceLinkFlowsAction: showDeviceLinkFlowsAction, | 211 | + addMultiSourceIntent: addMultiSourceIntent |
252 | - showNextIntentAction: showNextIntentAction, | ||
253 | - showPrevIntentAction: showPrevIntentAction, | ||
254 | - showSelectedIntentTrafficAction: showSelectedIntentTrafficAction, | ||
255 | - showAllFlowTrafficAction: showAllFlowTrafficAction, | ||
256 | - showAllPortTrafficAction: showAllPortTrafficAction | ||
257 | }; | 212 | }; |
258 | }]); | 213 | }]); |
259 | }()); | 214 | }()); | ... | ... |
... | @@ -16,7 +16,7 @@ | ... | @@ -16,7 +16,7 @@ |
16 | */ | 16 | */ |
17 | 17 | ||
18 | /* | 18 | /* |
19 | - ONOS GUI -- Topology Traffic Module. | 19 | + ONOS GUI -- Topology Traffic Overlay Module. |
20 | Defines behavior for viewing different traffic modes. | 20 | Defines behavior for viewing different traffic modes. |
21 | Installed as a Topology Overlay. | 21 | Installed as a Topology Overlay. |
22 | */ | 22 | */ |
... | @@ -24,7 +24,13 @@ | ... | @@ -24,7 +24,13 @@ |
24 | 'use strict'; | 24 | 'use strict'; |
25 | 25 | ||
26 | // injected refs | 26 | // injected refs |
27 | - var $log; | 27 | + var $log, tov, tts; |
28 | + | ||
29 | + // NOTE: no internal state here -- see TopoTrafficService for that | ||
30 | + | ||
31 | + // NOTE: providing button disabling requires too big a refactoring of | ||
32 | + // the button factory etc. Will have to be done another time. | ||
33 | + | ||
28 | 34 | ||
29 | // traffic overlay definition | 35 | // traffic overlay definition |
30 | var overlay = { | 36 | var overlay = { |
... | @@ -32,26 +38,112 @@ | ... | @@ -32,26 +38,112 @@ |
32 | glyphId: 'allTraffic', | 38 | glyphId: 'allTraffic', |
33 | tooltip: 'Traffic Overlay', | 39 | tooltip: 'Traffic Overlay', |
34 | 40 | ||
35 | - activate: activateTraffic, | 41 | + // NOTE: Traffic glyphs already installed as part of the base ONOS set. |
36 | - deactivate: deactivateTraffic | ||
37 | - }; | ||
38 | 42 | ||
39 | - // === implementation of overlay API (essentially callbacks) | 43 | + activate: function () { |
40 | - function activateTraffic() { | 44 | + $log.debug("Traffic overlay ACTIVATED"); |
41 | - $log.debug("Topology traffic overlay ACTIVATED"); | 45 | + }, |
42 | - } | 46 | + |
47 | + deactivate: function () { | ||
48 | + tts.cancelTraffic(); | ||
49 | + $log.debug("Traffic overlay DEACTIVATED"); | ||
50 | + }, | ||
51 | + | ||
52 | + // detail panel button definitions | ||
53 | + // (keys match button identifiers, also defined in TrafficOverlay.java) | ||
54 | + buttons: { | ||
55 | + showDeviceFlows: { | ||
56 | + gid: 'flows', | ||
57 | + tt: 'Show Device Flows', | ||
58 | + cb: function (data) { tts.showDeviceLinkFlows(); } | ||
59 | + }, | ||
43 | 60 | ||
44 | - function deactivateTraffic() { | 61 | + showRelatedTraffic: { |
45 | - $log.debug("Topology traffic overlay DEACTIVATED"); | 62 | + gid: 'relatedIntents', |
63 | + tt: 'Show Related Traffic', | ||
64 | + cb: function (data) { tts.showRelatedIntents(); } | ||
46 | } | 65 | } |
66 | + }, | ||
47 | 67 | ||
68 | + // key bindings for traffic overlay toolbar buttons | ||
69 | + // NOTE: fully qual. button ID is derived from overlay-id and key-name | ||
70 | + keyBindings: { | ||
71 | + 0: { | ||
72 | + cb: function () { tts.cancelTraffic(); }, | ||
73 | + tt: 'Cancel traffic monitoring', | ||
74 | + gid: 'xMark' | ||
75 | + }, | ||
76 | + | ||
77 | + A: { | ||
78 | + cb: function () { tts.showAllFlowTraffic(); }, | ||
79 | + tt: 'Monitor all traffic using flow stats', | ||
80 | + gid: 'allTraffic' | ||
81 | + }, | ||
82 | + Q: { | ||
83 | + cb: function () { tts.showAllPortTraffic(); }, | ||
84 | + tt: 'Monitor all traffic using port stats', | ||
85 | + gid: 'allTraffic' | ||
86 | + }, | ||
87 | + F: { | ||
88 | + cb: function () { tts.showDeviceLinkFlows(); }, | ||
89 | + tt: 'Show device link flows', | ||
90 | + gid: 'flows' | ||
91 | + }, | ||
92 | + V: { | ||
93 | + cb: function () { tts.showRelatedIntents(); }, | ||
94 | + tt: 'Show all related intents', | ||
95 | + gid: 'relatedIntents' | ||
96 | + }, | ||
97 | + leftArrow: { | ||
98 | + cb: function () { tts.showPrevIntent(); }, | ||
99 | + tt: 'Show previous related intent', | ||
100 | + gid: 'prevIntent' | ||
101 | + }, | ||
102 | + rightArrow: { | ||
103 | + cb: function () { tts.showNextIntent(); }, | ||
104 | + tt: 'Show next related intent', | ||
105 | + gid: 'nextIntent' | ||
106 | + }, | ||
107 | + W: { | ||
108 | + cb: function () { tts.showSelectedIntentTraffic(); }, | ||
109 | + tt: 'Monitor traffic of selected intent', | ||
110 | + gid: 'intentTraffic' | ||
111 | + }, | ||
112 | + | ||
113 | + _keyOrder: [ | ||
114 | + '0', 'A', 'Q', 'F', 'V', 'leftArrow', 'rightArrow', 'W' | ||
115 | + ] | ||
116 | + }, | ||
117 | + | ||
118 | + hooks: { | ||
119 | + // hook for handling escape key | ||
120 | + escape: function () { | ||
121 | + // Must return true to consume ESC, false otherwise. | ||
122 | + return tts.cancelTraffic(); | ||
123 | + }, | ||
124 | + | ||
125 | + // hooks for when the selection changes... | ||
126 | + empty: function () { | ||
127 | + tts.cancelTraffic(); | ||
128 | + }, | ||
129 | + single: function (data) { | ||
130 | + tts.requestTrafficForMode(); | ||
131 | + }, | ||
132 | + multi: function (selectOrder) { | ||
133 | + tts.requestTrafficForMode(); | ||
134 | + tov.addDetailButton('showRelatedTraffic'); | ||
135 | + } | ||
136 | + } | ||
137 | + }; | ||
48 | 138 | ||
49 | // invoke code to register with the overlay service | 139 | // invoke code to register with the overlay service |
50 | angular.module('ovTopo') | 140 | angular.module('ovTopo') |
51 | - .run(['$log', 'TopoOverlayService', | 141 | + .run(['$log', 'TopoOverlayService', 'TopoTrafficService', |
52 | 142 | ||
53 | - function (_$log_, tov) { | 143 | + function (_$log_, _tov_, _tts_) { |
54 | $log = _$log_; | 144 | $log = _$log_; |
145 | + tov = _tov_; | ||
146 | + tts = _tts_; | ||
55 | tov.register(overlay); | 147 | tov.register(overlay); |
56 | }]); | 148 | }]); |
57 | 149 | ... | ... |
-
Please register or login to post a comment