Class: Stativus::Statechart

Inherits:
Object
  • Object
show all
Defined in:
lib/stativus.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeStatechart

Returns a new instance of Statechart.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/stativus.rb', line 110

def initialize()
  @all_states = {}
  @all_states[DEFAULT_TREE] = {}
  @states_with_concurrent_substates = {}
  @current_subsates = {}
  @current_state = {}
  @current_state[DEFAULT_TREE] = nil
  @goto_state = false
  @send_event_locked = false
  @pending_state_transitions = []
  @pending_events = []
  @active_subtrees = {}
  @goto_state_locked = false
end

Instance Attribute Details

#active_subtreesObject

Returns the value of attribute active_subtrees.



100
101
102
# File 'lib/stativus.rb', line 100

def active_subtrees
  @active_subtrees
end

#all_statesObject

Returns the value of attribute all_states.



100
101
102
# File 'lib/stativus.rb', line 100

def all_states
  @all_states
end

#current_stateObject

Returns the value of attribute current_state.



100
101
102
# File 'lib/stativus.rb', line 100

def current_state
  @current_state
end

#current_subtreesObject

Returns the value of attribute current_subtrees.



100
101
102
# File 'lib/stativus.rb', line 100

def current_subtrees
  @current_subtrees
end

#goto_state_lockedObject

Returns the value of attribute goto_state_locked.



100
101
102
# File 'lib/stativus.rb', line 100

def goto_state_locked
  @goto_state_locked
end

#pending_eventsObject

Returns the value of attribute pending_events.



100
101
102
# File 'lib/stativus.rb', line 100

def pending_events
  @pending_events
end

#pending_state_transitionsObject

Returns the value of attribute pending_state_transitions.



100
101
102
# File 'lib/stativus.rb', line 100

def pending_state_transitions
  @pending_state_transitions
end

#send_event_lockedObject

Returns the value of attribute send_event_locked.



100
101
102
# File 'lib/stativus.rb', line 100

def send_event_locked
  @send_event_locked
end

#states_with_concurrent_substatesObject

Returns the value of attribute states_with_concurrent_substates.



100
101
102
# File 'lib/stativus.rb', line 100

def states_with_concurrent_substates
  @states_with_concurrent_substates
end

Instance Method Details

#add_state(state_class) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/stativus.rb', line 125

def add_state(state_class)
  state = state_class.new(self)
  tree = state.global_concurrent_state
  parent_state = state.parent_state
  current_tree = @states_with_concurrent_substates[tree]

  if(state.has_concurrent_substates)
    obj = @states_with_concurrent_substates[tree] || {}
    obj[state.name] = true
    @states_with_concurrent_substates[tree] = obj
  end

  if(parent_state and current_tree and current_tree[parent_state])
    parent_state = @all_states[tree][parent_state]
    if(parent_state)
      parent_state.substates.push(state.name)
    end
  end

  obj = @all_states[tree] || {}

  obj[state.name] = state

  sub_states = state.states || []
  
  sub_states.each do |sub_state|
    sub_state.parent_state = state
    sub_state.global_concurrent_state = tree
    add_state(sub_state)
  end
end

#goto_state(requested_state_name, tree, concurrent_tree = nil) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/stativus.rb', line 167

def goto_state(requested_state_name, tree, concurrent_tree=nil)
  all_states = @all_states[tree]
 
  #First, find the current tree off of the concurrentTree, then the main tree
  curr_state = concurrent_tree ? @current_state[concurrent_tree] : @current_state[tree]
  
  requested_state = all_states[requested_state_name]

  # if the current state is the same as the requested state do nothing
  return if(check_all_current_states(requested_state, concurrent_tree || tree))
  
  
  if(@goto_state_locked)
    #There is a state transition currently happening. Add this requested
    #state transition to the queue of pending state transitions. The req
    #will be invoked after the current state transition is finished
    @pending_state_transitions.push({
      :requested_state => requested_state_name,
      :tree => tree
    })
    return
  end
  # Lock for the current state transition, so that it all gets sorted out
  # in the right order
  @goto_state_locked = true
  
  # Get the parent states for the current state and the registered state.
  # we will use them to find the commen parent state
  enter_states = parent_states_with_root(requested_state)
  exit_states = curr_state ? parent_states_with_root(curr_state) : []
  #
  # continue by finding the common parent state for the current and 
  # requested states:
  #
  # At most, this takes O(m^2) time, where m is the maximum depth from the 
  # root of the tree to either the requested state or the current state.
  # Will always be less than or equal to O(n^2), where n is the number
  # of states in the tree
  enter_match_index = nil
  exit_match_index = 0
  exit_states.each_index do |idx|
    exit_match_index = idx
    enter_match_index = enter_states.index(exit_states[idx])
    break if(enter_match_index != nil)    
  end
  
  # In the case where we don't find a common parent state, we 
  # must enter from the root state
  enter_match_index = enter_states.length()-1 if(enter_match_index == nil)

  #setup the enter state sequence
  @enter_states = enter_states
  @enter_state_match_index = enter_match_index
  @enter_state_concurrent_tree = concurrent_tree
  @enter_state_tree = tree
  
  # Now, we will exit all the underlying states till we reach the common
  # parent state. We do not exit the parent state because we transition
  # within it.
  @exit_state_stack = []
  full_exit_from_substates(tree, curr_state) if(curr_state != nil and curr_state.has_concurrent_substates)
  
  if exit_match_index == nil || exit_match_index-1 < 0
    exit_match_index = 0 
  else
    0.upto(exit_match_index-1) do |i|
      @exit_state_stack.push(exit_states[i])
    end
  end
  
  #Now that we have the full stack of states to exit
  #we can exit them...
  
  unwind_exit_state_stack();
  
end

#send_event(evt, *args) ⇒ Object

end goto_state



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/stativus.rb', line 244

def send_event(evt, *args)
  
  # We want to prevent any events from occurring until
  # we have completed the state transitions and events
  if @in_initial_setup or @goto_state_locked or @send_event_locked
    @pending_events.push({
      :evt => evt,
      :args => args
    })
    return
  end
  
  @send_event_locked = true
  
  structure_crawl(evt, args)
  
  # Now, that the states have a chance to process the first action
  # we can go ahead and flush the queued events
  @send_event_locked = false;
  
  flush_pending_events() unless @in_initial_setup      
    
end

#start(state) ⇒ Object

call this in your programs main state is the initial state of the application in the default tree



160
161
162
163
164
165
# File 'lib/stativus.rb', line 160

def start(state)
  @in_initial_setup = true
  self.goto_state(state, DEFAULT_TREE)
  @in_initial_setup = false
  flush_pending_events
end