rmodbus 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -17,6 +17,40 @@
17
17
  2010-01-7 Timin Aleksey <atimin@gmail.com>
18
18
  * lib/rmodbus/tcp_client.rb: Fixed bug of overflow transaction counter (thanks
19
19
  Tallak Tveide)
20
+ 2010-01-8 Timin Aleksey <atimin@gmail.com>
21
+ * lib/rmodbus/tcp_client.rb: Added new method TCPClient::connect,
22
+ TCPClient#ipaddr, TCPClient#port, TCPClient#slave
23
+ * lib/rmodbus/rtu_client.rb: Added new methods RTUClient::connect,
24
+ RTUClient#close, RTUClient#port, RTUClient#baud, RTUClient#slave
25
+ 2010-01-9 Timin Aleksey <atimin@gmail.com>
26
+ * rmodbus.gemspec: Added Rakefile into gem, dependencie on
27
+ serialport-0.8.0.gem
28
+ * spec/logging_spec.rb: Added specs for debug logging
29
+ * lib/rmodbus/client.rb, lib/rmodbus/tcp_client.rb,
30
+ lib/rmodbus/rtu_client.rb: Added debug logging functional
20
31
  2010-01-12 Timin Aleksey <atimin@gmail.com>
21
- * lib/rmodbus/rtu_client.rb: RTUClient is live!!!
22
-
32
+ * lib/rmodbus/rtu_client.rb: RTUClient is live!
33
+ 2010-01-14 Timin Aleksey <atimin@gmail.com>
34
+ * lib/rmodbus/crc16.rb: Added module for calculation CRC16
35
+ * lib/rmodbus/parsers.rb: Added module for parsing requiest from clients
36
+ * lib/rmodbus/rtu_server.rb: Added RTUServer class
37
+ * rmodbus.gemspec: Update verison => 0.4.0
38
+ * README: Update information about project
39
+ 2010-01-16 Timin Aleksey <atimin@gmail.com>
40
+ * lib/rmodbus/ext.rb, lib/rmodbus/client.rb: Fixed for ruby-1.8.6 compatibility
41
+ * lib/rmodbus/parsers.rb: Fixed bug for response on write requests
42
+ * lib/rmodbus/rtu_client.rb: Fixed bug in verifing CRC
43
+ * lib/rmodbus/rtu_server.rb: Reanimation RTUServer
44
+ * examples/perfomance_rtu.rb: Added test perfomance for ModBus RTU
45
+ 2010-01-17 Timin Aleksey <atimin@gmail.com>
46
+ * lib/rmodbus/rtu_server, lib/rmodbus/tcp_server.rb: Fixed typo
47
+ discret_inputs -> discrete_inputs
48
+ * examples/perfomance_tcp.rb: Added test perfomance for ModBus TCP
49
+ * lib/rmodbus/tcp_client.rb: Added method TCPClient#closed?
50
+ * lib/rmodbus/rtu_client.rb: Added method RTUClient#closed?
51
+ 2010-01-18 Timin Aleksey <atimin@gmail.com>
52
+ * lib/rmodbus/rtu_client.rb, lib/rmodbus/rtu_server.rb: Added support
53
+ modem parameters - :data_bits => 5..8, :stop_bits => 1 or 2, :parity =>
54
+ SerialPort::NONE, SerialPort::EVEN, SerialPort::ODD
55
+ 2010-01-20 Timin Aleksey <atimin@gmail.com>
56
+ * lib/rmodbus/ext.rb: Fixed bug for divisible 8 data in Array#pack_to_word
data/README CHANGED
@@ -2,10 +2,11 @@
2
2
 
3
3
  *RModBus* - free implementation of protocol ModBus.
4
4
 
5
- == Feature
5
+ == Features
6
6
 
7
- * Client\Server ModBus-TCP
8
- * Client ModBus-RTU (experimental) with serialport gem
7
+ * Support Ruby 1.8, Ruby 1.9
8
+ * Support ModBus-TCP, ModBus-RTU protocol
9
+ * Support client(master) and server(slave)
9
10
  * Support functions:
10
11
  * 01 (0x01) Read Coils
11
12
  * 02 (0x02) Read Discrete Inputs
@@ -19,17 +20,19 @@
19
20
 
20
21
  == Installation
21
22
 
22
- You can install RModBus with the following command
23
+ Download and install RModBus with the following
23
24
 
24
- $ rake install
25
+ $ gem install --remote rmodbus
25
26
 
26
- from its distribution directory
27
+ == Example
27
28
 
28
- == Gem installation
29
+ require 'rmodbus'
29
30
 
30
- Download and install RModBus with the following
31
+ cl = ModBus::TCPClient.new('127.0.0.1', 8502, 1)
31
32
 
32
- $ gem install --remote rmodbus
33
+ puts cl.read_holding_registers(0,4)
34
+
35
+ cl.write_multiple_registers(0, [4,4,4])
33
36
 
34
37
  == GitHub
35
38
 
@@ -39,14 +42,10 @@ $ git clone git://github.com/flipback/RModBus.git
39
42
 
40
43
  == Reference
41
44
 
45
+ Home page: http://rmodbus.heroku.com
46
+
42
47
  RModBus project: http://rubyforge.org/projects/rmodbus
43
48
 
44
49
  RModBud on GitHub: http://github.com/flipback/RModBus
45
50
 
46
51
  ModBus community: http://www.modbus-ida.org
47
-
48
-
49
-
50
-
51
-
52
-
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ require 'rbconfig'
2
+ if RUBY_VERSION.to_f >= 1.9
3
+ require 'fileutils'
4
+ else
5
+ require 'ftools'
6
+ end
7
+
8
+ begin
9
+ require 'rubygems'
10
+ rescue Exception
11
+ end
12
+
13
+ begin
14
+ require 'spec/rake/spectask'
15
+
16
+ Spec::Rake::SpecTask.new do |t|
17
+ t.spec_opts = ['-c']
18
+ t.libs << 'lib'
19
+ t.spec_files = FileList['spec/**/*_spec.rb']
20
+ t.rcov = false
21
+ end
22
+ rescue Exception
23
+ puts 'RSpec not available. Install it with: sudo gem install rspec'
24
+ end
25
+
26
+ include Config
27
+
28
+ task :install do
29
+
30
+ sitedir = CONFIG['sitelibdir']
31
+ rmodbus_dest = File.join(sitedir, 'rmodbus')
32
+
33
+ File::makedirs(rmodbus_dest, true)
34
+ File::chmod(0755, rmodbus_dest)
35
+
36
+ files = Dir.chdir('lib') { Dir['**/*.rb'] }
37
+
38
+ files.each do |fn|
39
+ fn_dir = File.dirname(fn)
40
+ target_dir = File.join(sitedir, fn_dir)
41
+ File::makedirs(target_dir) unless File.exist?(target_dir)
42
+ File::install(File.join('lib', fn), File.join(sitedir, fn), 0644, true)
43
+ end
44
+
45
+ end
@@ -0,0 +1,58 @@
1
+ $:.unshift File.join(File.dirname(__FILE__),'../lib')
2
+
3
+ require 'rmodbus'
4
+ require 'benchmark'
5
+
6
+ include ModBus
7
+
8
+ BAUD = 9600
9
+ TIMES = 100
10
+
11
+ srv = RTUServer.new 'com3', BAUD
12
+ srv.coils = [0,1] * 50
13
+ srv.discret_inputs = [1,0] * 50
14
+ srv.holding_registers = [0,1,2,3,4,5,6,7,8,9] * 10
15
+ srv.input_registers = [0,1,2,3,4,5,6,7,8,9] * 10
16
+ srv.start
17
+
18
+
19
+ cl = RTUClient.new 'com4', BAUD
20
+
21
+
22
+ Benchmark.bmbm do |x|
23
+ x.report('Read coils') do
24
+ TIMES.times { cl.read_coils 0, 100 }
25
+ end
26
+
27
+ x.report('Read discrete inputs') do
28
+ TIMES.times { cl.read_discrete_inputs 0, 100 }
29
+ end
30
+
31
+ x.report('Read holding registers') do
32
+ TIMES.times { cl.read_holding_registers 0, 100 }
33
+ end
34
+
35
+ x.report('Read input registers') do
36
+ TIMES.times { cl.read_input_registers 0, 100 }
37
+ end
38
+
39
+ x.report('Write single coil') do
40
+ TIMES.times { cl.write_single_coil 0, 1 }
41
+ end
42
+
43
+ x.report('Write single register') do
44
+ TIMES.times { cl.write_single_register 100, 0xAAAA }
45
+ end
46
+
47
+ x.report('Write multiple coils') do
48
+ TIMES.times { cl.write_multiple_coils 0, [1,0] * 50 }
49
+ end
50
+
51
+
52
+ x.report('Write multiple registers') do
53
+ TIMES.times { cl.write_multiple_registers 0, [0,1,2,3,4,5,6,7,8,9] * 10 }
54
+ end
55
+ end
56
+
57
+ srv.stop
58
+ cl.close
@@ -0,0 +1,57 @@
1
+ $:.unshift File.join(File.dirname(__FILE__),'../lib')
2
+
3
+ require 'rmodbus'
4
+ require 'benchmark'
5
+
6
+ include ModBus
7
+
8
+ TIMES = 1000
9
+
10
+ srv = ModBus::TCPServer.new 1502
11
+ srv.coils = [0,1] * 50
12
+ srv.discret_inputs = [1,0] * 50
13
+ srv.holding_registers = [0,1,2,3,4,5,6,7,8,9] * 10
14
+ srv.input_registers = [0,1,2,3,4,5,6,7,8,9] * 10
15
+ srv.start
16
+
17
+
18
+ cl = TCPClient.new '127.0.0.1', 1502
19
+
20
+
21
+ Benchmark.bmbm do |x|
22
+ x.report('Read coils') do
23
+ TIMES.times { cl.read_coils 0, 100 }
24
+ end
25
+
26
+ x.report('Read discrete inputs') do
27
+ TIMES.times { cl.read_discrete_inputs 0, 100 }
28
+ end
29
+
30
+ x.report('Read holding registers') do
31
+ TIMES.times { cl.read_holding_registers 0, 100 }
32
+ end
33
+
34
+ x.report('Read input registers') do
35
+ TIMES.times { cl.read_input_registers 0, 100 }
36
+ end
37
+
38
+ x.report('Write single coil') do
39
+ TIMES.times { cl.write_single_coil 0, 1 }
40
+ end
41
+
42
+ x.report('Write single register') do
43
+ TIMES.times { cl.write_single_register 100, 0xAAAA }
44
+ end
45
+
46
+ x.report('Write multiple coils') do
47
+ TIMES.times { cl.write_multiple_coils 0, [1,0] * 50 }
48
+ end
49
+
50
+
51
+ x.report('Write multiple registers') do
52
+ TIMES.times { cl.write_multiple_registers 0, [0,1,2,3,4,5,6,7,8,9] * 10 }
53
+ end
54
+ end
55
+
56
+ srv.stop
57
+ cl.close
@@ -11,6 +11,7 @@ srv.audit = true
11
11
  srv.start
12
12
 
13
13
  cl = ModBus::TCPClient.new('127.0.0.1', 8502, 1)
14
+ cl.debug = true
14
15
  puts cl.read_holding_registers(0,4)
15
16
  cl.write_multiple_registers(0, [4,4,4])
16
17
  puts cl.read_holding_registers(0,4)
@@ -29,6 +29,7 @@ module ModBus
29
29
  @read_retries = 10
30
30
  end
31
31
  # Read value *ncoils* coils starting with *addr*
32
+ #
32
33
  # Return array of their values
33
34
  def read_coils(addr, ncoils)
34
35
  query("\x1" + addr.to_word + ncoils.to_word).unpack_bits[0..ncoils-1]
@@ -134,19 +135,17 @@ module ModBus
134
135
 
135
136
  tried = 0
136
137
  begin
137
- timeout(1, ModBusTimeout) do
138
- pdu = read_pdu
139
- end
138
+ timeout(1, ModBusTimeout) { pdu = read_pdu }
140
139
  rescue ModBusTimeout => err
141
140
  tried += 1
142
141
  retry unless tried >= @read_retries
143
142
  raise ModBusTimeout.new, "Timed out during read attempt"
144
- rescue
145
- raise ModBusException.new, "Server did not respond"
146
143
  end
147
144
 
148
- if pdu[0].to_i >= 0x80
149
- case pdu[1].to_i
145
+ return nil if pdu.size == 0
146
+
147
+ if pdu.getbyte(0) >= 0x80
148
+ case pdu.getbyte(1)
150
149
  when 1
151
150
  raise IllegalFunction.new, "The function code received in the query is not an allowable action for the server"
152
151
  when 2
@@ -178,6 +177,20 @@ module ModBus
178
177
  def close
179
178
  end
180
179
 
180
+ private
181
+ def logging_bytes(msg)
182
+ result = ""
183
+ msg.each_byte do |c|
184
+ byte = if c < 16
185
+ '0' + c.to_s(16)
186
+ else
187
+ c.to_s(16)
188
+ end
189
+ result << "[#{byte}]"
190
+ end
191
+ result
192
+ end
193
+
181
194
  end
182
195
 
183
196
  end
@@ -0,0 +1,69 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2010 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ module ModBus
16
+ module CRC16
17
+ def crc16(msg)
18
+ crc_lo = 0xff
19
+ crc_hi = 0xff
20
+
21
+ msg.unpack('c*').each do |byte|
22
+ i = crc_hi ^ byte
23
+ crc_hi = crc_lo ^ CrcHiTable[i]
24
+ crc_lo = CrcLoTable[i]
25
+ end
26
+
27
+ return ((crc_hi << 8) + crc_lo)
28
+ end
29
+
30
+ CrcHiTable = [
31
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
32
+ 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
33
+ 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
34
+ 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
35
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
36
+ 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
37
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
38
+ 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
39
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
40
+ 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
41
+ 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
42
+ 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
43
+ 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
44
+ 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
45
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
46
+ 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
47
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
48
+ 0x40]
49
+ CrcLoTable = [
50
+ 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
51
+ 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
52
+ 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
53
+ 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
54
+ 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
55
+ 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
56
+ 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
57
+ 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
58
+ 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
59
+ 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
60
+ 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
61
+ 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
62
+ 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
63
+ 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
64
+ 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
65
+ 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
66
+ 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
67
+ 0x40]
68
+ end
69
+ end
data/lib/rmodbus/ext.rb CHANGED
@@ -1,61 +1,65 @@
1
- # RModBus - free implementation of ModBus protocol on Ruby.
2
- #
3
- # Copyright (C) 2009 Timin Aleksey
4
- #
5
- # This program is free software: you can redistribute it and/or modify
6
- # it under the terms of the GNU General Public License as published by
7
- # the Free Software Foundation, either version 3 of the License, or
8
- # (at your option) any later version.
9
- #
10
- # This program is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- # GNU General Public License for more details.
14
-
15
- class String
16
-
17
- unless RUBY_VERSION.to_f == 1.9
18
- def getbyte(index)
19
- self[index].to_i
20
- end
21
- end
22
-
23
- def unpack_bits
24
- array_bit = []
25
- self.unpack('b*')[0].each_char do |b|
26
- array_bit << b.to_i
27
- end
28
- array_bit
29
- end
30
-
31
- end
32
-
33
- class Integer
34
-
35
- def to_word
36
- (self >> 8).chr + (self & 0xff).chr
37
- end
38
-
39
- end
40
-
41
- class Array
42
-
43
- def pack_to_word
44
- word = 0
45
- s = ""
46
- mask = 0x01
47
-
48
- self.each do |bit|
49
- word |= mask if bit > 0
50
- mask <<= 1
51
- if mask == 0x100
52
- mask = 0x01
53
- s << word.chr
54
- word = 0
55
- end
56
- end
57
- s << word.chr unless mask == 0x01
58
- end
59
-
60
- end
61
-
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 2009 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ class String
16
+
17
+ unless RUBY_VERSION =~ /^1\.9/
18
+ def getbyte(index)
19
+ self[index].to_i
20
+ end
21
+ end
22
+
23
+ def unpack_bits
24
+ array_bit = []
25
+ self.unpack('b*')[0].each_byte do |b|
26
+ array_bit << b.chr.to_i
27
+ end
28
+ array_bit
29
+ end
30
+
31
+ end
32
+
33
+ class Integer
34
+
35
+ def to_word
36
+ (self >> 8).chr + (self & 0xff).chr
37
+ end
38
+
39
+ end
40
+
41
+ class Array
42
+
43
+ def pack_to_word
44
+ word = 0
45
+ s = ""
46
+ mask = 0x01
47
+
48
+ self.each do |bit|
49
+ word |= mask if bit > 0
50
+ mask <<= 1
51
+ if mask == 0x100
52
+ mask = 0x01
53
+ s << word.chr
54
+ word = 0
55
+ end
56
+ end
57
+ unless mask == 0x01
58
+ s << word.chr
59
+ else
60
+ s
61
+ end
62
+ end
63
+
64
+ end
65
+