Simon Hunt

GUI -- Completed implementation of Instance events (add, update, remove)

- fixed instance color selection (using cat7() function)
- miscellaneous additions to utility functions.
- etc. and so on...

Change-Id: I61895489ccc60fa17beda9e920e65742e0f2c526
......@@ -132,8 +132,97 @@
$log.warn('SvgUtilService: loadGlow -- To Be Implemented');
}
// --- Ordinal scales for 7 values.
// TODO: tune colors for light and dark themes
// Note: These colors look good on the white background. Still, need to tune for dark.
// blue brown brick red sea green purple dark teal lime
var lightNorm = ['#3E5780', '#78533B', '#CB4D28', '#018D61', '#8A2979', '#006D73', '#56AF00'],
lightMute = ['#A8B8CC', '#CCB3A8', '#FFC2BD', '#96D6BF', '#D19FCE', '#8FCCCA', '#CAEAA4'],
darkNorm = ['#3E5780', '#78533B', '#CB4D28', '#018D61', '#8A2979', '#006D73', '#56AF00'],
darkMute = ['#A8B8CC', '#CCB3A8', '#FFC2BD', '#96D6BF', '#D19FCE', '#8FCCCA', '#CAEAA4'];
var colors= {
light: {
norm: d3.scale.ordinal().range(lightNorm),
mute: d3.scale.ordinal().range(lightMute)
},
dark: {
norm: d3.scale.ordinal().range(darkNorm),
mute: d3.scale.ordinal().range(darkMute)
}
};
function cat7() {
$log.warn('SvgUtilService: cat7 -- To Be Implemented');
var tcid = 'd3utilTestCard';
function getColor(id, muted, theme) {
// NOTE: since we are lazily assigning domain ids, we need to
// get the color from all 4 scales, to keep the domains
// in sync.
var ln = colors.light.norm(id),
lm = colors.light.mute(id),
dn = colors.dark.norm(id),
dm = colors.dark.mute(id);
if (theme === 'dark') {
return muted ? dm : dn;
} else {
return muted ? lm : ln;
}
}
function testCard(svg) {
var g = svg.select('g#' + tcid),
dom = d3.range(7),
k, muted, theme, what;
if (!g.empty()) {
g.remove();
} else {
g = svg.append('g')
.attr('id', tcid)
.attr('transform', 'scale(4)translate(20,20)');
for (k=0; k<4; k++) {
muted = k%2;
what = muted ? ' muted' : ' normal';
theme = k < 2 ? 'light' : 'dark';
dom.forEach(function (id, i) {
var x = i * 20,
y = k * 20,
f = get(id, muted, theme);
g.append('circle').attr({
cx: x,
cy: y,
r: 5,
fill: f
});
});
g.append('rect').attr({
x: 140,
y: k * 20 - 5,
width: 32,
height: 10,
rx: 2,
fill: '#888'
});
g.append('text').text(theme + what)
.attr({
x: 142,
y: k * 20 + 2,
fill: 'white'
})
.style('font-size', '4pt');
}
}
}
return {
testCard: testCard,
getColor: getColor
};
}
function translate(x, y) {
......
......@@ -45,7 +45,38 @@
// Returns true if all names in the array are defined as functions
// on the given api object; false otherwise.
// Also returns false if there are properties on the api that are NOT
// listed in the array of names.
function areFunctions(api, fnNames) {
var fnLookup = {},
extraFound = false;
if (!isA(fnNames)) {
return false;
}
var n = fnNames.length,
i, name;
for (i=0; i<n; i++) {
name = fnNames[i];
if (!isF(api[name])) {
return false;
}
fnLookup[name] = true;
}
// check for properties on the API that are not listed in the array,
angular.forEach(api, function (value, key) {
if (!fnLookup[key]) {
extraFound = true;
}
});
return !extraFound;
}
// Returns true if all names in the array are defined as functions
// on the given api object; false otherwise. This is a non-strict version
// that does not care about other properties on the api.
function areFunctionsNonStrict(api, fnNames) {
if (!isA(fnNames)) {
return false;
}
......@@ -71,6 +102,21 @@
};
}
// search through an array of objects, looking for the one with the
// tagged property matching the given key. tag defaults to 'id'.
// returns the index of the matching object, or -1 for no match.
function find(key, array, tag) {
var _tag = tag || 'id',
idx, n, d;
for (idx = 0, n = array.length; idx < n; idx++) {
d = array[idx];
if (d[_tag] === key) {
return idx;
}
}
return -1;
}
angular.module('onosUtil')
.factory('FnService', ['$window', function (_$window_) {
$window = _$window_;
......@@ -82,7 +128,9 @@
isO: isO,
contains: contains,
areFunctions: areFunctions,
windowSize: windowSize
areFunctionsNonStrict: areFunctionsNonStrict,
windowSize: windowSize,
find: find
};
}]);
......
......@@ -30,7 +30,9 @@
var evHandler = {
showSummary: showSummary,
addInstance: addInstance
addInstance: addInstance,
updateInstance: updateInstance,
removeInstance: removeInstance
// TODO: implement remaining handlers..
};
......@@ -51,6 +53,16 @@
tis.addInstance(ev.payload);
}
function updateInstance(ev) {
$log.debug(' **** Update Instance **** ', ev.payload);
tis.updateInstance(ev.payload);
}
function removeInstance(ev) {
$log.debug(' **** Remove Instance **** ', ev.payload);
tis.removeInstance(ev.payload);
}
// ==========================
var dispatcher = {
......
......@@ -23,7 +23,7 @@
'use strict';
// injected refs
var $log, ps, sus, gs;
var $log, ps, sus, gs, ts, fs;
// configuration
var instCfg = {
......@@ -79,6 +79,23 @@
}
}
function removeInstance(data) {
var id = data.id,
d = onosInstances[id];
if (d) {
var idx = fs.find(id, onosOrder);
if (idx >= 0) {
onosOrder.splice(idx, 1);
}
delete onosInstances[id];
updateInstances();
} else {
logicError('removeInstance lookup fail. ID = "' + id + '"');
}
}
// ==========================
function computeDim(self) {
var css = window.getComputedStyle(self);
return {
......@@ -143,9 +160,7 @@
}
function instColor(id, online) {
// TODO: fix this..
//return cat7.get(id, !online, network.view.getTheme());
return '#3E5780';
return sus.cat7().getColor(id, !online, ts.theme());
}
// ==============================
......@@ -288,17 +303,22 @@
angular.module('ovTopo')
.factory('TopoInstService',
['$log', 'PanelService', 'SvgUtilService', 'GlyphService',
'ThemeService', 'FnService',
function (_$log_, _ps_, _sus_, _gs_) {
function (_$log_, _ps_, _sus_, _gs_, _ts_, _fs_) {
$log = _$log_;
ps = _ps_;
sus = _sus_;
gs = _gs_;
ts = _ts_;
fs = _fs_;
return {
initInst: initInst,
destroyInst: destroyInst,
addInstance: addInstance
addInstance: addInstance,
updateInstance: updateInstance,
removeInstance: removeInstance
};
}]);
}());
......
......@@ -36,7 +36,7 @@ describe('factory: fw/remote/wsevent.js', function () {
it('should define api functions', function () {
expect(fs.areFunctions(wse, [
'sendEvent'
'sendEvent', 'resetSid'
])).toBeTruthy();
});
......
......@@ -39,15 +39,57 @@ describe('factory: fw/svg/svgUtil.js', function() {
it('should define api functions', function () {
expect(fs.areFunctions(sus, [
'createDragBehavior', 'loadGlow', 'cat7', 'translate'
'createDragBehavior', 'loadGlow', 'cat7', 'translate', 'stripPx'
])).toBeTruthy();
});
// TODO: add unit tests for drag behavior
// TODO: add unit tests for loadGlow
// TODO: add unit tests for cat7
// === cat7
it('should define two methods on the api', function () {
var cat7 = sus.cat7();
expect(fs.areFunctions(cat7, [
'testCard', 'getColor'
])).toBeTruthy();
});
it('should provide a certain shade of blue', function () {
expect(sus.cat7().getColor('foo', false, 'light')).toEqual('#3E5780');
});
it('should not matter what the ID really is for shade of blue', function () {
expect(sus.cat7().getColor('bar', false, 'light')).toEqual('#3E5780');
});
it('should provide different shade of blue for muted', function () {
expect(sus.cat7().getColor('foo', true, 'light')).toEqual('#A8B8CC');
});
it('should provide an alternate (dark) shade of blue', function () {
expect(sus.cat7().getColor('foo', false, 'dark')).toEqual('#3E5780');
});
it('should provide an alternate (dark) shade of blue for muted', function () {
expect(sus.cat7().getColor('foo', true, 'dark')).toEqual('#A8B8CC');
});
it('should iterate across the colors', function () {
expect(sus.cat7().getColor('foo', false, 'light')).toEqual('#3E5780');
expect(sus.cat7().getColor('bar', false, 'light')).toEqual('#78533B');
expect(sus.cat7().getColor('baz', false, 'light')).toEqual('#CB4D28');
expect(sus.cat7().getColor('goo', false, 'light')).toEqual('#018D61');
expect(sus.cat7().getColor('zoo', false, 'light')).toEqual('#8A2979');
expect(sus.cat7().getColor('pip', false, 'light')).toEqual('#006D73');
expect(sus.cat7().getColor('sdh', false, 'light')).toEqual('#56AF00');
// and cycle back to the first color for item #8
expect(sus.cat7().getColor('bri', false, 'light')).toEqual('#3E5780');
// and return the same color for the same ID
expect(sus.cat7().getColor('zoo', false, 'light')).toEqual('#8A2979');
});
// === translate()
......
......@@ -48,7 +48,7 @@ describe('factory: fw/svg/zoom.js', function() {
function verifyZoomerApi() {
expect(fs.areFunctions(zoomer, [
'panZoom', 'reset', 'translate', 'scale'
'panZoom', 'reset', 'translate', 'scale', 'scaleExtent'
])).toBeTruthy();
}
......
......@@ -38,7 +38,6 @@ describe('factory: fw/util/fn.js', function() {
$window.innerHeight = 200;
}));
// === Tests for isF()
it('isF(): null for undefined', function () {
expect(fs.isF(undefined)).toBeNull();
......@@ -181,15 +180,33 @@ describe('factory: fw/util/fn.js', function() {
b: 'not-a-function'
}, ['b', 'a'])).toBeFalsy();
});
it('areFunctions(): extraneous stuff ignored', function () {
it('areFunctions(): extraneous stuff NOT ignored', function () {
expect(fs.areFunctions({
a: function () {},
b: function () {},
c: 1,
d: 'foo'
}, ['a', 'b'])).toBeFalsy();
});
it('areFunctions(): extraneous stuff ignored (alternate fn)', function () {
expect(fs.areFunctionsNonStrict({
a: function () {},
b: function () {},
c: 1,
d: 'foo'
}, ['a', 'b'])).toBeTruthy();
});
// == use the now-tested areFunctions on our own api:
it('should define api functions', function () {
expect(fs.areFunctions(fs, [
'isF', 'isA', 'isS', 'isO', 'contains',
'areFunctions', 'areFunctionsNonStrict', 'windowSize', 'find'
])).toBeTruthy();
});
// === Tests for windowSize()
it('windowSize(): noargs', function () {
......@@ -215,4 +232,7 @@ describe('factory: fw/util/fn.js', function() {
expect(dim.width).toEqual(199);
expect(dim.height).toEqual(99);
});
// TODO: write unit tests for find()
});
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
ONOS GUI -- Topo View -- Topo Instance Service - Unit Tests
*/
describe('factory: view/topo/topoInst.js', function() {
var $log, fs, tis;
beforeEach(module('ovTopo', 'onosUtil', 'onosLayer'));
beforeEach(inject(function (_$log_, FnService, TopoInstService) {
$log = _$log_;
fs = FnService;
tis = TopoInstService;
}));
it('should define TopoInstService', function () {
expect(tis).toBeDefined();
});
it('should define api functions', function () {
expect(fs.areFunctions(tis, [
'initInst', 'destroyInst',
'addInstance', 'updateInstance', 'removeInstance'
])).toBeTruthy();
});
// TODO: more tests...
});
{
"event": "addInstance",
"payload": {
"id": "instB",
"ip": "123.22.33.241",
"online": true,
"uiAttached": false,
"switches": 14,
"labels": [
"instB",
"123.22.33.241"
]
}
}
{
"event": "addInstance",
"payload": {
"id": "instC",
"ip": "123.22.33.124",
"online": true,
"uiAttached": false,
"switches": 7,
"labels": [
"instC",
"123.22.33.124"
]
}
}
{
"event": "updateInstance",
"payload": {
"id": "instB",
"ip": "123.22.33.241",
"online": false,
"uiAttached": false,
"switches": 14,
"labels": [
"instB",
"123.22.33.241"
]
}
}
{
"event": "removeInstance",
"payload": {
"id": "instB",
"ip": "123.22.33.241",
"online": false,
"uiAttached": false,
"switches": 14,
"labels": [
"instB",
"123.22.33.241"
]
}
}
{
"event": "addInstance",
"payload": {
"id": "instD",
"ip": "123.33.44.55",
"online": true,
"uiAttached": false,
"switches": 133,
"labels": [
"instD",
"123.33.44.55"
]
}
}