diff --git a/lib/kiss/kiss_abstract.rb b/lib/kiss/kiss_abstract.rb index 86c8dadbcf58c748551ed1969dd0ca736a5a35b6..914e14865e4e04fb84c28fda212f3b9d4584ca8c 100644 --- a/lib/kiss/kiss_abstract.rb +++ b/lib/kiss/kiss_abstract.rb @@ -6,7 +6,7 @@ module Kiss class KissAbstract include Abstractify::Abstract - abstract :write_interface, :read_interface + abstract :write_interface, :read_interface, :connect, :close protected def initialize(strip_df_start=true) @@ -54,9 +54,132 @@ module Kiss (port << 4) & command_code end - protected - def write_setting(command, value) - write_interface([FEND] + [command] + escape_special_codes(value) + [FEND]) + private + def self.extract_path(start, raw_frame) + full_path = [] + + (2...start).each do |i| + path = identity_as_string(extract_callsign(raw_frame[i * 7..-1])) + if path and path.length > 0 + if raw_frame[i * 7 + 6] & 0x80 != 0 + full_path << [path, '*'].join + else + full_path << path + end + end + end + return full_path + end + + private + def self.parse_identity_string(identity_string) + # If we are parsing a spent token then first lets get rid of the astresick suffix. + if identity_string[-1] == '*' + identity_string = identity_string[0..-1] + end + + if identity_string.include? '-' + call_sign, ssid = identity_string.split('-') + else + call_sign = identity_string + ssid = 0 + end + + return {:callsign => call_sign, :ssid => ssid.to_i} + end + + private + def self.identity_as_string(identity) + if identity[:ssid] and identity[:ssid] > 0 + return [identity[:callsign], identity[:ssid].to_s].join('-') + else + return identity[:callsign] + end + end + + private + def self.extract_callsign(raw_frame) + callsign_as_array = raw_frame[0...6].map { |x| (x >> 1).chr } + callsign = callsign_as_array.join.strip + ssid = (raw_frame[6] >> 1) & 0x0f + ssid = (ssid == nil or ssid == 0 ? nil : ssid) + return {:callsign => callsign, :ssid => ssid} + end + + private + def self.encode_callsign(callsign) + call_sign = callsign[:callsign] + + enc_ssid = (callsign[:ssid] << 1) | 0x60 + + if call_sign.include? '*' + call_sign.gsub!(/\*/, '') + enc_ssid |= 0x80 + end + + while call_sign.length < 6 + call_sign = [call_sign, ' '].join + end + + return call_sign.chars.map { |p| p.ord << 1 } + [enc_ssid] + end + + private + def self.encode_frame(frame) + enc_frame = encode_callsign(parse_identity_string(frame[:destination].to_s)) + encode_callsign(parse_identity_string(frame[:source].to_s)) + + frame[:path].each do |hop| + enc_frame += encode_callsign(parse_identity_string(hop.to_s)) + end + + return enc_frame[0...-1] + [enc_frame[-1] | 0x01] + [Kiss::SLOT_TIME] + [0xf0] + frame[:payload].chars.map { |c| c.ord } + end + + private + def self.decode_frame(raw_frame) + frame_len = raw_frame.length + + if frame_len > 16 + (0...frame_len - 2).each do |raw_slice| + # Is address field length correct? + if raw_frame[raw_slice] & 0x01 != 0 and ((raw_slice + 1) % 7) == 0 + i = (raw_slice.to_f + 1.0) / 7.0 + # Less than 2 callsigns? + if 1.0 < i and i < 11.0 + if raw_frame[raw_slice + 1] & 0x03 == 0x03 and [0xf0, 0xcf].include? raw_frame[raw_slice + 2] + payload_as_array = raw_frame[raw_slice + 3..-1].map { |b| b.chr } + payload = payload_as_array.join + destination = identity_as_string(extract_callsign(raw_frame)) + source = identity_as_string(extract_callsign(raw_frame[7..-1])) + path = extract_path(i.to_i, raw_frame) + return {:source => source, :destination => destination, :path => path, :payload => payload} + end + end + end + end + end + return nil + end + + private + def self.valid_frame(raw_frame) + frame_len = raw_frame.length + + if frame_len > 16 + (0...frame_len - 2).each do |raw_slice| + # Is address field length correct? + if raw_frame[raw_slice] & 0x01 != 0 and ((raw_slice + 1) % 7) == 0 + i = (raw_slice.to_f + 1.0) / 7.0 + # Less than 2 callsigns? + if 1.0 < i and i < 11.0 + if raw_frame[raw_slice + 1] & 0x03 == 0x03 and [0xf0, 0xcf].include? raw_frame[raw_slice + 2] + return true + end + end + end + end + end + return false end private @@ -117,6 +240,34 @@ module Kiss end end + private + def read_bytes(*args, **kwargs) + if @frame_buffer.length == 0 + fill_buffer + end + + if @frame_buffer.length > 0 + return_frame = @frame_buffer[0] + @frame_buffer.shift + return return_frame + else + return nil + end + end + + private + def write_bytes(frame_bytes, port=0, *args, **kwargs) + kiss_packet = [FEND] + [KissAbstract.command_byte_combine(port, DATA_FRAME)] + + KissAbstract.escape_special_codes(frame_bytes) + [FEND] + + write_interface(kiss_packet) + end + + protected + def write_setting(command, value) + write_interface([FEND] + [command] + escape_special_codes(value) + [FEND]) + end + public def connect(mode_init=nil, *args, **kwargs) end @@ -127,29 +278,24 @@ module Kiss public def read(*args, **kwargs) - @lock.synchronize do - if @frame_buffer.length == 0 - fill_buffer - end - - if @frame_buffer.length > 0 - return_frame = @frame_buffer[0] - @frame_buffer.shift - return return_frame - else - return nil - end - end + @lock.synchronize do + frame = self.read_bytes(*args, **kwargs) + if frame and frame.length > 0 + return KissAbstract.decode_frame(frame) + else + return nil + end + end end public - def write(frame_bytes, port=0, *args, **kwargs) - @lock.synchronize do - kiss_packet = [FEND] + [KissAbstract.command_byte_combine(port, DATA_FRAME)] + - KissAbstract.escape_special_codes(frame_bytes) + [FEND] - - write_interface(kiss_packet) - end + def write(frame, port=0, *args, **kwargs) + @lock.synchronize do + encoded_frame = KissAbstract.encode_frame(frame) + if KissAbstract.valid_frame(encoded_frame) + self.write_bytes(encoded_frame, *args, **kwargs) + end + end end end end diff --git a/lib/kiss/kiss_serial.rb b/lib/kiss/kiss_serial.rb index 16244ed1c893cafaed8a8bfc688f85c15d9939f5..4bad9d8ee86520162a2d12ed1278890c5d394451 100644 --- a/lib/kiss/kiss_serial.rb +++ b/lib/kiss/kiss_serial.rb @@ -83,4 +83,4 @@ module Kiss end end end -end \ No newline at end of file +end diff --git a/lib/kiss/test/kiss_mock.rb b/lib/kiss/test/kiss_mock.rb index 9bbed21eff25bea81a5d0075ae7aa26d5859cda1..1f8aa238a1375f40ace129a2d9bca1d4bd1bde65 100644 --- a/lib/kiss/test/kiss_mock.rb +++ b/lib/kiss/test/kiss_mock.rb @@ -9,6 +9,14 @@ module Kiss @sent_to_interface = [] end + public + def connect(mode_init=nil, *args, **kwargs) + end + + public + def close(*args, **kwargs) + end + protected def read_interface if @read_from_interface.length == 0 @@ -38,4 +46,4 @@ module Kiss return @sent_to_interface end end -end \ No newline at end of file +end diff --git a/spec/kiss/kiss_abstract_spec.rb b/spec/kiss/kiss_abstract_spec.rb index fbb8a920771cfad46fd54338d41bb9045983a232..645723c85f8d40502cc08e8f00ae893e1c8c6b4b 100644 --- a/spec/kiss/kiss_abstract_spec.rb +++ b/spec/kiss/kiss_abstract_spec.rb @@ -1,15 +1,14 @@ require_relative '../../lib/kiss/test/kiss_mock' require 'abstractify' -# KG6WTF>S7TSUV,MTOSO-2,WIDE2*,qAR,KF6FIR-10:`17El#X-/kg6wtf@gosselinfamily.com -ENCODED_FRAME = [192, 0, 75, 71, 54, 87, 84, 70, 62, 83, 55, 84, 83, 85, 86, 44, 77, 84, 79, 83, 79, 45, 50, 44, 87, 73, - 68, 69, 50, 42, 44, 113, 65, 82, 44, 75, 70, 54, 70, 73, 82, 45, 49, 48, 58, 96, 49, 55, 69, 108, 35, - 88, 45, 47, 107, 103, 54, 119, 116, 102, 64, 103, 111, 115, 115, 101, 108, 105, 110, 102, 97, 109, 105, - 108, 121, 46, 99, 111, 109, 192] -DECODED_FRAME = [75, 71, 54, 87, 84, 70, 62, 83, 55, 84, 83, 85, 86, 44, 77, 84, 79, 83, 79, 45, 50, 44, 87, 73, 68, - 69, 50, 42, 44, 113, 65, 82, 44, 75, 70, 54, 70, 73, 82, 45, 49, 48, 58, 96, 49, 55, 69, 108, 35, - 88, 45, 47, 107, 103, 54, 119, 116, 102, 64, 103, 111, 115, 115, 101, 108, 105, 110, 102, 97, 109, - 105, 108, 121, 46, 99, 111, 109] +ENCODED_FRAME = [192, 0, 158, 154, 142, 64, 64, 64, 96, 174, 100, 142, 154, 136, 64, 98, 174, 146, 136, 138, 98, 64, 98, 174, 146, 136, 138, 100, 64, 101, 3, 240, 116, 101, 115, 116, 95, 101, 110, 99, 111, 100, 101, 95, 102, 114, 97, 109, 101, 192] + +DECODED_FRAME = { + :source => "W2GMD-1", + :destination => "OMG", + :payload => "test_encode_frame", + :path => ['WIDE1-1', 'WIDE2-2'] + } describe Kiss::KissAbstract do context "Do the things" do @@ -17,6 +16,7 @@ describe Kiss::KissAbstract do kiss_mock = Kiss::KissMock.new kiss_mock.add_read_from_interface(ENCODED_FRAME) translated_frame = kiss_mock.read + #puts translated_frame expect(translated_frame).to eql(DECODED_FRAME) kiss_mock = Kiss::KissMock.new