github.com/simpleiot/simpleiot@v0.18.3/modbus/client.go (about)

     1  package modbus
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/simpleiot/simpleiot/test"
     9  )
    10  
    11  // Client defines a Modbus client (master)
    12  type Client struct {
    13  	transport Transport
    14  	debug     int
    15  }
    16  
    17  // NewClient is used to create a new modbus client
    18  // port must return an entire packet for each Read().
    19  // github.com/simpleiot/simpleiot/respreader is a good
    20  // way to do this.
    21  func NewClient(transport Transport, debug int) *Client {
    22  	return &Client{
    23  		transport: transport,
    24  		debug:     debug,
    25  	}
    26  }
    27  
    28  // SetDebugLevel allows you to change debug level on the fly
    29  func (c *Client) SetDebugLevel(debug int) {
    30  	c.debug = debug
    31  }
    32  
    33  // Close closes the client transport
    34  func (c *Client) Close() error {
    35  	return c.transport.Close()
    36  }
    37  
    38  // ReadCoils is used to read modbus coils
    39  func (c *Client) ReadCoils(id byte, coil, count uint16) ([]bool, error) {
    40  	ret := []bool{}
    41  	req := ReadCoils(coil, count)
    42  	if c.debug >= 1 {
    43  		fmt.Printf("Modbus client Readcoils ID:0x%x req:%v\n", id, req)
    44  	}
    45  	packet, err := c.transport.Encode(id, req)
    46  	if err != nil {
    47  		return ret, err
    48  	}
    49  
    50  	if c.debug >= 9 {
    51  		fmt.Println("Modbus client ReadCoils tx: ", test.HexDump(packet))
    52  	}
    53  
    54  	_, err = c.transport.Write(packet)
    55  	if err != nil {
    56  		return ret, err
    57  	}
    58  
    59  	// FIXME, what is max modbus packet size?
    60  	buf := make([]byte, 200)
    61  	cnt, err := c.transport.Read(buf)
    62  	if err != nil {
    63  		return ret, err
    64  	}
    65  
    66  	buf = buf[:cnt]
    67  
    68  	if c.debug >= 9 {
    69  		fmt.Println("Modbus client ReadCoils rx: ", test.HexDump(buf))
    70  	}
    71  
    72  	_, resp, err := c.transport.Decode(buf)
    73  	if err != nil {
    74  		return ret, err
    75  	}
    76  
    77  	if c.debug >= 1 {
    78  		fmt.Printf("Modbus client Readcoils ID:0x%x resp:%v\n", id, resp)
    79  	}
    80  
    81  	return resp.RespReadBits()
    82  }
    83  
    84  // WriteSingleCoil is used to read modbus coils
    85  func (c *Client) WriteSingleCoil(id byte, coil uint16, v bool) error {
    86  	req := WriteSingleCoil(coil, v)
    87  	if c.debug >= 1 {
    88  		fmt.Printf("Modbus client WriteSingleCoil ID:0x%x req:%v\n", id, req)
    89  	}
    90  	packet, err := c.transport.Encode(id, req)
    91  	if err != nil {
    92  		return fmt.Errorf("RtuEncode error: %w", err)
    93  	}
    94  
    95  	if c.debug >= 9 {
    96  		fmt.Println("Modbus client WriteSingleCoil tx: ", test.HexDump(packet))
    97  	}
    98  
    99  	_, err = c.transport.Write(packet)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	// FIXME, what is max modbus packet size?
   105  	buf := make([]byte, 200)
   106  	cnt, err := c.transport.Read(buf)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	buf = buf[:cnt]
   112  
   113  	if c.debug >= 9 {
   114  		fmt.Println("Modbus client WriteSingleCoil rx: ", test.HexDump(buf))
   115  	}
   116  
   117  	_, resp, err := c.transport.Decode(buf)
   118  	if err != nil {
   119  		return fmt.Errorf("RtuDecode error: %w", err)
   120  	}
   121  
   122  	if c.debug >= 1 {
   123  		fmt.Printf("Modbus client WriteSingleCoil ID:0x%x resp:%v\n", id, resp)
   124  	}
   125  
   126  	if resp.FunctionCode != req.FunctionCode {
   127  		return errors.New("resp contains wrong function code")
   128  	}
   129  
   130  	if !bytes.Equal(req.Data, resp.Data) {
   131  		return errors.New("Did not get the correct response data")
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  // ReadDiscreteInputs is used to read modbus discrete inputs
   138  func (c *Client) ReadDiscreteInputs(id byte, input, count uint16) ([]bool, error) {
   139  	ret := []bool{}
   140  	req := ReadDiscreteInputs(input, count)
   141  	if c.debug >= 1 {
   142  		fmt.Printf("Modbus client ReadDiscreteInputs ID:0x%x req:%v\n", id, req)
   143  	}
   144  	packet, err := c.transport.Encode(id, req)
   145  	if err != nil {
   146  		return ret, err
   147  	}
   148  
   149  	if c.debug >= 9 {
   150  		fmt.Println("Modbus client ReadDiscreteInputs tx: ", test.HexDump(packet))
   151  	}
   152  
   153  	_, err = c.transport.Write(packet)
   154  	if err != nil {
   155  		return ret, err
   156  	}
   157  
   158  	// FIXME, what is max modbus packet size?
   159  	buf := make([]byte, 200)
   160  	cnt, err := c.transport.Read(buf)
   161  	if err != nil {
   162  		return ret, err
   163  	}
   164  
   165  	buf = buf[:cnt]
   166  
   167  	if c.debug >= 9 {
   168  		fmt.Println("Modbus client ReadDiscreteInputs rx: ", test.HexDump(buf))
   169  	}
   170  
   171  	_, resp, err := c.transport.Decode(buf)
   172  	if err != nil {
   173  		return ret, err
   174  	}
   175  
   176  	if c.debug >= 1 {
   177  		fmt.Printf("Modbus client ReadDiscreteInputs ID:0x%x resp:%v\n", id, resp)
   178  	}
   179  
   180  	if resp.FunctionCode != req.FunctionCode {
   181  		return []bool{}, errors.New("resp contains wrong function code")
   182  	}
   183  
   184  	return resp.RespReadBits()
   185  }
   186  
   187  // ReadHoldingRegs is used to read modbus coils
   188  func (c *Client) ReadHoldingRegs(id byte, reg, count uint16) ([]uint16, error) {
   189  	ret := []uint16{}
   190  	req := ReadHoldingRegs(reg, count)
   191  	if c.debug >= 1 {
   192  		fmt.Printf("Modbus client ReadHoldingRegs ID:0x%x req:%v\n", id, req)
   193  	}
   194  	packet, err := c.transport.Encode(id, req)
   195  	if err != nil {
   196  		return ret, err
   197  	}
   198  
   199  	if c.debug >= 9 {
   200  		fmt.Println("Modbus client ReadHoldingRegs tx: ", test.HexDump(packet))
   201  	}
   202  
   203  	_, err = c.transport.Write(packet)
   204  	if err != nil {
   205  		return ret, err
   206  	}
   207  
   208  	// FIXME, what is max modbus packet size?
   209  	buf := make([]byte, 200)
   210  	cnt, err := c.transport.Read(buf)
   211  	if err != nil {
   212  		return ret, err
   213  	}
   214  
   215  	buf = buf[:cnt]
   216  
   217  	if c.debug >= 9 {
   218  		fmt.Println("Modbus client ReadHoldingRegs rx: ", test.HexDump(buf))
   219  	}
   220  
   221  	_, resp, err := c.transport.Decode(buf)
   222  	if err != nil {
   223  		return ret, err
   224  	}
   225  
   226  	if c.debug >= 1 {
   227  		fmt.Printf("Modbus client ReadHoldingRegs ID:0x%x resp:%v\n", id, resp)
   228  	}
   229  
   230  	if resp.FunctionCode != req.FunctionCode {
   231  		return []uint16{}, errors.New("resp contains wrong function code")
   232  	}
   233  
   234  	return resp.RespReadRegs()
   235  }
   236  
   237  // ReadInputRegs is used to read modbus coils
   238  func (c *Client) ReadInputRegs(id byte, reg, count uint16) ([]uint16, error) {
   239  	ret := []uint16{}
   240  	req := ReadInputRegs(reg, count)
   241  	if c.debug >= 1 {
   242  		fmt.Printf("Modbus client ReadInputRegs ID:0x%x req:%v\n", id, req)
   243  	}
   244  	packet, err := c.transport.Encode(id, req)
   245  	if err != nil {
   246  		return ret, err
   247  	}
   248  
   249  	if c.debug >= 9 {
   250  		fmt.Println("Modbus client ReadInputRegs tx: ", test.HexDump(packet))
   251  	}
   252  
   253  	_, err = c.transport.Write(packet)
   254  	if err != nil {
   255  		return ret, err
   256  	}
   257  
   258  	// FIXME, what is max modbus packet size?
   259  	buf := make([]byte, 200)
   260  	cnt, err := c.transport.Read(buf)
   261  	if err != nil {
   262  		return ret, err
   263  	}
   264  
   265  	buf = buf[:cnt]
   266  
   267  	if c.debug >= 9 {
   268  		fmt.Println("Modbus client ReadInputRegs rx: ", test.HexDump(buf))
   269  	}
   270  
   271  	_, resp, err := c.transport.Decode(buf)
   272  	if err != nil {
   273  		return ret, err
   274  	}
   275  
   276  	if c.debug >= 1 {
   277  		fmt.Printf("Modbus client ReadInputRegs ID:0x%x resp:%v\n", id, resp)
   278  	}
   279  
   280  	if resp.FunctionCode != req.FunctionCode {
   281  		return []uint16{}, errors.New("resp contains wrong function code")
   282  	}
   283  
   284  	return resp.RespReadRegs()
   285  }
   286  
   287  // WriteSingleReg writes to a single holding register
   288  func (c *Client) WriteSingleReg(id byte, reg, value uint16) error {
   289  	req := WriteSingleReg(reg, value)
   290  	if c.debug >= 1 {
   291  		fmt.Printf("Modbus client WriteSingleReg ID:0x%x req:%v\n", id, req)
   292  	}
   293  	packet, err := c.transport.Encode(id, req)
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	if c.debug >= 9 {
   299  		fmt.Println("Modbus client WriteSingleReg tx: ", test.HexDump(packet))
   300  	}
   301  
   302  	_, err = c.transport.Write(packet)
   303  	if err != nil {
   304  		return err
   305  	}
   306  
   307  	// FIXME, what is max modbus packet size?
   308  	buf := make([]byte, 200)
   309  	cnt, err := c.transport.Read(buf)
   310  	if err != nil {
   311  		return err
   312  	}
   313  
   314  	buf = buf[:cnt]
   315  
   316  	if c.debug >= 9 {
   317  		fmt.Println("Modbus client WriteSingleReg rx: ", test.HexDump(buf))
   318  	}
   319  
   320  	_, resp, err := c.transport.Decode(buf)
   321  	if err != nil {
   322  		return err
   323  	}
   324  
   325  	if c.debug >= 1 {
   326  		fmt.Printf("Modbus client WriteSingleReg ID:0x%x resp:%v\n", id, resp)
   327  	}
   328  
   329  	if resp.FunctionCode != req.FunctionCode {
   330  		return errors.New("resp contains wrong function code")
   331  	}
   332  
   333  	if !bytes.Equal(req.Data, resp.Data) {
   334  		return errors.New("Did not get the correct response data")
   335  	}
   336  
   337  	return nil
   338  }