Class: Ansible::KNX::KNX_Transceiver

Inherits:
Transceiver show all
Includes:
AnsibleCallback
Defined in:
lib/ansible/knx/knx_transceiver.rb

Overview

The KNX Transceiver is an object responsible for i/o with the KNX bus. It does so using eibd, part of BCU-SDK the open-source libary for KNX.

Defined Under Namespace

Classes: NormalExit

Instance Attribute Summary collapse

Attributes inherited from Transceiver

#thread

Instance Method Summary collapse

Methods included from AnsibleCallback

#add_callback, #fire_callback, #remove_callback

Methods inherited from Transceiver

#stop

Constructor Details

#initialize(connURL = KNX_URL) ⇒ KNX_Transceiver

initialize a KNXTranceiver

*params:

[connURL] an eibd connection URL. see eibd --help for acceptable values


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/ansible/knx/knx_transceiver.rb', line 51

def initialize(connURL=KNX_URL)
    raise "Already initialized!" unless  Ansible::KNX::KNXValue.transceiver.nil?
    @connURL = connURL
    @monitor_conn_ok, @send_conn_ok = false, false
    @send_mutex = Mutex.new()
    @knxbuf = EIBBuffer.new()
    #
    super()
    # store reference to ourselves to the classes that use us
    Ansible::KNX::KNXValue.transceiver = self
    # register default handler for KNX frames
    add_callback(:onKNXtelegram) { | sender, cb, frame |
        puts(frame_inspect(frame)) if $DEBUG
        case frame.apci.value
        when 0 then # A_GroupValue_Read
            puts "read request for knx address #{addr2str(frame.dst_addr, frame.daf)}"
            AnsibleValue[:groups => [frame.dst_addr]].each { |v|
                unless v.current_value.nil? then
                    puts "==> responding with value #{v}"
                    send_apdu_raw(frame.dst_addr, v.to_apdu(0x40))
                end
            }
        when 1 then # A_GroupValue_Response
            puts "response frame by #{addr2str(frame.src_addr)} for knx address #{addr2str(frame.dst_addr, frame.daf)}"
            AnsibleValue[:groups => [frame.dst_addr]].each { |v|
                v.update_from_frame(frame)
                puts "synchronized knx value #{v} from frame #{frame.inspect}" if $DEBUG
            }
        when 2 then # A_GroupValue_Write
            AnsibleValue[:groups => [frame.dst_addr]].each { |v|
                v.update_from_frame(frame)
                puts "updated knx value #{v} from frame #{frame.inspect}" if $DEBUG
            }
        end
    }
end

Instance Attribute Details

#stompObject (readonly)

Returns the value of attribute stomp.



45
46
47
# File 'lib/ansible/knx/knx_transceiver.rb', line 45

def stomp
  @stomp
end

Instance Method Details

#eibd_connection(conn_symbol, conn_ok_symbol) ⇒ Object

get handle to eibd monitor connection



108
109
110
111
112
113
114
# File 'lib/ansible/knx/knx_transceiver.rb', line 108

def eibd_connection(conn_symbol, conn_ok_symbol)
    if instance_variable_get(conn_ok_symbol) then
        return(instance_variable_get(conn_symbol))
    else
        init_eibd(conn_symbol, conn_ok_symbol)
    end
end

#init_eibd(conn_symbol, conn_ok_symbol) ⇒ Object

initialize eibd connection



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/ansible/knx/knx_transceiver.rb', line 89

def init_eibd(conn_symbol, conn_ok_symbol)
    unless instance_variable_get(conn_ok_symbol)
        begin
            puts("KNX: init #{conn_symbol} to #{@connURL}")
            conn = EIBConnection.new()
            conn.EIBSocketURL(@connURL)
            instance_variable_set(conn_symbol, conn)
            instance_variable_set(conn_ok_symbol, true)
            return(conn)
        rescue Errno::ECONNRESET => e
            conn.EIBClose
            instance_variable_set(conn_ok_symbol, false)
            puts "init_eibd: Disconnected, retrying in 10 seconds..."
            sleep(10)
        end
    end
end

#monitor_connObject

get handle to KNX monitoring connection, reconnecting if necessary



117
# File 'lib/ansible/knx/knx_transceiver.rb', line 117

def monitor_conn; return(eibd_connection(:@monitor_conn, :@monitor_conn_ok)); end

#read_eibd_cache(ga, cache_only = false) ⇒ Object

(Try to) read a groupaddr from eibd cache.

return it if found, otherwise query the bus. In the latter case, the main receiver thread (in run()) will act on the response.

  • Arguments:

    ga

    Fixnum: group address (0-65535)

    cache_only

    boolean: when true, do not query the bus



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
# File 'lib/ansible/knx/knx_transceiver.rb', line 201

def read_eibd_cache(ga, cache_only=false)
    src = EIBAddr.new()
    buf = EIBBuffer.new()
    result = nil
    @send_mutex.synchronize {
        # query eibd for a cached value
        if (send_conn.EIB_Cache_Read_Sync(ga, src, buf, 0) == -1) then
            # value not found in cache
            puts "groupaddress #{addr2str(ga, true)} not found in cache."
            unless cache_only then
                puts ".. requesting value on bus .."
                if (send_conn.EIBOpenT_Group(ga, 1) == -1) then
                    raise("KNX client: error setting socket mode")
                end
                # send a read request to the bus
                send_conn.EIBSendAPDU([0,0x00])
            end
            send_conn.EIBReset()
        else
            send_conn.EIBReset()
            # value found in cache..
            puts "found in cache, last sender was #{addr2str(src.data)}"
            result = buf.buffer
        end
    }
    return result
end

#runObject

the main KNX transceiver thread



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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/ansible/knx/knx_transceiver.rb', line 123

def run()
    puts("KNX Transceiver thread is running!")
    @stomp = nil
    begin
        #### part 1: connect to STOMP broker
        @stomp = OnStomp.connect(STOMP_URL)
        #### part 2: subscribe to command channel, listen for messages and pass them to KNX
        # @stomp.subscribe KNX_COMMAND_TOPIC do |msg|
            # dest = msg.headers['dest_addr'].to_i
            # #TODO: check address limits
            # apdu = Marshal.load(CGI.unescape(msg.body))
            # send_apdu_raw(dest, apdu)
        # end
        ##### part 3: monitor KNX bus, post all activity to /knx/monitor
        vbm = monitor_conn.EIBOpenVBusmonitor()
        loop do
            len = monitor_conn.EIBGetBusmonitorPacket(@knxbuf)
            #puts "knxbuffer=="[email protected]
            frame = L_DATA_Frame.read(@knxbuf.buffer.pack('c*'))
            #puts "frame:\n\t"
            headers = {}
            frame.field_names.each { |fieldname|
                field = frame.send(fieldname)
                #puts "\t#{fieldname} == #{field.value}"
                headers[fieldname] = CGI.escape(field.value.to_s)
            }
            @stomp.send(KNX_MONITOR_TOPIC, "KNX Activity", headers)
            #puts Ansible::KNX::APCICODES[frame.apci] + " packet from " + 
            #  addr2str(frame.src_addr) + " to " + addr2str(frame.dst_addr, frame.daf) + 
            #  "  priority=" + Ansible::KNX::PRIOCLASSES[frame.prio_class]
            fire_callback(:onKNXtelegram, frame.dst_addr, frame)
            # 
        end
    rescue Errno::ECONNRESET => e
        @monitor_conn_ok = false
        puts("EIBD disconnected! retrying in 10 seconds..")
        sleep(10)
        retry                    
    rescue NormalExit => e
        puts("KNX transceiver terminating gracefully...")
    rescue Exception => e
        puts("Exception in KNX server thread: #{e}")
        puts("backtrace:\n  " << e.backtrace.join("\n  "))
        sleep(3)
        retry
#                ensure
        #puts "Closing EIB connection..."
        #@monitor_conn.EIBClose() if @monitor_conn
        #puts "Closing STOMP connection..."
        #@stomp.disconnect if @stomp
    end
end

#send_apdu_raw(dest, apdu) ⇒ Object

send a raw APDU to the KNX bus.

  • Arguments:

    dest

    destination (16-bit integer)

    apdu

    raw APDU (binary string)



181
182
183
184
185
186
187
188
189
190
191
# File 'lib/ansible/knx/knx_transceiver.rb', line 181

def send_apdu_raw(dest, apdu)
    @send_mutex.synchronize {
        raise 'apdu must be a byte array!' unless apdu.is_a?Array
        puts("KNX transceiver: sending to group address #{dest}, #{apdu.inspect}") if $DEBUG
        if (send_conn.EIBOpenT_Group(dest, 1) == -1) then
            raise("KNX client: error setting socket mode")
        end
        send_conn.EIBSendAPDU(apdu)
        send_conn.EIBReset()
    }
end

#send_connObject

get handle to KNX sending connection, reconnecting if necessary



120
# File 'lib/ansible/knx/knx_transceiver.rb', line 120

def send_conn; return(eibd_connection(:@send_conn, :@send_conn_ok)); end