Class: Roby::StateSpace

Inherits:
StateField show all
Defined in:
lib/roby/state/state_model.rb,
lib/roby/state/events.rb

Overview

Implementation of the state representation at runtime.

It gives access to three views to the state:

* the current values are directly accessible from this state object
* the last known value is stored in last_known.path.to.value
* the state model is stored in model.path.to.value
* the current data source for a state variable is stored in
  data_sources.path.to.value

Constant Summary

Constants inherited from OpenStruct

OpenStruct::FORBIDDEN_NAMES, OpenStruct::FORBIDDEN_NAMES_RX, OpenStruct::NOT_OVERRIDABLE, OpenStruct::NOT_OVERRIDABLE_RX

Instance Attribute Summary

Attributes inherited from OpenStruct

#__parent_name, #__parent_struct, #model

Instance Method Summary collapse

Methods inherited from StateField

#__get, #attach, #create_model, #data_sources, #last_known, #link_to, #method_missing, #model, #read, #to_s

Methods inherited from OpenStruct

#__get, #__merge, #__parent, #__respond_to__, #__root, #__root?, _load, #alias, #attach, #attach_child, #attach_model, #attach_to, #attached?, #clear, #clear_model, #create_model, #delete, #detached!, #each_member, #empty?, #filter, #freeze, #get, #global_filter, #has_method?, #link_to, #method_missing, #new_model, #on_change, #path, #pretty_print, #respond_to?, #set, #stable!, #stable?, #to_hash, #update, #updated

Constructor Details

#initialize(model = nil) ⇒ StateSpace

Returns a new instance of StateSpace.



230
231
232
233
# File 'lib/roby/state/state_model.rb', line 230

def initialize(model = nil)
    @exported_fields = nil
    super(model)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Roby::StateField

Instance Method Details

#_dump(lvl = -1)) ⇒ Object

Implementation of marshalling with Ruby’s Marshal

Only the fields that can be marshalled will be saved. Any other field will silently be ignored.

Which fields get marshalled can be controlled with #export_all, #export_none and #export. The default is to marshal all fields.



280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/roby/state/state_model.rb', line 280

def _dump(lvl = -1)
    if !@exported_fields
        super
    else
        marshalled_members = @exported_fields.map do |name|
            value = @members[name]
            [name, Marshal.dump(value)] rescue nil
        end
        marshalled_members.compact!
        Marshal.dump([marshalled_members, @aliases])
    end
end

#at(options) ⇒ Object

Returns an event which emits when the given state is reached. For now, the following state variables are available:

t

time as a Time object

See TimePointEvent



90
91
92
93
94
95
# File 'lib/roby/state/events.rb', line 90

def at(options)
    options = validate_options options, t: nil
    if time = options[:t]
        trigger_when { Time.now >= time }
    end
end

#create_subfield(name) ⇒ Object



268
269
270
271
# File 'lib/roby/state/state_model.rb', line 268

def create_subfield(name)
    model = if self.model then self.model.get(name) end
    StateField.new(model, self, name)
end

#deep_copyObject



293
294
295
296
297
298
# File 'lib/roby/state/state_model.rb', line 293

def deep_copy
    exported_fields, @exported_fields = @exported_fields, Set.new
    Marshal.load(Marshal.dump(self))
ensure
    @exported_fields = exported_fields
end

#export(*names) ⇒ Object

Declares that only the given names should be marshalled, instead of marshalling every field. It is cumulative, i.e. if multiple calls to #export follow each other then the fields get added to the list of exported fields instead of replacing it.

If #export_all has been called, a call to #export cancels it.

See also #export_none and #export_all



263
264
265
266
# File 'lib/roby/state/state_model.rb', line 263

def export(*names)
    @exported_fields ||= Set.new
    @exported_fields.merge names.map { |n| n.to_s }.to_set
end

#export_allObject

Declares that all the state fields should be marshalled. This is the default

It cancels any list of fields exported with #export

See also #export_none and #export



251
252
253
# File 'lib/roby/state/state_model.rb', line 251

def export_all
    @exported_fields = nil
end

#export_noneObject

Declares that no state fields should be marshalled. The default is to export everything

It cancels any list of fields exported with #export

See also #export_all and #export



241
242
243
# File 'lib/roby/state/state_model.rb', line 241

def export_none
    @exported_fields = Set.new
end

#on_delta(spec) ⇒ Object

Create an event which will be emitted everytime some state parameters vary more than the given deltas. The following state parameters are available:

t

time in seconds

d

distance in meters

yaw

heading in radians

For instance:

Roby.state.on_delta d: 10, t: 20

will emit everytime the robot moves more than 10 meters AND more than 20 seconds have elapsed.

If more than once specification is given, the resulting event is combined with the & operator. This can be changed by setting the :or option to ‘true’.

Roby.state.on_delta d: 10, t: 20, or: true

See DeltaEvent and its subclasses.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/roby/state/events.rb', line 23

def on_delta(spec)
    or_aggregate = spec.delete(:or)

    events = spec.map do |name, value|
        unless klass = DeltaEvent.event_types[name]
            raise "unknown delta type #{name}. Known types are #{DeltaEvent.event_types.keys}"
        end
        
        ev    = klass.new
        ev.threshold = value
        ev
    end

    if events.size > 1
        result = if or_aggregate then OrGenerator.new
                 else AndGenerator.new
                 end

        result.on { |ev| result.reset }
        def result.or(spec); DeltaEvent.or(spec, self) end
        events.each { |ev| result << ev }
        result
    else
        events.first
    end
end

#reset_when(event, state_name = nil, &block) ⇒ Object

Installs a condition at which the event should be reset



76
77
78
79
80
81
82
83
# File 'lib/roby/state/events.rb', line 76

def reset_when(event, state_name = nil, &block)
    reset_event = trigger_when(state_name, &block)
    reset_event.add_causal_link event
    reset_event.armed = !event.armed?
    event.on { |ev| reset_event.reset }
    reset_event.on { |ev| event.reset }
    reset_event
end

#simulation?Boolean

Returns:

  • (Boolean)


301
# File 'lib/roby/state/state_model.rb', line 301

def simulation?; Roby.app.simulation? end

#testing?Boolean

Returns:

  • (Boolean)


300
# File 'lib/roby/state/state_model.rb', line 300

def testing?; Roby.app.testing? end

#trigger_when(*state_path, &block) ⇒ Object

Returns a state event that emits the first time the block returns true

The block is given the value of the specified state value state_name. For instance, with

event = State.position.trigger_when(:x) do |value|
    value > 23
end

event will be emitted once if the X value of the position gets greater than 23. One can also specify a reset condition with

State.position.reset_when(event, :x) do |value|
    value < 20
end


66
67
68
69
70
71
72
73
# File 'lib/roby/state/events.rb', line 66

def trigger_when(*state_path, &block)
    if !block_given?
        raise ArgumentError, "#trigger_when expects a block"
    end

    state_path.map!(&:to_s)
    StateConditionEvent.new(self, state_path, block)
end