client.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  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. )
  9. // ClientHandler is the interface that groups the Packager and Transporter methods.
  10. type ClientHandler interface {
  11. Packager
  12. Transporter
  13. }
  14. type client struct {
  15. packager Packager
  16. transporter Transporter
  17. aduRequest []byte
  18. }
  19. // NewClient creates a new modbus client with given backend handler.
  20. func NewClient(handler ClientHandler) Client {
  21. return &client{packager: handler, transporter: handler}
  22. }
  23. // NewClient2 creates a new modbus client with given backend packager and transporter.
  24. func NewClient2(packager Packager, transporter Transporter) Client {
  25. return &client{packager: packager, transporter: transporter}
  26. }
  27. // Request:
  28. //
  29. // Function code : 1 byte (0x01)
  30. // Starting address : 2 bytes
  31. // Quantity of coils : 2 bytes
  32. //
  33. // Response:
  34. //
  35. // Function code : 1 byte (0x01)
  36. // Byte count : 1 byte
  37. // Coil status : N* bytes (=N or N+1)
  38. func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error) {
  39. if quantity < 1 || quantity > 2000 {
  40. err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 2000)
  41. return
  42. }
  43. request := ProtocolDataUnit{
  44. FunctionCode: FuncCodeReadCoils,
  45. Data: dataBlock(address, quantity),
  46. }
  47. response, err := mb.send(&request)
  48. if err != nil {
  49. return
  50. }
  51. count := int(response.Data[0])
  52. length := len(response.Data) - 1
  53. if count != length {
  54. err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count)
  55. return
  56. }
  57. results = response.Data[1:]
  58. return
  59. }
  60. // Request:
  61. //
  62. // Function code : 1 byte (0x02)
  63. // Starting address : 2 bytes
  64. // Quantity of inputs : 2 bytes
  65. //
  66. // Response:
  67. //
  68. // Function code : 1 byte (0x02)
  69. // Byte count : 1 byte
  70. // Input status : N* bytes (=N or N+1)
  71. func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, err error) {
  72. if quantity < 1 || quantity > 2000 {
  73. err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 2000)
  74. return
  75. }
  76. request := ProtocolDataUnit{
  77. FunctionCode: FuncCodeReadDiscreteInputs,
  78. Data: dataBlock(address, quantity),
  79. }
  80. response, err := mb.send(&request)
  81. if err != nil {
  82. return
  83. }
  84. count := int(response.Data[0])
  85. length := len(response.Data) - 1
  86. if count != length {
  87. err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count)
  88. return
  89. }
  90. results = response.Data[1:]
  91. return
  92. }
  93. // Request:
  94. //
  95. // Function code : 1 byte (0x03)
  96. // Starting address : 2 bytes
  97. // Quantity of registers : 2 bytes
  98. //
  99. // Response:
  100. //
  101. // Function code : 1 byte (0x03)
  102. // Byte count : 1 byte
  103. // Register value : Nx2 bytes
  104. func (mb *client) PackReadHoldingRegisters(address, quantity uint16) (results []byte, err error) {
  105. if quantity < 1 || quantity > 125 {
  106. err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 125)
  107. return
  108. }
  109. request := ProtocolDataUnit{
  110. FunctionCode: FuncCodeReadHoldingRegisters,
  111. Data: dataBlock(address, quantity),
  112. }
  113. response, err := mb.pack_post(&request)
  114. if err != nil {
  115. return
  116. }
  117. //存储要发送的字节数组,用于接收数据时验证
  118. mb.aduRequest = response
  119. results = response
  120. return
  121. }
  122. func (mb *client) DePackRegisters(aduResponse []byte) (results []byte, err error) {
  123. response, err := mb.depack_rcv(mb.aduRequest, aduResponse)
  124. if err != nil {
  125. return
  126. }
  127. count := int(response.Data[0])
  128. length := len(response.Data) - 1
  129. if count != length {
  130. err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count)
  131. return
  132. }
  133. results = response.Data[1:]
  134. return
  135. }
  136. // Request:
  137. //
  138. // Function code : 1 byte (0x03)
  139. // Starting address : 2 bytes
  140. // Quantity of registers : 2 bytes
  141. //
  142. // Response:
  143. //
  144. // Function code : 1 byte (0x03)
  145. // Byte count : 1 byte
  146. // Register value : Nx2 bytes
  147. func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte, err error) {
  148. if quantity < 1 || quantity > 125 {
  149. err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 125)
  150. return
  151. }
  152. request := ProtocolDataUnit{
  153. FunctionCode: FuncCodeReadHoldingRegisters,
  154. Data: dataBlock(address, quantity),
  155. }
  156. response, err := mb.send(&request)
  157. if err != nil {
  158. return
  159. }
  160. count := int(response.Data[0])
  161. length := len(response.Data) - 1
  162. if count != length {
  163. err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count)
  164. return
  165. }
  166. results = response.Data[1:]
  167. return
  168. }
  169. // Request:
  170. //
  171. // Function code : 1 byte (0x04)
  172. // Starting address : 2 bytes
  173. // Quantity of registers : 2 bytes
  174. //
  175. // Response:
  176. //
  177. // Function code : 1 byte (0x04)
  178. // Byte count : 1 byte
  179. // Input registers : N bytes
  180. func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, err error) {
  181. if quantity < 1 || quantity > 125 {
  182. err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 125)
  183. return
  184. }
  185. request := ProtocolDataUnit{
  186. FunctionCode: FuncCodeReadInputRegisters,
  187. Data: dataBlock(address, quantity),
  188. }
  189. response, err := mb.send(&request)
  190. if err != nil {
  191. return
  192. }
  193. count := int(response.Data[0])
  194. length := len(response.Data) - 1
  195. if count != length {
  196. err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count)
  197. return
  198. }
  199. results = response.Data[1:]
  200. return
  201. }
  202. // Request:
  203. //
  204. // Function code : 1 byte (0x05)
  205. // Output address : 2 bytes
  206. // Output value : 2 bytes
  207. //
  208. // Response:
  209. //
  210. // Function code : 1 byte (0x05)
  211. // Output address : 2 bytes
  212. // Output value : 2 bytes
  213. func (mb *client) WriteSingleCoil(address, value uint16) (results []byte, err error) {
  214. // The requested ON/OFF state can only be 0xFF00 and 0x0000
  215. if value != 0xFF00 && value != 0x0000 {
  216. err = fmt.Errorf("modbus: state '%v' must be either 0xFF00 (ON) or 0x0000 (OFF)", value)
  217. return
  218. }
  219. request := ProtocolDataUnit{
  220. FunctionCode: FuncCodeWriteSingleCoil,
  221. Data: dataBlock(address, value),
  222. }
  223. response, err := mb.send(&request)
  224. if err != nil {
  225. return
  226. }
  227. // Fixed response length
  228. if len(response.Data) != 4 {
  229. err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 4)
  230. return
  231. }
  232. respValue := binary.BigEndian.Uint16(response.Data)
  233. if address != respValue {
  234. err = fmt.Errorf("modbus: response address '%v' does not match request '%v'", respValue, address)
  235. return
  236. }
  237. results = response.Data[2:]
  238. respValue = binary.BigEndian.Uint16(results)
  239. if value != respValue {
  240. err = fmt.Errorf("modbus: response value '%v' does not match request '%v'", respValue, value)
  241. return
  242. }
  243. return
  244. }
  245. // Request:
  246. //
  247. // Function code : 1 byte (0x06)
  248. // Register address : 2 bytes
  249. // Register value : 2 bytes
  250. //
  251. // Response:
  252. //
  253. // Function code : 1 byte (0x06)
  254. // Register address : 2 bytes
  255. // Register value : 2 bytes
  256. func (mb *client) WriteSingleRegister(address, value uint16) (results []byte, err error) {
  257. request := ProtocolDataUnit{
  258. FunctionCode: FuncCodeWriteSingleRegister,
  259. Data: dataBlock(address, value),
  260. }
  261. response, err := mb.send(&request)
  262. if err != nil {
  263. return
  264. }
  265. // Fixed response length
  266. if len(response.Data) != 4 {
  267. err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 4)
  268. return
  269. }
  270. respValue := binary.BigEndian.Uint16(response.Data)
  271. if address != respValue {
  272. err = fmt.Errorf("modbus: response address '%v' does not match request '%v'", respValue, address)
  273. return
  274. }
  275. results = response.Data[2:]
  276. respValue = binary.BigEndian.Uint16(results)
  277. if value != respValue {
  278. err = fmt.Errorf("modbus: response value '%v' does not match request '%v'", respValue, value)
  279. return
  280. }
  281. return
  282. }
  283. // Request:
  284. //
  285. // Function code : 1 byte (0x0F)
  286. // Starting address : 2 bytes
  287. // Quantity of outputs : 2 bytes
  288. // Byte count : 1 byte
  289. // Outputs value : N* bytes
  290. //
  291. // Response:
  292. //
  293. // Function code : 1 byte (0x0F)
  294. // Starting address : 2 bytes
  295. // Quantity of outputs : 2 bytes
  296. func (mb *client) WriteMultipleCoils(address, quantity uint16, value []byte) (results []byte, err error) {
  297. if quantity < 1 || quantity > 1968 {
  298. err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 1968)
  299. return
  300. }
  301. request := ProtocolDataUnit{
  302. FunctionCode: FuncCodeWriteMultipleCoils,
  303. Data: dataBlockSuffix(value, address, quantity),
  304. }
  305. response, err := mb.send(&request)
  306. if err != nil {
  307. return
  308. }
  309. // Fixed response length
  310. if len(response.Data) != 4 {
  311. err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 4)
  312. return
  313. }
  314. respValue := binary.BigEndian.Uint16(response.Data)
  315. if address != respValue {
  316. err = fmt.Errorf("modbus: response address '%v' does not match request '%v'", respValue, address)
  317. return
  318. }
  319. results = response.Data[2:]
  320. respValue = binary.BigEndian.Uint16(results)
  321. if quantity != respValue {
  322. err = fmt.Errorf("modbus: response quantity '%v' does not match request '%v'", respValue, quantity)
  323. return
  324. }
  325. return
  326. }
  327. // Request:
  328. //
  329. // Function code : 1 byte (0x10)
  330. // Starting address : 2 bytes
  331. // Quantity of outputs : 2 bytes
  332. // Byte count : 1 byte
  333. // Registers value : N* bytes
  334. //
  335. // Response:
  336. //
  337. // Function code : 1 byte (0x10)
  338. // Starting address : 2 bytes
  339. // Quantity of registers : 2 bytes
  340. func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) (results []byte, err error) {
  341. if quantity < 1 || quantity > 123 {
  342. err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 123)
  343. return
  344. }
  345. request := ProtocolDataUnit{
  346. FunctionCode: FuncCodeWriteMultipleRegisters,
  347. Data: dataBlockSuffix(value, address, quantity),
  348. }
  349. response, err := mb.send(&request)
  350. if err != nil {
  351. return
  352. }
  353. // Fixed response length
  354. if len(response.Data) != 4 {
  355. err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 4)
  356. return
  357. }
  358. respValue := binary.BigEndian.Uint16(response.Data)
  359. if address != respValue {
  360. err = fmt.Errorf("modbus: response address '%v' does not match request '%v'", respValue, address)
  361. return
  362. }
  363. results = response.Data[2:]
  364. respValue = binary.BigEndian.Uint16(results)
  365. if quantity != respValue {
  366. err = fmt.Errorf("modbus: response quantity '%v' does not match request '%v'", respValue, quantity)
  367. return
  368. }
  369. return
  370. }
  371. // Request:
  372. //
  373. // Function code : 1 byte (0x16)
  374. // Reference address : 2 bytes
  375. // AND-mask : 2 bytes
  376. // OR-mask : 2 bytes
  377. //
  378. // Response:
  379. //
  380. // Function code : 1 byte (0x16)
  381. // Reference address : 2 bytes
  382. // AND-mask : 2 bytes
  383. // OR-mask : 2 bytes
  384. func (mb *client) MaskWriteRegister(address, andMask, orMask uint16) (results []byte, err error) {
  385. request := ProtocolDataUnit{
  386. FunctionCode: FuncCodeMaskWriteRegister,
  387. Data: dataBlock(address, andMask, orMask),
  388. }
  389. response, err := mb.send(&request)
  390. if err != nil {
  391. return
  392. }
  393. // Fixed response length
  394. if len(response.Data) != 6 {
  395. err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 6)
  396. return
  397. }
  398. respValue := binary.BigEndian.Uint16(response.Data)
  399. if address != respValue {
  400. err = fmt.Errorf("modbus: response address '%v' does not match request '%v'", respValue, address)
  401. return
  402. }
  403. respValue = binary.BigEndian.Uint16(response.Data[2:])
  404. if andMask != respValue {
  405. err = fmt.Errorf("modbus: response AND-mask '%v' does not match request '%v'", respValue, andMask)
  406. return
  407. }
  408. respValue = binary.BigEndian.Uint16(response.Data[4:])
  409. if orMask != respValue {
  410. err = fmt.Errorf("modbus: response OR-mask '%v' does not match request '%v'", respValue, orMask)
  411. return
  412. }
  413. results = response.Data[2:]
  414. return
  415. }
  416. // Request:
  417. //
  418. // Function code : 1 byte (0x17)
  419. // Read starting address : 2 bytes
  420. // Quantity to read : 2 bytes
  421. // Write starting address: 2 bytes
  422. // Quantity to write : 2 bytes
  423. // Write byte count : 1 byte
  424. // Write registers value : N* bytes
  425. //
  426. // Response:
  427. //
  428. // Function code : 1 byte (0x17)
  429. // Byte count : 1 byte
  430. // Read registers value : Nx2 bytes
  431. func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) {
  432. if readQuantity < 1 || readQuantity > 125 {
  433. err = fmt.Errorf("modbus: quantity to read '%v' must be between '%v' and '%v',", readQuantity, 1, 125)
  434. return
  435. }
  436. if writeQuantity < 1 || writeQuantity > 121 {
  437. err = fmt.Errorf("modbus: quantity to write '%v' must be between '%v' and '%v',", writeQuantity, 1, 121)
  438. return
  439. }
  440. request := ProtocolDataUnit{
  441. FunctionCode: FuncCodeReadWriteMultipleRegisters,
  442. Data: dataBlockSuffix(value, readAddress, readQuantity, writeAddress, writeQuantity),
  443. }
  444. response, err := mb.send(&request)
  445. if err != nil {
  446. return
  447. }
  448. count := int(response.Data[0])
  449. if count != (len(response.Data) - 1) {
  450. err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", len(response.Data)-1, count)
  451. return
  452. }
  453. results = response.Data[1:]
  454. return
  455. }
  456. // Request:
  457. //
  458. // Function code : 1 byte (0x18)
  459. // FIFO pointer address : 2 bytes
  460. //
  461. // Response:
  462. //
  463. // Function code : 1 byte (0x18)
  464. // Byte count : 2 bytes
  465. // FIFO count : 2 bytes
  466. // FIFO count : 2 bytes (<=31)
  467. // FIFO value register : Nx2 bytes
  468. func (mb *client) ReadFIFOQueue(address uint16) (results []byte, err error) {
  469. request := ProtocolDataUnit{
  470. FunctionCode: FuncCodeReadFIFOQueue,
  471. Data: dataBlock(address),
  472. }
  473. response, err := mb.send(&request)
  474. if err != nil {
  475. return
  476. }
  477. if len(response.Data) < 4 {
  478. err = fmt.Errorf("modbus: response data size '%v' is less than expected '%v'", len(response.Data), 4)
  479. return
  480. }
  481. count := int(binary.BigEndian.Uint16(response.Data))
  482. if count != (len(response.Data) - 1) {
  483. err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", len(response.Data)-1, count)
  484. return
  485. }
  486. count = int(binary.BigEndian.Uint16(response.Data[2:]))
  487. if count > 31 {
  488. err = fmt.Errorf("modbus: fifo count '%v' is greater than expected '%v'", count, 31)
  489. return
  490. }
  491. results = response.Data[4:]
  492. return
  493. }
  494. // Helpers
  495. func (mb *client) pack_post(request *ProtocolDataUnit) (aduRequest []byte, err error) {
  496. aduRequest, err = mb.packager.Encode(request)
  497. return
  498. }
  499. func (mb *client) depack_rcv(aduRequest, aduResponse []byte) (response *ProtocolDataUnit, err error) {
  500. var functionCode byte
  501. if err = mb.packager.Verify(aduRequest, aduResponse); err != nil {
  502. return
  503. }
  504. //fmt.Println("depack_rcv:", aduResponse)
  505. response, err = mb.packager.Decode(aduResponse)
  506. if err != nil {
  507. return
  508. }
  509. functionCode = aduRequest[1]
  510. // Check correct function code returned (exception)
  511. if response.FunctionCode != functionCode {
  512. err = responseError(response)
  513. return
  514. }
  515. if response.Data == nil || len(response.Data) == 0 {
  516. // Empty response
  517. err = fmt.Errorf("modbus: response data is empty")
  518. return
  519. }
  520. return
  521. }
  522. // send sends request and checks possible exception in the response.
  523. func (mb *client) send(request *ProtocolDataUnit) (response *ProtocolDataUnit, err error) {
  524. aduRequest, err := mb.packager.Encode(request)
  525. if err != nil {
  526. return
  527. }
  528. aduResponse, err := mb.transporter.Send(aduRequest)
  529. if err != nil {
  530. return
  531. }
  532. if err = mb.packager.Verify(aduRequest, aduResponse); err != nil {
  533. return
  534. }
  535. response, err = mb.packager.Decode(aduResponse)
  536. if err != nil {
  537. return
  538. }
  539. // Check correct function code returned (exception)
  540. if response.FunctionCode != request.FunctionCode {
  541. err = responseError(response)
  542. return
  543. }
  544. if response.Data == nil || len(response.Data) == 0 {
  545. // Empty response
  546. err = fmt.Errorf("modbus: response data is empty")
  547. return
  548. }
  549. return
  550. }
  551. // dataBlock creates a sequence of uint16 data.
  552. func dataBlock(value ...uint16) []byte {
  553. data := make([]byte, 2*len(value))
  554. for i, v := range value {
  555. binary.BigEndian.PutUint16(data[i*2:], v)
  556. }
  557. return data
  558. }
  559. // dataBlockSuffix creates a sequence of uint16 data and append the suffix plus its length.
  560. func dataBlockSuffix(suffix []byte, value ...uint16) []byte {
  561. length := 2 * len(value)
  562. data := make([]byte, length+1+len(suffix))
  563. for i, v := range value {
  564. binary.BigEndian.PutUint16(data[i*2:], v)
  565. }
  566. data[length] = uint8(len(suffix))
  567. copy(data[length+1:], suffix)
  568. return data
  569. }
  570. func responseError(response *ProtocolDataUnit) error {
  571. mbError := &ModbusError{FunctionCode: response.FunctionCode}
  572. if response.Data != nil && len(response.Data) > 0 {
  573. mbError.ExceptionCode = response.Data[0]
  574. }
  575. return mbError
  576. }