somfy_sdn 1.0.12 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|