somfy_sdn 2.1.5 → 2.2.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/exe/somfy_sdn +69 -0
- data/lib/sdn/cli/mqtt/group.rb +12 -10
- data/lib/sdn/cli/mqtt/motor.rb +19 -14
- data/lib/sdn/cli/mqtt/p_queue.rb +18 -0
- data/lib/sdn/cli/mqtt/read.rb +125 -126
- data/lib/sdn/cli/mqtt/subscriptions.rb +186 -140
- data/lib/sdn/cli/mqtt/write.rb +39 -34
- data/lib/sdn/cli/mqtt.rb +84 -53
- data/lib/sdn/cli/provisioner.rb +56 -33
- data/lib/sdn/cli/simulator.rb +99 -65
- data/lib/sdn/client.rb +38 -24
- data/lib/sdn/message/control.rb +60 -30
- data/lib/sdn/message/get.rb +6 -2
- data/lib/sdn/message/helpers.rb +23 -22
- data/lib/sdn/message/ilt2/get.rb +6 -3
- data/lib/sdn/message/ilt2/master_control.rb +10 -7
- data/lib/sdn/message/ilt2/post.rb +7 -5
- data/lib/sdn/message/ilt2/set.rb +28 -19
- data/lib/sdn/message/post.rb +3 -5
- data/lib/sdn/message/set.rb +48 -22
- data/lib/sdn/message.rb +50 -34
- data/lib/sdn/version.rb +3 -1
- data/lib/sdn.rb +18 -12
- data/lib/somfy_sdn.rb +3 -1
- metadata +43 -13
- data/bin/somfy_sdn +0 -60
    
        data/lib/sdn/cli/simulator.rb
    CHANGED
    
    | @@ -1,8 +1,26 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              module CLI
         | 
| 3 5 | 
             
                class Simulator
         | 
| 4 6 | 
             
                  class MockMotor
         | 
| 5 | 
            -
                    attr_accessor :address, | 
| 7 | 
            +
                    attr_accessor :address,
         | 
| 8 | 
            +
                                  :node_type,
         | 
| 9 | 
            +
                                  :label,
         | 
| 10 | 
            +
                                  :ips,
         | 
| 11 | 
            +
                                  :position_pulses,
         | 
| 12 | 
            +
                                  :up_limit,
         | 
| 13 | 
            +
                                  :down_limit,
         | 
| 14 | 
            +
                                  :groups,
         | 
| 15 | 
            +
                                  :network_lock_priority,
         | 
| 16 | 
            +
                                  :lock_priority,
         | 
| 17 | 
            +
                                  :ir_channels
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    ALLOWED_MOVE_TYPES = %i[up_limit
         | 
| 20 | 
            +
                                            down_limit
         | 
| 21 | 
            +
                                            ip
         | 
| 22 | 
            +
                                            position_pulses
         | 
| 23 | 
            +
                                            position_percent].freeze
         | 
| 6 24 |  | 
| 7 25 | 
             
                    def initialize(client)
         | 
| 8 26 | 
             
                      @client = client
         | 
| @@ -20,56 +38,67 @@ module SDN | |
| 20 38 | 
             
                        @client.receive do |message|
         | 
| 21 39 | 
             
                          SDN.logger.info "Received #{message.inspect}"
         | 
| 22 40 | 
             
                          next unless message.is_a?(Message::ILT2::MasterControl) ||
         | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 41 | 
            +
                                      message.dest == address ||
         | 
| 42 | 
            +
                                      message.dest == BROADCAST_ADDRESS
         | 
| 43 | 
            +
             | 
| 26 44 | 
             
                          case message
         | 
| 27 45 | 
             
                          when Message::GetGroupAddr
         | 
| 28 | 
            -
                            next nack(message) unless (1..16). | 
| 46 | 
            +
                            next nack(message) unless (1..16).cover?(message.group_index)
         | 
| 47 | 
            +
             | 
| 29 48 | 
             
                            respond(message.src, Message::PostGroupAddr.new(message.group_index, groups[message.group_index - 1]))
         | 
| 30 49 | 
             
                          when Message::GetMotorIP
         | 
| 31 | 
            -
                            next nack(message) unless (1..16). | 
| 32 | 
            -
             | 
| 50 | 
            +
                            next nack(message) unless (1..16).cover?(message.ip)
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                            respond(message.src,
         | 
| 53 | 
            +
                                    Message::PostMotorIP.new(message.ip, ips[message.ip - 1], to_percent(ips[message.ip - 1])))
         | 
| 33 54 | 
             
                          when Message::GetMotorLimits
         | 
| 34 55 | 
             
                            respond(message.src, Message::PostMotorLimits.new(up_limit, down_limit))
         | 
| 35 56 | 
             
                          when Message::GetMotorPosition
         | 
| 36 57 | 
             
                            respond(message.src, Message::PostMotorPosition.new(
         | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
                          when Message::GetNodeAddr | 
| 42 | 
            -
                          when Message::GetNodeLabel | 
| 43 | 
            -
                          when Message::ILT2::GetIRConfig | 
| 44 | 
            -
                          when Message::ILT2::GetLockStatus | 
| 45 | 
            -
             | 
| 46 | 
            -
                          when Message::ILT2:: | 
| 47 | 
            -
             | 
| 48 | 
            -
                          when Message::ILT2:: | 
| 49 | 
            -
             | 
| 58 | 
            +
                                                   position_pulses,
         | 
| 59 | 
            +
                                                   to_percent(position_pulses),
         | 
| 60 | 
            +
                                                   ips.index(position_pulses)&.+(1)
         | 
| 61 | 
            +
                                                 ))
         | 
| 62 | 
            +
                          when Message::GetNodeAddr then respond(message.src, Message::PostNodeAddr.new)
         | 
| 63 | 
            +
                          when Message::GetNodeLabel then respond(message.src, Message::PostNodeLabel.new(label))
         | 
| 64 | 
            +
                          when Message::ILT2::GetIRConfig then respond(message.src, Message::ILT2::PostIRConfig.new(ir_channels))
         | 
| 65 | 
            +
                          when Message::ILT2::GetLockStatus then respond(message.src,
         | 
| 66 | 
            +
                                                                         Message::ILT2::PostLockStatus.new(lock_priority))
         | 
| 67 | 
            +
                          when Message::ILT2::GetMotorIP
         | 
| 68 | 
            +
                            respond(message.src, Message::ILT2::PostMotorIP.new(message.ip, ips[message.ip - 1]))
         | 
| 69 | 
            +
                          when Message::ILT2::GetMotorPosition
         | 
| 70 | 
            +
                            respond(message.src, Message::ILT2::PostMotorPosition.new(position_pulses, to_percent(position_pulses)))
         | 
| 71 | 
            +
                          when Message::ILT2::GetMotorSettings
         | 
| 72 | 
            +
                            respond(message.src, Message::ILT2::PostMotorSettings.new(down_limit))
         | 
| 73 | 
            +
                          when Message::ILT2::SetIRConfig then self.ir_channels = message.channels
         | 
| 74 | 
            +
                          when Message::ILT2::SetLockStatus then self.lock_priority = message.priority
         | 
| 50 75 | 
             
                          when Message::ILT2::SetMotorIP
         | 
| 51 | 
            -
                            next nack(message) unless (1..16). | 
| 76 | 
            +
                            next nack(message) unless (1..16).cover?(message.ip)
         | 
| 77 | 
            +
             | 
| 52 78 | 
             
                            ips[message.ip - 1] = message.value
         | 
| 53 79 | 
             
                            ack(message)
         | 
| 54 80 | 
             
                          when Message::ILT2::SetMotorPosition
         | 
| 55 81 | 
             
                            next nack(message) unless down_limit
         | 
| 56 82 |  | 
| 57 83 | 
             
                            self.position_pulses = case message.target_type
         | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 84 | 
            +
                                                   when :up_limit then 0
         | 
| 85 | 
            +
                                                   when :down_limit then down_limit
         | 
| 86 | 
            +
                                                   when :ip
         | 
| 87 | 
            +
                                                     next nack(message) unless (1..16).cover?(message.target)
         | 
| 88 | 
            +
                                                     next nack(message) unless ips[message.target]
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                                                     ips[message.target]
         | 
| 91 | 
            +
                                                   when :position_pulses
         | 
| 92 | 
            +
                                                     next nack(message) if message.target - 1 > down_limit
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                                                     message.target - 1
         | 
| 95 | 
            +
                                                   when :jog_up_pulses then [0, position_pulses - message.target].max
         | 
| 96 | 
            +
                                                   when :jog_down_pulses then [down_limit, position_pulses + message.target].min
         | 
| 97 | 
            +
                                                   when :position_percent
         | 
| 98 | 
            +
                                                     next nack(message) if message.target > 100
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                                                     to_pulses(message.target.to_f)
         | 
| 101 | 
            +
                                                   end
         | 
| 73 102 | 
             
                            ack(message)
         | 
| 74 103 | 
             
                          when Message::ILT2::SetMotorSettings
         | 
| 75 104 | 
             
                            if message.down_limit != 0
         | 
| @@ -78,29 +107,33 @@ module SDN | |
| 78 107 | 
             
                            end
         | 
| 79 108 | 
             
                          when Message::MoveTo
         | 
| 80 109 | 
             
                            next nack(message) unless down_limit
         | 
| 81 | 
            -
                            next nack(message) unless  | 
| 82 | 
            -
             | 
| 110 | 
            +
                            next nack(message) unless ALLOWED_MOVE_TYPES.include?(message.target_type)
         | 
| 111 | 
            +
             | 
| 83 112 | 
             
                            self.position_pulses = case message.target_type
         | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 113 | 
            +
                                                   when :up_limit then 0
         | 
| 114 | 
            +
                                                   when :down_limit then down_limit
         | 
| 115 | 
            +
                                                   when :ip
         | 
| 116 | 
            +
                                                     next nack(message) unless (1..16).cover?(message.target)
         | 
| 117 | 
            +
                                                     next nack(message) unless ips[message.target - 1]
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                                                     ips[message.target - 1]
         | 
| 120 | 
            +
                                                   when :position_pulses
         | 
| 121 | 
            +
                                                     next nack(message) if message.target > down_limit
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                                                     message.target
         | 
| 124 | 
            +
                                                   when :position_percent
         | 
| 125 | 
            +
                                                     next nack(message) if message.target > 100
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                                                     to_pulses(message.target)
         | 
| 128 | 
            +
                                                   end
         | 
| 97 129 | 
             
                            ack(message)
         | 
| 98 130 | 
             
                          when Message::SetGroupAddr
         | 
| 99 | 
            -
                            next nack(message) unless (1..16). | 
| 100 | 
            -
             | 
| 131 | 
            +
                            next nack(message) unless (1..16).cover?(message.group_index)
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                            groups[message.group_index - 1] = (message.group_address == [0, 0, 0]) ? nil : message.group_address
         | 
| 101 134 | 
             
                            ack(message)
         | 
| 102 135 | 
             
                          when Message::SetMotorIP
         | 
| 103 | 
            -
                            next nack(message) unless (1..16). | 
| 136 | 
            +
                            next nack(message) unless (1..16).cover?(message.ip) || message.type == :distribute
         | 
| 104 137 |  | 
| 105 138 | 
             
                            case message.type
         | 
| 106 139 | 
             
                            when :delete
         | 
| @@ -122,7 +155,8 @@ module SDN | |
| 122 155 | 
             
                              end
         | 
| 123 156 | 
             
                            when :distribute
         | 
| 124 157 | 
             
                              next nack(message) unless down_limit
         | 
| 125 | 
            -
                              next nack(message) unless (1..15). | 
| 158 | 
            +
                              next nack(message) unless (1..15).cover?(message.value)
         | 
| 159 | 
            +
             | 
| 126 160 | 
             
                              span = down_limit / (message.value + 1)
         | 
| 127 161 | 
             
                              current = 0
         | 
| 128 162 | 
             
                              (0...message.value).each do |ip|
         | 
| @@ -134,8 +168,8 @@ module SDN | |
| 134 168 | 
             
                              ack(message)
         | 
| 135 169 | 
             
                            end
         | 
| 136 170 | 
             
                          when Message::SetMotorLimits
         | 
| 137 | 
            -
                            next nack(message) unless  | 
| 138 | 
            -
                            next nack(message) unless  | 
| 171 | 
            +
                            next nack(message) unless Message::SetMotorLimits::TARGET.key?(message.target)
         | 
| 172 | 
            +
                            next nack(message) unless message.type == :jog_pulses
         | 
| 139 173 |  | 
| 140 174 | 
             
                            self.up_limit ||= 0
         | 
| 141 175 | 
             
                            self.down_limit ||= 0
         | 
| @@ -150,27 +184,30 @@ module SDN | |
| 150 184 | 
             
                              self.position_pulses += message.value if message.target == :down
         | 
| 151 185 | 
             
                            end
         | 
| 152 186 | 
             
                            ack(message)
         | 
| 153 | 
            -
                          when Message::SetNodeLabel | 
| 187 | 
            +
                          when Message::SetNodeLabel then self.label = message.label
         | 
| 188 | 
            +
                                                          ack(message)
         | 
| 154 189 | 
             
                          end
         | 
| 155 190 | 
             
                        end
         | 
| 156 191 | 
             
                      end
         | 
| 157 192 | 
             
                    end
         | 
| 158 193 |  | 
| 159 194 | 
             
                    def to_percent(pulses)
         | 
| 160 | 
            -
                      pulses && down_limit ? 100.0 * pulses / down_limit : nil
         | 
| 195 | 
            +
                      (pulses && down_limit) ? 100.0 * pulses / down_limit : nil
         | 
| 161 196 | 
             
                    end
         | 
| 162 197 |  | 
| 163 198 | 
             
                    def to_pulses(percent)
         | 
| 164 | 
            -
                      percent && down_limit ? down_limit * percent / 100 : nil
         | 
| 199 | 
            +
                      (percent && down_limit) ? down_limit * percent / 100 : nil
         | 
| 165 200 | 
             
                    end
         | 
| 166 201 |  | 
| 167 202 | 
             
                    def ack(message)
         | 
| 168 203 | 
             
                      return unless message.ack_requested
         | 
| 204 | 
            +
             | 
| 169 205 | 
             
                      respond(Message::Ack.new(message.dest))
         | 
| 170 206 | 
             
                    end
         | 
| 171 207 |  | 
| 172 | 
            -
                    def nack(message,  | 
| 208 | 
            +
                    def nack(message, _error_code = nil)
         | 
| 173 209 | 
             
                      return unless message.ack_requested
         | 
| 210 | 
            +
             | 
| 174 211 | 
             
                      respond(Message::Nack.new(message.dest))
         | 
| 175 212 | 
             
                    end
         | 
| 176 213 |  | 
| @@ -178,14 +215,11 @@ module SDN | |
| 178 215 | 
             
                      message.src = address
         | 
| 179 216 | 
             
                      message.node_type = node_type
         | 
| 180 217 | 
             
                      message.dest = dest
         | 
| 181 | 
            -
                      SDN.logger.info "Sending #{message.inspect}"
         | 
| 182 218 | 
             
                      @client.send(message)
         | 
| 183 219 | 
             
                    end
         | 
| 184 220 | 
             
                  end
         | 
| 185 221 |  | 
| 186 | 
            -
                  def initialize( | 
| 187 | 
            -
                    sdn = Client.new(port)
         | 
| 188 | 
            -
             | 
| 222 | 
            +
                  def initialize(sdn, address = nil)
         | 
| 189 223 | 
             
                    motor = MockMotor.new(sdn)
         | 
| 190 224 | 
             
                    motor.address = Message.parse_address(address) if address
         | 
| 191 225 | 
             
                    motor.node_type = :lt50
         | 
    
        data/lib/sdn/client.rb
    CHANGED
    
    | @@ -1,33 +1,42 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "io/wait"
         | 
| 2 4 |  | 
| 3 5 | 
             
            module SDN
         | 
| 4 6 | 
             
              class Client
         | 
| 7 | 
            +
                attr_writer :trace
         | 
| 8 | 
            +
             | 
| 5 9 | 
             
                def initialize(port)
         | 
| 6 10 | 
             
                  uri = URI.parse(port)
         | 
| 7 11 | 
             
                  @io = if uri.scheme == "tcp"
         | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
                  @buffer = ""
         | 
| 12 | 
            +
                          require "socket"
         | 
| 13 | 
            +
                          TCPSocket.new(uri.host, uri.port)
         | 
| 14 | 
            +
                        elsif uri.scheme == "telnet" || uri.scheme == "rfc2217"
         | 
| 15 | 
            +
                          require "net/telnet/rfc2217"
         | 
| 16 | 
            +
                          Net::Telnet::RFC2217.new(host: uri.host,
         | 
| 17 | 
            +
                                                   port: uri.port || 23,
         | 
| 18 | 
            +
                                                   baud: 4800,
         | 
| 19 | 
            +
                                                   data_bits: 8,
         | 
| 20 | 
            +
                                                   parity: :odd,
         | 
| 21 | 
            +
                                                   stop_bits: 1)
         | 
| 22 | 
            +
                        elsif port == "/dev/ptmx"
         | 
| 23 | 
            +
                          require "pty"
         | 
| 24 | 
            +
                          io, slave = PTY.open
         | 
| 25 | 
            +
                          puts "Slave PTY available at #{slave.path}"
         | 
| 26 | 
            +
                          io
         | 
| 27 | 
            +
                        else
         | 
| 28 | 
            +
                          require "ccutrer-serialport"
         | 
| 29 | 
            +
                          CCutrer::SerialPort.new(port, baud: 4800, data_bits: 8, parity: :odd, stop_bits: 1)
         | 
| 30 | 
            +
                        end
         | 
| 31 | 
            +
                  @buffer = +""
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def trace?
         | 
| 35 | 
            +
                  @trace
         | 
| 28 36 | 
             
                end
         | 
| 29 37 |  | 
| 30 38 | 
             
                def send(message)
         | 
| 39 | 
            +
                  SDN.logger.debug("Sending #{message.inspect}")
         | 
| 31 40 | 
             
                  @io.write(message.serialize)
         | 
| 32 41 | 
             
                end
         | 
| 33 42 |  | 
| @@ -42,6 +51,7 @@ module SDN | |
| 42 51 | 
             
                    messages = transact(message)
         | 
| 43 52 | 
             
                    next if messages.empty?
         | 
| 44 53 | 
             
                    next unless message.class.expected_response?(messages.first)
         | 
| 54 | 
            +
             | 
| 45 55 | 
             
                    return messages.first
         | 
| 46 56 | 
             
                  end
         | 
| 47 57 | 
             
                end
         | 
| @@ -63,7 +73,7 @@ module SDN | |
| 63 73 | 
             
                      eofs = 0
         | 
| 64 74 | 
             
                      begin
         | 
| 65 75 | 
             
                        block = @io.read_nonblock(64 * 1024)
         | 
| 66 | 
            -
                        SDN.logger.debug | 
| 76 | 
            +
                        SDN.logger.debug("Read #{block.unpack1("H*").gsub(/\h{2}/, "\\0 ")}") if trace?
         | 
| 67 77 | 
             
                        @buffer.concat(block)
         | 
| 68 78 | 
             
                        next
         | 
| 69 79 | 
             
                      rescue IO::WaitReadable, EOFError => e
         | 
| @@ -77,8 +87,10 @@ module SDN | |
| 77 87 | 
             
                        wait = @buffer.empty? ? timeout : WAIT_TIME
         | 
| 78 88 | 
             
                        if @io.wait_readable(wait).nil?
         | 
| 79 89 | 
             
                          # timed out; just discard everything
         | 
| 80 | 
            -
                           | 
| 81 | 
            -
             | 
| 90 | 
            +
                          unless @buffer.empty?
         | 
| 91 | 
            +
                            SDN.logger.debug "Discarding #{@buffer.unpack1("H*").gsub(/\h{2}/, "\\0 ")} due to timeout"
         | 
| 92 | 
            +
                          end
         | 
| 93 | 
            +
                          @buffer = +""
         | 
| 82 94 | 
             
                          return messages if timeout
         | 
| 83 95 | 
             
                        end
         | 
| 84 96 |  | 
| @@ -86,6 +98,8 @@ module SDN | |
| 86 98 | 
             
                      end
         | 
| 87 99 | 
             
                      next
         | 
| 88 100 | 
             
                    end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    SDN.logger.debug("Received message #{message.inspect}")
         | 
| 89 103 | 
             
                    if block_given?
         | 
| 90 104 | 
             
                      yield message
         | 
| 91 105 | 
             
                    else
         | 
    
        data/lib/sdn/message/control.rb
    CHANGED
    
    | @@ -1,9 +1,11 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              class Message
         | 
| 3 5 | 
             
                class Lock < Message
         | 
| 4 6 | 
             
                  MSG = 0x06
         | 
| 5 7 | 
             
                  PARAMS_LENGTH = 5
         | 
| 6 | 
            -
                  TARGET_TYPE = { current: 0, up_limit: 1, down_limit: 2, ip: 4, unlock: 5, position_percent: 7 }
         | 
| 8 | 
            +
                  TARGET_TYPE = { current: 0, up_limit: 1, down_limit: 2, ip: 4, unlock: 5, position_percent: 7 }.freeze
         | 
| 7 9 |  | 
| 8 10 | 
             
                  attr_reader :target_type, :target, :priority
         | 
| 9 11 |  | 
| @@ -16,12 +18,16 @@ module SDN | |
| 16 18 | 
             
                  end
         | 
| 17 19 |  | 
| 18 20 | 
             
                  def target_type=(value)
         | 
| 19 | 
            -
                     | 
| 21 | 
            +
                    unless TARGET_TYPE.key?(value)
         | 
| 22 | 
            +
                      raise ArgumentError,
         | 
| 23 | 
            +
                            "target_type must be one of :current, :up_limit, :down_limit, :ip, :unlock, or :position_percent"
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 20 26 | 
             
                    @target_type = value
         | 
| 21 27 | 
             
                  end
         | 
| 22 28 |  | 
| 23 29 | 
             
                  def target=(value)
         | 
| 24 | 
            -
                    @target = value | 
| 30 | 
            +
                    @target = value&.& 0xffff
         | 
| 25 31 | 
             
                  end
         | 
| 26 32 |  | 
| 27 33 | 
             
                  def priority=(value)
         | 
| @@ -37,7 +43,8 @@ module SDN | |
| 37 43 | 
             
                  end
         | 
| 38 44 |  | 
| 39 45 | 
             
                  def params
         | 
| 40 | 
            -
                    transform_param(TARGET_TYPE[target_type]) + from_number(target, | 
| 46 | 
            +
                    transform_param(TARGET_TYPE[target_type]) + from_number(target,
         | 
| 47 | 
            +
                                                                            2) + transform_param(priority) + transform_param(0)
         | 
| 41 48 | 
             
                  end
         | 
| 42 49 | 
             
                end
         | 
| 43 50 |  | 
| @@ -56,29 +63,38 @@ module SDN | |
| 56 63 | 
             
                    self.direction = direction
         | 
| 57 64 | 
             
                    self.duration = duration
         | 
| 58 65 | 
             
                    self.speed = speed
         | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
                     | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  def parse(params)
         | 
| 69 | 
            +
                    super
         | 
| 70 | 
            +
                    self.direction = DIRECTION.invert[to_number(params[0])]
         | 
| 71 | 
            +
                    duration = to_number(params[1])
         | 
| 72 | 
            +
                    duration = nil if duration.zero?
         | 
| 73 | 
            +
                    self.duration = duration
         | 
| 74 | 
            +
                    self.speed = SPEED.invert[to_number(params[3])]
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  def direction=(value)
         | 
| 78 | 
            +
                    unless DIRECTION.key?(value)
         | 
| 79 | 
            +
                      raise ArgumentError,
         | 
| 80 | 
            +
                            "direction must be one of :down, :up, or :cancel (#{value})"
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 72 83 | 
             
                    @direction = value
         | 
| 73 | 
            -
             | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  def duration=(value)
         | 
| 87 | 
            +
                    if value && (value < 0x0a || value > 0xff)
         | 
| 88 | 
            +
                      raise ArgumentError,
         | 
| 89 | 
            +
                            "duration must be in range 0x0a to 0xff (#{value})"
         | 
| 90 | 
            +
                    end
         | 
| 74 91 |  | 
| 75 | 
            -
                 def duration=(value)
         | 
| 76 | 
            -
                    raise ArgumentError, "duration must be in range 0x0a to 0xff (#{value})" if value && (value < 0x0a || value > 0xff)
         | 
| 77 92 | 
             
                    @duration = value
         | 
| 78 | 
            -
             | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  def speed=(value)
         | 
| 96 | 
            +
                    raise ArgumentError, "speed must be one of :up, :down, or :slow (#{value})" unless SPEED.key?(value)
         | 
| 79 97 |  | 
| 80 | 
            -
                 def speed=(value)
         | 
| 81 | 
            -
                    raise ArgumentError, "speed must be one of :up, :down, or :slow (#{value})" unless SPEED.keys.include?(value)
         | 
| 82 98 | 
             
                    @speed = speed
         | 
| 83 99 | 
             
                  end
         | 
| 84 100 |  | 
| @@ -93,7 +109,12 @@ module SDN | |
| 93 109 | 
             
                class MoveOf < Message
         | 
| 94 110 | 
             
                  MSG = 0x04
         | 
| 95 111 | 
             
                  PARAMS_LENGTH = 4
         | 
| 96 | 
            -
                  TARGET_TYPE = { next_ip: 0x00, | 
| 112 | 
            +
                  TARGET_TYPE = { next_ip: 0x00,
         | 
| 113 | 
            +
                                  previous_ip: 0x01,
         | 
| 114 | 
            +
                                  jog_down_pulses: 0x02,
         | 
| 115 | 
            +
                                  jog_up_pulses: 0x03,
         | 
| 116 | 
            +
                                  jog_down_ms: 0x04,
         | 
| 117 | 
            +
                                  jog_up_ms: 0x05 }.freeze
         | 
| 97 118 |  | 
| 98 119 | 
             
                  attr_reader :target_type, :target
         | 
| 99 120 |  | 
| @@ -108,12 +129,16 @@ module SDN | |
| 108 129 | 
             
                    super
         | 
| 109 130 | 
             
                    self.target_type = TARGET_TYPE.invert[to_number(params[0])]
         | 
| 110 131 | 
             
                    target = to_number(params[1..2], nillable: true)
         | 
| 111 | 
            -
                    target *= 10 if %I | 
| 132 | 
            +
                    target *= 10 if %I[jog_down_ms jog_up_ms].include?(target_type)
         | 
| 112 133 | 
             
                    self.target = target
         | 
| 113 134 | 
             
                  end
         | 
| 114 135 |  | 
| 115 136 | 
             
                  def target_type=(value)
         | 
| 116 | 
            -
                     | 
| 137 | 
            +
                    unless value.nil? || TARGET_TYPE.key?(value)
         | 
| 138 | 
            +
                      raise ArgumentError, "target_type must be one of :next_ip, :previous_ip, " \
         | 
| 139 | 
            +
                                           ":jog_down_pulses, :jog_up_pulses, :jog_down_ms, :jog_up_ms"
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
             | 
| 117 142 | 
             
                    @target_type = value
         | 
| 118 143 | 
             
                  end
         | 
| 119 144 |  | 
| @@ -124,7 +149,7 @@ module SDN | |
| 124 149 |  | 
| 125 150 | 
             
                  def params
         | 
| 126 151 | 
             
                    param = target || 0xffff
         | 
| 127 | 
            -
                    param /= 10 if %I | 
| 152 | 
            +
                    param /= 10 if %I[jog_down_ms jog_up_ms].include?(target_type)
         | 
| 128 153 | 
             
                    transform_param(TARGET_TYPE[target_type]) + from_number(param, 2) + transform_param(0)
         | 
| 129 154 | 
             
                  end
         | 
| 130 155 | 
             
                end
         | 
| @@ -138,7 +163,7 @@ module SDN | |
| 138 163 |  | 
| 139 164 | 
             
                  attr_reader :target_type, :target, :speed
         | 
| 140 165 |  | 
| 141 | 
            -
                  def initialize(dest= nil, target_type = :down_limit, target = nil, speed = :up, **kwargs)
         | 
| 166 | 
            +
                  def initialize(dest = nil, target_type = :down_limit, target = nil, speed = :up, **kwargs)
         | 
| 142 167 | 
             
                    kwargs[:dest] ||= dest
         | 
| 143 168 | 
             
                    super(**kwargs)
         | 
| 144 169 | 
             
                    self.target_type = target_type
         | 
| @@ -154,7 +179,11 @@ module SDN | |
| 154 179 | 
             
                  end
         | 
| 155 180 |  | 
| 156 181 | 
             
                  def target_type=(value)
         | 
| 157 | 
            -
                     | 
| 182 | 
            +
                    unless TARGET_TYPE.key?(value)
         | 
| 183 | 
            +
                      raise ArgumentError,
         | 
| 184 | 
            +
                            "target_type must be one of :down_limit, :up_limit, :ip, :position_pulses, or :position_percent"
         | 
| 185 | 
            +
                    end
         | 
| 186 | 
            +
             | 
| 158 187 | 
             
                    @target_type = value
         | 
| 159 188 | 
             
                  end
         | 
| 160 189 |  | 
| @@ -164,7 +193,8 @@ module SDN | |
| 164 193 | 
             
                  end
         | 
| 165 194 |  | 
| 166 195 | 
             
                  def speed=(value)
         | 
| 167 | 
            -
                    raise ArgumentError, "speed must be one of :up, :down, or :slow" unless SPEED. | 
| 196 | 
            +
                    raise ArgumentError, "speed must be one of :up, :down, or :slow" unless SPEED.key?(value)
         | 
| 197 | 
            +
             | 
| 168 198 | 
             
                    @speed = value
         | 
| 169 199 | 
             
                  end
         | 
| 170 200 |  | 
    
        data/lib/sdn/message/get.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              class Message
         | 
| 3 5 | 
             
                class GetGroupAddr < Message
         | 
| @@ -18,7 +20,8 @@ module SDN | |
| 18 20 | 
             
                  end
         | 
| 19 21 |  | 
| 20 22 | 
             
                  def group_index=(value)
         | 
| 21 | 
            -
                    raise ArgumentError, "group_index is out of range" unless (1..16). | 
| 23 | 
            +
                    raise ArgumentError, "group_index is out of range" unless (1..16).cover?(value)
         | 
| 24 | 
            +
             | 
| 22 25 | 
             
                    @group_index = value
         | 
| 23 26 | 
             
                  end
         | 
| 24 27 |  | 
| @@ -49,7 +52,8 @@ module SDN | |
| 49 52 | 
             
                  end
         | 
| 50 53 |  | 
| 51 54 | 
             
                  def ip=(value)
         | 
| 52 | 
            -
                    raise ArgumentError, "invalid IP #{value} (should be 1-16)" unless (1..16). | 
| 55 | 
            +
                    raise ArgumentError, "invalid IP #{value} (should be 1-16)" unless (1..16).cover?(value)
         | 
| 56 | 
            +
             | 
| 53 57 | 
             
                    @ip = value
         | 
| 54 58 | 
             
                  end
         | 
| 55 59 |  | 
    
        data/lib/sdn/message/helpers.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              class Message
         | 
| 3 5 | 
             
                module Helpers
         | 
| @@ -6,39 +8,39 @@ module SDN | |
| 6 8 | 
             
                  end
         | 
| 7 9 |  | 
| 8 10 | 
             
                  def print_address(addr_bytes)
         | 
| 9 | 
            -
                    "%02X.%02X.%02X"  | 
| 11 | 
            +
                    format("%02X.%02X.%02X", *addr_bytes)
         | 
| 10 12 | 
             
                  end
         | 
| 11 13 |  | 
| 12 | 
            -
                  def  | 
| 14 | 
            +
                  def group_address?(addr_bytes)
         | 
| 13 15 | 
             
                    addr_bytes[0..1] == [1, 1]
         | 
| 14 16 | 
             
                  end
         | 
| 15 17 |  | 
| 16 18 | 
             
                  def node_type_from_number(number)
         | 
| 17 19 | 
             
                    case number
         | 
| 18 | 
            -
                    when 1 | 
| 19 | 
            -
                    when 2 | 
| 20 | 
            -
                    when 6 | 
| 21 | 
            -
                    when 7 | 
| 22 | 
            -
                    when 8 | 
| 23 | 
            -
                    when 0x70 | 
| 20 | 
            +
                    when 1 then :st50ilt2
         | 
| 21 | 
            +
                    when 2 then :st30
         | 
| 22 | 
            +
                    when 6 then :glydea
         | 
| 23 | 
            +
                    when 7 then :st50ac
         | 
| 24 | 
            +
                    when 8 then :st50dc
         | 
| 25 | 
            +
                    when 0x70 then :lt50
         | 
| 24 26 | 
             
                    else; number
         | 
| 25 27 | 
             
                    end
         | 
| 26 28 | 
             
                  end
         | 
| 27 29 |  | 
| 28 30 | 
             
                  def node_type_to_number(type)
         | 
| 29 31 | 
             
                    case type
         | 
| 30 | 
            -
                    when :st50ilt2 | 
| 31 | 
            -
                    when :st30 | 
| 32 | 
            -
                    when :glydea | 
| 33 | 
            -
                    when :st50ac | 
| 34 | 
            -
                    when :st50dc | 
| 35 | 
            -
                    when :lt50 | 
| 32 | 
            +
                    when :st50ilt2 then 1
         | 
| 33 | 
            +
                    when :st30 then 2
         | 
| 34 | 
            +
                    when :glydea then 6
         | 
| 35 | 
            +
                    when :st50ac then 7
         | 
| 36 | 
            +
                    when :st50dc then 8
         | 
| 37 | 
            +
                    when :lt50 then 0x70
         | 
| 36 38 | 
             
                    else; type
         | 
| 37 39 | 
             
                    end
         | 
| 38 40 | 
             
                  end
         | 
| 39 41 |  | 
| 40 42 | 
             
                  def node_type_to_string(type)
         | 
| 41 | 
            -
                    type.is_a?(Integer) ? "%02xh"  | 
| 43 | 
            +
                    type.is_a?(Integer) ? format("%02xh", type) : type.inspect
         | 
| 42 44 | 
             
                  end
         | 
| 43 45 |  | 
| 44 46 | 
             
                  def transform_param(param)
         | 
| @@ -52,28 +54,27 @@ module SDN | |
| 52 54 | 
             
                  end
         | 
| 53 55 |  | 
| 54 56 | 
             
                  def from_number(number, bytes = 1)
         | 
| 55 | 
            -
                    number ||= 1 | 
| 57 | 
            +
                    number ||= (1**(bytes * 8)) - 1
         | 
| 56 58 | 
             
                    number = number.to_i
         | 
| 57 | 
            -
                    bytes.times. | 
| 58 | 
            -
                      res << (0xff - number & 0xff)
         | 
| 59 | 
            +
                    bytes.times.each_with_object([]) do |_, res|
         | 
| 60 | 
            +
                      res << ((0xff - number) & 0xff)
         | 
| 59 61 | 
             
                      number >>= 8
         | 
| 60 | 
            -
                      res
         | 
| 61 62 | 
             
                    end
         | 
| 62 63 | 
             
                  end
         | 
| 63 64 |  | 
| 64 65 | 
             
                  def to_string(param)
         | 
| 65 66 | 
             
                    chars = param.map { |b| 0xff - b }
         | 
| 66 | 
            -
                    chars.pack("C*").sub(/\0+$/,  | 
| 67 | 
            +
                    chars.pack("C*").sub(/\0+$/, "").strip
         | 
| 67 68 | 
             
                  end
         | 
| 68 69 |  | 
| 69 70 | 
             
                  def from_string(string, bytes)
         | 
| 70 71 | 
             
                    chars = string.bytes
         | 
| 71 | 
            -
                    chars = chars[0...bytes].fill( | 
| 72 | 
            +
                    chars = chars[0...bytes].fill(" ".ord, chars.length, bytes - chars.length)
         | 
| 72 73 | 
             
                    chars.map { |b| 0xff - b }
         | 
| 73 74 | 
             
                  end
         | 
| 74 75 |  | 
| 75 76 | 
             
                  def checksum(bytes)
         | 
| 76 | 
            -
                    result = bytes. | 
| 77 | 
            +
                    result = bytes.sum
         | 
| 77 78 | 
             
                    [result >> 8, result & 0xff]
         | 
| 78 79 | 
             
                  end
         | 
| 79 80 | 
             
                end
         | 
    
        data/lib/sdn/message/ilt2/get.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              class Message
         | 
| 3 5 | 
             
                module ILT2
         | 
| @@ -22,15 +24,16 @@ module SDN | |
| 22 24 | 
             
                    end
         | 
| 23 25 |  | 
| 24 26 | 
             
                    def ip=(value)
         | 
| 25 | 
            -
                      raise ArgumentError, "invalid IP #{value} (should be 1-16)" unless (1..16). | 
| 27 | 
            +
                      raise ArgumentError, "invalid IP #{value} (should be 1-16)" unless (1..16).cover?(value)
         | 
| 28 | 
            +
             | 
| 26 29 | 
             
                      @ip = value
         | 
| 27 30 | 
             
                    end
         | 
| 28 | 
            -
             | 
| 31 | 
            +
             | 
| 29 32 | 
             
                    def parse(params)
         | 
| 30 33 | 
             
                      super
         | 
| 31 34 | 
             
                      self.ip = to_number(params[0]) + 1
         | 
| 32 35 | 
             
                    end
         | 
| 33 | 
            -
             | 
| 36 | 
            +
             | 
| 34 37 | 
             
                    def params
         | 
| 35 38 | 
             
                      transform_param(@ip - 1)
         | 
| 36 39 | 
             
                    end
         |