spnet 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +6 -3
- data/lib/spnet.rb +23 -5
- data/lib/spnet/core/block.rb +46 -0
- data/lib/spnet/core/in_port.rb +41 -0
- data/lib/spnet/core/limiter.rb +30 -0
- data/lib/spnet/core/link.rb +70 -0
- data/lib/spnet/core/network.rb +54 -0
- data/lib/spnet/core/out_port.rb +41 -0
- data/lib/spnet/limiters/enum_limiter.rb +24 -0
- data/lib/spnet/limiters/lower_limiter.rb +31 -0
- data/lib/spnet/limiters/no_limiter.rb +11 -0
- data/lib/spnet/limiters/range_limiter.rb +24 -0
- data/lib/spnet/limiters/upper_limiter.rb +31 -0
- data/lib/spnet/ports/command_in_port.rb +16 -8
- data/lib/spnet/ports/command_out_port.rb +17 -11
- data/lib/spnet/ports/param_in_port.rb +42 -0
- data/lib/spnet/ports/param_out_port.rb +32 -0
- data/lib/spnet/ports/signal_in_port.rb +20 -15
- data/lib/spnet/ports/signal_out_port.rb +12 -7
- data/lib/spnet/storage/block_state.rb +41 -0
- data/lib/spnet/storage/link_state.rb +38 -0
- data/lib/spnet/storage/network_state.rb +44 -0
- data/lib/spnet/storage/port_locater.rb +21 -0
- data/lib/spnet/version.rb +2 -1
- data/spec/core/block_spec.rb +80 -0
- data/spec/core/in_port_spec.rb +46 -0
- data/spec/core/limiter_spec.rb +7 -0
- data/spec/core/link_spec.rb +103 -0
- data/spec/core/network_spec.rb +40 -0
- data/spec/core/out_port_spec.rb +46 -0
- data/spec/limiters/enum_limiter_spec.rb +28 -0
- data/spec/limiters/lower_limiter_spec.rb +48 -0
- data/spec/limiters/no_limiter_spec.rb +12 -0
- data/spec/limiters/range_limiter_spec.rb +121 -0
- data/spec/limiters/upper_limiter_spec.rb +47 -0
- data/spec/ports/command_out_port_spec.rb +8 -15
- data/spec/ports/{value_in_port_spec.rb → param_in_port_spec.rb} +2 -2
- data/spec/ports/param_out_port_spec.rb +34 -0
- data/spec/ports/signal_in_port_spec.rb +2 -1
- data/spec/ports/signal_out_port_spec.rb +6 -37
- data/spec/spec_helper.rb +43 -0
- data/spec/storage/block_state_spec.rb +40 -0
- data/spec/storage/link_state_spec.rb +28 -0
- data/spec/storage/network_state_spec.rb +42 -0
- data/spec/storage/port_locater_spec.rb +11 -0
- data/spnet.gemspec +0 -1
- metadata +55 -35
- data/lib/spnet/block.rb +0 -40
- data/lib/spnet/in_port.rb +0 -29
- data/lib/spnet/out_port.rb +0 -64
- data/lib/spnet/ports/value_in_port.rb +0 -27
- data/lib/spnet/ports/value_out_port.rb +0 -28
- data/spec/block_spec.rb +0 -38
- data/spec/in_port_spec.rb +0 -29
- data/spec/out_port_spec.rb +0 -46
- data/spec/ports/value_out_port_spec.rb +0 -41
data/README.rdoc
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
= spnet
|
2
2
|
|
3
3
|
* {Homepage}[https://rubygems.org/gems/spnet]
|
4
|
+
* {Source Code}[https://github.com/jamestunnell/spnet]
|
4
5
|
* {Documentation}[http://rubydoc.info/gems/spnet/frames]
|
5
|
-
* {Email}[mailto:jamestunnell
|
6
|
+
* {Email}[mailto:jamestunnell@lavabit.com]
|
6
7
|
|
7
8
|
== Description
|
8
9
|
|
@@ -12,9 +13,11 @@ Provide infrastructure for forming processing networks.
|
|
12
13
|
|
13
14
|
Signal ports: Used to pass a stream of data through a block.
|
14
15
|
|
15
|
-
|
16
|
+
Param ports: Used to get/set a value within a block (usually to control a parameter).
|
16
17
|
|
17
|
-
Command ports: Used to trigger the execution a command within a block, with optional data
|
18
|
+
Command ports: Used to trigger the execution a command within a block, with optional data.
|
19
|
+
|
20
|
+
Limiters: Control the limits on values that can be sent into signal or param ports.
|
18
21
|
|
19
22
|
== Examples
|
20
23
|
|
data/lib/spnet.rb
CHANGED
@@ -1,11 +1,29 @@
|
|
1
|
+
require 'hashmake'
|
2
|
+
|
1
3
|
require 'spnet/version'
|
2
|
-
|
3
|
-
require 'spnet/
|
4
|
-
require 'spnet/
|
4
|
+
|
5
|
+
require 'spnet/core/in_port'
|
6
|
+
require 'spnet/core/out_port'
|
7
|
+
require 'spnet/core/limiter'
|
8
|
+
|
9
|
+
require 'spnet/limiters/no_limiter'
|
10
|
+
require 'spnet/limiters/lower_limiter'
|
11
|
+
require 'spnet/limiters/upper_limiter'
|
12
|
+
require 'spnet/limiters/range_limiter'
|
13
|
+
require 'spnet/limiters/enum_limiter'
|
5
14
|
|
6
15
|
require 'spnet/ports/signal_in_port'
|
7
16
|
require 'spnet/ports/signal_out_port'
|
8
|
-
require 'spnet/ports/
|
9
|
-
require 'spnet/ports/
|
17
|
+
require 'spnet/ports/param_in_port'
|
18
|
+
require 'spnet/ports/param_out_port'
|
10
19
|
require 'spnet/ports/command_in_port'
|
11
20
|
require 'spnet/ports/command_out_port'
|
21
|
+
|
22
|
+
require 'spnet/core/block'
|
23
|
+
require 'spnet/core/link'
|
24
|
+
require 'spnet/core/network'
|
25
|
+
|
26
|
+
require 'spnet/storage/port_locater.rb'
|
27
|
+
require 'spnet/storage/link_state.rb'
|
28
|
+
require 'spnet/storage/block_state.rb'
|
29
|
+
require 'spnet/storage/network_state.rb'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module SPNet
|
2
|
+
|
3
|
+
# Base class for encapsulating processing functionality. Connects to
|
4
|
+
# other blocks via input and output ports.
|
5
|
+
#
|
6
|
+
# @author James Tunnell
|
7
|
+
class Block
|
8
|
+
include Hashmake::HashMakeable
|
9
|
+
|
10
|
+
attr_reader :in_ports, :out_ports, :sample_rate
|
11
|
+
|
12
|
+
# Define ArgSpec's to use in processing hashed arguments during #initialize.
|
13
|
+
ARG_SPECS = {
|
14
|
+
:sample_rate => arg_spec(:reqd => true, :type => Numeric, :validator => ->(a){ a > 0 }),
|
15
|
+
:algorithm => arg_spec(:reqd => true, :type => Proc, :validator => ->(p){ p.arity == 1 }),
|
16
|
+
:in_ports => arg_spec_hash(:reqd => false, :type => InPort),
|
17
|
+
:out_ports => arg_spec_hash(:reqd => false, :type => OutPort),
|
18
|
+
}
|
19
|
+
|
20
|
+
# A new instance of Block.
|
21
|
+
# @param [Hash] args Hashed arguments for initialization. See Block::ARG_SPECS
|
22
|
+
# for details of which keys are required.
|
23
|
+
def initialize args = {}
|
24
|
+
hash_make Block::ARG_SPECS, args
|
25
|
+
end
|
26
|
+
|
27
|
+
# Execute the block algorithm.
|
28
|
+
# @param [Fixnum] count The number of steps to execute. Passed on to the algorithm Proc.
|
29
|
+
def step count
|
30
|
+
@algorithm.call count
|
31
|
+
end
|
32
|
+
|
33
|
+
# Produces a BlockState object based on this Block object.
|
34
|
+
# @return [BlockState]
|
35
|
+
def export_state
|
36
|
+
port_params = {}
|
37
|
+
@in_ports.each do |name, port|
|
38
|
+
if port.is_a?(ParamInPort)
|
39
|
+
port_params[name] = port.get_value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
BlockState.new(:class_sym => self.class.to_s.to_sym, :port_params => port_params)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module SPNet
|
2
|
+
|
3
|
+
# A port that exposes some functionality in a block.
|
4
|
+
#
|
5
|
+
# @author James Tunnell
|
6
|
+
class InPort
|
7
|
+
include Hashmake::HashMakeable
|
8
|
+
|
9
|
+
# Define ArgSpec's to use in processing hashed arguments during #initialize.
|
10
|
+
ARG_SPECS = {
|
11
|
+
:matching_class => arg_spec(:reqd => true, :type => Class),
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_reader :link, :matching_class
|
15
|
+
|
16
|
+
# A new instance of InPort.
|
17
|
+
# @param [Hash] args Hashed arguments for initialization. See InPort::ARG_SPECS
|
18
|
+
# for details.
|
19
|
+
def initialize args
|
20
|
+
hash_make InPort::ARG_SPECS, args
|
21
|
+
@link = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# Set @link to the given Link object.
|
25
|
+
def set_link link
|
26
|
+
raise ArgumentError, "link 'to' port is not self" unless link.to == self
|
27
|
+
@link = link
|
28
|
+
end
|
29
|
+
|
30
|
+
# Set @link to nil.
|
31
|
+
def clear_link
|
32
|
+
@link = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return true if @link is not nil.
|
36
|
+
def linked?
|
37
|
+
!link.nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SPNet
|
2
|
+
|
3
|
+
# Defines a limit, where a value should not be above (for upper limit) or below
|
4
|
+
# (for lower limit). If inclusive is set to true, the limit indicates that values
|
5
|
+
# at the limit are OK.
|
6
|
+
#
|
7
|
+
# @author James Tunnell
|
8
|
+
class Limit
|
9
|
+
attr_reader :value, :inclusive
|
10
|
+
|
11
|
+
def inclusive?
|
12
|
+
return @inclusive
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize value, inclusive
|
16
|
+
@value = value
|
17
|
+
@inclusive = inclusive
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Base class for limiting values. Does nothing on it's own.
|
22
|
+
#
|
23
|
+
# @author James Tunnell
|
24
|
+
class Limiter
|
25
|
+
def apply_limit value, current_value
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module SPNet
|
2
|
+
|
3
|
+
# Form a connection between an OutPort and an InPort.
|
4
|
+
#
|
5
|
+
# @author James Tunnell
|
6
|
+
class Link
|
7
|
+
include Hashmake::HashMakeable
|
8
|
+
|
9
|
+
# Define arg specs to use in processing hashed arguments during #initialize.
|
10
|
+
ARG_SPECS = {
|
11
|
+
:from => arg_spec(:reqd => true, :type => OutPort),
|
12
|
+
:to => arg_spec(:reqd => true, :type => InPort)
|
13
|
+
}
|
14
|
+
|
15
|
+
attr_reader :to, :from
|
16
|
+
|
17
|
+
# A new instance of Link. Link is not active by default (does not set
|
18
|
+
# from.link and to.link to self).
|
19
|
+
# @param [Hash] args Hashed arguments for initialization. See Link::ARG_SPECS
|
20
|
+
# for details of which keys are required.
|
21
|
+
def initialize args = {}
|
22
|
+
hash_make Link::ARG_SPECS, args
|
23
|
+
|
24
|
+
raise ArgumentError, "from port class #{@from.class} is not a #{@to.matching_class}" unless @from.is_a?(@to.matching_class)
|
25
|
+
raise ArgumentError, "to port class #{@to.class} is not a #{@from.matching_class}" unless @to.is_a?(@from.matching_class)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Make the link active by setting from.link and to.link to self.
|
29
|
+
def activate
|
30
|
+
@from.set_link self
|
31
|
+
@to.set_link self
|
32
|
+
end
|
33
|
+
|
34
|
+
# Make the link inactive by setting from.link and to.link to nil.
|
35
|
+
def deactivate
|
36
|
+
@from.clear_link
|
37
|
+
@to.clear_link
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return true if the link is active (from.link and to.link are set to to self).
|
41
|
+
def active?
|
42
|
+
(@from.link == self) && (@to.link == self)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Produce a LinkState object from the current Link object.
|
46
|
+
def export_state blocks
|
47
|
+
from, to = nil, nil
|
48
|
+
|
49
|
+
blocks.each do |block_name, block|
|
50
|
+
block.out_ports.each do |port_name, port|
|
51
|
+
if port == @from
|
52
|
+
from = PortLocater.new(:block_name => block_name, :port_name => port_name)
|
53
|
+
break
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
block.in_ports.each do |port_name, port|
|
58
|
+
if port == @to
|
59
|
+
to = PortLocater.new(:block_name => block_name, :port_name => port_name)
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
raise "could not find from port" if from.nil?
|
66
|
+
raise "could not find to port" if to.nil?
|
67
|
+
return LinkState.new(:from => from, :to => to)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module SPNet
|
2
|
+
|
3
|
+
# A signal processing network, formed by connecting Block objects with Link objects.
|
4
|
+
#
|
5
|
+
# @author James Tunnell
|
6
|
+
class Network
|
7
|
+
include Hashmake::HashMakeable
|
8
|
+
|
9
|
+
# Define arg specs to use in processing hashed arguments during #initialize.
|
10
|
+
ARG_SPECS = {
|
11
|
+
:sample_rate => arg_spec(:reqd => true, :type => Numeric, :validator => ->(a){a > 0.0}),
|
12
|
+
:name => arg_spec(:reqd => false, :type => String, :default => ""),
|
13
|
+
:blocks => arg_spec_hash(:reqd => false, :type => Block),
|
14
|
+
:links => arg_spec_array(:reqd => false, :type => Link),
|
15
|
+
}
|
16
|
+
|
17
|
+
attr_reader :sample_rate, :name, :blocks, :links
|
18
|
+
|
19
|
+
# A new instance of Network. Changes all block sample rates (if necessary) to
|
20
|
+
# match the given sample rate. Activates links.
|
21
|
+
# @param [Hash] args Hashed arguments for initialization. See Network::ARG_SPECS
|
22
|
+
# for details of which keys are required.
|
23
|
+
def initialize args = {}
|
24
|
+
hash_make Network::ARG_SPECS, args
|
25
|
+
|
26
|
+
# ensure that all sample rates match given rate
|
27
|
+
@blocks.each do |block_name, block|
|
28
|
+
if block.sample_rate != @sample_rate
|
29
|
+
raise ArgumentError, "block sample rate #{block.sample_rate} does not match network sample rate #{@sample_rate}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
@links.each do |link|
|
34
|
+
link.activate
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Produce a NetworkState object from the current Network object.
|
39
|
+
def export_state
|
40
|
+
block_states = {}
|
41
|
+
@blocks.each do |block_name, block|
|
42
|
+
block_states[block_name] = block.export_state
|
43
|
+
end
|
44
|
+
|
45
|
+
link_states = []
|
46
|
+
@links.each do |link|
|
47
|
+
link_states.push link.export_state(@blocks)
|
48
|
+
end
|
49
|
+
|
50
|
+
return NetworkState.new(:name => @name, :block_states => block_states, :link_states => link_states)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module SPNet
|
2
|
+
|
3
|
+
# A port that is used to exercise some functionality exposed by an InPort.
|
4
|
+
#
|
5
|
+
# @author James Tunnell
|
6
|
+
class OutPort
|
7
|
+
include Hashmake::HashMakeable
|
8
|
+
|
9
|
+
# Define ArgSpec's to use in processing hashed arguments during #initialize.
|
10
|
+
ARG_SPECS = {
|
11
|
+
:matching_class => arg_spec(:reqd => true, :type => Class),
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_reader :name, :link, :matching_class
|
15
|
+
|
16
|
+
# A new instance of OutPort.
|
17
|
+
# @param [Hash] args Hashed arguments for initialization. See OutPort::ARG_SPECS
|
18
|
+
# for details.
|
19
|
+
def initialize args
|
20
|
+
hash_make OutPort::ARG_SPECS, args
|
21
|
+
@link = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# Set @link to the given Link object.
|
25
|
+
def set_link link
|
26
|
+
raise ArgumentError, "link 'from' port is not self" unless link.from == self
|
27
|
+
@link = link
|
28
|
+
end
|
29
|
+
|
30
|
+
# Set @link to nil.
|
31
|
+
def clear_link
|
32
|
+
@link = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return true if @link is not nil.
|
36
|
+
def linked?
|
37
|
+
!link.nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SPNet
|
2
|
+
# Keeps values to those found in the given Enumerable object.
|
3
|
+
#
|
4
|
+
# @author James Tunnell
|
5
|
+
class EnumLimiter < Limiter
|
6
|
+
attr_reader :values
|
7
|
+
|
8
|
+
def initialize values
|
9
|
+
raise ArgumentError, "values is not an Enumerable" unless values.is_a?(Enumerable)
|
10
|
+
@values = values
|
11
|
+
end
|
12
|
+
|
13
|
+
# Limit the given value to those given by @values. If the given value is not
|
14
|
+
# found, return the current value.
|
15
|
+
def apply_limit value, current_value
|
16
|
+
if @values.include? value
|
17
|
+
return value
|
18
|
+
else
|
19
|
+
return current_value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SPNet
|
2
|
+
# Keeps values at or above the given Limit.
|
3
|
+
#
|
4
|
+
# @author James Tunnell
|
5
|
+
class LowerLimiter < Limiter
|
6
|
+
attr_reader :limit, :inclusive
|
7
|
+
|
8
|
+
def initialize limit, inclusive
|
9
|
+
@limit = limit
|
10
|
+
@inclusive = inclusive
|
11
|
+
end
|
12
|
+
|
13
|
+
# Limit the given value to be at or above @limit. Ignores the current_value parameter.
|
14
|
+
def apply_limit value, current_value = nil
|
15
|
+
if inclusive
|
16
|
+
if value >= @limit
|
17
|
+
return value
|
18
|
+
else
|
19
|
+
return @limit
|
20
|
+
end
|
21
|
+
else
|
22
|
+
if value > @limit
|
23
|
+
return value
|
24
|
+
else
|
25
|
+
return @limit + Float::EPSILON
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SPNet
|
2
|
+
# Keeps values between the given Limit objects.
|
3
|
+
#
|
4
|
+
# @author James Tunnell
|
5
|
+
class RangeLimiter < Limiter
|
6
|
+
attr_reader :lower, :upper
|
7
|
+
|
8
|
+
def initialize lower_limit, lower_inclusive, upper_limit, upper_inclusive
|
9
|
+
@lower = LowerLimiter.new(lower_limit, lower_inclusive)
|
10
|
+
@upper = UpperLimiter.new(upper_limit, upper_inclusive)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Limit the given value to be between lower and upper limits. Ignores the current_value parameter.
|
14
|
+
def apply_limit value, current_value = nil
|
15
|
+
new_value = @lower.apply_limit value
|
16
|
+
if(new_value == value)
|
17
|
+
# value is OK so far. Make sure the right (upper) limit is OK too.
|
18
|
+
new_value = @upper.apply_limit value
|
19
|
+
end
|
20
|
+
return new_value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|