Module: XMODEM
- Defined in:
- lib/xmodem.rb,
lib/xmodem/version.rb
Defined Under Namespace
Classes: RXChecksumError, RXSynchError, RXTimeout
Constant Summary collapse
- XMODEM_MAX_TIMEOUTS =
how many timeouts in a row before the sender gives up?
5
- XMODEM_MAX_ERRORS =
how many errors on a single block before the receiver gives up?
10
- XMODEM_CRC_ATTEMPTS =
how many times should receiver attempt to use CRC?
3
- LOG_NAME =
'XModem'
- VERSION =
"0.1.1"
Class Method Summary collapse
-
.block_size ⇒ Object
how long does the protocol wait before giving up?.
- .block_size=(val) ⇒ Object
-
.ccitt16_crc(block) ⇒ Object
calculate a 16-bit CRC.
-
.checksum(block) ⇒ Object
calculate an 8-bit XMODEM checksum this is just the sum of all bytes modulo 0x100.
-
.receive(remote, local, options = nil) ⇒ Object
- receive a file using XMODEM protocol (block size = 128 bytes) remote
- must be an IO object connected to an XMODEM sender local
- must be an IO object - the inbound file (trimmed of any final padding) will be written to this options
-
hash of options.
-
.send(remote, local) ⇒ Object
- send a file using standard XMODEM protocol (block size = 128 bytes) will use CRC mode if requested by sender, else use 8-bit checksum remote
- must be an IO object connected to an XMODEM receiver local
-
must be an IO object containing the data to be sent.
-
.timeout_seconds ⇒ Object
how long does the protocol wait before giving up?.
- .timeout_seconds=(val) ⇒ Object
Class Method Details
.block_size ⇒ Object
how long does the protocol wait before giving up?
30 31 32 |
# File 'lib/xmodem.rb', line 30 def XMODEM::block_size @block_size end |
.block_size=(val) ⇒ Object
34 35 36 |
# File 'lib/xmodem.rb', line 34 def XMODEM::block_size=(val) @block_size = val end |
.ccitt16_crc(block) ⇒ Object
calculate a 16-bit CRC
245 246 247 248 249 250 251 |
# File 'lib/xmodem.rb', line 245 def XMODEM::ccitt16_crc(block) # cribbed from http://www.hadermann.be/blog/32/ruby-crc16-implementation/ raise RXChecksumError.new("checksum requested of invalid block {size should be #{block_size}, was #{block.length}") unless block.length==block_size crc=0 block.each_byte{|x| crc = ((crc<<8) ^ CCITT_16[(crc>>8) ^ x])&0xffff} crc end |
.checksum(block) ⇒ Object
calculate an 8-bit XMODEM checksum this is just the sum of all bytes modulo 0x100
235 236 237 238 239 240 241 242 |
# File 'lib/xmodem.rb', line 235 def XMODEM::checksum(block) raise RXChecksumError.new("checksum requested of invalid block {size should be #{block_size}, was #{block.length}") unless block.length==block_size checksum=0 block.each_byte do |b| checksum = (checksum+b) % 0x100 end checksum end |
.receive(remote, local, options = nil) ⇒ Object
receive a file using XMODEM protocol (block size = 128 bytes)
- remote
-
must be an IO object connected to an XMODEM sender
- local
-
must be an IO object - the inbound file (trimmed of any final padding) will be written to this
- options
-
hash of options. options are: values :crc (use 16bit CRC instead of 8 bit checksum)
-
:mode=> :crc or :checksum (default is 8 bit checksum)
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 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 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 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 |
# File 'lib/xmodem.rb', line 44 def XMODEM::receive(remote,local,=nil) mode = ( (.nil?) || [:mode].nil? ) ? :checksum : [:mode] logger.debug "receiver: XMODEM - #{mode}" #flush the input buffer loop do break if (select([remote],nil,nil,0.01).nil?) remote.getc end #trigger sending case mode when :crc XMODEM_CRC_ATTEMPTS.times do |attempt| remote.putc('C') break unless (select([remote],nil,nil,timeout_seconds).nil?) #if we don't get a response, fall back to checksum mode if attempt==XMODEM_CRC_ATTEMPTS-1 then logger.warn "receiver: crc-16 request failed, falling back to checksum mode" remote.putc(NAK) mode=:checksum end end else remote.putc(NAK) end expected_block = 1 error_count = 0 last_block = "" data = "" loop do begin rx_cmd = receive_getbyte(remote).ord if rx_cmd == EOT then remote.putc(ACK) trimmed_block=last_block.sub(/(\x1A+)\Z/,'') local<< trimmed_block#trim any trailing FILLER characters in last block break end if rx_cmd!=SOH then logger.warn "receiver: expected SOH (0x#{SOH}) got 0x#{"%x" % rx_cmd} - pos = #{remote.pos}" next end data="" block= receive_getbyte(remote).ord block_check = receive_getbyte(remote).ord validity = :valid validity = :invalid unless (block_check + block)==0xFF logger.debug "receiver: #{validity} block number 0x#{"%02x" % block} / block number check 0x#{"%02x" % block_check}" logger.debug "receiver: receiving block #{block} / expected block #{expected_block}" raise RXSynchError if block != expected_block && block != ((expected_block-1) % 0x100) if (block==expected_block) && (validity==:valid) then local<<last_block last_block="" end block_size.times do b=(receive_getbyte(remote)) data<<b Thread.pass end check_ok=false case mode when :crc rx_crc = ( receive_getbyte(remote).ord<<8) + receive_getbyte(remote).ord crc = ccitt16_crc(data) check_ok = (crc==rx_crc) if !check_ok then logger.warn "receiver: invalid crc-16 for block #{block}: calculated 0x#{'%04x' % crc}, got 0x#{'%02x' % rx_crc}" end else rx_checksum = receive_getbyte(remote).ord checksum = XMODEM::checksum(data) check_ok = (checksum == rx_checksum) if !check_ok then logger.warn "receiver: invalid checksum for block #{block}: calculated 0x#{'%02x' % checksum}, got 0x#{'%02x' % rx_checksum}" end end if (check_ok) then last_block = data logger.debug "receiver: #{mode} test passed for block #{block}" remote.putc(ACK) if (block == expected_block) then expected_block = ((expected_block+1) % 0x100) error_count=0 #reset the error count end else remote.putc(NAK) error_count+=1 logger.warn "receiver: checksum error # #{error_count} / max #{XMODEM_MAX_ERRORS}" raise RXChecksumError.new("too many receive errors on block #{block}") if error_count>XMODEM_MAX_ERRORS end rescue RXTimeout error_count+=1 logger.warn "receiver: timeout error # #{error_count} / max #{XMODEM_MAX_ERRORS}" raise RXTimeout("too many receive errors on block #{block}").new if error_count>XMODEM_MAX_ERRORS end end logger.info "receive complete" end |
.send(remote, local) ⇒ Object
send a file using standard XMODEM protocol (block size = 128 bytes) will use CRC mode if requested by sender, else use 8-bit checksum
- remote
-
must be an IO object connected to an XMODEM receiver
- local
-
must be an IO object containing the data to be sent
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/xmodem.rb', line 161 def XMODEM::send(remote,local) block_number=1 current_block="" sent_eot=false block_size.times do b=(local.eof? ? FILLER : local.getc) current_block<<b.chr Thread.pass end checksum = XMODEM::checksum(current_block) mode=:checksum loop do logger.info "sender: waiting for ACK/NAK/CAN (eot_sent: #{sent_eot})" if select([remote],nil,nil,timeout_seconds*XMODEM_MAX_TIMEOUTS).nil? then raise RXTimeout.new("timeout waiting for input on tx (#{timeout_seconds*XMODEM_MAX_TIMEOUTS} seconds)") unless sent_eot logger.info "sender: timeout waiting for ACK of EOT" return end if remote.eof? then logger.warn "sender: unexpected eof on input" break end tx_cmd=remote.getc.ord logger.debug "sender: got 0x#{"%x" % tx_cmd}" if tx_cmd==ACK then if sent_eot then logger.debug "sender: got ACK of EOT" break end if local.eof? then remote.putc(EOT) logger.debug "sender: got ACK of last block" sent_eot=true next end block_number=((block_number+1)%0x100) current_block="" block_size.times do b=(local.eof? ? FILLER : local.getc) current_block<<b Thread.pass end elsif (block_number==1) && (tx_cmd==CRC_MODE) then mode=:crc logger.debug "sender: using crc-16 mode" end next unless [ACK,NAK,CRC_MODE].include?(tx_cmd.ord) logger.info "sender: sending block #{block_number}" remote.putc(SOH) #start of block remote.putc(block_number) #block number remote.putc(0xff-block_number) #1's complement of block number current_block.each_byte {|b| remote.putc(b)} case mode when :crc then crc = ccitt16_crc (current_block) remote.putc(crc >> 8) #crc hi byte remote.putc(crc & 0xFF) #crc lo byte logger.debug "sender: crc-16 for block #{block_number}:#{ "%04x" % crc}" else checksum = XMODEM::checksum(current_block) remote.putc(checksum) logger.debug "sender: checksum for block #{block_number}:#{ "%02x" % checksum}" end end logger.info "sending complete (eot_sent: #{sent_eot})" end |
.timeout_seconds ⇒ Object
how long does the protocol wait before giving up?
21 22 23 |
# File 'lib/xmodem.rb', line 21 def XMODEM::timeout_seconds @timeout_seconds end |
.timeout_seconds=(val) ⇒ Object
25 26 27 |
# File 'lib/xmodem.rb', line 25 def XMODEM::timeout_seconds=(val) @timeout_seconds = val end |