topo2Instance.js 7.32 KB
(function () {
    'use strict';

    // injected refs
    var $log,
        ps,
        sus,
        gs,
        ts,
        fs,
        flash;

    // api from topo
    var api;

    // configuration
    var showLogicErrors = true,
        idIns = 'topo-p-instance',
        instOpts = {
            edge: 'left',
            width: 20
        };

    // internal state
    var onosInstances,
        onosOrder,
        oiShowMaster,
        oiBox;


    function addInstance(data) {
        var id = data.id;

        if (onosInstances[id]) {
            updateInstance(data);
            return;
        }
        onosInstances[id] = data;
        onosOrder.push(data);
        updateInstances();
    }

    function updateInstance(data) {
        var id = data.id,
            d = onosInstances[id];
        if (d) {
            angular.extend(d, data);
            updateInstances();
        } else {
            logicError('updateInstance: lookup fail: ID = "' + id + '"');
        }
    }

    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 clickInst(d) {
        var el = d3.select(this),
            aff = el.classed('affinity');
        if (!aff) {
            setAffinity(el, d);
        } else {
            cancelAffinity();
        }
    }

    function setAffinity(el, d) {
        d3.selectAll('.onosInst')
            .classed('mastership', true)
            .classed('affinity', false);
        el.classed('affinity', true);

        // suppress all elements except nodes whose master is this instance
        api.showMastership(d.id);
        oiShowMaster = true;
    }

    function cancelAffinity() {
        d3.selectAll('.onosInst')
            .classed('mastership affinity', false);

        api.showMastership(null);
        oiShowMaster = false;
    }

    function attachUiBadge(svg) {
        gs.addGlyph(svg, 'uiAttached', 24, true, [14, 54])
            .classed('badgeIcon uiBadge', true);
    }

    function attachReadyBadge(svg) {
        gs.addGlyph(svg, 'checkMark', 16, true, [18, 40])
            .classed('badgeIcon readyBadge', true);
    }

    function instColor(id, online) {
        return sus.cat7().getColor(id, !online, ts.theme());
    }

    // ==============================

    function updateInstances() {
        var rox = 5,
            roy = 5,
            rw = 160,
            rhh = 30,
            rbh = 45,
            tx = 48,
            instSvg = {
                width: 170,
                height: 85,
                viewBox: '0 0 170 85'
            },
            headRect = {
                x: rox,
                y: roy,
                width: rw,
                height: rhh
            },
            bodyRect = {
                x: rox,
                y: roy + rhh,
                width: rw,
                height: rbh
            },
            titleAttr = {
                class: 'instTitle',
                x: tx,
                y: 27
            };

        var onoses = oiBox.el().selectAll('.onosInst')
                .data(onosOrder, function (d) { return d.id; });

        function nSw(n) {
            return 'Devices: ' + n;
        }

        // operate on existing onos instances if necessary
        onoses.each(function (d) {
            var el = d3.select(this),
                svg = el.select('svg');

            // update online state
            el.classed('online', d.online);
            el.classed('ready', d.ready);

            // update ui-attached state
            svg.select('use.uiBadge').remove();
            if (d.uiAttached) {
                attachUiBadge(svg);
            }

            function updAttr(id, value) {
                svg.select('text.instLabel.' + id).text(value);
            }

            updAttr('ip', d.ip);
            updAttr('ns', nSw(d.switches));
        });


        // operate on new onos instances
        var entering = onoses.enter()
            .append('div')
            .classed('onosInst', true)
            .classed('online', function (d) { return d.online; })
            .classed('ready', function (d) { return d.ready; })
            .on('click', clickInst);

        entering.each(function (d) {
            var el = d3.select(this),
                svg = el.append('svg').attr(instSvg);

            svg.append('rect').attr(headRect);
            svg.append('rect').attr(bodyRect);

            gs.addGlyph(svg, 'bird', 20, false, [15, 10])
                .classed('badgeIcon bird', true);

            attachReadyBadge(svg);

            if (d.uiAttached) {
                attachUiBadge(svg);
            }

            svg.append('text')
                .attr(titleAttr)
                .text(d.id);

            var ty = 55;
            function addAttr(id, label) {
                svg.append('text').attr({
                    class: 'instLabel ' + id,
                    x: tx,
                    y: ty
                }).text(label);
                ty += 18;
            }

            addAttr('ip', d.ip);
            addAttr('ns', nSw(d.switches));
        });

        // operate on existing + new onoses here
        // set the affinity colors...
        onoses.each(function (d) {

            var el = d3.select(this),
                rect = el.select('svg').select('rect'),
                col = instColor(d.id, d.online);

            rect.style('fill', col);
        });

        // adjust the panel size appropriately...
        oiBox.width(instSvg.width * onosOrder.length);
        oiBox.height(instSvg.height);

        // remove any outgoing instances
        onoses.exit().remove();
    }


    // ==========================

    function logicError(msg) {
        if (showLogicErrors) {
            $log.warn('TopoInstService: ' + msg);
        }
    }

    function initInst(_api_) {
        api = _api_;
        oiBox = ps.createPanel(idIns, instOpts);
        oiBox.show();

        onosInstances = {};
        onosOrder = [];
        oiShowMaster = false;

        // we want to update the instances, each time the theme changes
        ts.addListener(updateInstances);
    }

    function destroyInst() {
        ts.removeListener(updateInstances);

        ps.destroyPanel(idIns);
        oiBox = null;

        onosInstances = {};
        onosOrder = [];
        oiShowMaster = false;
    }

    function allInstances(data) {
        $log.debug('Update all instances', data);

        var members = data.members;

        members.forEach(function (member) {
            addInstance(member);
        });
    }

    angular.module('ovTopo2')
        .factory('Topo2InstanceService',
        ['$log', 'PanelService', 'SvgUtilService', 'GlyphService',
        'ThemeService', 'FnService', 'FlashService',

        function (_$log_, _ps_, _sus_, _gs_, _ts_, _fs_, _flash_) {
            $log = _$log_;
            ps = _ps_;
            sus = _sus_;
            gs = _gs_;
            ts = _ts_;
            fs = _fs_;
            flash = _flash_;

            return {
                initInst: initInst,
                allInstances: allInstances
            };
        }]);

}());