fsevent 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ # periodicschedule.rb --- periodic schedule
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::PeriodicSchedule
19
+ def initialize(initial_time, interval)
20
+ @initial_time = initial_time
21
+ @interval = interval
22
+ @n = 0
23
+ end
24
+
25
+ def first
26
+ @initial_time + @n * @interval
27
+ end
28
+
29
+ def shift
30
+ t = first
31
+ @n += 1
32
+ t
33
+ end
34
+ end
@@ -0,0 +1,93 @@
1
+ # processdevice.rb --- run device on another process
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::ProcessDevice < FSEvent::AbstractDevice
19
+ def self.spawner(defs)
20
+ Spawner.new(defs)
21
+ end
22
+
23
+ class Spawner
24
+ def initialize(defs)
25
+ @defs = defs
26
+ end
27
+
28
+ def assemble_code(device_name, *args)
29
+ code = ''
30
+
31
+ libpath = File.dirname(File.dirname(__FILE__))
32
+ code << "$:.unshift #{libpath.dump}\n"
33
+
34
+ code << <<-'End'
35
+ require 'fsevent'
36
+ def ep(arg) STDERR.puts arg.inspect end
37
+ End
38
+
39
+ code << "class FSEvent::ProcessDevice_#{device_name} < FSEvent::ProcessDeviceC\n#{@defs}\nend\n"
40
+ marshaled_args = Marshal.dump(args)
41
+ code << "FSEvent::ProcessDeviceC.main(FSEvent::ProcessDevice_#{device_name}.new(#{device_name.dump}, *(Marshal.load(#{marshaled_args.dump}))))\n"
42
+ end
43
+
44
+ def new(device_name, *args)
45
+ code = assemble_code(device_name, *args)
46
+ io = IO.popen([RbConfig.ruby], "r+")
47
+ io.sync = true
48
+ io.write code
49
+ io.write "__END__\n"
50
+ FSEvent::ProcessDevice.send(:new, device_name, io)
51
+ end
52
+ end
53
+
54
+ class << self
55
+ private :new
56
+ end
57
+
58
+ def initialize(device_name, io)
59
+ super device_name
60
+ @io = io
61
+ end
62
+
63
+ def call_subprocess(method, *args)
64
+ Marshal.dump([:call_child, method, *args], @io)
65
+ msgtype, *rest = Marshal.load(@io)
66
+ while msgtype == :call_parent
67
+ method, *args = rest
68
+ ret = @framework.send(method, *args)
69
+ Marshal.dump([:return_to_child, ret], @io)
70
+ msgtype, *rest = Marshal.load(@io)
71
+ end
72
+ if msgtype != :return_to_parent
73
+ raise RuntimeError, "unexpected message type: #{msgtype.inspect}"
74
+ end
75
+ rest[0]
76
+ end
77
+ #private :call_subprocess
78
+
79
+ def finish
80
+ @io.close_write
81
+ @io.close
82
+ nil
83
+ end
84
+
85
+ def framework=(framework)
86
+ @framework = framework
87
+ call_subprocess(:processdevice_framework_set)
88
+ end
89
+
90
+ def registered
91
+ call_subprocess(:processdevice_registered)
92
+ end
93
+ end
@@ -0,0 +1,79 @@
1
+ # processdevicec.rb --- child process code for processdevice.rb
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::ProcessDeviceC < FSEvent::AbstractDevice
19
+ class StubFramework
20
+ def initialize(obj)
21
+ @obj = obj
22
+ end
23
+
24
+ def add_watch(watchee_device_name, status_name)
25
+ @obj.call_parent(:add_watch, watchee_device_name, status_name)
26
+ end
27
+
28
+ def status_changed(status_name, value)
29
+ @obj.call_parent(:status_changed, status_name, value)
30
+ end
31
+ end
32
+
33
+ def initialize(device_name)
34
+ super(device_name)
35
+ end
36
+
37
+ def processdevice_framework_set
38
+ framework = StubFramework.new(self)
39
+ self.framework = framework
40
+ nil
41
+ end
42
+
43
+ def processdevice_registered
44
+ registered
45
+ nil
46
+ end
47
+
48
+ def call_parent(method, *args)
49
+ Marshal.dump([:call_parent, method, *args], STDOUT)
50
+ msgtype, *rest = Marshal.load(STDIN)
51
+ while msgtype == :call_child
52
+ method, *args = rest
53
+ ret = self.send(method, *args)
54
+ Marshal.dump([:return_to_parent, ret], STDOUT)
55
+ msgtype, *rest = Marshal.load(STDIN)
56
+ end
57
+ if msgtype != :return_to_child
58
+ raise RuntimeError, "unexpected message type: #{msgtype.inspect}"
59
+ end
60
+ rest[0]
61
+ end
62
+
63
+ def self.main(this_device)
64
+ STDOUT.sync = true
65
+ while true
66
+ begin
67
+ msgtype, *rest = Marshal.load(STDIN)
68
+ rescue EOFError
69
+ exit true
70
+ end
71
+ if msgtype != :call_child
72
+ raise RuntimeError, "unexpected message type: #{msgtype.inspect}"
73
+ end
74
+ method, *args = rest
75
+ ret = this_device.send(method, *args)
76
+ Marshal.dump([:return_to_parent, ret], STDOUT)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,48 @@
1
+ # schedulemerger.rb --- merge multiple schedules
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::ScheduleMerger
19
+ def initialize(*schedules)
20
+ @q = Depq.new
21
+ schedules.each {|s|
22
+ merge_schedule(s)
23
+ }
24
+ end
25
+
26
+ def merge_schedule(s)
27
+ t = s.shift
28
+ if t
29
+ @q.insert s, t
30
+ end
31
+ end
32
+
33
+ def first
34
+ return nil if @q.empty?
35
+ @q.find_min_priority[1]
36
+ end
37
+
38
+ def shift
39
+ return nil if @q.empty?
40
+ s, t = @q.delete_min_priority
41
+ t2 = s.shift
42
+ if t2
43
+ @q.insert s, t2
44
+ end
45
+ t
46
+ end
47
+ end
48
+
@@ -0,0 +1,46 @@
1
+ # simpledevice.rb --- simple device definition using constructor arguments
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::SimpleDevice < FSEvent::AbstractDevice
19
+ def initialize(device_name, initial_status, watches, registered_elapsed_time, schedule=nil, &run_block)
20
+ super device_name
21
+ @name = device_name
22
+ @initial_status = initial_status
23
+ @watches = watches
24
+ @registered_elapsed_time = registered_elapsed_time
25
+ @schedule.merge_schedule schedule if schedule
26
+ @run_block = run_block
27
+ end
28
+ attr_writer :registered_elapsed_time
29
+
30
+ def registered
31
+ @initial_status.each {|status_name, value|
32
+ define_status status_name, value
33
+ }
34
+ @watches.each {|watchee_device_name, status_name, reaction|
35
+ reaction ||= :immediate
36
+ add_watch watchee_device_name, status_name, reaction
37
+ }
38
+ if @registered_elapsed_time
39
+ set_elapsed_time @registered_elapsed_time
40
+ end
41
+ end
42
+
43
+ def run(watched_status_change)
44
+ @run_block.call watched_status_change
45
+ end
46
+ end
@@ -0,0 +1,77 @@
1
+ # util.rb --- various utilities
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module FSEvent::Util
19
+ module_function
20
+
21
+ def nested_hash(n)
22
+ if n == 1
23
+ {}
24
+ else
25
+ Hash.new {|h, k|
26
+ h[k] = nested_hash(n-1)
27
+ }
28
+ end
29
+ end
30
+
31
+ def nonempty_hash(h, level)
32
+ return nil if h.nil?
33
+ result = {}
34
+ h.each {|k, v|
35
+ if level == 1
36
+ result[k] = v
37
+ else
38
+ h2 = nonempty_hash(v, level-1)
39
+ if h2
40
+ result[k] = h2
41
+ end
42
+ end
43
+ }
44
+ if result.empty?
45
+ nil
46
+ else
47
+ result
48
+ end
49
+ end
50
+
51
+ def reaction_immediate_at_beginning?(reaction)
52
+ case reaction
53
+ when :immediate
54
+ true
55
+ when :immediate_only_at_beginning
56
+ true
57
+ when :schedule
58
+ false
59
+ else
60
+ raise "unexpected reaction: #{reaction.inspect}"
61
+ end
62
+ end
63
+
64
+ def reaction_immediate_at_subsequent?(reaction)
65
+ case reaction
66
+ when :immediate
67
+ true
68
+ when :immediate_only_at_beginning
69
+ false
70
+ when :schedule
71
+ false
72
+ else
73
+ raise "unexpected reaction: #{reaction.inspect}"
74
+ end
75
+ end
76
+
77
+ end
data/sample/repeat.rb ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'time'
4
+ require 'fsevent'
5
+
6
+ class Repeater < FSEvent::AbstractDevice
7
+ def initialize(name, schedule)
8
+ super name
9
+ @schedule = schedule
10
+ end
11
+
12
+ def run(watched_status_change)
13
+ @framework.set_elapsed_time(1)
14
+ p @framework.current_time.iso8601(3)
15
+ end
16
+ end
17
+
18
+ schedule = FSEvent::PeriodicSchedule.new(Time.utc(2000), 10)
19
+ repeat = Repeater.new("repeat", schedule)
20
+
21
+ fsevent = FSEvent.new(Time.utc(2000))
22
+ fsevent.register_device(repeat)
23
+ fsevent.start
24
+
data/sample/repeat2.rb ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'fsevent'
4
+
5
+ class Repeater < FSEvent::AbstractDevice
6
+ def initialize(name, schedule)
7
+ super name
8
+ @schedule = schedule
9
+ end
10
+
11
+ def run(watched_status_change)
12
+ p [self.name, @framework.current_time]
13
+ end
14
+ end
15
+
16
+ repeat1 = Repeater.new("repeat1", FSEvent::PeriodicSchedule.new(Time.now, 3))
17
+ repeat2 = Repeater.new("repeat2", FSEvent::PeriodicSchedule.new(Time.now, 10))
18
+
19
+ fsevent = FSEvent.new
20
+ fsevent.register_device(repeat1)
21
+ fsevent.register_device(repeat2)
22
+ fsevent.start
23
+
@@ -0,0 +1,159 @@
1
+ # test_failsafedevice.rb --- tests for fail safe device
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'test/unit'
19
+ require 'fsevent'
20
+
21
+ class TestFSEventFailSafeDevice < Test::Unit::TestCase
22
+ class SrcDevice < FSEvent::AbstractDevice
23
+ def initialize(device_name, init, pairs)
24
+ super device_name
25
+ @init = init
26
+ @elapsed = 1
27
+ @schedule = pairs.map {|t, v| t-@elapsed }
28
+ @values = pairs.map {|t, v| v }
29
+ @test_result = []
30
+ end
31
+ attr_reader :test_result
32
+
33
+ def registered
34
+ set_elapsed_time(1)
35
+ define_status("s", @init)
36
+ end
37
+
38
+ def run(watched_status_change)
39
+ @test_result << [@framework.current_time, @values.first]
40
+ set_elapsed_time(@elapsed)
41
+ status_changed "s", @values.shift
42
+ end
43
+ end
44
+
45
+ def test_srcdevice
46
+ t = Time.utc(2000)
47
+ fsevent = FSEvent.new(t)
48
+ srcdevice = SrcDevice.new("src", 0,
49
+ [[t+10,8],
50
+ [t+20,2],
51
+ [t+30,3],
52
+ [t+40,5],
53
+ [t+50,1]])
54
+ fsevent.register_device(srcdevice)
55
+ fsevent.start
56
+ assert_equal(
57
+ [[t+9,8],
58
+ [t+19,2],
59
+ [t+29,3],
60
+ [t+39,5],
61
+ [t+49,1]],
62
+ srcdevice.test_result)
63
+ end
64
+
65
+ class FailSafeDeviceT < FSEvent::FailSafeDevice
66
+ def registered
67
+ super
68
+ set_elapsed_time(1)
69
+ end
70
+
71
+ def run(watched_status_change)
72
+ set_elapsed_time(1)
73
+ super(watched_status_change)
74
+ end
75
+ end
76
+
77
+ class DstDevice < FSEvent::AbstractDevice
78
+ def initialize(device_name, watchee_device_name, watchee_status)
79
+ super device_name
80
+ @watchee_device_name = watchee_device_name
81
+ @watchee_status = watchee_status
82
+ @test_result = []
83
+ end
84
+ attr_reader :test_result
85
+
86
+ def registered
87
+ super
88
+ set_elapsed_time(1)
89
+ add_watch(@watchee_device_name, @watchee_status)
90
+ end
91
+
92
+ def run(watched_status_change)
93
+ set_elapsed_time(1)
94
+ @test_result << [@framework.current_time, watched_status_change]
95
+ end
96
+ end
97
+
98
+ def test_failsafe1
99
+ t = Time.utc(2000)
100
+ fsevent = FSEvent.new(t)
101
+ src1 = SrcDevice.new("src1", 0,
102
+ [[t+10,9],
103
+ [t+20,2],
104
+ [t+30,3],
105
+ [t+40,5],
106
+ [t+50,1]])
107
+ fsd = FailSafeDeviceT.new("fs",
108
+ [["s", 0, lambda {|cur, val| val }]],
109
+ "src1")
110
+ dst = DstDevice.new("dst", "fs", "s")
111
+ fsevent.register_device(src1)
112
+ fsevent.register_device(fsd)
113
+ fsevent.register_device(dst)
114
+ fsevent.start
115
+ assert_equal(
116
+ [[t + 1, {"fs"=>{"s"=>0}}],
117
+ [t + 11, {"fs"=>{"s"=>9}}],
118
+ [t + 21, {"fs"=>{"s"=>2}}],
119
+ [t + 31, {"fs"=>{"s"=>3}}],
120
+ [t + 41, {"fs"=>{"s"=>5}}],
121
+ [t + 51, {"fs"=>{"s"=>1}}]],
122
+ dst.test_result)
123
+ end
124
+
125
+ def test_failsafe2_max
126
+ t = Time.utc(2000)
127
+ fsevent = FSEvent.new(t)
128
+ src1 = SrcDevice.new("src1", 0,
129
+ [[t+10,9],
130
+ [t+20,2],
131
+ [t+30,3],
132
+ [t+40,5],
133
+ [t+50,1]])
134
+ src2 = SrcDevice.new("src2", 0,
135
+ [[t+11,9],
136
+ [t+19,2],
137
+ [t+31,3],
138
+ [t+39,5],
139
+ [t+51,1]])
140
+ fsd = FailSafeDeviceT.new("fs",
141
+ [["s", 0, :max]],
142
+ "src1", "src2")
143
+ dst = DstDevice.new("dst", "fs", "s")
144
+ fsevent.register_device(src1)
145
+ fsevent.register_device(src2)
146
+ fsevent.register_device(fsd)
147
+ fsevent.register_device(dst)
148
+ fsevent.start
149
+ assert_equal(
150
+ [[t + 1, {"fs"=>{"s"=>0}}],
151
+ [t + 10+1, {"fs"=>{"s"=>9}}],
152
+ [t + 20+1, {"fs"=>{"s"=>2}}],
153
+ [t + 30+1, {"fs"=>{"s"=>3}}],
154
+ [t + 39+1, {"fs"=>{"s"=>5}}],
155
+ [t + 51+1, {"fs"=>{"s"=>1}}]],
156
+ dst.test_result)
157
+ end
158
+
159
+ end