Class: Interlnk::Channel::Serial
- Inherits:
-
Object
- Object
- Interlnk::Channel::Serial
- Defined in:
- lib/interlnk/channel/serial.rb
Defined Under Namespace
Classes: ProtocolException
Constant Summary collapse
- SPEED_INDEXES =
{ 1200 => 0, 2400 => 1, 4800 => 2, 9600 => 3, 19200 => 4, 38400 => 5, 57600 => 6, 115200 => 7 }
Instance Attribute Summary collapse
-
#debug ⇒ Object
Returns the value of attribute debug.
Instance Method Summary collapse
- #baud=(baud) ⇒ Object
-
#calculate_ack_for(input) ⇒ Object
private.
- #channel_turnaround_to_recv ⇒ Object
- #channel_turnaround_to_send ⇒ Object
- #connect ⇒ Object
- #expect(expect) ⇒ Object
- #expect_and_send(expect:, send:) ⇒ Object
-
#initialize(transport:) ⇒ Serial
constructor
A new instance of Serial.
- #next_seqnbr ⇒ Object
- #perform_idle ⇒ Object
- #receive_packet(type: nil) ⇒ Object
- #request_more_data ⇒ Object
- #send(send) ⇒ Object
- #send_and_expect(send:, expect:) ⇒ Object
- #send_flags_seq(packet_length:) ⇒ Object
- #send_packet(data) ⇒ Object
Constructor Details
#initialize(transport:) ⇒ Serial
Returns a new instance of Serial.
29 30 31 32 33 34 35 |
# File 'lib/interlnk/channel/serial.rb', line 29 def initialize(transport:) @transport = transport @debug = false @next_seqnbr = 0 @next_needs_seqflags = true end |
Instance Attribute Details
#debug ⇒ Object
Returns the value of attribute debug.
13 14 15 |
# File 'lib/interlnk/channel/serial.rb', line 13 def debug @debug end |
Instance Method Details
#baud=(baud) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/interlnk/channel/serial.rb', line 44 def baud=(baud) # set the baud rate send_and_expect send: 0xff, expect: 0x00 send_and_expect send: SPEED_INDEXES[baud], expect: SPEED_INDEXES[baud] << 4 # TODO: I believe if this was real serial we'd change baud rates now # but it's possible we have to change between the send and expect in the above # send shifted baud rate again, expect inverse send_and_expect send: SPEED_INDEXES[baud]<<4, expect: (SPEED_INDEXES[baud]<<4).invert_bits(width: 8) # send/receive a bunch of pre-defined bytes # presumably to make sure the new baud rate is working? verify_send_seq = [0xff, 0x00, 0x5a, 0x55, 0xaa, 0xf0, 0x0f, 0xe7, 0x7e, 0xc3, 0x3c, 0x81, 0x18, 0x00, 0xff] verify_recv_seq = [0x00, 0xff, 0xa5, 0xaa, 0x55, 0xf0, 0x0f, 0xff, 0x00, 0x18, 0x81, 0x3c, 0xc3, 0x7e, 0xe7] verify_send_seq.each_index do |idx| send_and_expect send: verify_send_seq[idx], expect: verify_recv_seq[idx] end end |
#calculate_ack_for(input) ⇒ Object
private
183 184 185 186 |
# File 'lib/interlnk/channel/serial.rb', line 183 def calculate_ack_for(input) # this is weird but it's just how ack bytes work for seq/flags input ^ 0xFF & 0xFB end |
#channel_turnaround_to_recv ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/interlnk/channel/serial.rb', line 100 def channel_turnaround_to_recv @next_needs_seqflags = true this_seqnbr = next_seqnbr ack = (@last_seq_flags & 0xFC) | this_seqnbr send_and_expect send: this_seqnbr, expect: ack # TODO: 0x80 probably shouldn't be hardcoded for the length flag send calculate_ack_for(this_seqnbr | 0x80) @last_seq_flags = ack end |
#channel_turnaround_to_send ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/interlnk/channel/serial.rb', line 112 def channel_turnaround_to_send # we don't need to do seq/flags right after this turnaround @next_needs_seqflags = false this_seqnbr = next_seqnbr ack = (@last_seq_flags & 0xFC) | this_seqnbr send ack what = @transport.read(1).unpack('C')[0] # TODO: validate this, don't just ignore it printf "CT< 0x%02X\n", what if @debug @last_seq_flags = ack end |
#connect ⇒ Object
37 38 39 40 41 42 |
# File 'lib/interlnk/channel/serial.rb', line 37 def connect # do the initial sync handshake send_and_expect send: 0xaa, expect: 0x00 send_and_expect send: 0x55, expect: 0xff send_and_expect send: 0x5a, expect: 0x11 end |
#expect(expect) ⇒ Object
228 229 230 231 232 233 234 |
# File 'lib/interlnk/channel/serial.rb', line 228 def expect(expect) response = @transport.read(1).unpack('C')[0] puts "SE< 0x#{response.to_s(16).rjust(2, '0')} (seq bits #{(response & 0x03).to_s(2).rjust(2, '0')})" if @debug if(response != expect) then raise ProtocolException, "Expected 0x#{expect.to_s(16).rjust(2, '0')}, got 0x#{response.to_s(16).rjust(2, '0')}" end end |
#expect_and_send(expect:, send:) ⇒ Object
218 219 220 221 |
# File 'lib/interlnk/channel/serial.rb', line 218 def expect_and_send(expect:, send:) expect(expect) send(send) end |
#next_seqnbr ⇒ Object
204 205 206 207 208 209 210 211 |
# File 'lib/interlnk/channel/serial.rb', line 204 def next_seqnbr this_seqnbr = @next_seqnbr @next_seqnbr = @next_seqnbr += 1 # roll sequence number around after 3 since it's only 2 bits wide @next_seqnbr = 0 if @next_seqnbr >= 4 this_seqnbr end |
#perform_idle ⇒ Object
176 177 178 179 |
# File 'lib/interlnk/channel/serial.rb', line 176 def perform_idle idle_byte = 0xFC | (@next_seqnbr ^ 0x01) send_and_expect send: idle_byte, expect: idle_byte end |
#receive_packet(type: nil) ⇒ Object
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/interlnk/channel/serial.rb', line 125 def receive_packet(type: nil) packet_length = @transport.read(1).unpack('C')[0] packet_length_bytes = [packet_length] length_type = :byte if(packet_length == 0) then # packet is larger than 255 bytes, get the MSB now # TODO: first length byte of 00 isn't the right way # to identify this, but it works for now length_type = :word packet_length_msb = @transport.read(1).unpack('C')[0] packet_length_bytes << packet_length_msb packet_length = packet_length | (packet_length_msb << 8) end puts "\nReceiving #{packet_length} byte packet." if @debug printf "RL< 0x%02X\n", packet_length if @debug packet_data = @transport.read(packet_length+2) crc = Crc.new() packet_length_bytes.each { |byte| crc << byte } print "RD< " if @debug packet_data.each_byte do |datum| printf "0x%02X ", datum if @debug crc << datum end print "\n" if @debug # TODO: retry, don't just give up raise ProtocolException, "Bad CRC, aborting." unless crc.residue_ok? puts "CRC OK" if @debug ack_byte = @last_seq_flags ack_byte &= ~0x80 if length_type == :word send_and_expect send: ack_byte, expect: @next_seqnbr # trim off the CRC packet_data = packet_data[0..-3] if(type) return type.new packet_data else return packet_data end end |
#request_more_data ⇒ Object
167 168 169 170 171 172 173 174 |
# File 'lib/interlnk/channel/serial.rb', line 167 def request_more_data # TODO: I don't fully understand how this works # It seems that for read_data we send another # ack after the ack-ack from the status answer # to get the server to send the data part? send calculate_ack_for(@next_seqnbr) @last_seq_flags = next_seqnbr | 0x80 end |
#send(send) ⇒ Object
223 224 225 226 |
# File 'lib/interlnk/channel/serial.rb', line 223 def send(send) puts "SE> 0x#{send.to_s(16).rjust(2, '0')} (seq bits #{(send & 0x03).to_s(2).rjust(2, '0')})" if @debug @transport.write [send].pack('C') end |
#send_and_expect(send:, expect:) ⇒ Object
213 214 215 216 |
# File 'lib/interlnk/channel/serial.rb', line 213 def send_and_expect(send:, expect:) send(send) expect(expect) end |
#send_flags_seq(packet_length:) ⇒ Object
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/interlnk/channel/serial.rb', line 188 def send_flags_seq(packet_length:) length_flag = 0x80 if(packet_length > 0xFF) then raise ProtocolException, "Word-length packets are not yet supported." end seqnbr = next_seqnbr seq_flags = seqnbr | length_flag # TODO: support other flags ack_byte = calculate_ack_for(seq_flags) send_and_expect send: seq_flags, expect: ack_byte @last_seq_flags = seq_flags seq_flags end |
#send_packet(data) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/interlnk/channel/serial.rb', line 65 def send_packet(data) puts "Sending #{data.length} byte packet (#{@next_needs_seqflags ? "with" : "no"} seq/flags)." if @debug seq_flags = send_flags_seq(packet_length: data.length) if @next_needs_seqflags @next_needs_seqflags = true crc = Crc.new # send length # TODO: support 'word-length' packets, not just 'byte-length' printf "SL> 0x%02X\n", data.length if @debug crc << data.length @transport.write [data.length].pack('C') # send the packet data itself print "SD> " if @debug data.each do |datum| printf "0x%02X ", datum if @debug crc << datum end print "\n" if @debug @transport.write data.pack('C*') # send the CRC crc_low = crc.result & 0x00FF crc_high = (crc.result & 0xFF00) >> 8 printf "SC> 0x%02X 0x%02X\n", crc_high, crc_low if @debug @transport.write [crc_high, crc_low].pack('CC') if(seq_flags) then expect(seq_flags) else expect(@last_seq_flags) end end |