Class: Xap::Schema::XapBscDevice

Inherits:
XapDevice show all
Defined in:
lib/xap/schema/xap_bsc_device.rb

Overview

Represents an xAP BSC Device. See the xAP Basic Status and Control Schema. www.xapautomation.org/index.php?title=Basic_Status_and_Control_Schema

Instance Attribute Summary

Attributes inherited from XapDevice

#address, #interval, #uid

Instance Method Summary collapse

Methods inherited from XapDevice

#to_s

Constructor Details

#initialize(address, uid, endpoints, interval = 5) ⇒ XapBscDevice

Initializes an XapBscDevice with the given address, uid. Endpoints is an array of hashes containing :State, :Level, :Text, and/or optionally :DisplayText. :State should be a boolean value or nil, :Level should be an array of [numerator, denominator], and :Text and :DisplayText should be Strings. Each input and output block must also have an :endpoint key that contains the endpoint name for the given block and may include a :uid key that contains an integer from 1 to 254. Endpoint names and UIDs must be unique within this device. Output hashes (i.e. those endpoints that can be changed via xAP) are identified by including a :callback key that is a proc to be called with the endpoint hash when any of the output endpoint’s attributes are changed by an incoming xAP message.

See add_endpoint for a summary of endpoint fields.

Example:

XapBscDevice.new XapAddress.new(‘vendor’, ‘dev’, ‘hostname’), Xap.random_uid, [ { :endpoint => ‘Input 1’, :uid => 1, :State => true }, { :endpoint => ‘Output 1’, :State => true, :callback => proc { |ep| puts ‘Output 1’ } } ]



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/xap/schema/xap_bsc_device.rb', line 31

def initialize address, uid, endpoints, interval = 5
  super address, uid, interval

  # TODO: Make endpoints a hash, with what is currently the
  # :endpoint field as the hash key, then store the hash key back
  # into the hash (will simplify calls to XapBscDevice.new, so
  # the user can type => instead of :endpoint)

  @input_count = 0
  @output_count = 0
  @endpoints = {} # Mapping from endpoint name to endpoint hash
  @uids = []
  @outputs = [] # Array containing only output endpoints to simplify handling command messages
  endpoints.each do |ep|
    add_endpoint ep
  end
end

Instance Method Details

#add_endpoint(ep) ⇒ Object

Adds a new endpoint hash to the list of endpoints, generating an xAPBSC.info message if the addition is successful. The endpoint’s name must be unique. UID collision will result in an exception being raised. If the UID is not specified, the lowest available UID will be used.

Summary of endpoint fields: :endpoint - Name of endpoint - mandatory, must be unique when downcased, String :uid - UID of endpoint - optional, must be unique, Fixnum 1-254 :callback - Output change callback - mandatory for outputs, may be nil :State - On/off/? state - mandatory according to xAP BSC spec :Level - numerator / denominator - optional :Text - Stream text - optional (mutually exclusive with :Level according to xAP BSC spec) :DisplayText - UI display text - optional

Example: add_endpoint { :endpoint => ‘Input 1’, :uid => 4, :State => false, :Level => [ 0, 30 ] }



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/xap/schema/xap_bsc_device.rb', line 153

def add_endpoint ep
  unless ep.include?(:endpoint) && ep.include?(:State) && (!ep.include?(:uid) || ep[:uid].is_a?(Fixnum))
    raise 'An endpoint is missing one or more required fields (:endpoint, :State).'
  end

  raise "Duplicate endpoint name #{ep[:endpoint]}." if @endpoints.include? ep[:endpoint].downcase

  ep[:uid] ||= find_free_uid
  raise "Duplicate UID #{ep[:uid]}." if @uids[ep[:uid]]

  # TODO: Additional verification of :Level, :Text, and :DisplayText

  if ep.include? :callback
    @output_count = @output_count + 1
    @outputs << ep
  else
    @input_count = @input_count + 1
  end

  @endpoints[ep[:endpoint].downcase] = ep
  @uids[ep[:uid]] = ep

  send_info ep if @handler
end

#find_free_uidObject

Finds and returns the lowest-available endpoint UID, or nil if there are no free endpoint IDs.



195
196
197
198
199
200
# File 'lib/xap/schema/xap_bsc_device.rb', line 195

def find_free_uid
  for idx in 1..254
    return idx unless @uids[idx]
  end
  nil
end

#get_display_text(endpoint) ⇒ Object

Returns the DisplayText field of the endpoint with the given name.



282
283
284
# File 'lib/xap/schema/xap_bsc_device.rb', line 282

def get_display_text endpoint
  @endpoints[endpoint.downcase][:DisplayText]
end

#get_level(endpoint) ⇒ Object

Returns the Level field of the endpoint with the given name.



231
232
233
# File 'lib/xap/schema/xap_bsc_device.rb', line 231

def get_level endpoint
  @endpoints[endpoint.downcase][:Level]
end

#get_state(endpoint) ⇒ Object

Returns the State field of the endpoint with the given name.



209
210
211
# File 'lib/xap/schema/xap_bsc_device.rb', line 209

def get_state endpoint
  @endpoints[endpoint.downcase][:State]
end

#get_text(endpoint) ⇒ Object

Returns the Text field of the endpoint with the given name.



261
262
263
# File 'lib/xap/schema/xap_bsc_device.rb', line 261

def get_text endpoint
  @endpoints[endpoint.downcase][:Text]
end

#get_uid(endpoint) ⇒ Object

Returns the UID for the endpoint with the given name, or nil if no such endpoint exists.



204
205
206
# File 'lib/xap/schema/xap_bsc_device.rb', line 204

def get_uid endpoint
  @endpoints[endpoint.downcase][:uid]
end

#handler=(handler) ⇒ Object

Assigns the handler that owns this device, then sends the initial state of all input and output blocks as xAPBSC.info messages.



51
52
53
54
# File 'lib/xap/schema/xap_bsc_device.rb', line 51

def handler= handler
  super handler
  announce_endpoints
end

#receive_message(msg) ⇒ Object

Called when a message targeting this device’s address is received.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/xap/schema/xap_bsc_device.rb', line 71

def receive_message msg
  if msg.is_a? XapBscCommand
    Xap.log "Command message for #{self}"

    if @output_count > 0
      eps = []
      if msg.target_addr.wildcard?
        @outputs.each do |out|
          eps << out if msg.target_addr.endpoint_match out[:endpoint]
        end
      else
        ep = @endpoints[msg.target_addr.endpoint.downcase]
        eps << ep if ep
      end

      # For each message block, if ID=*, apply the
      # change to all matched endpoints.  If ID!=*,
      # apply the change to the matching endpoint,
      # iff that endpoint is in the eps list.
      msg.each_block do |blk|
        id = blk.id
        if id == nil || id == '*'
          eps.each do |ep|
            update_endpoint ep, blk
          end
        else
          ep = @uids[id.to_i]
          if ep && eps.include?(ep)
            update_endpoint ep, blk
          end
        end
      end
    end

  elsif msg.is_a? XapBscQuery
    Xap.log "Query message for #{self}, target #{msg.target_addr}, wildcard #{msg.target_addr.wildcard?}"

    if msg.target_addr.wildcard?
      @endpoints.each do |name, ep|
        if msg.target_addr.endpoint_match name
          Xap.log "Matching endpoint found: #{ep[:endpoint]}"
          send_info ep
        end
      end
    elsif msg.target_addr.endpoint
      ep = @endpoints[msg.target_addr.endpoint.to_s.downcase]
      if ep
        Xap.log "Matching endpoint found: #{ep[:endpoint]}"
        send_info ep
      else
        Xap.log "No matching endpoint found"
      end
    else
      Xap.log "Error: No endpoint was given in the query"
    end

  elsif msg.is_a? XapBscInfo
    Xap.log "Info message for #{self}"

  elsif msg.is_a? XapBscEvent
    Xap.log "Event message for #{self}"

  end
end

#remove_endpoint(ep) ⇒ Object

Removes the given endpoint, which may be the endpoint hash or name. Doesn’t verify that the endpoint actually exists.



180
181
182
183
184
185
186
# File 'lib/xap/schema/xap_bsc_device.rb', line 180

def remove_endpoint ep
  if ep.is_a? String
    ep = @endpoints[ep.downcase]
  end
  @endpoints.delete ep[:endpoint].downcase
  @uids[ep[:uid]] = nil
end

#set_address(address) ⇒ Object

Sets the xAP address of this virtual device, then sends an xAPBSC.info message for all endpoints.



65
66
67
68
# File 'lib/xap/schema/xap_bsc_device.rb', line 65

def set_address address
  super address
  announce_endpoints
end

#set_display_text(endpoint, text) ⇒ Object

Sets the DisplayText field of the endpoint with the given name. If the new state is different from the old state, an event message will be generated. Otherwise, an info message will be generated.



289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/xap/schema/xap_bsc_device.rb', line 289

def set_display_text endpoint, text
  ep = @endpoints[endpoint.downcase]

  old = ep[:DisplayText]
  ep[:DisplayText] = old

  if text != old
    send_event ep
  else
    send_info ep
  end
end

#set_level(endpoint, level) ⇒ Object

Sets the Level field of the endpoint with the given name. If level is an array, then both the numerator and denominator are replaced. If level is a Fixnum, only the numerator is replaced. If the new level is different from the old level, an event message will be generated. Otherwise, an info message will be generated. Error checking is not performed on the level parameter. Pass nil to remove the level from this endpoint.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/xap/schema/xap_bsc_device.rb', line 242

def set_level endpoint, level
  ep = @endpoints[endpoint.downcase]

  old = ep[:Level]
  if level != nil
    level = [ level, ep[:Level][1] ] if level.is_a? Fixnum
    ep[:Level] = level
  else
    ep.delete :Level
  end

  if level != old
    send_event ep
  else
    send_info ep
  end
end

#set_state(endpoint, state) ⇒ Object

Sets the State field of the endpoint with the given name. If the new state is different from the old state, an event message will be generated. Otherwise, an info message will be generated.



216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/xap/schema/xap_bsc_device.rb', line 216

def set_state endpoint, state
  raise 'state must be true, false, or nil.' unless state == true || state == false || state == nil

  ep = @endpoints[endpoint.downcase]
  old = ep[:State]
  ep[:State] = state

  if state != old
    send_event ep
  else
    send_info ep
  end
end

#set_text(endpoint, text) ⇒ Object

Sets the Text field of the endpoint with the given name. If the new state is different from the old state, an event message will be generated. Otherwise, an info message will be generated.



268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/xap/schema/xap_bsc_device.rb', line 268

def set_text endpoint, text
  ep = @endpoints[endpoint.downcase]

  old = ep[:Text]
  ep[:Text] = old

  if text != old
    send_event ep
  else
    send_info ep
  end
end

#uid=(uid) ⇒ Object

Sets the xAP UID of this virtual device, then sends an xAPBSC.info message for all endpoints.



58
59
60
61
# File 'lib/xap/schema/xap_bsc_device.rb', line 58

def uid= uid
  super uid
  announce_endpoints
end

#uid_endpoint(uid) ⇒ Object

Returns the endpoint having the given UID, if any.



189
190
191
# File 'lib/xap/schema/xap_bsc_device.rb', line 189

def uid_endpoint uid
  @uids[uid]
end