pulse-meter 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/basic.ru +4 -2
- data/examples/basic_sensor_data.rb +11 -0
- data/lib/pulse-meter/version.rb +1 -1
- data/lib/pulse-meter/visualize/sensor.rb +19 -18
- data/lib/pulse-meter/visualize/series_extractor.rb +96 -0
- data/lib/pulse-meter/visualize/widget.rb +2 -4
- data/spec/pulse_meter/visualize/series_extractor_spec.rb +80 -0
- metadata +4 -1
data/examples/basic.ru
CHANGED
@@ -26,7 +26,7 @@ layout = PulseMeter::Visualizer.draw do |l|
|
|
26
26
|
c.timespan 1200
|
27
27
|
end
|
28
28
|
|
29
|
-
p.spline "Rhino & Lama count comparison" do |c|
|
29
|
+
p.spline "Rhino & Lama & Goose count comparison" do |c|
|
30
30
|
c.redraw_interval 5
|
31
31
|
c.values_label 'Count'
|
32
32
|
c.width 5
|
@@ -35,9 +35,10 @@ layout = PulseMeter::Visualizer.draw do |l|
|
|
35
35
|
|
36
36
|
c.sensor :rhino_count, color: '#AAAAAA'
|
37
37
|
c.sensor :lama_count, color: '#CC1155'
|
38
|
+
c.sensor :goose_count
|
38
39
|
end
|
39
40
|
|
40
|
-
p.pie "Rhino & Lama count comparison" do |c|
|
41
|
+
p.pie "Rhino & Lama & Gooze count comparison" do |c|
|
41
42
|
c.redraw_interval 5
|
42
43
|
c.values_label 'Count'
|
43
44
|
c.width 5
|
@@ -46,6 +47,7 @@ layout = PulseMeter::Visualizer.draw do |l|
|
|
46
47
|
|
47
48
|
c.sensor :rhino_count, color: '#AAAAAA'
|
48
49
|
c.sensor :lama_count, color: '#CC1155'
|
50
|
+
c.sensor :goose_count
|
49
51
|
end
|
50
52
|
|
51
53
|
end
|
@@ -22,6 +22,12 @@ rhino_counter = PulseMeter::Sensor::Timelined::Counter.new(:rhino_count,
|
|
22
22
|
ttl: 3600
|
23
23
|
)
|
24
24
|
|
25
|
+
goose_counter = PulseMeter::Sensor::Timelined::HashedCounter.new(:goose_count,
|
26
|
+
annotation: 'Goose Count',
|
27
|
+
interval: 10,
|
28
|
+
ttl: 3600
|
29
|
+
)
|
30
|
+
|
25
31
|
rhino_average_age = PulseMeter::Sensor::Timelined::Average.new(:rhino_average_age,
|
26
32
|
annotation: 'Rhino average age',
|
27
33
|
interval: 20,
|
@@ -35,4 +41,9 @@ while true
|
|
35
41
|
rhino_counter.event(2)
|
36
42
|
lama_average_age.event(Random.rand(50))
|
37
43
|
rhino_average_age.event(Random.rand(100))
|
44
|
+
|
45
|
+
10.times do
|
46
|
+
goose_n = Random.rand(4)
|
47
|
+
goose_counter.event("goose_#{goose_n}" => 1)
|
48
|
+
end
|
38
49
|
end
|
data/lib/pulse-meter/version.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "pulse-meter/visualize/series_extractor"
|
2
|
+
|
1
3
|
module PulseMeter
|
2
4
|
module Visualize
|
3
5
|
class Sensor
|
@@ -6,7 +8,7 @@ module PulseMeter
|
|
6
8
|
|
7
9
|
def initialize(args)
|
8
10
|
raise ArgumentError unless args.respond_to?('[]')
|
9
|
-
@name = args[:sensor] or raise ArgumentError, ":
|
11
|
+
@name = args[:sensor] or raise ArgumentError, ":sensor not specified"
|
10
12
|
@color = args[:color]
|
11
13
|
end
|
12
14
|
|
@@ -27,24 +29,26 @@ module PulseMeter
|
|
27
29
|
end
|
28
30
|
|
29
31
|
def last_point_data(need_incomplete=false)
|
30
|
-
|
31
|
-
name: real_sensor.annotation,
|
32
|
-
y: to_float(last_value(need_incomplete))
|
33
|
-
}
|
34
|
-
res[:color] = color if color
|
35
|
-
res
|
32
|
+
extractor.point_data(last_value(need_incomplete))
|
36
33
|
end
|
37
34
|
|
38
35
|
def timeline_data(time_span, need_incomplete = false)
|
39
36
|
sensor = real_sensor
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
37
|
+
timeline_data = sensor.timeline(time_span)
|
38
|
+
timeline_data.pop unless need_incomplete
|
39
|
+
extractor.series_data(timeline_data)
|
40
|
+
end
|
41
|
+
|
42
|
+
def annotation
|
43
|
+
real_sensor.annotation
|
44
|
+
end
|
45
|
+
|
46
|
+
def type
|
47
|
+
real_sensor.class
|
48
|
+
end
|
49
|
+
|
50
|
+
def extractor
|
51
|
+
PulseMeter::Visualize.extractor(self)
|
48
52
|
end
|
49
53
|
|
50
54
|
protected
|
@@ -54,9 +58,6 @@ module PulseMeter
|
|
54
58
|
PulseMeter::Sensor::Base.restore(@name)
|
55
59
|
end
|
56
60
|
|
57
|
-
def to_float(val)
|
58
|
-
val && val.to_f
|
59
|
-
end
|
60
61
|
|
61
62
|
end
|
62
63
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module PulseMeter
|
2
|
+
module Visualize
|
3
|
+
module SeriesExtractor
|
4
|
+
class Simple
|
5
|
+
def initialize(sensor)
|
6
|
+
@sensor = sensor
|
7
|
+
end
|
8
|
+
|
9
|
+
def opts_to_add
|
10
|
+
opts = {}
|
11
|
+
opts[:color] = @sensor.color if @sensor.color
|
12
|
+
opts[:name] = @sensor.annotation
|
13
|
+
opts
|
14
|
+
end
|
15
|
+
|
16
|
+
def series_data(timeline_data)
|
17
|
+
{
|
18
|
+
data: timeline_data.map{|sd| {x: sd.start_time.to_i*1000, y: to_float(sd.value)}}
|
19
|
+
}.merge(opts_to_add)
|
20
|
+
end
|
21
|
+
|
22
|
+
def point_data(value)
|
23
|
+
{
|
24
|
+
y: to_float(value)
|
25
|
+
}.merge(opts_to_add)
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def to_float(val)
|
31
|
+
val && val.to_f
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
class Hashed < Simple
|
37
|
+
|
38
|
+
def parse_data(value)
|
39
|
+
if value
|
40
|
+
if value.is_a?(String)
|
41
|
+
JSON.parse(value)
|
42
|
+
else
|
43
|
+
value
|
44
|
+
end
|
45
|
+
else
|
46
|
+
{}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def point_data(value)
|
51
|
+
data = parse_data(value)
|
52
|
+
data.keys.map do |k|
|
53
|
+
{
|
54
|
+
y: to_float(data[k]),
|
55
|
+
name: k
|
56
|
+
}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def series_data(timeline_data)
|
61
|
+
series_data = {}
|
62
|
+
parsed_data = timeline_data.map do |sd|
|
63
|
+
data = parse_data(sd.value)
|
64
|
+
data.keys.each{|k| series_data[k] ||= {name: k, data: []}}
|
65
|
+
[sd.start_time.to_i*1000, data]
|
66
|
+
end
|
67
|
+
|
68
|
+
series_names = series_data.keys.sort
|
69
|
+
parsed_data.each do |data|
|
70
|
+
series_names.each do |k|
|
71
|
+
series_data[k][:data] << {x: data[0], y: to_float(data[1][k])}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
series_data.values
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
SPECIAL_SERIES_EXTRACTORS = {
|
80
|
+
'HashedCounter' => SeriesExtractor::Hashed
|
81
|
+
}.freeze
|
82
|
+
|
83
|
+
DEFAULT_SERIES_EXTRACTOR = SeriesExtractor::Simple
|
84
|
+
|
85
|
+
def extractor(sensor)
|
86
|
+
key = sensor.type.to_s.split('::').last
|
87
|
+
extractor_class = SPECIAL_SERIES_EXTRACTORS[key] || DEFAULT_SERIES_EXTRACTOR
|
88
|
+
extractor_class.new(sensor)
|
89
|
+
end
|
90
|
+
|
91
|
+
module_function :extractor
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
@@ -51,16 +51,14 @@ module PulseMeter
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def line_series_data(tspan)
|
54
|
-
sensors.map
|
55
|
-
s.timeline_data(tspan, show_last_point)
|
56
|
-
end
|
54
|
+
sensors.map{ |s| s.timeline_data(tspan, show_last_point) }.flatten(1)
|
57
55
|
end
|
58
56
|
|
59
57
|
def pie_series_data
|
60
58
|
[{
|
61
59
|
type: type,
|
62
60
|
name: values_label,
|
63
|
-
data: sensors.map{|s| s.last_point_data(show_last_point)}
|
61
|
+
data: sensors.map{|s| s.last_point_data(show_last_point)}.flatten(1)
|
64
62
|
}]
|
65
63
|
end
|
66
64
|
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe PulseMeter::Visualize::SeriesExtractor do
|
4
|
+
let(:interval){ 100 }
|
5
|
+
let!(:real_simple_sensor){ PulseMeter::Sensor::Timelined::Counter.new(:simple_sensor,
|
6
|
+
ttl: 1000,
|
7
|
+
interval: interval,
|
8
|
+
annotation: 'simple sensor'
|
9
|
+
) }
|
10
|
+
let!(:real_hashed_sensor){ PulseMeter::Sensor::Timelined::HashedCounter.new(:hashed_sensor,
|
11
|
+
ttl: 1000,
|
12
|
+
interval: interval,
|
13
|
+
annotation: 'hashed sensor'
|
14
|
+
) }
|
15
|
+
|
16
|
+
let!(:simple_sensor){PulseMeter::Visualize::Sensor.new(sensor: :simple_sensor)}
|
17
|
+
let!(:hashed_sensor){PulseMeter::Visualize::Sensor.new(sensor: :hashed_sensor)}
|
18
|
+
|
19
|
+
describe "simple extractor" do
|
20
|
+
|
21
|
+
let(:extractor) {PulseMeter::Visualize.extractor(simple_sensor)}
|
22
|
+
|
23
|
+
it "should be created for simple sensors" do
|
24
|
+
extractor.should be_kind_of(PulseMeter::Visualize::SeriesExtractor::Simple)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should create point data correctly" do
|
28
|
+
extractor.point_data(123).should == {y: 123, name: 'simple sensor'}
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should create timeline data correctly" do
|
32
|
+
tl_data = [
|
33
|
+
PulseMeter::SensorData.new(Time.at(1), 11),
|
34
|
+
PulseMeter::SensorData.new(Time.at(2), "22")
|
35
|
+
]
|
36
|
+
extractor.series_data(tl_data).should == {
|
37
|
+
name: 'simple sensor',
|
38
|
+
data: [{x: 1000, y: 11}, {x: 2000, y: 22}]
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "hash extractor" do
|
45
|
+
let(:extractor) {PulseMeter::Visualize.extractor(hashed_sensor)}
|
46
|
+
|
47
|
+
it "should be created for hash sensors" do
|
48
|
+
extractor.should be_kind_of(PulseMeter::Visualize::SeriesExtractor::Hashed)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should create point data correctly" do
|
52
|
+
extractor.point_data('{"x": 123, "y": 321}').should == [
|
53
|
+
{y: 123, name: 'x'},
|
54
|
+
{y: 321, name: 'y'}
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should create timeline data correctly" do
|
59
|
+
tl_data = [
|
60
|
+
PulseMeter::SensorData.new(Time.at(1), {"a" => 5, "b" => 6}),
|
61
|
+
PulseMeter::SensorData.new(Time.at(2), '{"c": 7, "b": 6}')
|
62
|
+
]
|
63
|
+
extractor.series_data(tl_data).should == [
|
64
|
+
{
|
65
|
+
name: 'a',
|
66
|
+
data: [{x: 1000, y: 5}, {x: 2000, y: nil}]
|
67
|
+
},
|
68
|
+
{
|
69
|
+
name: 'b',
|
70
|
+
data: [{x: 1000, y: 6}, {x: 2000, y: 6}]
|
71
|
+
},
|
72
|
+
{
|
73
|
+
name: 'c',
|
74
|
+
data: [{x: 1000, y: nil}, {x: 2000, y: 7}]
|
75
|
+
}
|
76
|
+
]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pulse-meter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -339,6 +339,7 @@ files:
|
|
339
339
|
- lib/pulse-meter/visualize/public/js/json2.js
|
340
340
|
- lib/pulse-meter/visualize/public/js/underscore-min.js
|
341
341
|
- lib/pulse-meter/visualize/sensor.rb
|
342
|
+
- lib/pulse-meter/visualize/series_extractor.rb
|
342
343
|
- lib/pulse-meter/visualize/views/main.haml
|
343
344
|
- lib/pulse-meter/visualize/widget.rb
|
344
345
|
- lib/pulse-meter/visualizer.rb
|
@@ -365,6 +366,7 @@ files:
|
|
365
366
|
- spec/pulse_meter/visualize/layout_spec.rb
|
366
367
|
- spec/pulse_meter/visualize/page_spec.rb
|
367
368
|
- spec/pulse_meter/visualize/sensor_spec.rb
|
369
|
+
- spec/pulse_meter/visualize/series_extractor_spec.rb
|
368
370
|
- spec/pulse_meter/visualize/widget_spec.rb
|
369
371
|
- spec/pulse_meter/visualizer_spec.rb
|
370
372
|
- spec/pulse_meter_spec.rb
|
@@ -420,6 +422,7 @@ test_files:
|
|
420
422
|
- spec/pulse_meter/visualize/layout_spec.rb
|
421
423
|
- spec/pulse_meter/visualize/page_spec.rb
|
422
424
|
- spec/pulse_meter/visualize/sensor_spec.rb
|
425
|
+
- spec/pulse_meter/visualize/series_extractor_spec.rb
|
423
426
|
- spec/pulse_meter/visualize/widget_spec.rb
|
424
427
|
- spec/pulse_meter/visualizer_spec.rb
|
425
428
|
- spec/pulse_meter_spec.rb
|