diff options
Diffstat (limited to 'ui/imports/ui/components/flow-graph')
-rw-r--r-- | ui/imports/ui/components/flow-graph/flow-graph.html | 17 | ||||
-rw-r--r-- | ui/imports/ui/components/flow-graph/flow-graph.js | 383 | ||||
-rw-r--r-- | ui/imports/ui/components/flow-graph/flow-graph.styl | 18 |
3 files changed, 418 insertions, 0 deletions
diff --git a/ui/imports/ui/components/flow-graph/flow-graph.html b/ui/imports/ui/components/flow-graph/flow-graph.html new file mode 100644 index 0000000..5dd006d --- /dev/null +++ b/ui/imports/ui/components/flow-graph/flow-graph.html @@ -0,0 +1,17 @@ +<!-- +######################################################################################## +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +######################################################################################## + --> +<template name="FlowGraph"> + <div class="os-flow-graph"> + <div class="sm-graph-container"> + <svg width="500" height="400" class="sm-graph"></svg> + </div> + </div> +</template> diff --git a/ui/imports/ui/components/flow-graph/flow-graph.js b/ui/imports/ui/components/flow-graph/flow-graph.js new file mode 100644 index 0000000..fd2c859 --- /dev/null +++ b/ui/imports/ui/components/flow-graph/flow-graph.js @@ -0,0 +1,383 @@ +///////////////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others / +// / +// All rights reserved. This program and the accompanying materials / +// are made available under the terms of the Apache License, Version 2.0 / +// which accompanies this distribution, and is available at / +// http://www.apache.org/licenses/LICENSE-2.0 / +///////////////////////////////////////////////////////////////////////////////////////// +/* + * Template Component: FlowGraph + */ + +//import { Meteor } from 'meteor/meteor'; +import { Template } from 'meteor/templating'; +import { ReactiveDict } from 'meteor/reactive-dict'; +import { SimpleSchema } from 'meteor/aldeed:simple-schema'; +// We import d3 v4 not into d3 because old code network visualization use globaly d3 v3. +import * as d3v4 from 'd3'; +import * as R from 'ramda'; +import { Statistics } from '/imports/api/statistics/statistics'; +import { createGraphQuerySchema } from '/imports/api/statistics/helpers'; +//import * as BSON from 'bson'; + +import './flow-graph.html'; + +/* + * Lifecycles + */ + +Template.FlowGraph.onCreated(function() { + let instance = this; + + instance.state = new ReactiveDict(); + instance.state.setDefault({ + environment: instance.data.environment, + object_id: instance.data.object_id, + type: instance.data.type, + flowType: instance.data.flowType, + sourceMacAddress: instance.data.sourceMacAddress, + destinationMacAddress: instance.data.destinationMacAddress, + sourceIPv4Address: instance.data.sourceIPv4Address, + destinationIPv4Address: instance.data.destinationIPv4Address, + simulateGraph: instance.data.simulateGraph, + yScale: instance.data.yScale, + timeDeltaNano: 0, + timeDeltaSeconds: 0 + }); + + instance.autorun(() => { + new SimpleSchema({ + env: { type: String }, + object_id: { type: String }, + type: { type: String }, + flowType: { type: String }, + sourceMacAddress: { type: String, optional: true }, + destinationMacAddress: { type: String, optional: true }, + sourceIPv4Address: { type: String, optional: true }, + destinationIPv4Address: { type: String, optional: true }, + simulateGraph: { type: Boolean, optional: true }, + yScale: { type: Number, optional: true }, + startDateTime: { type: String, optional: true }, + }).validate(Template.currentData()); + + let data = Template.currentData(); + + instance.state.set('environment', data.env); + instance.state.set('object_id', data.object_id); + instance.state.set('type', data.type); + instance.state.set('flowType', data.flowType); + instance.state.set('sourceMacAddress', data.sourceMacAddress); + instance.state.set('destinationMacAddress', data.destinationMacAddress); + instance.state.set('sourceIPv4Address', data.sourceIPv4Address); + instance.state.set('destinationIPv4Address', data.destinationIPv4Address); + instance.state.set('simulateGraph', data.simulateGraph); + instance.state.set('yScale', data.yScale); + + let startDateTime = R.ifElse(R.isNil, (_p) => { return moment();}, moment)(data.startDateTime); + let deltaSeconds = moment().diff(startDateTime, 'seconds'); + //let deltaNano = deltaMili * 1000000; + //instance.state.set('timeDeltaNano', deltaNano); + instance.state.set('timeDeltaSeconds', deltaSeconds); + + //let timeStart = startDateTime.valueOf() * 1000000; + let timeStart = startDateTime.unix(); + + //debugger; + // debug purpose: + //let timeStart = 1486661034810432900;// 1486661034810432945; + //let timeDeltaNano = Date.now() * 1000000 - timeStart; + //instance.state.set('timeDeltaNano', timeDeltaNano); + // debug end + + instance.subscribe('statistics!graph-frames', { + env: data.env, + object_id: data.object_id, + type: data.type, + flowType: data.flowType, + timeStart: timeStart, + sourceMacAddress: data.sourceMacAddress, + destinationMacAddress: data.destinationMacAddress, + sourceIPv4Address: data.sourceIPv4Address, + destinationIPv4Address: data.destinationIPv4Address + }); + }); + +}); + +Template.FlowGraph.onDestroyed(function () { + (function (d3) { + let instance = Template.instance(); + let graphContainer = instance.$('.sm-graph'); + var svg = d3.select(graphContainer[0]); + + svg.interrupt(); + var lineSvg = svg.select('g g path.line'); + lineSvg.interrupt(); + })(d3v4); +}); + +Template.FlowGraph.rendered = function() { + let instance = Template.instance(); + + instance.autorun(() => { + + let environment = instance.state.get('environment'); + let object_id = instance.state.get('object_id'); + let type = instance.state.get('type'); + let flowType = instance.state.get('flowType'); + let sourceMacAddress = instance.state.get('sourceMacAddress'); + let destinationMacAddress = instance.state.get('destinationMacAddress'); + let sourceIPv4Address = instance.state.get('sourceIPv4Address'); + let destinationIPv4Address = instance.state.get('destinationIPv4Address'); + let simulateGraph = instance.state.get('simulateGraph'); + let yScale = instance.state.get('yScale'); + //let timeDeltaNano = instance.state.get('timeDeltaNano'); + let timeDeltaSeconds = instance.state.get('timeDeltaSeconds'); + + let graphContainer = instance.$('.sm-graph'); + + generateAllGraph( + d3v4, + graphContainer, + environment, + object_id, + type, + flowType, + sourceMacAddress, + destinationMacAddress, + sourceIPv4Address, + destinationIPv4Address, + simulateGraph, + yScale, + //timeDeltaNano + timeDeltaSeconds + ); + + }); +}; + +/* + * Events + */ + +Template.FlowGraph.events({ +}); + +/* + * Helpers + */ + +Template.FlowGraph.helpers({ +}); + +function generateAllGraph( + d3, + graphContainer, + environment, + object_id, + type, + flowType, + sourceMacAddress, + destinationMacAddress, + sourceIPv4Address, + destinationIPv4Address, + simulateGraph, + yScale, + //timeDeltaNano) { + timeDeltaSeconds) { + + let dataRetrivalFn = createDataRetrivalFn( + d3, + simulateGraph, + environment, + object_id, + type, + flowType, + sourceMacAddress, + destinationMacAddress, + sourceIPv4Address, + destinationIPv4Address, + yScale + ); + + generateGraph( + d3, + dataRetrivalFn, + graphContainer, + //timeDeltaNano, + timeDeltaSeconds, + yScale + ); +} + +function createDataRetrivalFn( + d3, + simulateGraph, + environment, + object_id, + type, + flowType, + sourceMacAddress, + destinationMacAddress, + sourceIPv4Address, + destinationIPv4Address, + yScale +) { + + if (simulateGraph) { + let random = d3.randomNormal(0, yScale); + return function (_start, _end) { + return { + averageThroughput: random() + }; + }; + } + + //return function (startNano, endNano) { + return function (startSeconds, endSeconds) { + //let startBson = BSON.Long.fromNumber(startNano); + //let endBson = BSON.Long.fromNumber(endNano); + //let startBson = startNano; + //let endBson = endNano; + + let query = createGraphQuerySchema( + environment, + object_id, + type, + flowType, + //startBson, + //endBson, + startSeconds, + endSeconds, + sourceMacAddress, + destinationMacAddress, + sourceIPv4Address, + destinationIPv4Address); + + return Statistics.findOne(query); + }; + +/* + return function (timeStart, timeEnd, callback) { + Meteor.call('statistics!graph-frames', { + env: environment, + object_id: object_id, + type: type, + flowType: flowType, + timeStart: timeStart, + timeEnd: timeEnd, + sourceMacAddress: sourceMacAddress, + destinationMacAddress: destinationMacAddress, + sourceIPv4Address: sourceIPv4Address, + destinationIPv4Address: destinationIPv4Address + }, (_err, res) => { + callback(res); + }); + + }; + */ +} + +function generateGraph( + d3, + dataRetrivalFn, + graphContainer, + //timeDeltaNano, + timeDeltaSeconds, + yScale +) { + var n = 40; + + let data = d3.range(n).map(R.always(0)); + let svg = d3.select(graphContainer[0]); + let margin = {top: 20, right: 20, bottom: 20, left: 80}; + let width = +svg.attr('width') - margin.left - margin.right; + let height = +svg.attr('height') - margin.top - margin.bottom; + + svg.interrupt(); + var lineSvg = svg.select('g g path.line'); + lineSvg.interrupt(); + + svg.select('g').remove(); + + var g = svg.append('g').attr( + 'transform', 'translate(' + margin.left + ',' + margin.top + ')'); + + var x = d3.scaleLinear() + .domain([0, n - 1]) + .range([0, width]); + + var y = d3.scaleLinear() + .domain([0, yScale]) + .range([height, 0]); + + var line = d3.line() + .x(function(d, i) { return x(i); }) + .y(function(d, _i) { return y(d); }); + + g.append('defs').append('clipPath') + .attr('id', 'clip') + .append('rect') + .attr('width', width) + .attr('height', height); + + g.append('g') + .attr('class', 'axis axis--x') + .attr('transform', 'translate(0,' + y(0) + ')') + .call(d3.axisBottom(x)); + + g.append('g') + .attr('class', 'axis axis--y') + .call(d3.axisLeft(y)); + + g.append('g') + .attr('clip-path', 'url(#clip)') + .append('path') + .datum(data) + .attr('class', 'line') + .transition() + .duration(500) + .ease(d3.easeLinear) + .on('start', tick); + + //let timeStart = (Date.now() * 1000000) - timeDeltaNano; + let timeStart = moment().unix() - timeDeltaSeconds; + let timeEnd; + let dataPoint; + let lastDataPoint = 0; + + function tick() { + //timeEnd = (Date.now() * 1000000) - timeDeltaNano; + timeEnd = (moment().unix()) - timeDeltaSeconds; + + let statItem = dataRetrivalFn(timeStart, timeEnd); + + if (!R.isNil(statItem)) { + dataPoint = statItem.averageThroughput; + } else { + dataPoint = lastDataPoint; + } + + data.push(dataPoint); + + //timeStart = timeEnd - (4 * 1000000000); + timeStart = timeEnd; + + // Redraw the line. + d3.select(this) + .attr('d', line) + .attr('transform', null); + + // Slide it to the left. + d3.active(this) + .attr('transform', 'translate(' + x(-1) + ',0)') + .transition() + .on('start', tick); + + // Pop the old data point off the front. + data.shift(); + + lastDataPoint = dataPoint; + } +} diff --git a/ui/imports/ui/components/flow-graph/flow-graph.styl b/ui/imports/ui/components/flow-graph/flow-graph.styl new file mode 100644 index 0000000..e858fb9 --- /dev/null +++ b/ui/imports/ui/components/flow-graph/flow-graph.styl @@ -0,0 +1,18 @@ +/* Set the component style here */ +// "FlowGraph" +.os-flow-graph + + path + stroke: steelblue; + stroke-width: 2; + fill: none; + + line + stroke: black; + + text + font-family: Arial; + font-size: 9pt; + + .sm-graph + background-color:#FDFEFF; |