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 }