Class: AutomateEm::Device::Base

Inherits:
EventMachine::Connection
  • Object
show all
Includes:
AutomateEm::DeviceConnection, Utilities
Defined in:
lib/automate-em/device/tcp_control.rb

Overview

This is how sending works Send receives data, turns a mutex on and sends the data – It goes into the receive mutex critical section and sleeps waiting for a response – a timeout is used as a backup in case no response is received The receive function does the following – If the send lock is not active it processes the received data – otherwise it notifies the send function that data is avaliable

Instance Method Summary collapse

Methods included from AutomateEm::DeviceConnection

#add_to_queue, #call_connected, #config=, #default_send_options=, #do_process_response, #do_receive_data, #do_send_command, #initialize, #logger, #process_next_send, #process_response, #process_response_complete, #process_result, #process_send, #received_lock, #sending_timeout, #shutdown

Methods included from Utilities

array_to_str, byte_to_hex, hex_to_byte, schedule, str_to_array, task

Instance Method Details

#connection_completedObject



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/automate-em/device/tcp_control.rb', line 42

def connection_completed
	# set status
	resume if paused?
	
	@connect_retry = @connect_retry || Atomic.new(0)
	EM.defer do
		@connect_retry.value = 0
	end
	
	if !@tls_enabled
		@connected = true
		@connecting = false
		call_connected
	else
		if !@parent.respond_to?(:certificates)
			start_tls
		else
			begin
				certs = @parent.certificates
				start_tls(certs)
			rescue => e
				EM.defer do
					AutomateEm.print_error(logger, e, {
						:message => "module #{@parent.class} error whilst starting TLS with certificates",
						:level => Logger::ERROR
					})
				end
			end
		end
	end

	@make_occured = true
end

#do_connectObject



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
175
176
177
178
179
180
181
# File 'lib/automate-em/device/tcp_control.rb', line 129

def do_connect
	if @disconnecting
		EM.next_tick do
			do_connect
		end
		return
	end
	return if @connected	# possible to get here

	makebreak = @make_break
	@connecting = true
	EM.defer do
		if !@shutting_down.value
			begin
				settings = DeviceModule.lookup(@parent)	#.reload # Don't reload here (user driven)
				
				@connect_retry.update { |v| v += 1}
				
				if @connect_retry.value == 1 || makebreak
					res = ResolverJob.new(settings.ip)
					res.callback {|ip|
						reconnect ip, settings.port
					}
					res.errback {|error|
						EM.defer do
							logger.info "module #{@parent.class} in tcp_control.rb, unbind"
							logger.info "Reconnect failed for #{settings.ip}:#{settings.port} - #{error.inspect}"
						end
						@connect_retry.value = 2
						do_reconnect(settings) unless makebreak
					}
				else
					#
					# log this once if had to retry more than once
					#
					if @connect_retry.value == 2
						logger.info "module #{@parent.class} in tcp_control.rb, unbind"
						logger.info "Reconnect failed for #{settings.ip}:#{settings.port}"
					end
	
					do_reconnect(settings)
				end
			rescue
				AutomateEm.print_error(logger, e, {
					:message => "module #{@parent.class} in tcp_control.rb, unbind\nFailed to lookup settings. Device probably going offline.",
					:level => Logger::FATAL
				})
				
				# Do not attempt to reconnect this device!
			end
		end
	end
end

#do_reconnect(settings) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/automate-em/device/tcp_control.rb', line 183

def do_reconnect(settings)
	EM::Timer.new(5) do
		if !@shutting_down.value
			res = ResolverJob.new(settings.ip)
			res.callback {|ip|
				reconnect ip, settings.port
			}
			res.errback {|error|
				do_reconnect(settings)
			}
		end
	end
end

#do_send_data(data) ⇒ Object



201
202
203
# File 'lib/automate-em/device/tcp_control.rb', line 201

def do_send_data(data)
	send_data(data)
end

#receive_data(data) ⇒ Object



197
198
199
# File 'lib/automate-em/device/tcp_control.rb', line 197

def receive_data(data)
	do_receive_data(data)
end

#ssl_handshake_completedObject



76
77
78
79
80
# File 'lib/automate-em/device/tcp_control.rb', line 76

def ssl_handshake_completed
	@connected = true
	@connecting = false
	call_connected(get_peer_cert)		# this will mark the true connection complete stage for encrypted devices
end

#unbindObject



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
# File 'lib/automate-em/device/tcp_control.rb', line 83

def unbind
	@connected = false	# set offline
	@connecting = false
	@disconnecting = false

	if @config[:flush_buffer_on_disconnect]
		process_response(@buf.flush, nil) unless @buf.nil?
	else
		@buf.flush unless @buf.nil?	# Any incomplete from TCP stream is now invalid
	end

	@connect_retry = @connect_retry || Atomic.new(0)
	
	if @config[:clear_queue_on_disconnect] || (@make_break && !@make_occured)
		@send_queue.clear
	end
	@make_occured = false
	
	EM.defer do
		if !@shutting_down.value
			
			@task_queue.push lambda {
				@parent[:connected] = false
				return unless @parent.respond_to?(:disconnected)
				begin
					@parent.disconnected
				rescue => e
					#
					# save from bad user code (don't want to deplete thread pool)
					#
					AutomateEm.print_error(logger, e, {
						:message => "module #{@parent.class} error whilst calling: disconnected",
						:level => Logger::ERROR
					})
				end
			}
		end
	end
	
	if !@make_break
		do_connect
	elsif @send_queue.size() > 0
		do_connect
	end
end