somfy_sdn 1.0.12 → 2.1.2
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/cli/mqtt/group.rb +31 -0
- data/lib/sdn/cli/mqtt/motor.rb +103 -0
- data/lib/sdn/cli/mqtt/read.rb +156 -0
- data/lib/sdn/cli/mqtt/subscriptions.rb +156 -0
- data/lib/sdn/cli/mqtt/write.rb +93 -0
- data/lib/sdn/cli/mqtt.rb +397 -0
- data/lib/sdn/cli/provisioner.rb +234 -0
- data/lib/sdn/cli/simulator.rb +197 -0
- data/lib/sdn/client.rb +86 -0
- 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/message.rb +81 -30
- data/lib/sdn/version.rb +1 -1
- data/lib/sdn.rb +16 -1
- metadata +63 -26
- 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,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,86 @@
|
|
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
|
+
data_bits: 8,
|
16
|
+
parity: :odd,
|
17
|
+
stop_bits: 1)
|
18
|
+
elsif port == "/dev/ptmx"
|
19
|
+
require 'pty'
|
20
|
+
io, slave = PTY.open
|
21
|
+
puts "Slave PTY available at #{slave.path}"
|
22
|
+
io
|
23
|
+
else
|
24
|
+
require 'ccutrer-serialport'
|
25
|
+
CCutrer::SerialPort.new(port, baud: 4800, data_bits: 8, parity: :odd, stop_bits: 1)
|
26
|
+
end
|
27
|
+
@buffer = ""
|
28
|
+
end
|
29
|
+
|
30
|
+
def send(message)
|
31
|
+
@io.write(message.serialize)
|
32
|
+
end
|
33
|
+
|
34
|
+
def transact(message)
|
35
|
+
message.ack_requested = true
|
36
|
+
send(message)
|
37
|
+
receive(1)
|
38
|
+
end
|
39
|
+
|
40
|
+
def ensure(message)
|
41
|
+
loop do
|
42
|
+
messages = transact(message)
|
43
|
+
next if messages.empty?
|
44
|
+
next unless message.class.expected_response?(messages.first)
|
45
|
+
return messages.first
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
WAIT_TIME = 0.25
|
50
|
+
|
51
|
+
def receive(timeout = nil)
|
52
|
+
messages = []
|
53
|
+
|
54
|
+
loop do
|
55
|
+
message, bytes_read = Message.parse(@buffer.bytes)
|
56
|
+
# discard how much we read
|
57
|
+
@buffer = @buffer[bytes_read..-1] if bytes_read
|
58
|
+
unless message
|
59
|
+
break unless messages.empty?
|
60
|
+
|
61
|
+
begin
|
62
|
+
block = @io.read_nonblock(64 * 1024)
|
63
|
+
SDN.logger.debug "read #{block.unpack("H*").first.gsub(/\h{2}/, "\\0 ")}"
|
64
|
+
@buffer.concat(block)
|
65
|
+
next
|
66
|
+
rescue IO::WaitReadable, EOFError
|
67
|
+
wait = @buffer.empty? ? timeout : WAIT_TIME
|
68
|
+
if @io.wait_readable(wait).nil?
|
69
|
+
# timed out; just discard everything
|
70
|
+
SDN.logger.debug "discarding #{@buffer.unpack("H*").first.gsub(/\h{2}/, "\\0 ")} due to timeout"
|
71
|
+
@buffer = ""
|
72
|
+
end
|
73
|
+
end
|
74
|
+
next
|
75
|
+
end
|
76
|
+
if block_given?
|
77
|
+
yield message
|
78
|
+
else
|
79
|
+
messages << message
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
messages
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -1,5 +1,46 @@
|
|
1
1
|
module SDN
|
2
2
|
class Message
|
3
|
+
class Lock < Message
|
4
|
+
MSG = 0x06
|
5
|
+
PARAMS_LENGTH = 5
|
6
|
+
TARGET_TYPE = { current: 0, up_limit: 1, down_limit: 2, ip: 4, unlock: 5, position_percent: 7 }
|
7
|
+
|
8
|
+
attr_reader :target_type, :target, :priority
|
9
|
+
|
10
|
+
def initialize(dest = nil, target_type = :unlock, target = nil, priority = 1, **kwargs)
|
11
|
+
kwargs[:dest] ||= dest
|
12
|
+
super(**kwargs)
|
13
|
+
self.target_type = target_type
|
14
|
+
self.target = target
|
15
|
+
self.priority = priority
|
16
|
+
end
|
17
|
+
|
18
|
+
def target_type=(value)
|
19
|
+
raise ArgumentError, "target_type must be one of :current, :up_limit, :down_limit, :ip, :unlock, or :position_percent" unless TARGET_TYPE.keys.include?(value)
|
20
|
+
@target_type = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def target=(value)
|
24
|
+
@target = value&. & 0xffff
|
25
|
+
end
|
26
|
+
|
27
|
+
def priority=(value)
|
28
|
+
@priority = value & 0xff
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse(params)
|
32
|
+
super
|
33
|
+
self.target_type = TARGET_TYPE.invert[to_number(params[0])]
|
34
|
+
target = to_number(params[1..2], nillable: true)
|
35
|
+
self.target = target
|
36
|
+
self.priority = to_number(params[3])
|
37
|
+
end
|
38
|
+
|
39
|
+
def params
|
40
|
+
transform_param(TARGET_TYPE[target_type]) + from_number(target, 2) + transform_param(priority) + transform_param(0)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
3
44
|
# Move in momentary mode
|
4
45
|
class Move < Message
|
5
46
|
MSG = 0x01
|
@@ -32,7 +73,7 @@ module SDN
|
|
32
73
|
end
|
33
74
|
|
34
75
|
def duration=(value)
|
35
|
-
raise ArgumentError, "duration must be in range 0x0a to 0xff (#{value})"
|
76
|
+
raise ArgumentError, "duration must be in range 0x0a to 0xff (#{value})" if value && (value < 0x0a || value > 0xff)
|
36
77
|
@duration = value
|
37
78
|
end
|
38
79
|
|
@@ -48,18 +89,43 @@ module SDN
|
|
48
89
|
end
|
49
90
|
end
|
50
91
|
|
51
|
-
#
|
52
|
-
class
|
53
|
-
MSG =
|
54
|
-
PARAMS_LENGTH =
|
92
|
+
# Move relative to current position
|
93
|
+
class MoveOf < Message
|
94
|
+
MSG = 0x04
|
95
|
+
PARAMS_LENGTH = 4
|
96
|
+
TARGET_TYPE = { next_ip: 0x00, previous_ip: 0x01, jog_down_pulses: 0x02, jog_up_pulses: 0x03, jog_down_ms: 0x04, jog_up_ms: 0x05 }
|
55
97
|
|
56
|
-
|
98
|
+
attr_reader :target_type, :target
|
99
|
+
|
100
|
+
def initialize(dest = nil, target_type = nil, target = nil, **kwargs)
|
57
101
|
kwargs[:dest] ||= dest
|
58
102
|
super(**kwargs)
|
103
|
+
self.target_type = target_type
|
104
|
+
self.target = target
|
105
|
+
end
|
106
|
+
|
107
|
+
def parse(params)
|
108
|
+
super
|
109
|
+
self.target_type = TARGET_TYPE.invert[to_number(params[0])]
|
110
|
+
target = to_number(params[1..2], nillable: true)
|
111
|
+
target *= 10 if %I{jog_down_ms jog_up_ms}.include?(target_type)
|
112
|
+
self.target = target
|
113
|
+
end
|
114
|
+
|
115
|
+
def target_type=(value)
|
116
|
+
raise ArgumentError, "target_type must be one of :next_ip, :previous_ip, :jog_down_pulses, :jog_up_pulses, :jog_down_ms, :jog_up_ms" unless value.nil? || TARGET_TYPE.keys.include?(value)
|
117
|
+
@target_type = value
|
118
|
+
end
|
119
|
+
|
120
|
+
def target=(value)
|
121
|
+
value &= 0xffff if value
|
122
|
+
@target = value
|
59
123
|
end
|
60
124
|
|
61
125
|
def params
|
62
|
-
|
126
|
+
param = target || 0xffff
|
127
|
+
param /= 10 if %I{jog_down_ms jog_up_ms}.include?(target_type)
|
128
|
+
transform_param(TARGET_TYPE[target_type]) + from_number(param, 2) + transform_param(0)
|
63
129
|
end
|
64
130
|
end
|
65
131
|
|
@@ -107,43 +173,18 @@ module SDN
|
|
107
173
|
end
|
108
174
|
end
|
109
175
|
|
110
|
-
#
|
111
|
-
class
|
112
|
-
MSG =
|
113
|
-
PARAMS_LENGTH =
|
114
|
-
TARGET_TYPE = { next_ip: 0x00, previous_ip: 0x01, jog_down_pulses: 0x02, jog_up_pulses: 0x03, jog_down_ms: 0x04, jog_up_ms: 0x05 }
|
115
|
-
|
116
|
-
attr_reader :target_type, :target
|
176
|
+
# Stop movement
|
177
|
+
class Stop < Message
|
178
|
+
MSG = 0x02
|
179
|
+
PARAMS_LENGTH = 1
|
117
180
|
|
118
|
-
def initialize(dest = nil,
|
181
|
+
def initialize(dest = nil, **kwargs)
|
119
182
|
kwargs[:dest] ||= dest
|
120
183
|
super(**kwargs)
|
121
|
-
self.target_type = target_type
|
122
|
-
self.target = target
|
123
|
-
end
|
124
|
-
|
125
|
-
def parse(params)
|
126
|
-
super
|
127
|
-
self.target_type = TARGET_TYPE.invert[to_number(params[0])]
|
128
|
-
target = to_number(params[1..2], nillable: true)
|
129
|
-
target *= 10 if %I{jog_down_ms jog_up_ms}.include?(target_type)
|
130
|
-
self.target = target
|
131
|
-
end
|
132
|
-
|
133
|
-
def target_type=(value)
|
134
|
-
raise ArgumentError, "target_type must be one of :next_ip, :previous_ip, :jog_down_pulses, :jog_up_pulses, :jog_down_ms, :jog_up_ms" unless value.nil? || TARGET_TYPE.keys.include?(value)
|
135
|
-
@target_type = value
|
136
|
-
end
|
137
|
-
|
138
|
-
def target=(value)
|
139
|
-
value &= 0xffff if value
|
140
|
-
@target = value
|
141
184
|
end
|
142
185
|
|
143
186
|
def params
|
144
|
-
|
145
|
-
param /= 10 if %I{jog_down_ms jog_up_ms}.include?(target_type)
|
146
|
-
transform_param(TARGET_TYPE[target_type]) + from_number(param, 2) + transform_param(0)
|
187
|
+
transform_param(0)
|
147
188
|
end
|
148
189
|
end
|
149
190
|
|
@@ -1,32 +1,43 @@
|
|
1
1
|
module SDN
|
2
2
|
class Message
|
3
|
-
class
|
4
|
-
MSG =
|
5
|
-
|
3
|
+
class GetGroupAddr < Message
|
4
|
+
MSG = 0x41
|
5
|
+
PARAMS_LENGTH = 1
|
6
6
|
|
7
|
-
|
8
|
-
MSG = 0x0e
|
9
|
-
end
|
7
|
+
attr_reader :group_index
|
10
8
|
|
11
|
-
|
12
|
-
|
9
|
+
def initialize(dest = nil, group_index = 1, **kwargs)
|
10
|
+
kwargs[:dest] ||= dest
|
11
|
+
super(**kwargs)
|
12
|
+
self.group_index = group_index
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(params)
|
16
|
+
super
|
17
|
+
self.group_index = to_number(params[0]) + 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def group_index=(value)
|
21
|
+
raise ArgumentError, "group_index is out of range" unless (1..16).include?(value)
|
22
|
+
@group_index = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def params
|
26
|
+
transform_param(group_index - 1)
|
27
|
+
end
|
13
28
|
end
|
14
29
|
|
15
30
|
class GetMotorDirection < SimpleRequest
|
16
31
|
MSG = 0x22
|
17
32
|
end
|
18
33
|
|
19
|
-
class GetMotorRollingSpeed < SimpleRequest
|
20
|
-
MSG = 0x23
|
21
|
-
end
|
22
|
-
|
23
34
|
class GetMotorIP < Message
|
24
35
|
MSG = 0x25
|
25
36
|
PARAMS_LENGTH = 1
|
26
37
|
|
27
38
|
attr_reader :ip
|
28
39
|
|
29
|
-
def initialize(dest = nil, ip =
|
40
|
+
def initialize(dest = nil, ip = 1, **kwargs)
|
30
41
|
kwargs[:dest] ||= dest
|
31
42
|
super(**kwargs)
|
32
43
|
self.ip = ip
|
@@ -34,48 +45,37 @@ module SDN
|
|
34
45
|
|
35
46
|
def parse(params)
|
36
47
|
super
|
37
|
-
self.ip = to_number(params[0]
|
48
|
+
self.ip = to_number(params[0])
|
38
49
|
end
|
39
50
|
|
40
51
|
def ip=(value)
|
41
|
-
raise ArgumentError, "invalid IP #{
|
52
|
+
raise ArgumentError, "invalid IP #{value} (should be 1-16)" unless (1..16).include?(value)
|
42
53
|
@ip = value
|
43
54
|
end
|
44
55
|
|
45
56
|
def params
|
46
|
-
transform_param(
|
57
|
+
transform_param(ip)
|
47
58
|
end
|
48
59
|
end
|
49
60
|
|
50
|
-
class
|
51
|
-
MSG =
|
52
|
-
|
53
|
-
|
54
|
-
attr_reader :group_index
|
55
|
-
|
56
|
-
def initialize(dest = nil, group_index = 0, **kwargs)
|
57
|
-
kwargs[:dest] ||= dest
|
58
|
-
super(**kwargs)
|
59
|
-
self.group_index = group_index
|
60
|
-
end
|
61
|
+
class GetMotorLimits < SimpleRequest
|
62
|
+
MSG = 0x21
|
63
|
+
end
|
61
64
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
65
|
+
class GetMotorPosition < SimpleRequest
|
66
|
+
MSG = 0x0c
|
67
|
+
end
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
69
|
+
class GetMotorRollingSpeed < SimpleRequest
|
70
|
+
MSG = 0x23
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
end
|
73
|
+
class GetMotorStatus < SimpleRequest
|
74
|
+
MSG = 0x0e
|
75
75
|
end
|
76
76
|
|
77
|
-
class
|
78
|
-
MSG =
|
77
|
+
class GetNetworkLock < SimpleRequest
|
78
|
+
MSG = 0x26
|
79
79
|
end
|
80
80
|
|
81
81
|
class GetNodeAddr < Message
|
@@ -88,6 +88,14 @@ module SDN
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
class GetNodeAppVersion < SimpleRequest
|
92
|
+
MSG = 0x74
|
93
|
+
end
|
94
|
+
|
95
|
+
class GetNodeLabel < SimpleRequest
|
96
|
+
MSG = 0x45
|
97
|
+
end
|
98
|
+
|
91
99
|
class GetNodeSerialNumber < SimpleRequest
|
92
100
|
MSG = 0x4c
|
93
101
|
end
|
@@ -13,6 +13,34 @@ module SDN
|
|
13
13
|
addr_bytes[0..1] == [1, 1]
|
14
14
|
end
|
15
15
|
|
16
|
+
def node_type_from_number(number)
|
17
|
+
case number
|
18
|
+
when 1; :st50ilt2
|
19
|
+
when 2; :st30
|
20
|
+
when 6; :glydea
|
21
|
+
when 7; :st50ac
|
22
|
+
when 8; :st50dc
|
23
|
+
when 0x70; :lt50
|
24
|
+
else; number
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def node_type_to_number(type)
|
29
|
+
case type
|
30
|
+
when :st50ilt2; 1
|
31
|
+
when :st30; 2
|
32
|
+
when :glydea; 6
|
33
|
+
when :st50ac; 7
|
34
|
+
when :st50dc; 8
|
35
|
+
when :lt50; 0x70
|
36
|
+
else; type
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def node_type_to_string(type)
|
41
|
+
type.is_a?(Integer) ? "%02xh" % type : type.inspect
|
42
|
+
end
|
43
|
+
|
16
44
|
def transform_param(param)
|
17
45
|
Array(param).reverse.map { |byte| 0xff - byte }
|
18
46
|
end
|
@@ -23,7 +51,9 @@ module SDN
|
|
23
51
|
result
|
24
52
|
end
|
25
53
|
|
26
|
-
def from_number(number, bytes)
|
54
|
+
def from_number(number, bytes = 1)
|
55
|
+
number ||= 1 ** (bytes * 8) - 1
|
56
|
+
number = number.to_i
|
27
57
|
bytes.times.inject([]) do |res, _|
|
28
58
|
res << (0xff - number & 0xff)
|
29
59
|
number >>= 8
|
@@ -33,12 +63,12 @@ module SDN
|
|
33
63
|
|
34
64
|
def to_string(param)
|
35
65
|
chars = param.map { |b| 0xff - b }
|
36
|
-
chars
|
66
|
+
chars.pack("C*").sub(/\0+$/, '').strip
|
37
67
|
end
|
38
68
|
|
39
69
|
def from_string(string, bytes)
|
40
70
|
chars = string.bytes
|
41
|
-
chars = chars[0...
|
71
|
+
chars = chars[0...bytes].fill(' '.ord, chars.length, bytes - chars.length)
|
42
72
|
chars.map { |b| 0xff - b }
|
43
73
|
end
|
44
74
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module SDN
|
2
|
+
class Message
|
3
|
+
module ILT2
|
4
|
+
class GetIRConfig < SimpleRequest
|
5
|
+
MSG = 0x49
|
6
|
+
end
|
7
|
+
|
8
|
+
class GetLockStatus < SimpleRequest
|
9
|
+
MSG = 0x4b
|
10
|
+
end
|
11
|
+
|
12
|
+
class GetMotorIP < Message
|
13
|
+
MSG = 0x43
|
14
|
+
PARAMS_LENGTH = 1
|
15
|
+
|
16
|
+
attr_reader :ip
|
17
|
+
|
18
|
+
def initialize(dest = nil, ip = 1, **kwargs)
|
19
|
+
kwargs[:dest] ||= dest
|
20
|
+
super(**kwargs)
|
21
|
+
self.ip = ip
|
22
|
+
end
|
23
|
+
|
24
|
+
def ip=(value)
|
25
|
+
raise ArgumentError, "invalid IP #{value} (should be 1-16)" unless (1..16).include?(value)
|
26
|
+
@ip = value
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse(params)
|
30
|
+
super
|
31
|
+
self.ip = to_number(params[0]) + 1
|
32
|
+
end
|
33
|
+
|
34
|
+
def params
|
35
|
+
transform_param(@ip - 1)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class GetMotorPosition < SimpleRequest
|
40
|
+
MSG = 0x44
|
41
|
+
end
|
42
|
+
|
43
|
+
class GetMotorSettings < SimpleRequest
|
44
|
+
MSG = 0x42
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|