packing.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. package modbus
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. )
  6. // ProtocolDataUnit (PDU) is independent of underlying communication layers.
  7. type ProtocolDataUnit struct {
  8. FuncCode byte
  9. Data []byte
  10. }
  11. func EncodeRTUFrame(slaveID byte, pdu ProtocolDataUnit) ([]byte, error) {
  12. length := len(pdu.Data) + 4
  13. if length > rtuAduMaxSize {
  14. return nil, fmt.Errorf("modbus: length of data '%v' must not be bigger than '%v'",
  15. length, rtuAduMaxSize)
  16. }
  17. requestAdu := make([]byte, 0, length)
  18. requestAdu = append(requestAdu, slaveID, pdu.FuncCode)
  19. requestAdu = append(requestAdu, pdu.Data...)
  20. checksum := CRC16(requestAdu)
  21. requestAdu = append(requestAdu, byte(checksum), byte(checksum>>8))
  22. return requestAdu, nil
  23. }
  24. // DecodeRTUFrame decode extracts slaveID and PDU from RTU frame and verify CRC.
  25. func DecodeRTUFrame(adu []byte) (uint8, []byte, error) {
  26. if len(adu) < rtuAduMinSize { // Minimum size (including address, funcCode and CRC)
  27. return 0, nil, fmt.Errorf("modbus: response length '%v' does not meet minimum '%v'",
  28. len(adu), rtuAduMinSize)
  29. }
  30. // Calculate checksum
  31. crc := CRC16(adu[0 : len(adu)-2])
  32. expect := binary.LittleEndian.Uint16(adu[len(adu)-2:])
  33. if crc != expect {
  34. return 0, nil, fmt.Errorf("modbus: response crc '%x' does not match expected '%x'",
  35. expect, crc)
  36. }
  37. // slaveID & PDU but pass crc
  38. return adu[0], adu[1 : len(adu)-2], nil
  39. }
  40. // Verify verify confirms valid data(including slaveID,funcCode,response data)
  41. //其中rspPDU = ProtocolDataUnit{pdu[0], pdu[1:]}
  42. func Verify(reqSlaveID, rspSlaveID uint8, reqPDU, rspPDU ProtocolDataUnit) error {
  43. switch {
  44. case reqSlaveID != rspSlaveID: // Check slaveID same
  45. return fmt.Errorf("modbus: response slave id '%v' does not match request '%v'",
  46. rspSlaveID, reqSlaveID)
  47. case rspPDU.FuncCode != reqPDU.FuncCode: // Check correct function code returned (exception)
  48. return responseError(rspPDU)
  49. case rspPDU.Data == nil || len(rspPDU.Data) == 0: // check Empty response
  50. return fmt.Errorf("modbus: response data is empty")
  51. }
  52. return nil
  53. }
  54. // responseError response error
  55. func responseError(response ProtocolDataUnit) error {
  56. mbError := &ExceptionError{}
  57. if response.Data != nil && len(response.Data) > 0 {
  58. mbError.ExceptionCode = response.Data[0]
  59. }
  60. return mbError
  61. }