Class: SerialProtocol::RCA2006

Inherits:
Object
  • Object
show all
Defined in:
lib/protocol/rca2006.rb

Overview

Serial protocol Roboterclub Aachen 2006 www.roboterclub.rwth-aachen.de/

Packet format:

[0x65, 0xEB, <type:8>, <counter:8>, <length:8>, data:length>, <crc:16>]

<type> is one of the following:

0b00000000 --> Data packet, discard on checksum mismatch
0b00011111 --> Data packet, resend on checksum mismatch
0b11100011 --> ACK packet
0b11111100 --> NACK packet

Direct Known Subclasses

RCA2006Simple

Constant Summary collapse

STARTBYTES =
"\x65\xeb"
TYPE =
{
:data_no_crc => 0, 
:data        => 0b00011111, 
:ack         => 0b11100011, 
:nack        => 0b11111100 }

Instance Method Summary collapse

Constructor Details

#initialize(send_callback, receive_callback, options = {}) ⇒ RCA2006

Returns a new instance of RCA2006.



24
25
26
27
28
29
# File 'lib/protocol/rca2006.rb', line 24

def initialize(send_callback, receive_callback, options = {})
  @rec_queue = Queue.new
  @state = :first_startbyte
  @send_callback = send_callback
  @receive_callback = receive_callback
end

Instance Method Details

#add_char_to_packet(char) ⇒ Object

Big and ugly state machine that does most of the work



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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/protocol/rca2006.rb', line 74

def add_char_to_packet(char)
  @state = :first_checksum if (@state == 0)
  case @state
  when :first_startbyte
    @data = ""
    @state = ((char == STARTBYTES[0]) ? :second_startbyte : :first_startbyte)
  when :second_startbyte
    @state = (char == STARTBYTES[1]) ? :type : 
      # special case: first startbyte is repeated
      (char == STARTBYTES[0] ? :second_startbyte : :first_startbyte)
  when :type
    @type = TYPE.invert[char]
    @state = :counter
  when :counter
    @counter = char
    @state = :length
  when :length
    @length = char
    @state = @length
  when Integer
    @data << char
    @state -= 1
  when :first_checksum
    @checksum = (char << 8)
    @state = :second_checksum
  when :second_checksum
    @checksum = @checksum + char
    @state = :first_startbyte

    crc = ("" << @counter << @length << @data).crc_xmodem
      # received a valid packet
      
    if @type == :data || @type == :data_no_crc
      if @checksum == crc
         
        # send ACK
        send_packet(nil, :type => :ack, :counter => @counter)
        receive_handler(@type, @counter, @data,@checksum)
      else
        # send NACK and discard packet
        send_packet(nil, :type => :nack, :counter => @counter)
        raise ChecksumMismatch, "ChecksumMismatch, expected #{crc}, was #{@checksum}"
      end
    else
      # the packet is ACK, NACK or unknown, call receive-handler
      # data may be mangled since the checksum is not checked
      #
      receive_handler(@type, @counter, @data, @checksum)
    end
  end        
end

#on_raw_receive(&block) ⇒ Object

Set callbacks whenever a packet is sent or received.



33
34
35
# File 'lib/protocol/rca2006.rb', line 33

def on_raw_receive(&block)
  @raw_receive_callback = block
end

#on_raw_send(&block) ⇒ Object



36
37
38
# File 'lib/protocol/rca2006.rb', line 36

def on_raw_send(&block)
  @raw_send_callback = block
end

#receive_handler(type, counter, data, checksum) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/protocol/rca2006.rb', line 59

def receive_handler(type, counter, data, checksum)
  @raw_receive_callback.call(type,counter,data,checksum) if @raw_receive_callback
  
  case type
  when :ack
  when :nack
  when :data
    @receive_callback.call(data)
  when :data_no_crc
    @receive_callback.call(data)
  end
end

#send_packet(data, options = {}) ⇒ Object

Wrap a string into a packet

the options-hash can be used to override the default packet format



44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/protocol/rca2006.rb', line 44

def send_packet(data, options = {})
  str = data.to_s
  type = TYPE[ options[:type] || :data_no_crc].chr
  counter = options[:counter] || 0
  checksum = options[:checksum] || ("" << counter << str.length << str).crc_xmodem

  @raw_send_callback.call(type, counter, data, checksum) if @raw_send_callback
  
  p = "" << STARTBYTES << type << counter << str.length << str << [checksum].pack("S").reverse

  # send the packet, using the callback
  #
  @send_callback.call(p)
end