spnet 0.1.4 → 0.1.5
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/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
|