rmodbus 1.0.0-java

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.
@@ -0,0 +1,140 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2010 - 2011 Timin Aleksey
4
+ # Copyright (C) 2010 Kelley Reynolds
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ module ModBus
17
+ module RTU
18
+ private
19
+
20
+ # We have to read specific amounts of numbers of bytes from the network depending on the function code and content
21
+ def read_rtu_response(io)
22
+ # Read the slave_id and function code
23
+ msg = io.read(2)
24
+ function_code = msg.getbyte(1)
25
+ case function_code
26
+ when 1,2,3,4 then
27
+ # read the third byte to find out how much more
28
+ # we need to read + CRC
29
+ msg += io.read(1)
30
+ msg += io.read(msg.getbyte(2)+2)
31
+ when 5,6,15,16 then
32
+ # We just read in an additional 6 bytes
33
+ msg += io.read(6)
34
+ when 22 then
35
+ msg += io.read(8)
36
+ when 0x80..0xff then
37
+ msg += io.read(4)
38
+ else
39
+ raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
40
+ end
41
+ end
42
+
43
+ def read_rtu_request(io)
44
+ # Read the slave_id and function code
45
+ msg = io.read(2)
46
+
47
+ # If msg is nil, then our client never sent us anything and it's time to disconnect
48
+ return if msg.nil?
49
+
50
+ function_code = msg.getbyte(1)
51
+ if [1, 2, 3, 4, 5, 6].include?(function_code)
52
+ # read 6 more bytes and return the message total message
53
+ msg += io.read(6)
54
+ elsif [15, 16].include?(function_code)
55
+ # Read in first register, register count, and data bytes
56
+ msg += io.read(5)
57
+ # Read in however much data we need to + 2 CRC bytes
58
+ msg += io.read(msg.getbyte(6) + 2)
59
+ else
60
+ raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
61
+ end
62
+
63
+ log "Server RX (#{msg.size} bytes): #{logging_bytes(msg)}"
64
+
65
+ msg
66
+ end
67
+
68
+ def serv_rtu_requests(io, &blk)
69
+ loop do
70
+ # read the RTU message
71
+ msg = read_rtu_request(io)
72
+ # If there is no RTU message, we're done serving this client
73
+ break if msg.nil?
74
+
75
+ if msg.getbyte(0) == @uid and msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
76
+ pdu = yield msg
77
+ resp = @uid.chr + pdu
78
+ resp << crc16(resp).to_word
79
+ log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
80
+ io.write resp
81
+ end
82
+ end
83
+ end
84
+
85
+ # Calc CRC16 for massage
86
+ def crc16(msg)
87
+ crc_lo = 0xff
88
+ crc_hi = 0xff
89
+
90
+ msg.unpack('c*').each do |byte|
91
+ i = crc_hi ^ byte
92
+ crc_hi = crc_lo ^ CrcHiTable[i]
93
+ crc_lo = CrcLoTable[i]
94
+ end
95
+
96
+ return ((crc_hi << 8) + crc_lo)
97
+ end
98
+
99
+ CrcHiTable = [
100
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
101
+ 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
102
+ 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
103
+ 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
104
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
105
+ 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
106
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
107
+ 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
108
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
109
+ 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
110
+ 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
111
+ 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
112
+ 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
113
+ 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
114
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
115
+ 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
116
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
117
+ 0x40]
118
+ CrcLoTable = [
119
+ 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
120
+ 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
121
+ 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
122
+ 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
123
+ 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
124
+ 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
125
+ 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
126
+ 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
127
+ 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
128
+ 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
129
+ 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
130
+ 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
131
+ 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
132
+ 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
133
+ 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
134
+ 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
135
+ 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
136
+ 0x40]
137
+
138
+ end
139
+ end
140
+
@@ -0,0 +1,41 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2009-2011 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
+ module ModBus
15
+ # RTU client implementation
16
+ # @example
17
+ # RTUClient.connect('/dev/ttyS1', 9600) do |cl|
18
+ # cl.with_slave(uid) do |slave|
19
+ # slave.holding_registers[0..100]
20
+ # end
21
+ # end
22
+ #
23
+ # @see RTUClient#open_connection
24
+ # @see Client#initialize
25
+ class RTUClient < Client
26
+ include RTU
27
+ include SP
28
+
29
+ protected
30
+ # Open serial port
31
+ # @see SP#open_serial_port
32
+ def open_connection(port, baud=9600, opts = {})
33
+ open_serial_port(port, baud, opts)
34
+ end
35
+
36
+ # @private
37
+ def get_slave(uid, io)
38
+ RTUSlave.new(uid, io)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,61 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2010-2011 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
+ require 'serialport'
15
+
16
+ module ModBus
17
+ # RTU server implementation
18
+ # @example
19
+ # srv = RTUServer.new('/dev/ttyS1', 9600, 1)
20
+ # srv.coils = [1,0,1,1]
21
+ # srv.discrete_inputs = [1,1,0,0]
22
+ # srv.holding_registers = [1,2,3,4]
23
+ # srv.input_registers = [1,2,3,4]
24
+ # srv.debug = true
25
+ # srv.start
26
+ class RTUServer
27
+ include Common
28
+ include Server
29
+ include RTU
30
+ include SP
31
+
32
+ # Init RTU server
33
+ # @param [Integer] uid slave device
34
+ # @see SP#open_serial_port
35
+ def initialize(port, baud=9600, uid=1, opts = {})
36
+ Thread.abort_on_exception = true
37
+ @sp = open_serial_port(port, baud, opts)
38
+ @uid = uid
39
+ end
40
+
41
+ # Start server
42
+ def start
43
+ @serv = Thread.new do
44
+ serv_rtu_requests(@sp) do |msg|
45
+ exec_req(msg[1..-3], @coils, @discrete_inputs, @holding_registers, @input_registers)
46
+ end
47
+ end
48
+ end
49
+
50
+ # Stop server
51
+ def stop
52
+ Thread.kill(@serv)
53
+ @sp.close
54
+ end
55
+
56
+ # Join server
57
+ def join
58
+ @serv.join
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,59 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2011 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
+ module ModBus
15
+ # RTU slave implementation
16
+ # @example
17
+ # RTUClient.connect(port, baud, opts) do |cl|
18
+ # cl.with_slave(uid) do |slave|
19
+ # slave.holding_registers[0..100]
20
+ # end
21
+ # end
22
+ #
23
+ # @see RTUClient#open_connection
24
+ # @see Client#with_slave
25
+ # @see Slave
26
+ class RTUSlave < Slave
27
+ include RTU
28
+
29
+ protected
30
+
31
+ # overide method for RTU implamentaion
32
+ # @see Slave#query
33
+ def send_pdu(pdu)
34
+ msg = @uid.chr + pdu
35
+ msg << crc16(msg).to_word
36
+ @io.write msg
37
+
38
+ log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
39
+ end
40
+
41
+ # overide method for RTU implamentaion
42
+ # @see Slave#query
43
+ def read_pdu
44
+ msg = read_rtu_response(@io)
45
+
46
+ log "Rx (#{msg.size} bytes): " + logging_bytes(msg)
47
+
48
+ if msg.getbyte(0) == @uid
49
+ return msg[1..-3] if msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
50
+ log "Ignore package: don't match CRC"
51
+ else
52
+ log "Ignore package: don't match uid ID"
53
+ end
54
+ loop do
55
+ #waite timeout
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,41 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2009-2011 Timin Aleksey
4
+ # Copyright (C) 2010 Kelley Reynolds
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ module ModBus
16
+ # RTU over TCP client implementation
17
+ # @example
18
+ # RTUViaTCPClient.connect('127.0.0.1', 10002) do |cl|
19
+ # cl.with_slave(uid) do |slave|
20
+ # slave.holding_registers[0..100]
21
+ # end
22
+ # end
23
+ #
24
+ # @see RTUViaTCPClient#open_connection
25
+ # @see Client#initialize
26
+ class RTUViaTCPClient < Client
27
+ include RTU
28
+ include TCP
29
+
30
+ protected
31
+ # Open TCP\IP connection
32
+ # @see TCP#open_tcp_connection
33
+ def open_connection(ipaddr, port = 10002, opts = {})
34
+ io = open_tcp_connection(ipaddr, port, opts)
35
+ end
36
+
37
+ def get_slave(uid, io)
38
+ RTUViaTCPSlave.new(uid, io)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2010 Timin Aleksey
4
+ # Copyright (C) 2010 Kelley Reynolds
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ require 'gserver'
17
+
18
+ module ModBus
19
+ # RTU over TCP server implementation
20
+ # @example
21
+ # srv = RTUViaTCPServer.new(10002, 1)
22
+ # srv.coils = [1,0,1,1]
23
+ # srv.discrete_inputs = [1,1,0,0]
24
+ # srv.holding_registers = [1,2,3,4]
25
+ # srv.input_registers = [1,2,3,4]
26
+ # srv.debug = true
27
+ # srv.start
28
+ class RTUViaTCPServer < GServer
29
+ include Common
30
+ include RTU
31
+ include Server
32
+
33
+ # Init server
34
+ # @param [Integer] port listen port
35
+ # @param [Integer] uid slave device
36
+ def initialize(port = 10002, uid = 1)
37
+ @uid = uid
38
+ super(port)
39
+ end
40
+
41
+ protected
42
+ # Serve requests
43
+ # @param [TCPSocket] io socket
44
+ def serve(io)
45
+ serv_rtu_requests(io) do |msg|
46
+ exec_req(msg[1..-3], @coils, @discrete_inputs, @holding_registers, @input_registers)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,58 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2011 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
+ module ModBus
15
+ # RTU over TCP slave implementation
16
+ # @example
17
+ # RTUViaTCP.connect('127.0.0.1', 10002) do |cl|
18
+ # cl.with_slave(uid) do |slave|
19
+ # slave.holding_registers[0..100]
20
+ # end
21
+ # end
22
+ #
23
+ # @see RTUViaTCPClient#open_connection
24
+ # @see Client#with_slave
25
+ # @see Slave
26
+ class RTUViaTCPSlave < Slave
27
+ include RTU
28
+
29
+ protected
30
+ # overide method for RTU over TCP implamentaion
31
+ # @see Slave#query
32
+ def send_pdu(pdu)
33
+ msg = @uid.chr + pdu
34
+ msg << crc16(msg).to_word
35
+ @io.write msg
36
+
37
+ log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
38
+ end
39
+
40
+ # overide method for RTU over TCP implamentaion
41
+ # @see Slave#query
42
+ def read_pdu
43
+ # Read the response appropriately
44
+ msg = read_rtu_response(@io)
45
+
46
+ log "Rx (#{msg.size} bytes): " + logging_bytes(msg)
47
+ if msg.getbyte(0) == @uid
48
+ return msg[1..-3] if msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
49
+ log "Ignore package: don't match CRC"
50
+ else
51
+ log "Ignore package: don't match uid ID"
52
+ end
53
+ loop do
54
+ #waite timeout
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,142 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2010 Timin Aleksey
4
+ # Copyright (C) 2010 Kelley Reynolds
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ module ModBus
17
+ # Module for implementation ModBus server
18
+ module Server
19
+ Funcs = [1,2,3,4,5,6,15,16]
20
+
21
+ attr_accessor :coils, :discrete_inputs, :holding_registers, :input_registers, :uid
22
+ @coils = []
23
+ @discrete_inputs = []
24
+ @holding_registers =[]
25
+ @input_registers = []
26
+
27
+ private
28
+
29
+ def exec_req(req, coils, discrete_inputs, holding_registers, input_registers)
30
+ func = req.getbyte(0)
31
+
32
+ unless Funcs.include?(func)
33
+ params = { :err => 1 }
34
+ end
35
+
36
+ case func
37
+ when 1
38
+ params = parse_read_func(req, coils)
39
+ if params[:err] == 0
40
+ val = coils[params[:addr],params[:quant]].pack_to_word
41
+ pdu = func.chr + val.size.chr + val
42
+ end
43
+ when 2
44
+ params = parse_read_func(req, discrete_inputs)
45
+ if params[:err] == 0
46
+ val = discrete_inputs[params[:addr],params[:quant]].pack_to_word
47
+ pdu = func.chr + val.size.chr + val
48
+ end
49
+ when 3
50
+ params = parse_read_func(req, holding_registers)
51
+ if params[:err] == 0
52
+ pdu = func.chr + (params[:quant] * 2).chr + holding_registers[params[:addr],params[:quant]].pack('n*')
53
+ end
54
+ when 4
55
+ params = parse_read_func(req, input_registers)
56
+ if params[:err] == 0
57
+ pdu = func.chr + (params[:quant] * 2).chr + input_registers[params[:addr],params[:quant]].pack('n*')
58
+ end
59
+ when 5
60
+ params = parse_write_coil_func(req)
61
+ if params[:err] == 0
62
+ coils[params[:addr]] = params[:val]
63
+ pdu = req
64
+ end
65
+ when 6
66
+ params = parse_write_register_func(req)
67
+ if params[:err] == 0
68
+ holding_registers[params[:addr]] = params[:val]
69
+ pdu = req
70
+ end
71
+ when 15
72
+ params = parse_write_multiple_coils_func(req)
73
+ if params[:err] == 0
74
+ coils[params[:addr],params[:quant]] = params[:val][0,params[:quant]]
75
+ pdu = req[0,5]
76
+ end
77
+ when 16
78
+ params = parse_write_multiple_registers_func(req)
79
+ if params[:err] == 0
80
+ holding_registers[params[:addr],params[:quant]] = params[:val][0,params[:quant]]
81
+ pdu = req[0,5]
82
+ end
83
+ end
84
+
85
+ if params[:err] == 0
86
+ pdu
87
+ else
88
+ pdu = (func | 0x80).chr + params[:err].chr
89
+ end
90
+ end
91
+
92
+ def parse_read_func(req, field)
93
+ quant = req[3,2].unpack('n')[0]
94
+
95
+ return { :err => 3} unless quant <= 0x7d
96
+
97
+ addr = req[1,2].unpack('n')[0]
98
+ return { :err => 2 } unless addr + quant <= field.size
99
+
100
+ return { :err => 0, :quant => quant, :addr => addr }
101
+ end
102
+
103
+ def parse_write_coil_func(req)
104
+ addr = req[1,2].unpack('n')[0]
105
+ return { :err => 2 } unless addr <= @coils.size
106
+
107
+ val = req[3,2].unpack('n')[0]
108
+ return { :err => 3 } unless val == 0 or val == 0xff00
109
+
110
+ val = 1 if val == 0xff00
111
+ return { :err => 0, :addr => addr, :val => val }
112
+ end
113
+
114
+ def parse_write_register_func(req)
115
+ addr = req[1,2].unpack('n')[0]
116
+ return { :err => 2 } unless addr <= @coils.size
117
+
118
+ val = req[3,2].unpack('n')[0]
119
+
120
+ return { :err => 0, :addr => addr, :val => val }
121
+ end
122
+
123
+ def parse_write_multiple_coils_func(req)
124
+ params = parse_read_func(req, @coils)
125
+
126
+ if params[:err] == 0
127
+ params = {:err => 0, :addr => params[:addr], :quant => params[:quant], :val => req[6,params[:quant]].unpack_bits }
128
+ end
129
+ params
130
+ end
131
+
132
+ def parse_write_multiple_registers_func(req)
133
+ params = parse_read_func(req, @holding_registers)
134
+
135
+ if params[:err] == 0
136
+ params = {:err => 0, :addr => params[:addr], :quant => params[:quant], :val => req[6,params[:quant] * 2].unpack('n*')}
137
+ end
138
+ params
139
+ end
140
+
141
+ end
142
+ end