libmodbus4r 0.3.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +24 -41
- data/examples/use_tcp_master.rb +17 -11
- data/ext/extconf.rb +2 -7
- data/lib/modbus4r.so +0 -0
- data/spec/tcp_master_spec.rb +27 -36
- metadata +6 -33
- data/NEWS +0 -18
- data/examples/use_rtu_master.rb +0 -43
- data/examples/use_rtu_slave.rb +0 -24
- data/examples/use_tcp_slave.rb +0 -21
- data/ext/errors.c +0 -81
- data/ext/errors.h +0 -24
- data/ext/master.c +0 -215
- data/ext/master.h +0 -37
- data/ext/modbus.c +0 -1959
- data/ext/modbus.h +0 -347
- data/ext/modbus4r.c +0 -115
- data/ext/modbus4r.h +0 -26
- data/ext/rtu_master.c +0 -36
- data/ext/rtu_master.h +0 -21
- data/ext/rtu_slave.c +0 -131
- data/ext/rtu_slave.h +0 -23
- data/ext/slave.c +0 -215
- data/ext/slave.h +0 -44
- data/ext/tcp_master.c +0 -29
- data/ext/tcp_master.h +0 -20
- data/ext/tcp_slave.c +0 -134
- data/ext/tcp_slave.h +0 -21
- data/lib/modbus4r.rb +0 -15
- data/spec/rtu_master_spec.rb +0 -21
- data/spec/rtu_slave_spec.rb +0 -16
- data/spec/tcp_slave_spec.rb +0 -95
data/README
CHANGED
@@ -1,46 +1,29 @@
|
|
1
|
-
== About
|
1
|
+
== About
|
2
2
|
|
3
|
-
|
3
|
+
Binding use *libmodbus* (free implementation of modbus for Linux\MacOS) for Ruby.
|
4
4
|
|
5
|
-
==
|
5
|
+
== Futures
|
6
6
|
|
7
|
-
* Use
|
8
|
-
*
|
9
|
-
* ModBus TCP and RTU master(client);
|
10
|
-
* ModBus TCP and RTU slave(server);
|
7
|
+
* Use C library *libmodbus*;
|
8
|
+
* ModBus TCP master(client);
|
11
9
|
* Support functions:
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
==
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
==
|
31
|
-
|
10
|
+
* * read coil status (0x01)
|
11
|
+
* * read input status (0x02)
|
12
|
+
* * read holding registers (0x03)
|
13
|
+
* * read input registers (0x04)
|
14
|
+
* * force single coil (0x05)
|
15
|
+
* * preset single register (0x06)
|
16
|
+
* * force multiple coils (0x0F)
|
17
|
+
* * preset multiple registers (0x10)
|
18
|
+
|
19
|
+
== Install
|
20
|
+
|
21
|
+
1. Install last release ruby 1.9.1 from http://ruby-lang.org
|
22
|
+
2. Install libmodbus-2.0.3 from https://launchpad.net/libmodbus
|
23
|
+
3. Install libmodbus4r
|
24
|
+
*$ sudo rake install*
|
25
|
+
or
|
26
|
+
*$ gem install flipback-libmodbus4r --source http://gems.github.com
|
27
|
+
|
28
|
+
== AUTHORS
|
32
29
|
Aleksey Timin <atimin@gmail.com>
|
33
|
-
|
34
|
-
== References
|
35
|
-
|
36
|
-
Home page projects : http://flipback.github.com/libmodbus4r/
|
37
|
-
|
38
|
-
Hosting on RubyForge : http://rubyforge.org/projects/libmodbus4r
|
39
|
-
|
40
|
-
Hosting on GitHub : http://github.com/flipback/libmodbus4r/tree/master
|
41
|
-
|
42
|
-
Ruby site : http://ruby-lang.org
|
43
|
-
|
44
|
-
libmobus on Launchpad : https://launchpad.net/libmodbus
|
45
|
-
|
46
|
-
ModBus community: http://www.modbus-ida.org
|
data/examples/use_tcp_master.rb
CHANGED
@@ -5,36 +5,42 @@ end
|
|
5
5
|
|
6
6
|
require 'modbus4r'
|
7
7
|
|
8
|
-
mstr = ModBus::TCPMaster.new('127.0.0.1',
|
9
|
-
1502, #port
|
10
|
-
1) #slave id
|
8
|
+
mstr = ModBus::TCPMaster.new('127.0.0.1', 1502)
|
11
9
|
mstr.connect
|
12
10
|
# Reads the coils status in the slave
|
13
|
-
puts mstr.read_coil_status(
|
11
|
+
puts mstr.read_coil_status(1, #slave id
|
12
|
+
0, #start address
|
14
13
|
10) #number
|
15
14
|
# Reads the input status in the slave
|
16
|
-
puts mstr.read_input_status(
|
15
|
+
puts mstr.read_input_status(1, #slave id
|
16
|
+
0, #start address
|
17
17
|
10) #number
|
18
18
|
# Reads the holding registers in the slave
|
19
|
-
puts mstr.read_holding_registers(
|
19
|
+
puts mstr.read_holding_registers(1, #slave id
|
20
|
+
0, #start address
|
20
21
|
5) #number
|
21
22
|
# Reads the input registers in the slave
|
22
|
-
puts mstr.read_input_registers(
|
23
|
+
puts mstr.read_input_registers(1, #slave id
|
24
|
+
0, #start address
|
23
25
|
5) #number
|
24
26
|
# Turns a single coil in the slave
|
25
|
-
mstr.force_single_coil(
|
27
|
+
mstr.force_single_coil(1, #slave id
|
28
|
+
0, #coil address
|
26
29
|
true) #state
|
27
30
|
|
28
31
|
# Sets a value in one holding register in the slave
|
29
|
-
mstr.preset_single_register(
|
32
|
+
mstr.preset_single_register(1, #slave id
|
33
|
+
0, #reg address
|
30
34
|
0xa0a0) #value
|
31
35
|
|
32
36
|
# Sets\reset the coils in the slave
|
33
|
-
mstr.force_multiple_coils(
|
37
|
+
mstr.force_multiple_coils(1, #slave id
|
38
|
+
0, #start address
|
34
39
|
[true, false, true]) #data
|
35
40
|
|
36
41
|
#Copies the values in th slave
|
37
|
-
mstr.preset_multiple_registers(
|
42
|
+
mstr.preset_multiple_registers(1, #slave id
|
43
|
+
0, #start address
|
38
44
|
[1, 0xdf, 2]) #data
|
39
45
|
|
40
46
|
mstr.close
|
data/ext/extconf.rb
CHANGED
data/lib/modbus4r.so
ADDED
Binary file
|
data/spec/tcp_master_spec.rb
CHANGED
@@ -1,23 +1,15 @@
|
|
1
1
|
require 'modbus4r'
|
2
|
+
require 'socket'
|
2
3
|
|
3
4
|
describe ModBus::TCPMaster do
|
4
5
|
|
5
6
|
before(:all) do
|
6
|
-
@
|
7
|
-
@sl.coil_status = [false, false, false, false,
|
8
|
-
false, false, false, false]
|
9
|
-
@sl.input_status = [false, false, false, false,
|
10
|
-
false, false, false, false]
|
11
|
-
@sl.holding_registers = [0, 0, 0, 0, 0, 0, 0, 0, 0]
|
12
|
-
@sl.input_registers = [0, 0, 0, 0, 0, 0, 0, 0, 0]
|
13
|
-
@sl.start
|
14
|
-
|
15
|
-
@mstr = ModBus::TCPMaster.new('127.0.0.1', 1502, 1)
|
7
|
+
@mstr = ModBus::TCPMaster.new('127.0.0.1', 1502)
|
16
8
|
@mstr.connect
|
17
9
|
end
|
18
10
|
|
19
11
|
it "should raise exception if have'n connected with slave" do
|
20
|
-
mstr = ModBus::TCPMaster.new('127.0.0.1', 1503
|
12
|
+
mstr = ModBus::TCPMaster.new('127.0.0.1', 1503)
|
21
13
|
mstr.closed?.should == true
|
22
14
|
lambda { mstr.connect }.should raise_error(ModBus::Errors::ModBusError, "Connection refused")
|
23
15
|
end
|
@@ -28,102 +20,101 @@ describe ModBus::TCPMaster do
|
|
28
20
|
|
29
21
|
# Read coil status
|
30
22
|
it "should read coil status" do
|
31
|
-
@mstr.read_coil_status(0, 4).should == [false, false, false, false]
|
23
|
+
@mstr.read_coil_status(0x11, 0, 4).should == [false, false, false, false]
|
32
24
|
end
|
33
25
|
|
34
26
|
it "should raise exception if illegal data address" do
|
35
|
-
lambda { @mstr.read_coil_status(0, 501) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
27
|
+
lambda { @mstr.read_coil_status(0x11, 0, 501) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
36
28
|
end
|
37
29
|
|
38
30
|
|
39
31
|
it "should raise exception if too many data" do
|
40
|
-
lambda { @mstr.read_coil_status(0, 0x07D1) }.should raise_error(ModBus::Errors::ModBusError, "
|
32
|
+
lambda { @mstr.read_coil_status(0x11, 0, 0x07D1) }.should raise_error(ModBus::Errors::ModBusError, "Too many data (-15)")
|
41
33
|
end
|
42
34
|
|
43
35
|
# Read input status
|
44
36
|
it "should read discrete inputs" do
|
45
|
-
@mstr.read_input_status(0, 4).should == [false, false, false, false]
|
37
|
+
@mstr.read_input_status(0x11, 0, 4).should == [false, false, false, false]
|
46
38
|
end
|
47
39
|
|
48
40
|
it "should raise exception if illegal data address" do
|
49
|
-
lambda { @mstr.read_input_status(0, 501) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
41
|
+
lambda { @mstr.read_input_status(0x11, 0, 501) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
50
42
|
end
|
51
43
|
|
52
44
|
|
53
45
|
it "should raise exception if too many data" do
|
54
|
-
lambda { @mstr.read_input_status(0, 0x07D1) }.should raise_error(ModBus::Errors::ModBusError, "
|
46
|
+
lambda { @mstr.read_input_status(0x11, 0, 0x07D1) }.should raise_error(ModBus::Errors::ModBusError, "Too many data (-15)")
|
55
47
|
end
|
56
48
|
|
57
49
|
# Read holding registers
|
58
50
|
it "should read discrete inputs" do
|
59
|
-
@mstr.read_holding_registers(0, 4).should == [0, 0, 0, 0]
|
51
|
+
@mstr.read_holding_registers(0x11, 0, 4).should == [0, 0, 0, 0]
|
60
52
|
end
|
61
53
|
|
62
54
|
it "should raise exception if illegal data address" do
|
63
|
-
lambda { @mstr.read_holding_registers(402, 99) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
55
|
+
lambda { @mstr.read_holding_registers(0x11, 402, 99) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
64
56
|
end
|
65
57
|
|
66
58
|
|
67
59
|
it "should raise exception if too many data" do
|
68
|
-
lambda { @mstr.read_holding_registers(0, 0x007E) }.should raise_error(ModBus::Errors::ModBusError, "
|
60
|
+
lambda { @mstr.read_holding_registers(0x11, 0, 0x007E) }.should raise_error(ModBus::Errors::ModBusError, "Too many data (-15)")
|
69
61
|
end
|
70
62
|
|
71
63
|
# Read input registers
|
72
64
|
it "should read discrete inputs" do
|
73
|
-
@mstr.read_input_registers(0, 4).should == [0, 0, 0, 0]
|
65
|
+
@mstr.read_input_registers(0x11, 0, 4).should == [0, 0, 0, 0]
|
74
66
|
end
|
75
67
|
|
76
68
|
it "should raise exception if illegal data address" do
|
77
|
-
lambda { @mstr.read_input_registers(402, 99) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
69
|
+
lambda { @mstr.read_input_registers(0x11, 402, 99) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
78
70
|
end
|
79
71
|
|
80
72
|
it "should raise exception if too many data" do
|
81
|
-
lambda { @mstr.read_input_registers(0, 0x007E) }.should raise_error(ModBus::Errors::ModBusError, "
|
73
|
+
lambda { @mstr.read_input_registers(0x11, 0, 0x007E) }.should raise_error(ModBus::Errors::ModBusError, "Too many data (-15)")
|
82
74
|
end
|
83
75
|
|
84
76
|
# Force single coil
|
85
77
|
it "should force single coil" do
|
86
|
-
@mstr.force_single_coil(4, true).should == @mstr
|
87
|
-
@mstr.read_coil_status(4, 4).should == [true, false, false, false]
|
78
|
+
@mstr.force_single_coil(0x11, 4, true).should == @mstr
|
79
|
+
@mstr.read_coil_status(0x11, 4, 4).should == [true, false, false, false]
|
88
80
|
end
|
89
81
|
|
90
82
|
it "should raise exception if illegal data address" do
|
91
|
-
lambda { @mstr.force_single_coil(501, true) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
83
|
+
lambda { @mstr.force_single_coil(0x11, 501, true) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
92
84
|
end
|
93
85
|
|
94
86
|
# Preset single register
|
95
87
|
it "should preset single register" do
|
96
|
-
@mstr.preset_single_register(4, 0x0AA0).should == @mstr
|
97
|
-
@mstr.read_holding_registers(4, 1).should == [0x0AA0]
|
88
|
+
@mstr.preset_single_register(0x11, 4, 0x0AA0).should == @mstr
|
89
|
+
@mstr.read_holding_registers(0x11, 4, 1).should == [0x0AA0]
|
98
90
|
end
|
99
91
|
|
100
92
|
it "should raise exception if illegal data address" do
|
101
|
-
lambda { @mstr.preset_single_register(501, 0x0AA0) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
93
|
+
lambda { @mstr.preset_single_register(0x11, 501, 0x0AA0) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
102
94
|
end
|
103
95
|
|
104
96
|
# Force multiple coils
|
105
97
|
it "should force multiple coils" do
|
106
|
-
@mstr.force_multiple_coils(
|
107
|
-
@mstr.read_coil_status(
|
98
|
+
@mstr.force_multiple_coils(0x11, 14, [false, true, false, true]).should == @mstr
|
99
|
+
@mstr.read_coil_status(0x11, 13, 5).should == [false, false, true, false, true]
|
108
100
|
end
|
109
101
|
|
110
102
|
it "should raise exception if illegal data address" do
|
111
|
-
lambda { @mstr.force_multiple_coils(501, [true, false]) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
103
|
+
lambda { @mstr.force_multiple_coils(0x11, 501, [true, false]) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
112
104
|
end
|
113
105
|
|
114
106
|
# Preset multiple registers
|
115
107
|
it "should preset multiple registers" do
|
116
|
-
@mstr.preset_multiple_registers(
|
117
|
-
@mstr.read_holding_registers(
|
108
|
+
@mstr.preset_multiple_registers(0x11, 14, [1, 2, 3, 0xAACC]).should == @mstr
|
109
|
+
@mstr.read_holding_registers(0x11, 13, 5).should == [0, 1, 2, 3, 0xAACC]
|
118
110
|
end
|
119
111
|
|
120
112
|
it "should raise exception if illegal data address" do
|
121
|
-
lambda { @mstr.preset_multiple_registers(501, [1, 2]) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
113
|
+
lambda { @mstr.preset_multiple_registers(0x11, 501, [1, 2]) }.should raise_error(ModBus::Errors::IllegalDataAddress, "Illegal data address (-2)")
|
122
114
|
end
|
123
115
|
|
124
116
|
after(:all) do
|
125
117
|
@mstr.close unless @mstr.closed?
|
126
|
-
@sl.stop unless @sl.stoped?
|
127
118
|
end
|
128
119
|
|
129
120
|
end
|
metadata
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: libmodbus4r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- A.Timin
|
8
8
|
autorequire: libmodbus4r
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-07-30 00:00:00 +06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description:
|
17
|
-
email:
|
17
|
+
email:
|
18
18
|
executables: []
|
19
19
|
|
20
20
|
extensions:
|
@@ -22,39 +22,12 @@ extensions:
|
|
22
22
|
extra_rdoc_files:
|
23
23
|
- README
|
24
24
|
- LICENSE
|
25
|
-
- NEWS
|
26
25
|
files:
|
27
|
-
- lib/modbus4r.
|
28
|
-
- examples/use_rtu_slave.rb
|
29
|
-
- examples/use_tcp_slave.rb
|
30
|
-
- examples/use_rtu_master.rb
|
26
|
+
- lib/modbus4r.so
|
31
27
|
- examples/use_tcp_master.rb
|
32
|
-
- spec/rtu_master_spec.rb
|
33
28
|
- spec/tcp_master_spec.rb
|
34
|
-
- spec/rtu_slave_spec.rb
|
35
|
-
- spec/tcp_slave_spec.rb
|
36
|
-
- ext/errors.h
|
37
|
-
- ext/slave.c
|
38
|
-
- ext/modbus.h
|
39
|
-
- ext/master.h
|
40
|
-
- ext/modbus4r.h
|
41
|
-
- ext/rtu_master.c
|
42
|
-
- ext/extconf.rb
|
43
|
-
- ext/slave.h
|
44
|
-
- ext/rtu_master.h
|
45
|
-
- ext/errors.c
|
46
|
-
- ext/master.c
|
47
|
-
- ext/rtu_slave.c
|
48
|
-
- ext/rtu_slave.h
|
49
|
-
- ext/tcp_master.h
|
50
|
-
- ext/tcp_slave.c
|
51
|
-
- ext/tcp_slave.h
|
52
|
-
- ext/modbus.c
|
53
|
-
- ext/tcp_master.c
|
54
|
-
- ext/modbus4r.c
|
55
29
|
- README
|
56
30
|
- LICENSE
|
57
|
-
- NEWS
|
58
31
|
has_rdoc: true
|
59
32
|
homepage:
|
60
33
|
licenses: []
|
@@ -83,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
56
|
requirements: []
|
84
57
|
|
85
58
|
rubyforge_project:
|
86
|
-
rubygems_version: 1.3.
|
59
|
+
rubygems_version: 1.3.4
|
87
60
|
signing_key:
|
88
61
|
specification_version: 3
|
89
62
|
summary: Binding use *libmodbus* (free implementation of modbus for Linux\MacOS) for Ruby.
|
data/NEWS
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
libmodbus4r 0.3.0 (2009-09-21)
|
2
|
-
===========================
|
3
|
-
- Added support slave for TCP and RTU protocol
|
4
|
-
- Migrated on libmodbus 0.2.2
|
5
|
-
- Added source of libmodbus in project
|
6
|
-
- Compatibility with ruby-1.8.7
|
7
|
-
|
8
|
-
libmodbus4r 0.2.2 (2009-08-16)
|
9
|
-
===========================
|
10
|
-
- Fixed bug in Master::read_input_registers
|
11
|
-
|
12
|
-
libmodbus4r 0.2.1 (2009-08-10)
|
13
|
-
===========================
|
14
|
-
- Fixed bug in Master::read_input_coils
|
15
|
-
|
16
|
-
libmodbus4r 0.2.0 (2009-07-30)
|
17
|
-
===========================
|
18
|
-
- First public release
|
data/examples/use_rtu_master.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'rubygems'
|
3
|
-
rescue
|
4
|
-
end
|
5
|
-
|
6
|
-
require 'modbus4r'
|
7
|
-
|
8
|
-
mstr = ModBus::RTUMaster.new('/dev/ttyS0', # device
|
9
|
-
9600, # baud: 9600, 19200, etc
|
10
|
-
"none", # parity: "even", "odd", "none"
|
11
|
-
8, # data bits: 5, 6, 7, 8
|
12
|
-
1, # stop bits: 1, 2
|
13
|
-
1) # slave id
|
14
|
-
mstr.connect
|
15
|
-
# Reads the coils status in the slave
|
16
|
-
puts mstr.read_coil_status(0, #start address
|
17
|
-
10) #number
|
18
|
-
# Reads the input status in the slave
|
19
|
-
puts mstr.read_input_status(0, #start address
|
20
|
-
10) #number
|
21
|
-
# Reads the holding registers in the slave
|
22
|
-
puts mstr.read_holding_registers(0, #start address
|
23
|
-
5) #number
|
24
|
-
# Reads the input registers in the slave
|
25
|
-
puts mstr.read_input_registers(0, #start address
|
26
|
-
5) #number
|
27
|
-
# Turns a single coil in the slave
|
28
|
-
mstr.force_single_coil(0, #coil address
|
29
|
-
true) #state
|
30
|
-
|
31
|
-
# Sets a value in one holding register in the slave
|
32
|
-
mstr.preset_single_register(0, #reg address
|
33
|
-
0xa0a0) #value
|
34
|
-
|
35
|
-
# Sets\reset the coils in the slave
|
36
|
-
mstr.force_multiple_coils(0, #start address
|
37
|
-
[true, false, true]) #data
|
38
|
-
|
39
|
-
#Copies the values in th slave
|
40
|
-
mstr.preset_multiple_registers(0, #start address
|
41
|
-
[1, 0xdf, 2]) #data
|
42
|
-
|
43
|
-
mstr.close
|
data/examples/use_rtu_slave.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'rubygems'
|
3
|
-
rescue
|
4
|
-
end
|
5
|
-
|
6
|
-
require 'modbus4r'
|
7
|
-
|
8
|
-
sl = ModBus::RTUSlave.new('/dev/ttyS0', # device
|
9
|
-
9600, # baud: 9600, 19200, etc
|
10
|
-
"none", # parity: "even", "odd", "none"
|
11
|
-
8, # data bits: 5, 6, 7, 8
|
12
|
-
1, # stop bits: 1, 2
|
13
|
-
1) # slave id
|
14
|
-
|
15
|
-
sl.coil_status = [false, false, false, false, false]
|
16
|
-
sl.input_status = [false, false, false, false, false]
|
17
|
-
sl.holding_registers = [0, 0, 0, 0, 0, 0, 0, 0]
|
18
|
-
sl.input_registers = [0, 0, 0, 0, 0, 0, 0, 0]
|
19
|
-
|
20
|
-
sl.start
|
21
|
-
|
22
|
-
sl.join
|
23
|
-
|
24
|
-
sl.stop
|