rtuclient.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Copyright 2014 Quoc-Viet Nguyen. All rights reserved.
  2. // This software may be modified and distributed under the terms
  3. // of the BSD license. See the LICENSE file for details.
  4. package modbus
  5. import (
  6. "encoding/binary"
  7. "fmt"
  8. "io"
  9. "time"
  10. )
  11. const (
  12. rtuMinSize = 4
  13. rtuMaxSize = 256
  14. rtuExceptionSize = 5
  15. )
  16. // RTUClientHandler implements Packager and Transporter interface.
  17. type RTUClientHandler struct {
  18. rtuPackager
  19. rtuSerialTransporter
  20. }
  21. // NewRTUClientHandler allocates and initializes a RTUClientHandler.
  22. func NewRTUClientHandler(address string) *RTUClientHandler {
  23. handler := &RTUClientHandler{}
  24. handler.Address = address
  25. handler.Timeout = serialTimeout
  26. handler.IdleTimeout = serialIdleTimeout
  27. return handler
  28. }
  29. // RTUClient creates RTU client with default handler and given connect string.
  30. func RTUClient(address string) Client {
  31. handler := NewRTUClientHandler(address)
  32. return NewClient(handler)
  33. }
  34. // rtuPackager implements Packager interface.
  35. type rtuPackager struct {
  36. SlaveId byte
  37. }
  38. // Encode encodes PDU in a RTU frame:
  39. //
  40. // SlaveConfig Address : 1 byte
  41. // Function : 1 byte
  42. // Data : 0 up to 252 bytes
  43. // CRC : 2 byte
  44. func (mb *rtuPackager) Encode(pdu *ProtocolDataUnit) (adu []byte, err error) {
  45. length := len(pdu.Data) + 4
  46. if length > rtuMaxSize {
  47. err = fmt.Errorf("modbus: length of data '%v' must not be bigger than '%v'", length, rtuMaxSize)
  48. return
  49. }
  50. adu = make([]byte, length)
  51. adu[0] = mb.SlaveId
  52. adu[1] = pdu.FunctionCode
  53. copy(adu[2:], pdu.Data)
  54. // Append crc
  55. var crc crc
  56. crc.reset().pushBytes(adu[0 : length-2])
  57. checksum := crc.value()
  58. adu[length-1] = byte(checksum >> 8)
  59. adu[length-2] = byte(checksum)
  60. return
  61. }
  62. // Verify verifies response length and slave id.
  63. func (mb *rtuPackager) Verify(aduRequest []byte, aduResponse []byte) (err error) {
  64. length := len(aduResponse)
  65. // Minimum size (including address, function and CRC)
  66. if length < rtuMinSize {
  67. err = fmt.Errorf("modbus: response length '%v' does not meet minimum '%v'", length, rtuMinSize)
  68. return
  69. }
  70. // SlaveConfig address must match
  71. if aduResponse[0] != aduRequest[0] {
  72. err = fmt.Errorf("modbus: response slave id '%v' does not match request '%v'", aduResponse[0], aduRequest[0])
  73. return
  74. }
  75. return
  76. }
  77. // Decode extracts PDU from RTU frame and verify CRC.
  78. func (mb *rtuPackager) Decode(adu []byte) (pdu *ProtocolDataUnit, err error) {
  79. length := len(adu)
  80. // Calculate checksum
  81. var crc crc
  82. crc.reset().pushBytes(adu[0 : length-2])
  83. checksum := uint16(adu[length-1])<<8 | uint16(adu[length-2])
  84. if checksum != crc.value() {
  85. fmt.Println("Decode Err:", adu, ";length=", length)
  86. err = fmt.Errorf("modbus-rtu: response crc '%v' does not match expected '%v'", checksum, crc.value())
  87. return
  88. }
  89. // Function code & data
  90. pdu = &ProtocolDataUnit{}
  91. pdu.FunctionCode = adu[1]
  92. pdu.Data = adu[2 : length-2]
  93. return
  94. }
  95. // rtuSerialTransporter implements Transporter interface.
  96. type rtuSerialTransporter struct {
  97. serialPort
  98. }
  99. func (mb *rtuSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) {
  100. // Make sure port is connected
  101. if err = mb.serialPort.connect(); err != nil {
  102. return
  103. }
  104. // Start the timer to close when idle
  105. mb.serialPort.lastActivity = time.Now()
  106. mb.serialPort.startCloseTimer()
  107. // Send the request
  108. mb.serialPort.logf("modbus: sending % x\n", aduRequest)
  109. if _, err = mb.port.Write(aduRequest); err != nil {
  110. return
  111. }
  112. function := aduRequest[1]
  113. functionFail := aduRequest[1] & 0x80
  114. bytesToRead := calculateResponseLength(aduRequest)
  115. time.Sleep(mb.calculateDelay(len(aduRequest) + bytesToRead))
  116. var n int
  117. var n1 int
  118. var data [rtuMaxSize]byte
  119. //We first read the minimum length and then read either the full package
  120. //or the error package, depending on the error status (byte 2 of the response)
  121. n, err = io.ReadAtLeast(mb.port, data[:], rtuMinSize)
  122. if err != nil {
  123. return
  124. }
  125. //if the function is correct
  126. if data[1] == function {
  127. //we read the rest of the bytes
  128. if n < bytesToRead {
  129. if bytesToRead > rtuMinSize && bytesToRead <= rtuMaxSize {
  130. if bytesToRead > n {
  131. n1, err = io.ReadFull(mb.port, data[n:bytesToRead])
  132. n += n1
  133. }
  134. }
  135. }
  136. } else if data[1] == functionFail {
  137. //for error we need to read 5 bytes
  138. if n < rtuExceptionSize {
  139. n1, err = io.ReadFull(mb.port, data[n:rtuExceptionSize])
  140. }
  141. n += n1
  142. }
  143. if err != nil {
  144. return
  145. }
  146. aduResponse = data[:n]
  147. mb.serialPort.logf("modbus: received % x\n", aduResponse)
  148. return
  149. }
  150. // calculateDelay roughly calculates time needed for the next frame.
  151. // See MODBUS over Serial Line - Specification and Implementation Guide (page 13).
  152. func (mb *rtuSerialTransporter) calculateDelay(chars int) time.Duration {
  153. var characterDelay, frameDelay int // us
  154. if mb.BaudRate <= 0 || mb.BaudRate > 19200 {
  155. characterDelay = 750
  156. frameDelay = 1750
  157. } else {
  158. characterDelay = 15000000 / mb.BaudRate
  159. frameDelay = 35000000 / mb.BaudRate
  160. }
  161. return time.Duration(characterDelay*chars+frameDelay) * time.Microsecond
  162. }
  163. func calculateResponseLength(adu []byte) int {
  164. length := rtuMinSize
  165. switch adu[1] {
  166. case FuncCodeReadDiscreteInputs,
  167. FuncCodeReadCoils:
  168. count := int(binary.BigEndian.Uint16(adu[4:]))
  169. length += 1 + count/8
  170. if count%8 != 0 {
  171. length++
  172. }
  173. case FuncCodeReadInputRegisters,
  174. FuncCodeReadHoldingRegisters,
  175. FuncCodeReadWriteMultipleRegisters:
  176. count := int(binary.BigEndian.Uint16(adu[4:]))
  177. length += 1 + count*2
  178. case FuncCodeWriteSingleCoil,
  179. FuncCodeWriteMultipleCoils,
  180. FuncCodeWriteSingleRegister,
  181. FuncCodeWriteMultipleRegisters:
  182. length += 4
  183. case FuncCodeMaskWriteRegister:
  184. length += 6
  185. case FuncCodeReadFIFOQueue:
  186. // undetermined
  187. default:
  188. }
  189. return length
  190. }