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.
data/lib/sdn/message.rb CHANGED
@@ -1,15 +1,21 @@
1
- require 'sdn/messages/helpers'
1
+ require 'sdn/message/helpers'
2
2
 
3
3
  module SDN
4
4
  class MalformedMessage < RuntimeError; end
5
5
 
6
6
  class Message
7
7
  class << self
8
- def expected_response
8
+ def inherited(klass)
9
+ return Message.inherited(klass) unless self == Message
10
+ @message_map = nil
11
+ (@subclasses ||= []) << klass
12
+ end
13
+
14
+ def expected_response?(message)
9
15
  if name =~ /::Get([A-Za-z]+)/
10
- const_get("Post#{$1}", true)
16
+ message.class.name == name.sub("::Get", "::Post")
11
17
  else
12
- Ack
18
+ message.is_a?(Ack) || message.is_a?(Nack)
13
19
  end
14
20
  end
15
21
 
@@ -19,20 +25,22 @@ module SDN
19
25
  # we loop here scanning for a valid message
20
26
  loop do
21
27
  offset += 1
28
+ # give these weird messages a chance
29
+ result = ILT2::MasterControl.parse(data)
30
+ return result if result
31
+
22
32
  return [nil, 0] if data.length - offset < 11
23
33
  msg = to_number(data[offset])
24
34
  length = to_number(data[offset + 1])
25
35
  ack_requested = length & 0x80 == 0x80
26
36
  length &= 0x7f
27
37
  # impossible message
28
- next if length < 11 || length > 32
38
+ next if length < 11 || length > 43
29
39
  # don't have enough data for what this message wants;
30
40
  # it could be garbage on the line so keep scanning
31
41
  next if length > data.length - offset
32
42
 
33
- message_class = constants.find { |c| (const_get(c, false).const_get(:MSG, false) rescue nil) == msg }
34
- message_class = const_get(message_class, false) if message_class
35
- message_class ||= UnknownMessage
43
+ message_class = message_map[msg] || UnknownMessage
36
44
 
37
45
  calculated_sum = checksum(data.slice(offset, length - 2))
38
46
  read_sum = data.slice(offset + length - 2, 2)
@@ -41,34 +49,41 @@ module SDN
41
49
  break
42
50
  end
43
51
 
44
- puts "discarding invalid data prior to message #{data[0...offset].map { |b| '%02x' % b }.join(' ')}" unless offset == 0
45
- puts "read #{data.slice(offset, length).map { |b| '%02x' % b }.join(' ')}"
46
-
47
- reserved = to_number(data[offset + 2])
52
+ node_type = node_type_from_number(to_number(data[offset + 2]))
48
53
  src = transform_param(data.slice(offset + 3, 3))
49
54
  dest = transform_param(data.slice(offset + 6, 3))
50
55
  begin
51
- result = message_class.new(reserved: reserved, ack_requested: ack_requested, src: src, dest: dest)
56
+ result = message_class.new(node_type: node_type, ack_requested: ack_requested, src: src, dest: dest)
52
57
  result.parse(data.slice(offset + 9, length - 11))
53
58
  result.msg = msg if message_class == UnknownMessage
54
59
  rescue ArgumentError => e
55
- puts "discarding illegal message #{e}"
60
+ SDN.logger.warn "discarding illegal message of type #{message_class.name}: #{e}"
56
61
  result = nil
57
62
  end
58
63
  [result, offset + length]
59
64
  end
65
+
66
+ private
67
+
68
+ def message_map
69
+ @message_map ||=
70
+ @subclasses.inject({}) do |memo, klass|
71
+ next memo unless klass.constants(false).include?(:MSG)
72
+ memo[klass.const_get(:MSG, false)] = klass
73
+ memo
74
+ end
75
+ end
60
76
  end
61
77
 
62
78
  include Helpers
63
79
  singleton_class.include Helpers
64
80
 
65
- attr_reader :reserved, :ack_requested, :src, :dest
66
- attr_writer :ack_requested
81
+ attr_accessor :node_type, :ack_requested, :src, :dest
67
82
 
68
- def initialize(reserved: nil, ack_requested: false, src: nil, dest: nil)
69
- @reserved = reserved || 0x02 # message sent to Sonesse 30
83
+ def initialize(node_type: nil, ack_requested: false, src: nil, dest: nil)
84
+ @node_type = node_type || 0
70
85
  @ack_requested = ack_requested
71
- if src.nil? && is_group_address?(dest)
86
+ if src.nil? && !dest.nil? && is_group_address?(dest)
72
87
  src = dest
73
88
  dest = nil
74
89
  end
@@ -81,7 +96,7 @@ module SDN
81
96
  end
82
97
 
83
98
  def serialize
84
- result = transform_param(reserved) + transform_param(src) + transform_param(dest) + params
99
+ result = transform_param(node_type_to_number(node_type)) + transform_param(src) + transform_param(dest) + params
85
100
  length = result.length + 4
86
101
  length |= 0x80 if ack_requested
87
102
  result = transform_param(self.class.const_get(:MSG)) + transform_param(length) + result
@@ -94,11 +109,11 @@ module SDN
94
109
  end
95
110
 
96
111
  def inspect
97
- "#<%s @reserved=%02xh, @ack_requested=%s, @src=%s, @dest=%s%s>" % [self.class.name, reserved, ack_requested, print_address(src), print_address(dest), class_inspect]
112
+ "#<%s @node_type=%s, @ack_requested=%s, @src=%s, @dest=%s%s>" % [self.class.name, node_type_to_string(node_type), ack_requested, print_address(src), print_address(dest), class_inspect]
98
113
  end
99
114
 
100
115
  def class_inspect
101
- ivars = instance_variables - [:@reserved, :@ack_requested, :@src, :@dest]
116
+ ivars = instance_variables - [:@node_type, :@ack_requested, :@src, :@dest, :@params]
102
117
  return if ivars.empty?
103
118
  ivars.map { |iv| ", #{iv}=#{instance_variable_get(iv).inspect}" }.join
104
119
  end
@@ -121,19 +136,36 @@ module SDN
121
136
  class Nack < Message
122
137
  MSG = 0x6f
123
138
  PARAMS_LENGTH = 1
124
- VALUES = { data_error: 0x01, unknown_message: 0x10, node_is_locked: 0x20, wrong_position: 0x21, limits_not_set: 0x22, ip_not_set: 0x23, out_of_range: 0x24, busy: 0xff }
139
+ VALUES = { data_error: 0x01,
140
+ unknown_message: 0x10,
141
+ node_is_locked: 0x20,
142
+ wrong_position: 0x21,
143
+ limits_not_set: 0x22,
144
+ ip_not_set: 0x23,
145
+ out_of_range: 0x24,
146
+ busy: 0xff }
147
+ # 17 limits not set?
148
+ # 37 not implemented? (get motor rolling speed)
149
+ # 39 at limit? blocked?
150
+
125
151
 
126
152
  # presumed
127
153
  attr_accessor :error_code
128
154
 
155
+ def initialize(dest = nil, error_code = nil, **kwargs)
156
+ kwargs[:dest] ||= dest
157
+ super(**kwargs)
158
+ self.error_code = error_code
159
+ end
160
+
129
161
  def parse(params)
130
162
  super
131
163
  error_code = to_number(params[0])
132
- self.error_code = VALUES[error_code] || error_code
164
+ self.error_code = VALUES.invert[error_code] || error_code
133
165
  end
134
166
  end
135
167
 
136
- class Ack < Message
168
+ class Ack < SimpleRequest
137
169
  MSG = 0x7f
138
170
  PARAMS_LENGTH = 0
139
171
  end
@@ -142,10 +174,25 @@ module SDN
142
174
  class UnknownMessage < Message
143
175
  attr_accessor :msg, :params
144
176
 
177
+ def initialize(params = [], **kwargs)
178
+ super(**kwargs)
179
+ self.params = params
180
+ end
181
+
145
182
  alias parse params=
146
183
 
184
+ def serialize
185
+ # prevent serializing something we don't know
186
+ raise NotImplementedError unless params
187
+ super
188
+ end
189
+
147
190
  def class_inspect
148
- result = ", @msg=%02xh" % msg
191
+ result = if self.class == UnknownMessage
192
+ result = ", @msg=%02xh" % msg
193
+ else
194
+ super || ""
195
+ end
149
196
  return result if params.empty?
150
197
 
151
198
  result << ", @params=#{params.map { |b| "%02x" % b }.join(' ')}"
@@ -154,7 +201,11 @@ module SDN
154
201
  end
155
202
  end
156
203
 
157
- require 'sdn/messages/control'
158
- require 'sdn/messages/get'
159
- require 'sdn/messages/post'
160
- require 'sdn/messages/set'
204
+ require 'sdn/message/control'
205
+ require 'sdn/message/get'
206
+ require 'sdn/message/post'
207
+ require 'sdn/message/set'
208
+ require 'sdn/message/ilt2/get'
209
+ require 'sdn/message/ilt2/master_control'
210
+ require 'sdn/message/ilt2/post'
211
+ require 'sdn/message/ilt2/set'
@@ -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})" unless value && value >= 0x0a && value <= 0xff
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
- # Stop movement
52
- class Stop < Message
53
- MSG = 0x02
54
- PARAMS_LENGTH = 1
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
- def initialize(dest = nil, **kwargs)
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
- transform_param(0)
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
- # Move relative to current position
111
- class MoveOf < Message
112
- MSG = 0x04
113
- PARAMS_LENGTH = 4
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, target_type = nil, target = nil, **kwargs)
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
- param = target || 0xffff
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 GetMotorPosition < SimpleRequest
4
- MSG = 0x0c
5
- end
3
+ class GetGroupAddr < Message
4
+ MSG = 0x41
5
+ PARAMS_LENGTH = 1
6
6
 
7
- class GetMotorStatus < SimpleRequest
8
- MSG = 0x0e
9
- end
7
+ attr_reader :group_index
10
8
 
11
- class GetMotorLimits < SimpleRequest
12
- MSG = 0x21
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 = nil, **kwargs)
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], nillable: true)
48
+ self.ip = to_number(params[0])
38
49
  end
39
50
 
40
51
  def ip=(value)
41
- raise ArgumentError, "invalid IP #{ip} (should be 1-16)" unless ip.nil? || (1..16).include?(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(@ip || 0xff)
57
+ transform_param(ip)
47
58
  end
48
59
  end
49
60
 
50
- class GetGroupAddr < Message
51
- MSG = 0x41
52
- PARAMS_LENGTH = 1
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
- def parse(params)
63
- super
64
- self.group_index = to_number(params[0])
65
- end
65
+ class GetMotorPosition < SimpleRequest
66
+ MSG = 0x0c
67
+ end
66
68
 
67
- def group_index=(value)
68
- raise ArgumentError, "group_index is out of range" unless (0...16).include?(value)
69
- @group_index = value
70
- end
69
+ class GetMotorRollingSpeed < SimpleRequest
70
+ MSG = 0x23
71
+ end
71
72
 
72
- def params
73
- transform_param(group_index)
74
- end
73
+ class GetMotorStatus < SimpleRequest
74
+ MSG = 0x0e
75
75
  end
76
76
 
77
- class GetNodeLabel < SimpleRequest
78
- MSG = 0x45
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