Search code examples
javascriptd3.jspolymerpubnubcubism.js

Pubnub and Cubism.js


I'm using to power a dashboard I'm developing. All my widgets are based on code like this:

Polymer({
    is: 'widget-horizon',
    properties: { ... },
    ready: function() {
        registerCallback(this.channel, function (data) {

        }); 
    }
});

Then when that pubnub channel receives a message, the callback is called and passes the data. For this particular chart, the data looks something like this:

{
    "id": "RegResponseTimes",
    "type": null,
    "timestamp": "2016-10-14T11:12:13.2313043-05:00",
    "data": [
        {
            "label": "Metric1",
            "Value": 0.05
        },
        {
            "label": "Metric2",
            "Value": 0.17
        }
    ]
}

Now I'm trying to develop a widget based on cubism.js, but all the documentation is targeted at pulling data, more specifically using Cube or Graphite.

I'm having lots of trouble integrating the cubism code into my callback. I'm stumbling over all the language like "Metric Accessor" and "Extents".


After much trial and error, I kind of got one value working but here are the issues I need to fix:

  1. There is no actual chart, just the value.
  2. I need one horizon for each data set.

Here is what it currently looks like:

enter image description here

And below is my full code (it's a polymer component). Major changes from the "random" sample include:

  1. Stopping and starting the context. I wasn't sure how else to keep it from ticking while I wasn't getting data.
  2. Instead of pushing a random number, push one of the values as a POC.

        </style>
        <strong id="widgetName">{{name}}</strong>
        <div id="chart1"></div>
    </template>
    <script>
    
        var data = [];
    
        Polymer({
            is: 'widget-horizon',
            properties: {
                dataid: {
                    type: String
                },
                name: {
                    type: String
                },
                gethistory: {
                    type: Number
                },
                testvalue: {
                    type: Number
                },
                isloading: {
                    type: Boolean,
                    value: true
                }
            },
            ready: function() {
                var self = this;
                subscribe(self.dataid);
                getHistory(self.dataid,self.gethistory);
                registerCallback(this.dataid, function (data) {
                    context.start();
                    var y = data.data[0];
                    self.testvalue = y.Value;
                    self.isloading = false;
                });
    
                var context = cubism.context()
                        .serverDelay(0)
                        .clientDelay(0)
                        .step(1e4)
                        .size(600);
                var foo = setval("foo");
    
                var chart = self.$$('#chart1');
                d3.select(chart).call(function(div) {
                    div.append("div")
                            .attr("class", "axis")
                            .call(context.axis().orient("top"));
                    div.selectAll(".horizon")
                            .data([foo])
                            .enter().append("div")
                            .attr("class", "horizon")
                            .call(context.horizon().extent([0, 120]));
                    div.append("div")
                            .attr("class", "rule")
                            .call(context.rule());
    
                });
    
    
                function setval(name) {
                    var value = 0,
                            values = [],
                            last;
                    return context.metric(function(start, stop, step, callback) {
                        start = +start, stop = +stop;
                        if (isNaN(last)) last = start;
                        while (last < stop) {
                            last += step;
                            value = self.testvalue;
                            if (value != "") {
                                values.push(self.testvalue);
                                context.stop();
                            }
                        }
                        callback(null, values = values.slice((start - stop) / step));
                    }, name);
                }
    
                context.on("focus", function(i) {
                    d3.selectAll(".value").style("right", i == null ? null : context.size() - i + "px");
                });
            }
        });
    </script>
    


Solution

  • I got it working. Here is my full callback:

    registerCallback(this.dataid, function (data) {
        if (self.isloading) {
            for (var i = 0; i < data.data.length; i++){
                datas[i] = setval(data.data[i].MessageBodyClassName);
            }
    
            d3.select(chart).call(function(div) {
                div.append("div")
                        .attr("class", "axis")
                        .call(context.axis().orient("top"));
                div.selectAll(".horizon")
                        .data(datas)
                        .enter().append("div")
                        .attr("class", "horizon")
                        .call(context.horizon().extent([0, 120]));
                div.append("div")
                        .attr("class", "rule")
                        .call(context.rule());
    
            });
        } else {
            for (var i = 0; i < data.data.length; i++){
                vals[data.data[i].MessageBodyClassName] = data.data[i].Value;
            }
        }
        context.start();
        self.isloading = false;
    });
    

    And here is the converted random function:

    function setval(name) {
        var value = 0,
                values = [],
                last;
        return context.metric(function(start, stop, step, callback) {
            start = +start, stop = +stop;
            if (isNaN(last)) last = start;
            while (last < stop) {
                last += step;
                value = self.testvalue;
                if (value != "") {
                    values.push(vals[name]);
                    context.stop();
                }
            }
            callback(null, values = values.slice((start - stop) / step));
        }, name);
    }
    

    essentially I just defined a global vals array and I'm using it to store the new values when pubnub updates and update the values with the Metric Accessor... I guess.