rmodbus 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+