Class: Ansible::KNX::KNXValue
- Inherits:
-
Object
- Object
- Ansible::KNX::KNXValue
- Includes:
- AnsibleValue
- Defined in:
- lib/ansible/knx/knx_value.rb
Overview
a KNXValue is a device-dependant datapoint. It is initialized by a
DPT type name (e.g. “1.001” for binary switch) and is extended by the initializer with the corresponding DPT module (e.g. KNX::DPT1) so as to handle DPT1 frames. Each KNXValue is linked to zero or more group addresses, the first of which will be the “update” value
Constant Summary collapse
- @@ids =
—— CLASS VARIABLES & METHODS
0
- @@AllGroups =
a Hash containing all known group addresses
{}
- @@transceiver =
the transceiver responsible for all things KNX
nil
Instance Attribute Summary collapse
-
#description ⇒ Object
Returns the value of attribute description.
-
#dpt_basetype ⇒ Object
readonly
Returns the value of attribute dpt_basetype.
-
#dpt_subtype ⇒ Object
readonly
Returns the value of attribute dpt_subtype.
-
#flags ⇒ Object
readonly
set flag: knxvalue.flags = true get flag: knxvalue.flags (evaluates to true, meaning the read flag is set).
-
#groups ⇒ Object
Returns the value of attribute groups.
-
#id ⇒ Object
readonly
Returns the value of attribute id.
Attributes included from AnsibleValue
#current_value, #last_update, #previous_value
Class Method Summary collapse
Instance Method Summary collapse
-
#==(other) ⇒ Object
equality checking.
-
#explain(frame) ⇒ Object
return a human-readable representation of a DPT frame.
-
#getparam(param, field = nil) ⇒ Object
get a DPT parameter, trying to locate it in the following order: 1) in the DPTFrame field definition 2) in the DPT subtype definition 3) in the DPT basetype definition.
-
#group_primary=(grpaddr) ⇒ Object
set primary group address.
-
#initialize(dpt, groups = [], flags = nil) ⇒ KNXValue
constructor
initialize a KNXValue ===Arguments: [dpt] string representing the DPT (datapoint type) of the value e.g.
-
#read_only? ⇒ Boolean
is this KNX datapoint read only?.
-
#read_value ⇒ Object
read value from eibd’s group cache, or issue a read request, hoping that someone will respond with the last known status for this value.
-
#to_apdu(frame, apci_code = 0x40) ⇒ Object
create apdu for this KNXValue APDU types are: 0x00 => Read 0x40 => Response (default) 0x80 => Write.
-
#to_s ⇒ Object
human-readable representation of the value.
-
#update_from_frame(rawframe) ⇒ Object
update internal state from raw KNX frame.
-
#validate_ranges ⇒ Object
make sure all frame fields are valid (within min,max range) see Ansible::AnsibleValue.update.
-
#write_only? ⇒ Boolean
is this KNX datapoint write only?.
-
#write_value(new_val) ⇒ Object
write value to the bus argument: new_val according to DPT if :basic, then new_val is plain data value else (:composite) new_val is a hash of field_name => field_value pairs all values get mapped using to_protocol_value() in AnsibleValue::update() return true if successful, false otherwise.
Methods included from AnsibleValue
[], #as_canonical_value, #get, insert, #matches?, #set, #to_protocol_value, #update
Methods included from AnsibleCallback
#add_callback, #fire_callback, #remove_callback
Constructor Details
#initialize(dpt, groups = [], flags = nil) ⇒ KNXValue
initialize a KNXValue
Arguments:
- dpt
-
string representing the DPT (datapoint type) of the value e.g. “5.001” meaning DPT5 percentage value (8-bit unsigned)
- groups
-
array of group addresses associated with this datapoint
- flags
-
hash of symbol=>boolean flags regarding its behaviour e.g. => true the value can only respond to read requests on the KNX bus. default flags: READ and WRITE
c => Communication r => Read w => Write t => Transmit u => Update i => read on Init
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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/ansible/knx/knx_value.rb', line 96 def initialize(dpt, groups=[], flags=nil) # init DPT info if md = /(\d*)\.(\d*)/.match(dpt) then @dpt = dpt @dpt_mod = Ansible::KNX.module_eval("DPT#{md[1]}") raise "unknown/undeclared DPT module #{dpt}" unless @dpt_mod.is_a?Module @parserclass = @dpt_mod.module_eval("DPT#{md[1]}_Frame") raise "unknown/undeclared parser for DPT #{dpt}" unless @parserclass.ancestors.include?(DPTFrame) @dpt_basetype = @dpt_mod::Basetype raise "missing Basetype info for #{dpt}" unless @dpt_basetype.is_a?Hash @dpt_subtype = @dpt_mod::Subtypes[md[2]] raise "missing sybtype info for #{dpt}" unless @dpt_subtype.is_a?Hash # extend this object with DPT-specific module self.extend(@dpt_mod) # print out some useful debug info puts " dpt_basetype = #{@dpt_basetype}, dpt_subtype = #{@dpt_subtype}" if $DEBUG else raise "invalid datapoint type (#{dpt})" end # array of GroupAddress objects associated with this datapoint # only the first address is used in a write operation (TODO: CHECKME) @groups = case groups when Fixnum then Array[group] when String then Array[str2addr(groups)] when Array then groups end # store DPT info about these group addresses @groups.each { |grp| # sanity check: is this groupaddr already decaled as a different basetype? # FIXME: specs dont forbid it, only check required is datalength compatibility if @@AllGroups[grp] and (old_dpt = @@AllGroups[grp][:dpt_basetype]) and not (old_dpt.eql?(@dpt_basetype)) raise "Group address #{addr2str(grp)} is already declared as DPT basetype #{old_dpt}!" end puts "adding groupaddr #{addr2str(grp,true) } (#{@dpt}: #{@dpt_subtype[:name]}), to global hash" @@AllGroups[grp] = {:basetype => @dpt_basetype, :subtype => @dpt_subtype} } if flags.nil? # default flags: READ and WRITE @flags = {:r => true,:w => true} else raise "flags parameter must be a Hash!" unless flags.is_a?Hash @flags = flags end # TODO: physical address: set only for remote nodes we are monitoring # when left to nil, it/ means a datapoint on this KNXTransceiver @physaddr = nil # id of datapoint # initialized by class method KNXValue.id_generator @id = KNXValue.id_generator() @description = '' # store this KNXValue in the Ansible database AnsibleValue.insert(self) end |
Instance Attribute Details
#description ⇒ Object
Returns the value of attribute description.
77 78 79 |
# File 'lib/ansible/knx/knx_value.rb', line 77 def description @description end |
#dpt_basetype ⇒ Object (readonly)
Returns the value of attribute dpt_basetype.
76 77 78 |
# File 'lib/ansible/knx/knx_value.rb', line 76 def dpt_basetype @dpt_basetype end |
#dpt_subtype ⇒ Object (readonly)
Returns the value of attribute dpt_subtype.
76 77 78 |
# File 'lib/ansible/knx/knx_value.rb', line 76 def dpt_subtype @dpt_subtype end |
#flags ⇒ Object (readonly)
set flag: knxvalue.flags = true get flag: knxvalue.flags (evaluates to true, meaning the read flag is set)
74 75 76 |
# File 'lib/ansible/knx/knx_value.rb', line 74 def flags @flags end |
#groups ⇒ Object
Returns the value of attribute groups.
75 76 77 |
# File 'lib/ansible/knx/knx_value.rb', line 75 def groups @groups end |
#id ⇒ Object (readonly)
Returns the value of attribute id.
76 77 78 |
# File 'lib/ansible/knx/knx_value.rb', line 76 def id @id end |
Class Method Details
.id_generator ⇒ Object
48 49 50 51 |
# File 'lib/ansible/knx/knx_value.rb', line 48 def KNXValue.id_generator @@ids = @@ids + 1 return @@ids end |
.transceiver ⇒ Object
58 |
# File 'lib/ansible/knx/knx_value.rb', line 58 def KNXValue.transceiver; return @@transceiver; end |
.transceiver=(other) ⇒ Object
59 60 61 |
# File 'lib/ansible/knx/knx_value.rb', line 59 def KNXValue.transceiver=(other); @@transceiver = other if other.is_a? Ansible::KNX::KNX_Transceiver end |
Instance Method Details
#==(other) ⇒ Object
equality checking
68 69 70 |
# File 'lib/ansible/knx/knx_value.rb', line 68 def == (other) return (other.is_a?(KNXValue) and (@id == other.id)) end |
#explain(frame) ⇒ Object
return a human-readable representation of a DPT frame
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/ansible/knx/knx_value.rb', line 291 def explain(frame) raise "explain() expects a DPTFrame, got a #{frame.class}" unless frame.is_a?DPTFrame fielddata = [] # iterate over all available DPT fields frame.field_names.each { |fieldname| # skip padding fields next if /pad/.match(fieldname) field = frame.send(fieldname) fval = field.value # get value encoding hashmap, if any vhash = getparam(:enc, field) # get value units units = getparam(:unit, field) fval = as_canonical_value() # add field value, according to encoding hashtable fielddata << "#{(vhash.is_a?Hash) ? vhash[fval] : fval} #{units}" } return fielddata.join(', ') end |
#getparam(param, field = nil) ⇒ Object
get a DPT parameter, trying to locate it in the following order:
1) in the DPTFrame field definition
2) in the DPT subtype definition
3) in the DPT basetype definition
315 316 317 318 319 |
# File 'lib/ansible/knx/knx_value.rb', line 315 def getparam(param, field=nil) return ((field and field.get_parameter(param)) or (@dpt_subtype and @dpt_subtype[param]) or (@dpt_basetype and @dpt_basetype[param])) end |
#group_primary=(grpaddr) ⇒ Object
set primary group address
231 232 233 |
# File 'lib/ansible/knx/knx_value.rb', line 231 def group_primary=(grpaddr) @groups.unshift(grpaddr) end |
#read_only? ⇒ Boolean
is this KNX datapoint read only?
159 160 161 |
# File 'lib/ansible/knx/knx_value.rb', line 159 def read_only? return((defined? @flags) and (@flags[:r] and not @flags[:w])) end |
#read_value ⇒ Object
read value from eibd’s group cache, or issue a read request, hoping that someone will respond with the last known status for this value
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/ansible/knx/knx_value.rb', line 171 def read_value() if (not @groups.nil?) and (group = @groups[0]) then if (data = @@transceiver.read_eibd_cache(group)) then fire_callback(:onReadCacheHit) # update the value update(@parserclass.read(data.pack('c*'))) else # value not found in cache, maybe some other # device on the bus will respond... fire_callback(:onReadCacheMiss) nil end else return false end end |
#to_apdu(frame, apci_code = 0x40) ⇒ Object
create apdu for this KNXValue APDU types are:
0x00 => Read
0x40 => Response (default)
0x80 => Write
254 255 256 257 258 259 260 261 262 263 |
# File 'lib/ansible/knx/knx_value.rb', line 254 def to_apdu(frame, apci_code = 0x40) apdu = if @dpt_mod::Basetype[:bitlength] <= 6 then #[0, apci_code | @current_value] [0, apci_code | frame.data] else #[0, apci_code] + @current_value.to_a [0, apci_code] + frame.to_binary_s.unpack('C*') end return apdu end |
#to_s ⇒ Object
human-readable representation of the value. Uses all field info from its DPT included module, if available.
280 281 282 283 284 285 286 287 288 |
# File 'lib/ansible/knx/knx_value.rb', line 280 def to_s dpt_name = (@dpt_subtype.nil?) ? '' : @dpt_subtype[:name] dpt_info = "KNXValue[#{@dpt} #{dpt_name}]" # add field values explanation, if any vstr = (defined?(@current_value) ? explain(@current_value) : '(value undefined)') # return @dpt: values.explained gaddrs = @groups.collect{|ga| addr2str(ga, true)}.join(', ') return [@description, gaddrs, dpt_info].compact.join(' ') + " : #{vstr}" end |
#update_from_frame(rawframe) ⇒ Object
update internal state from raw KNX frame
266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/ansible/knx/knx_value.rb', line 266 def update_from_frame(rawframe) data = if @dpt_mod::Basetype[:bitlength] <= 6 then # bindata always expects a binary string [rawframe.apci_data].pack('c') else rawframe.data end foo = @parserclass.read(data) puts "update_from_frame: #{foo.class} = #{foo.inspect}" update(foo) end |
#validate_ranges ⇒ Object
make sure all frame fields are valid (within min,max range) see Ansible::AnsibleValue.update
243 244 245 246 247 |
# File 'lib/ansible/knx/knx_value.rb', line 243 def validate_ranges if defined? @current_value then @current_value.validate_ranges end end |
#write_only? ⇒ Boolean
is this KNX datapoint write only?
164 165 166 |
# File 'lib/ansible/knx/knx_value.rb', line 164 def write_only? return((defined? @flags) and (@flags[:w] and not @flags[:r])) end |
#write_value(new_val) ⇒ Object
write value to the bus argument: new_val according to DPT
if :basic, then new_val is plain data value
else (:composite) new_val is a hash of
field_name => field_value pairs
all values get mapped using to_protocol_value() in AnsibleValue::update()
return true if successful, false otherwise
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 |
# File 'lib/ansible/knx/knx_value.rb', line 196 def write_value(new_val) #write value to primary group address dest, telegram = nil, nil if @groups.length > 0 then dest = @groups[0] else raise "#{self}: primary group address not set!!!" end case @dpt_mod::Basetype[:valuetype] when :basic then # basic value: single 'data' field telegram = @parserclass.new(:data => new_val) when :composite then if new_val.is_a?Hash then telegram = @parserclass.new(new_val) else if @parserclass.methods.include?(:assign) then puts "FIXME" else raise "#{self} has a composite DPT, set() expects a hash!" end end end # puts "#{self}: Writing new value (#{new_val}) to #{addr2str(dest, true)}" # if (@@transceiver.send_apdu_raw(dest, to_apdu(telegram, 0x80)) > -1) then update(telegram) return(true) else return(false) end end |