pulse-meter 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|