Module: Denko::Behaviors::Reader

Constant Summary collapse

READ_WAIT_TIME =
0.001

Constants included from Lifecycle

Lifecycle::CALLBACK_METHODS

Instance Attribute Summary

Attributes included from State

#state

Instance Method Summary collapse

Methods included from Callbacks

#add_callback, #callbacks, #pre_callback_filter, #remove_callback

Methods included from State

#update_state

Methods included from Lifecycle

included

Instance Method Details

#_readObject

NEVER call this directly. Use #read_nb instead.

Define #_read in including class to get data which updates the peripheral state. See #read_using comments for more info.

Raises:

  • (NotImplementedError)


72
73
74
# File 'lib/denko/behaviors/reader.rb', line 72

def _read
  raise NotImplementedError.new("#{self.class.name}#_read is not defined.")
end

#read(*args, **kwargs, &block) ⇒ Object

Delegates to #_read. Data passes through #pre_callback_filter, runs all callbacks, and @state is set. BLOCKS calling thread.



50
51
52
# File 'lib/denko/behaviors/reader.rb', line 50

def read(*args, **kwargs, &block)
  read_using(self.method(:_read), *args, **kwargs, &block)
end

#read_busy?Boolean

Returns:

  • (Boolean)


131
132
133
134
135
# File 'lib/denko/behaviors/reader.rb', line 131

def read_busy?
  # mruby gets stuck waiting somewhere, but doesn't have threads
  # so it can never really be busy.
  !Denko.mruby? && (@reading_normally || @reading_raw)
end

#read_nb(*args, **kwargs, &block) ⇒ Object

Delegates to #_read. Data passes through #pre_callback_filter, runs all callbacks, and @state is set. DOES NOT BLOCK calling thread.



58
59
60
61
62
63
64
# File 'lib/denko/behaviors/reader.rb', line 58

def read_nb(*args, **kwargs, &block)
  @reader_mutex.lock
  sleep READ_WAIT_TIME while read_busy?
  @reading_normally = true
  _read(*args, **kwargs, &block)
  @reader_mutex.unlock
end

#read_raw(reader, *args, **kwargs) ⇒ Object

Similar to #read_using, but does not trigger #pre_callback_filter, or run any callbacks except :read_raw. BLOCKS calling thread. Use for things like sensor status, config etc.

Raises:

  • (StandardError)


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/denko/behaviors/reader.rb', line 110

def read_raw(reader, *args, **kwargs)
  # Can't guarantee read order.
  raise StandardError, "#read_raw unavailable while listening" if @listening

  # Lock, THEN wait for any normal read to finish.
  @reader_mutex.lock
  sleep READ_WAIT_TIME while read_busy?
  @reading_raw = true

  # Special :read_raw one-time callback.
  return_value = nil
  add_callback(:read_raw) { |bytes| return_value = bytes }

  # Call reader, but block and keep the lock until :read_raw callback gets run.
  reader.call(*args, **kwargs)
  sleep READ_WAIT_TIME while callbacks[:read_raw]
  @reader_mutex.unlock

  return_value
end

#read_using(reader, *args, **kwargs, &block) ⇒ Object

Take a proc/lambda/method as the first agrument and use it to read. Arguments are passed through, allowing dynamic read methods to be defined. Eg. send commands (in args) to a bus, then wait for data read back.

Data is received when the board/bus calls #update on us. If a read was started by this method, the data will pass through #pre_callback_filter, trigger all callbacks, and set @state. Use this for reading the state of peripherals, like digital pin level, enviro sensor reading etc.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/denko/behaviors/reader.rb', line 86

def read_using(reader, *args, **kwargs, &block)
  # Lock, THEN wait for other normal reads to finish.
  @reader_mutex.lock
  sleep READ_WAIT_TIME while read_busy?
  @reading_normally = true

  # One-time callbacks.
  return_value = nil
  add_callback(:read) { |filtered_data| return_value = filtered_data }
  add_callback(:read, &block) if block_given?

  reader.call(*args, **kwargs)
  @reader_mutex.unlock

  # Wait for #update to remove the :read callbacks (return_value is set).
  sleep READ_WAIT_TIME while callbacks[:read]
  return_value
end

#update(data) ⇒ Object

Override #update to allow “raw reads” or “normal reads”:

- Normal reads perform normal #update behavior, passing through
  #pre_callback_filter, running all callbacks and returning filtered_data.
  Use normal for anything that updates the state of a component, and handle
  that in in #pre_callback_filter and #update_state.
  May or may not block calling thread, depending on platform.

- Raw reads bypass #pre_callback_filter, callbacks, and return
  raw data. Use raw for reading things like sensor config/serial etc.
  DOES NOT take block callbacks from the use. ALWAYS handle the return value.
  ALWAYS blocks the calling thread.


31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/denko/behaviors/reader.rb', line 31

def update(data)
  if @reading_raw
    @callback_mutex.lock
    @callbacks[:read_raw].each { |c| c.call(data) }
    @callbacks.delete(:read_raw)
    @callback_mutex.unlock
    @reading_raw = false
    data
  else
    return_value = super(data)
    @reading_normally = false
    return_value
  end
end