somfy_sdn 1.0.12 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/somfy_sdn +60 -0
- data/lib/sdn.rb +16 -1
- data/lib/sdn/cli/mqtt.rb +369 -0
- data/lib/sdn/cli/mqtt/group.rb +31 -0
- data/lib/sdn/cli/mqtt/motor.rb +103 -0
- data/lib/sdn/cli/mqtt/read.rb +154 -0
- data/lib/sdn/cli/mqtt/subscriptions.rb +156 -0
- data/lib/sdn/cli/mqtt/write.rb +93 -0
- data/lib/sdn/cli/provisioner.rb +234 -0
- data/lib/sdn/cli/simulator.rb +197 -0
- data/lib/sdn/client.rb +84 -0
- data/lib/sdn/message.rb +81 -30
- data/lib/sdn/{messages → message}/control.rb +79 -38
- data/lib/sdn/{messages → message}/get.rb +48 -40
- data/lib/sdn/{messages → message}/helpers.rb +33 -3
- data/lib/sdn/message/ilt2/get.rb +48 -0
- data/lib/sdn/message/ilt2/master_control.rb +35 -0
- data/lib/sdn/message/ilt2/post.rb +127 -0
- data/lib/sdn/message/ilt2/set.rb +192 -0
- data/lib/sdn/{messages → message}/post.rb +127 -61
- data/lib/sdn/{messages → message}/set.rb +99 -84
- data/lib/sdn/version.rb +1 -1
- metadata +53 -16
- data/bin/sdn_mqtt_bridge +0 -5
- data/lib/sdn/messages/ilt2/get.rb +0 -9
- data/lib/sdn/messages/ilt2/post.rb +0 -18
- data/lib/sdn/messages/ilt2/set.rb +0 -59
- data/lib/sdn/mqtt_bridge.rb +0 -711
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'curses'
|
2
|
+
|
3
|
+
module SDN
|
4
|
+
module CLI
|
5
|
+
class Provisioner
|
6
|
+
attr_reader :win, :sdn, :addr, :ns
|
7
|
+
|
8
|
+
def initialize(port, addr = nil)
|
9
|
+
@sdn = Client.new(port)
|
10
|
+
@reversed = false
|
11
|
+
@pulse_count = 10
|
12
|
+
|
13
|
+
if addr
|
14
|
+
@addr = addr = Message.parse_address(addr)
|
15
|
+
else
|
16
|
+
puts "Discovering motor..."
|
17
|
+
message = sdn.ensure(Message::GetNodeAddr.new)
|
18
|
+
puts "Found #{message.node_type}"
|
19
|
+
@addr = addr = message.src
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "Preparing to provision motor #{Message.print_address(addr)}"
|
23
|
+
|
24
|
+
message = sdn.ensure(Message::GetNodeLabel.new(addr))
|
25
|
+
|
26
|
+
node_type = message.node_type
|
27
|
+
@ns = ns = node_type == :st50ilt2 ? Message::ILT2 : Message
|
28
|
+
|
29
|
+
print "Motor is currently labeled '#{message.label}'; what would you like to change it to (blank to leave alone)? "
|
30
|
+
new_label = STDIN.gets
|
31
|
+
|
32
|
+
unless new_label == "\n"
|
33
|
+
new_label.strip!
|
34
|
+
sdn.ensure(ns::SetNodeLabel.new(addr, new_label))
|
35
|
+
end
|
36
|
+
|
37
|
+
# make sure some limits exist
|
38
|
+
unless ns == Message::ILT2
|
39
|
+
limits = sdn.ensure(Message::GetMotorLimits.new(addr))
|
40
|
+
if limits.up_limit.nil? || limits.down_limit.nil?
|
41
|
+
sdn.ensure(Message::SetMotorLimits.new(addr, :delete, :up))
|
42
|
+
sdn.ensure(Message::SetMotorLimits.new(addr, :delete, :down))
|
43
|
+
sdn.ensure(Message::SetMotorLimits.new(addr, :current_position, :up))
|
44
|
+
sdn.ensure(Message::SetMotorLimits.new(addr, :specified_position, :down, 500))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Curses.init_screen
|
49
|
+
begin
|
50
|
+
Curses.noecho
|
51
|
+
Curses.crmode
|
52
|
+
Curses.nonl
|
53
|
+
Curses.curs_set(0)
|
54
|
+
@win = Curses.stdscr
|
55
|
+
|
56
|
+
process
|
57
|
+
rescue => e
|
58
|
+
win.setpos(0, 0)
|
59
|
+
win.addstr(e.inspect)
|
60
|
+
win.addstr("\n")
|
61
|
+
win.addstr(e.backtrace.join("\n"))
|
62
|
+
win.refresh
|
63
|
+
sleep 10
|
64
|
+
ensure
|
65
|
+
Curses.close_screen
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def process
|
70
|
+
win.keypad = true
|
71
|
+
print_help
|
72
|
+
refresh
|
73
|
+
|
74
|
+
loop do
|
75
|
+
char = win.getch
|
76
|
+
case char
|
77
|
+
when 27 # Esc
|
78
|
+
stop
|
79
|
+
refresh
|
80
|
+
when Curses::Key::UP
|
81
|
+
if ilt2?
|
82
|
+
sdn.ensure(Message::ILT2::SetMotorPosition.new(addr, :up_limit))
|
83
|
+
else
|
84
|
+
sdn.ensure(Message::MoveTo.new(addr, :up_limit))
|
85
|
+
end
|
86
|
+
wait_for_stop
|
87
|
+
when Curses::Key::DOWN
|
88
|
+
if ilt2?
|
89
|
+
sdn.ensure(Message::ILT2::SetMotorPosition.new(addr, :down_limit))
|
90
|
+
else
|
91
|
+
sdn.ensure(Message::MoveTo.new(addr, :down_limit))
|
92
|
+
end
|
93
|
+
wait_for_stop
|
94
|
+
when Curses::Key::LEFT
|
95
|
+
if @pos < @pulse_count
|
96
|
+
sdn.ensure(Message::ILT2::SetMotorSettings.new(addr, reversed_int, @limit + @pulse_count - @pos, @pulse_count))
|
97
|
+
refresh
|
98
|
+
end
|
99
|
+
sdn.ensure(Message::ILT2::SetMotorPosition.new(addr, :jog_up_pulses, @pulse_count))
|
100
|
+
wait_for_stop
|
101
|
+
when Curses::Key::RIGHT
|
102
|
+
if @limit - @pos < @pulse_count
|
103
|
+
sdn.ensure(Message::ILT2::SetMotorSettings.new(addr, reversed_int, @pos + @pulse_count, @pos))
|
104
|
+
refresh
|
105
|
+
end
|
106
|
+
sdn.ensure(Message::ILT2::SetMotorPosition.new(addr, :jog_down_pulses, @pulse_count))
|
107
|
+
wait_for_stop
|
108
|
+
when 'u'
|
109
|
+
if ilt2?
|
110
|
+
sdn.ensure(Message::ILT2::SetMotorSettings.new(addr, reversed_int, @limit - @pos, 0))
|
111
|
+
else
|
112
|
+
sdn.ensure(Message::SetMotorLimits.new(addr, :current_position, :up))
|
113
|
+
end
|
114
|
+
refresh
|
115
|
+
when 'l'
|
116
|
+
if ilt2?
|
117
|
+
sdn.ensure(Message::ILT2::SetMotorSettings.new(addr, reversed_int, @pos, @pos))
|
118
|
+
else
|
119
|
+
sdn.ensure(Message::SetMotorLimits.new(addr, :current_position, :down))
|
120
|
+
end
|
121
|
+
refresh
|
122
|
+
when 'r'
|
123
|
+
@reversed = !@reversed
|
124
|
+
if ilt2?
|
125
|
+
sdn.ensure(Message::ILT2::SetMotorSettings.new(addr, reversed_int, @limit, @limit - @pos))
|
126
|
+
else
|
127
|
+
sdn.ensure(Message::SetMotorDirection.new(addr, @reversed ? :reversed : :standard))
|
128
|
+
end
|
129
|
+
refresh
|
130
|
+
when 'R'
|
131
|
+
next unless ilt2?
|
132
|
+
@reversed = !@reversed
|
133
|
+
sdn.ensure(Message::ILT2::SetMotorSettings.new(addr, reversed_int, @limit, @pos))
|
134
|
+
refresh
|
135
|
+
when '<'
|
136
|
+
@pulse_count /= 2 if @pulse_count > 5
|
137
|
+
print_help
|
138
|
+
when '>'
|
139
|
+
@pulse_count *= 2
|
140
|
+
print_help
|
141
|
+
when 'q'
|
142
|
+
break
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def print_help
|
148
|
+
win.setpos(0, 0)
|
149
|
+
win.addstr(<<-INSTRUCTIONS)
|
150
|
+
Move the motor. Keys:
|
151
|
+
Esc stop movement
|
152
|
+
\u2191 go to upper limit
|
153
|
+
\u2193 go to lower limit
|
154
|
+
\u2190 jog up #{@pulse_count} pulses
|
155
|
+
\u2192 jog down #{@pulse_count} pulses
|
156
|
+
> increase jog size
|
157
|
+
< decrease jog size
|
158
|
+
u set upper limit at current position
|
159
|
+
l set lower limit at current position
|
160
|
+
r reverse motor
|
161
|
+
INSTRUCTIONS
|
162
|
+
|
163
|
+
if ilt2?
|
164
|
+
win.addstr("R reverse motor (but leave position alone)\n")
|
165
|
+
end
|
166
|
+
win.addstr("q quit\n")
|
167
|
+
win.refresh
|
168
|
+
end
|
169
|
+
|
170
|
+
def wait_for_stop
|
171
|
+
win.setpos(13, 0)
|
172
|
+
win.addstr("Moving...\n")
|
173
|
+
loop do
|
174
|
+
win.nodelay = true
|
175
|
+
stop if win.getch == 27 # Esc
|
176
|
+
sdn.send(ns::GetMotorPosition.new(addr))
|
177
|
+
sdn.receive do |message|
|
178
|
+
next unless message.is_a?(ns::PostMotorPosition)
|
179
|
+
last_pos = @pos
|
180
|
+
@pos = message.position_pulses
|
181
|
+
win.setpos(14, 0)
|
182
|
+
win.addstr("Position: #{@pos}\n")
|
183
|
+
|
184
|
+
if last_pos == @pos
|
185
|
+
win.setpos(13, 0)
|
186
|
+
win.addstr("\n")
|
187
|
+
win.nodelay = false
|
188
|
+
refresh
|
189
|
+
return
|
190
|
+
end
|
191
|
+
end
|
192
|
+
sleep 0.1
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def refresh
|
198
|
+
pos = sdn.ensure(ns::GetMotorPosition.new(addr))
|
199
|
+
@pos = pos.position_pulses
|
200
|
+
if ilt2?
|
201
|
+
settings = sdn.ensure(Message::ILT2::GetMotorSettings.new(addr))
|
202
|
+
@limit = settings.limit
|
203
|
+
else
|
204
|
+
limits = sdn.ensure(Message::GetMotorLimits.new(addr))
|
205
|
+
@limit = limits.down_limit
|
206
|
+
direction = sdn.ensure(Message::GetMotorDirection.new(addr))
|
207
|
+
@reversed = direction.direction == :reversed
|
208
|
+
end
|
209
|
+
|
210
|
+
win.setpos(14, 0)
|
211
|
+
win.addstr("Position: #{@pos}\n")
|
212
|
+
win.addstr("Limit: #{@limit}\n")
|
213
|
+
win.addstr("Reversed: #{@reversed}\n")
|
214
|
+
win.refresh
|
215
|
+
end
|
216
|
+
|
217
|
+
def stop
|
218
|
+
if ilt2?
|
219
|
+
sdn.ensure(Message::ILT2::SetMotorPosition.new(addr, :stop))
|
220
|
+
else
|
221
|
+
sdn.ensure(Message::Stop.new(addr))
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def ilt2?
|
226
|
+
ns == Message::ILT2
|
227
|
+
end
|
228
|
+
|
229
|
+
def reversed_int
|
230
|
+
@reversed ? 1 : 0
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module SDN
|
2
|
+
module CLI
|
3
|
+
class Simulator
|
4
|
+
class MockMotor
|
5
|
+
attr_accessor :address, :node_type, :label, :ips, :position_pulses, :up_limit, :down_limit, :groups, :network_lock_priority, :lock_priority, :ir_channels
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
@client = client
|
9
|
+
self.address = Message.parse_address("00.00.00")
|
10
|
+
self.node_type = :st30
|
11
|
+
self.label = ""
|
12
|
+
self.ips = Array.new(16)
|
13
|
+
self.groups = Array.new(16)
|
14
|
+
self.ir_channels = 0
|
15
|
+
self.lock_priority = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def process
|
19
|
+
loop do
|
20
|
+
@client.receive do |message|
|
21
|
+
SDN.logger.info "Received #{message.inspect}"
|
22
|
+
next unless message.is_a?(Message::ILT2::MasterControl) ||
|
23
|
+
message.dest == address ||
|
24
|
+
message.dest == BROADCAST_ADDRESS
|
25
|
+
|
26
|
+
case message
|
27
|
+
when Message::GetGroupAddr
|
28
|
+
next nack(message) unless (1..16).include?(message.group_index)
|
29
|
+
respond(message.src, Message::PostGroupAddr.new(message.group_index, groups[message.group_index - 1]))
|
30
|
+
when Message::GetMotorIP
|
31
|
+
next nack(message) unless (1..16).include?(message.ip)
|
32
|
+
respond(message.src, Message::PostMotorIP.new(message.ip, ips[message.ip - 1], to_percent(ips[message.ip - 1])))
|
33
|
+
when Message::GetMotorLimits
|
34
|
+
respond(message.src, Message::PostMotorLimits.new(up_limit, down_limit))
|
35
|
+
when Message::GetMotorPosition
|
36
|
+
respond(message.src, Message::PostMotorPosition.new(
|
37
|
+
position_pulses,
|
38
|
+
to_percent(position_pulses),
|
39
|
+
ips.index(position_pulses)&.+(1)
|
40
|
+
))
|
41
|
+
when Message::GetNodeAddr; respond(message.src, Message::PostNodeAddr.new)
|
42
|
+
when Message::GetNodeLabel; respond(message.src, Message::PostNodeLabel.new(label))
|
43
|
+
when Message::ILT2::GetIRConfig; respond(message.src, Message::ILT2::PostIRConfig.new(ir_channels))
|
44
|
+
when Message::ILT2::GetLockStatus; respond(message.src, Message::ILT2::PostLockStatus.new(lock_priority))
|
45
|
+
when Message::ILT2::GetMotorIP; respond(message.src, Message::ILT2::PostMotorIP.new(message.ip, ips[message.ip - 1]))
|
46
|
+
when Message::ILT2::GetMotorPosition; respond(message.src, Message::ILT2::PostMotorPosition.new(position_pulses, to_percent(position_pulses)))
|
47
|
+
when Message::ILT2::GetMotorSettings; respond(message.src, Message::ILT2::PostMotorSettings.new(down_limit))
|
48
|
+
when Message::ILT2::SetIRConfig; self.ir_channels = message.channels
|
49
|
+
when Message::ILT2::SetLockStatus; self.lock_priority = message.priority
|
50
|
+
when Message::ILT2::SetMotorIP
|
51
|
+
next nack(message) unless (1..16).include?(message.ip)
|
52
|
+
ips[message.ip - 1] = message.value
|
53
|
+
ack(message)
|
54
|
+
when Message::ILT2::SetMotorPosition
|
55
|
+
next nack(message) unless down_limit
|
56
|
+
|
57
|
+
self.position_pulses = case message.target_type
|
58
|
+
when :up_limit; 0
|
59
|
+
when :down_limit; down_limit
|
60
|
+
when :ip
|
61
|
+
next nack(message) unless (1..16).include?(message.target)
|
62
|
+
next nack(message) unless ips[message.target]
|
63
|
+
ips[message.target]
|
64
|
+
when :position_pulses
|
65
|
+
next nack(message) if message.target - 1 > down_limit
|
66
|
+
message.target - 1
|
67
|
+
when :jog_up_pulses; [0, position_pulses - message.target].max
|
68
|
+
when :jog_down_pulses; [down_limit, position_pulses + message.target].min
|
69
|
+
when :position_percent
|
70
|
+
next nack(message) if message.target > 100
|
71
|
+
to_pulses(message.target.to_f)
|
72
|
+
end
|
73
|
+
ack(message)
|
74
|
+
when Message::ILT2::SetMotorSettings
|
75
|
+
if message.down_limit != 0
|
76
|
+
self.down_limit = message.down_limit
|
77
|
+
self.position_pulses = message.position_pulses
|
78
|
+
end
|
79
|
+
when Message::MoveTo
|
80
|
+
next nack(message) unless down_limit
|
81
|
+
next nack(message) unless %I{up_limit down_limit ip position_pulses position_percent}.include?(message.target_type)
|
82
|
+
|
83
|
+
self.position_pulses = case message.target_type
|
84
|
+
when :up_limit; 0
|
85
|
+
when :down_limit; down_limit;
|
86
|
+
when :ip
|
87
|
+
next nack(message) unless (1..16).include?(message.target)
|
88
|
+
next nack(message) unless ips[message.target - 1]
|
89
|
+
ips[message.target - 1]
|
90
|
+
when :position_pulses
|
91
|
+
next nack(message) if message.target > down_limit
|
92
|
+
message.target
|
93
|
+
when :position_percent
|
94
|
+
next nack(message) if message.target > 100
|
95
|
+
to_pulses(message.target)
|
96
|
+
end
|
97
|
+
ack(message)
|
98
|
+
when Message::SetGroupAddr
|
99
|
+
next nack(message) unless (1..16).include?(message.group_index)
|
100
|
+
groups[message.group_index - 1] = message.group_address == [0, 0, 0] ? nil : message.group_address
|
101
|
+
ack(message)
|
102
|
+
when Message::SetMotorIP
|
103
|
+
next nack(message) unless (1..16).include?(message.ip) || message.type == :distribute
|
104
|
+
|
105
|
+
case message.type
|
106
|
+
when :delete
|
107
|
+
ips[message.ip - 1] = nil
|
108
|
+
ack(message)
|
109
|
+
when :current_position
|
110
|
+
ips[message.ip - 1] = position_pulses
|
111
|
+
ack(message)
|
112
|
+
when :position_pulses
|
113
|
+
ips[message.ip - 1] = message.value
|
114
|
+
ack(message)
|
115
|
+
when :position_percent
|
116
|
+
pulses = to_pulses(message.value)
|
117
|
+
if pulses
|
118
|
+
ips[message.ip - 1] = pulses
|
119
|
+
ack(message)
|
120
|
+
else
|
121
|
+
nack(message)
|
122
|
+
end
|
123
|
+
when :distribute
|
124
|
+
next nack(message) unless down_limit
|
125
|
+
next nack(message) unless (1..15).include?(message.value)
|
126
|
+
span = down_limit / (message.value + 1)
|
127
|
+
current = 0
|
128
|
+
(0...message.value).each do |ip|
|
129
|
+
ips[ip] = (current += span)
|
130
|
+
end
|
131
|
+
(message.value...16).each do |ip|
|
132
|
+
ips[ip] = nil
|
133
|
+
end
|
134
|
+
ack(message)
|
135
|
+
end
|
136
|
+
when Message::SetMotorLimits
|
137
|
+
next nack(message) unless [:up, :down].include?(message.target)
|
138
|
+
next nack(message) unless [:jog_pulses].include?(message.type)
|
139
|
+
|
140
|
+
self.up_limit ||= 0
|
141
|
+
self.down_limit ||= 0
|
142
|
+
self.position_pulses ||= 0
|
143
|
+
|
144
|
+
next nack(message) if message.target == :up && position_pulses != 0
|
145
|
+
next nack(message) if message.target == :down && position_pulses != down_limit
|
146
|
+
|
147
|
+
case message.type
|
148
|
+
when :jog_pulses
|
149
|
+
self.down_limit += message.value
|
150
|
+
self.position_pulses += message.value if message.target == :down
|
151
|
+
end
|
152
|
+
ack(message)
|
153
|
+
when Message::SetNodeLabel; self.label = message.label; ack(message)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def to_percent(pulses)
|
160
|
+
pulses && down_limit ? 100.0 * pulses / down_limit : nil
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_pulses(percent)
|
164
|
+
percent && down_limit ? down_limit * percent / 100 : nil
|
165
|
+
end
|
166
|
+
|
167
|
+
def ack(message)
|
168
|
+
return unless message.ack_requested
|
169
|
+
respond(Message::Ack.new(message.dest))
|
170
|
+
end
|
171
|
+
|
172
|
+
def nack(message, error_code = nil)
|
173
|
+
return unless message.ack_requested
|
174
|
+
respond(Message::Nack.new(message.dest))
|
175
|
+
end
|
176
|
+
|
177
|
+
def respond(dest, message)
|
178
|
+
message.src = address
|
179
|
+
message.node_type = node_type
|
180
|
+
message.dest = dest
|
181
|
+
SDN.logger.info "Sending #{message.inspect}"
|
182
|
+
@client.send(message)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def initialize(port, address = nil)
|
187
|
+
sdn = Client.new(port)
|
188
|
+
|
189
|
+
motor = MockMotor.new(sdn)
|
190
|
+
motor.address = Message.parse_address(address) if address
|
191
|
+
motor.node_type = :lt50
|
192
|
+
|
193
|
+
motor.process
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
data/lib/sdn/client.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'io/wait'
|
2
|
+
|
3
|
+
module SDN
|
4
|
+
class Client
|
5
|
+
def initialize(port)
|
6
|
+
uri = URI.parse(port)
|
7
|
+
@io = if uri.scheme == "tcp"
|
8
|
+
require 'socket'
|
9
|
+
TCPSocket.new(uri.host, uri.port)
|
10
|
+
elsif uri.scheme == "telnet" || uri.scheme == "rfc2217"
|
11
|
+
require 'net/telnet/rfc2217'
|
12
|
+
Net::Telnet::RFC2217.new('Host' => uri.host,
|
13
|
+
'Port' => uri.port || 23,
|
14
|
+
'baud' => 4800,
|
15
|
+
'parity' => Net::Telnet::RFC2217::ODD)
|
16
|
+
elsif port == "/dev/ptmx"
|
17
|
+
require 'pty'
|
18
|
+
io, slave = PTY.open
|
19
|
+
puts "Slave PTY available at #{slave.path}"
|
20
|
+
io
|
21
|
+
else
|
22
|
+
require 'ccutrer-serialport'
|
23
|
+
CCutrer::SerialPort.new(port, baud: 4800, data_bits: 8, parity: :odd, stop_bits: 1)
|
24
|
+
end
|
25
|
+
@buffer = ""
|
26
|
+
end
|
27
|
+
|
28
|
+
def send(message)
|
29
|
+
@io.write(message.serialize)
|
30
|
+
end
|
31
|
+
|
32
|
+
def transact(message)
|
33
|
+
message.ack_requested = true
|
34
|
+
send(message)
|
35
|
+
receive(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
def ensure(message)
|
39
|
+
loop do
|
40
|
+
messages = transact(message)
|
41
|
+
next if messages.empty?
|
42
|
+
next unless message.class.expected_response?(messages.first)
|
43
|
+
return messages.first
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
WAIT_TIME = 0.25
|
48
|
+
|
49
|
+
def receive(timeout = nil)
|
50
|
+
messages = []
|
51
|
+
|
52
|
+
loop do
|
53
|
+
message, bytes_read = Message.parse(@buffer.bytes)
|
54
|
+
# discard how much we read
|
55
|
+
@buffer = @buffer[bytes_read..-1] if bytes_read
|
56
|
+
unless message
|
57
|
+
break unless messages.empty?
|
58
|
+
|
59
|
+
begin
|
60
|
+
block = @io.read_nonblock(64 * 1024)
|
61
|
+
SDN.logger.debug "read #{block.unpack("H*").first.gsub(/\h{2}/, "\\0 ")}"
|
62
|
+
@buffer.concat(block)
|
63
|
+
next
|
64
|
+
rescue IO::WaitReadable, EOFError
|
65
|
+
wait = @buffer.empty? ? timeout : WAIT_TIME
|
66
|
+
if @io.wait_readable(wait).nil?
|
67
|
+
# timed out; just discard everything
|
68
|
+
SDN.logger.debug "discarding #{@buffer.unpack("H*").first.gsub(/\h{2}/, "\\0 ")} due to timeout"
|
69
|
+
@buffer = ""
|
70
|
+
end
|
71
|
+
end
|
72
|
+
next
|
73
|
+
end
|
74
|
+
if block_given?
|
75
|
+
yield message
|
76
|
+
else
|
77
|
+
messages << message
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
messages
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|