From b98740f3eb0e3a3bfbb7d98392d75597bb55da7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Germ=C3=A1n?= Date: Thu, 31 Oct 2019 15:39:27 +0100 Subject: [PATCH 01/13] Added WriteRecord function --- api.go | 3 +++ client.go | 38 ++++++++++++++++++++++++++++++++++++++ modbus.go | 1 + 3 files changed, 42 insertions(+) diff --git a/api.go b/api.go index fe06ab0..60028a5 100644 --- a/api.go +++ b/api.go @@ -35,6 +35,9 @@ type Client interface { // (1 to 123 registers) in a remote device and returns quantity of // registers. WriteMultipleRegisters(address, quantity uint16, value []byte) (results []byte, err error) + // WriteFileRecord writes a block of a Record (1 to 251 registers) + // in a remote device and returns quantity of registers written. + WriteFileRecord(address, quantity uint16, blockSize uint16, value []byte) error // ReadWriteMultipleRegisters performs a combination of one read // operation and one write operation. It returns read registers value. ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) diff --git a/client.go b/client.go index ac3ee2e..57d4f7a 100644 --- a/client.go +++ b/client.go @@ -315,6 +315,44 @@ func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) return } +// Request: +// Function code : 1 byte (0x15) +// Starting address : 2 bytes +// Quantity of outputs : 2 bytes +// Byte count : 1 byte +// Registers value : N* bytes +// Response: +// Function code : 1 byte (0x15) +// Starting address : 2 bytes +// Quantity of registers : 2 bytes +func (mb *client) WriteFileRecord(address, quantity uint16, blockSize uint16, value []byte) error { + var responseLength int + var err error + if quantity < 1 || quantity > 123 { + err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 123) + return err + } + for i := uint16(0); i < quantity; i++ { + addressNext := address + (i * blockSize) + valueNext := value[(i * blockSize):((i + 1) * blockSize)] + request := ProtocolDataUnit{ + FunctionCode: FuncCodeWriteFileRecord, + Data: dataBlockSuffix(valueNext, addressNext, blockSize), + } + response, err := mb.send(&request) + if err != nil { + return err + } + responseLength += len(response.Data) + } + // Fixed response length + if responseLength != int(quantity*blockSize) { + err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", responseLength, (quantity * blockSize)) + return err + } + return nil +} + // Request: // Function code : 1 byte (0x16) // Reference address : 2 bytes diff --git a/modbus.go b/modbus.go index 869473c..1ffa476 100644 --- a/modbus.go +++ b/modbus.go @@ -23,6 +23,7 @@ const ( FuncCodeReadHoldingRegisters = 3 FuncCodeWriteSingleRegister = 6 FuncCodeWriteMultipleRegisters = 16 + FuncCodeWriteFileRecord = 21 FuncCodeReadWriteMultipleRegisters = 23 FuncCodeMaskWriteRegister = 22 FuncCodeReadFIFOQueue = 24 From 3d544af532512e13dda79609821b0aadd961434e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Germ=C3=A1n?= Date: Mon, 4 Nov 2019 14:59:06 +0100 Subject: [PATCH 02/13] Fixed WriteRecord function according to ModBus Application Protocol Specification V1.1b3 --- client.go | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/client.go b/client.go index 57d4f7a..26a8481 100644 --- a/client.go +++ b/client.go @@ -20,6 +20,9 @@ type client struct { transporter Transporter } +const wFileRecReferenceType = uint16(6) +const wFileRecUpdateFileNumber = uint16(65535) //0xFFFF as per STM specs + // NewClient creates a new modbus client with given backend handler. func NewClient(handler ClientHandler) Client { return &client{packager: handler, transporter: handler} @@ -328,23 +331,30 @@ func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) func (mb *client) WriteFileRecord(address, quantity uint16, blockSize uint16, value []byte) error { var responseLength int var err error - if quantity < 1 || quantity > 123 { - err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 123) + + totalLength := quantity * blockSize + if totalLength < 1 || totalLength > 251 { + err = fmt.Errorf("modbus: totalLength per modbus msg'%v' must be between '%v' and '%v',", quantity, 1, 251) return err } - for i := uint16(0); i < quantity; i++ { - addressNext := address + (i * blockSize) + bffrReq := make([]byte, 0) + for i := uint16(address); i < address+totalLength; i = i + blockSize { + recordFilenumber := []byte{byte(i >> 8), byte(i & 0xFF)} + bffrReq = append(bffrReq, recordFilenumber...) + bffrReq = append(bffrReq, byte(blockSize/2)) valueNext := value[(i * blockSize):((i + 1) * blockSize)] - request := ProtocolDataUnit{ - FunctionCode: FuncCodeWriteFileRecord, - Data: dataBlockSuffix(valueNext, addressNext, blockSize), - } - response, err := mb.send(&request) - if err != nil { - return err - } - responseLength += len(response.Data) + bffrReq = append(bffrReq, valueNext...) + } + request := ProtocolDataUnit{ + FunctionCode: FuncCodeWriteFileRecord, + Data: dataBlockSuffix(bffrReq, totalLength, wFileRecReferenceType, wFileRecUpdateFileNumber), } + response, err := mb.send(&request) + if err != nil { + return err + } + responseLength += len(response.Data) + // Fixed response length if responseLength != int(quantity*blockSize) { err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", responseLength, (quantity * blockSize)) From bc081f98f7a0b4e4a89ef24e9d40f40dc54f7f4b Mon Sep 17 00:00:00 2001 From: cgerman-circ <53077375+cgerman-circ@users.noreply.github.com> Date: Tue, 5 Nov 2019 15:42:36 +0100 Subject: [PATCH 03/13] Fixed Reference Type and File Number implementation inside request --- client.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client.go b/client.go index 26a8481..e7f57b8 100644 --- a/client.go +++ b/client.go @@ -20,8 +20,8 @@ type client struct { transporter Transporter } -const wFileRecReferenceType = uint16(6) -const wFileRecUpdateFileNumber = uint16(65535) //0xFFFF as per STM specs +var wFileRecReferenceType = []byte{6} +var wFileRecUpdateFileNumber = []byte{255, 255} //0xFFFF as per STM specs // NewClient creates a new modbus client with given backend handler. func NewClient(handler ClientHandler) Client { @@ -339,6 +339,8 @@ func (mb *client) WriteFileRecord(address, quantity uint16, blockSize uint16, va } bffrReq := make([]byte, 0) for i := uint16(address); i < address+totalLength; i = i + blockSize { + bffrReq = append(bffrReq, wFileRecReferenceType...) + bffrReq = append(bffrReq, wFileRecUpdateFileNumber...) recordFilenumber := []byte{byte(i >> 8), byte(i & 0xFF)} bffrReq = append(bffrReq, recordFilenumber...) bffrReq = append(bffrReq, byte(blockSize/2)) @@ -347,7 +349,7 @@ func (mb *client) WriteFileRecord(address, quantity uint16, blockSize uint16, va } request := ProtocolDataUnit{ FunctionCode: FuncCodeWriteFileRecord, - Data: dataBlockSuffix(bffrReq, totalLength, wFileRecReferenceType, wFileRecUpdateFileNumber), + Data: dataBlockSuffix(bffrReq, totalLength), } response, err := mb.send(&request) if err != nil { From 2ff1deb0229eb240f063f5f79f52a8988d980cc6 Mon Sep 17 00:00:00 2001 From: cgerman-circ <53077375+cgerman-circ@users.noreply.github.com> Date: Tue, 5 Nov 2019 17:40:03 +0100 Subject: [PATCH 04/13] Fixed totalLength inside frame --- client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index e7f57b8..7eaccfa 100644 --- a/client.go +++ b/client.go @@ -339,6 +339,7 @@ func (mb *client) WriteFileRecord(address, quantity uint16, blockSize uint16, va } bffrReq := make([]byte, 0) for i := uint16(address); i < address+totalLength; i = i + blockSize { + bffrReq = append(bffrReq, byte(totalLength)) bffrReq = append(bffrReq, wFileRecReferenceType...) bffrReq = append(bffrReq, wFileRecUpdateFileNumber...) recordFilenumber := []byte{byte(i >> 8), byte(i & 0xFF)} @@ -349,7 +350,7 @@ func (mb *client) WriteFileRecord(address, quantity uint16, blockSize uint16, va } request := ProtocolDataUnit{ FunctionCode: FuncCodeWriteFileRecord, - Data: dataBlockSuffix(bffrReq, totalLength), + Data: bffrReq, } response, err := mb.send(&request) if err != nil { From 0893d76f02ca5c4939b3b571da8f91f91a4d91c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Germ=C3=A1n?= Date: Thu, 7 Nov 2019 16:30:21 +0100 Subject: [PATCH 05/13] Minor fixes and improvements in new WriteRecord function --- api.go | 2 +- client.go | 51 ++++++++++++++++++++++++++------------------------- rtuclient.go | 2 ++ 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/api.go b/api.go index 60028a5..68ac88c 100644 --- a/api.go +++ b/api.go @@ -37,7 +37,7 @@ type Client interface { WriteMultipleRegisters(address, quantity uint16, value []byte) (results []byte, err error) // WriteFileRecord writes a block of a Record (1 to 251 registers) // in a remote device and returns quantity of registers written. - WriteFileRecord(address, quantity uint16, blockSize uint16, value []byte) error + WriteFileRecord(address, quantity uint16, SubReqSize uint16, RecUpdateFileNum []byte, value []byte) (results []byte, err error) // ReadWriteMultipleRegisters performs a combination of one read // operation and one write operation. It returns read registers value. ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) diff --git a/client.go b/client.go index 7eaccfa..3f89221 100644 --- a/client.go +++ b/client.go @@ -20,9 +20,6 @@ type client struct { transporter Transporter } -var wFileRecReferenceType = []byte{6} -var wFileRecUpdateFileNumber = []byte{255, 255} //0xFFFF as per STM specs - // NewClient creates a new modbus client with given backend handler. func NewClient(handler ClientHandler) Client { return &client{packager: handler, transporter: handler} @@ -328,24 +325,29 @@ func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) // Function code : 1 byte (0x15) // Starting address : 2 bytes // Quantity of registers : 2 bytes -func (mb *client) WriteFileRecord(address, quantity uint16, blockSize uint16, value []byte) error { - var responseLength int - var err error +func (mb *client) WriteFileRecord(address, quantity uint16, subReqSize uint16, recUpdateFileNum []byte, value []byte) (results []byte, err error) { + wRecControlFieldsLength := uint16(7) - totalLength := quantity * blockSize - if totalLength < 1 || totalLength > 251 { - err = fmt.Errorf("modbus: totalLength per modbus msg'%v' must be between '%v' and '%v',", quantity, 1, 251) - return err + dataFieldsLength := quantity * subReqSize + totalLength := (quantity * (subReqSize + wRecControlFieldsLength)) + 1 //+1 because of Function Code also included + if totalLength < 1 || totalLength > 253 { + err = fmt.Errorf("modbus: total length per modbus request must be between 1 and 253") + return + } + if len(recUpdateFileNum) != 2 { + err = fmt.Errorf("modbus: sub request file number should be a 2 byte value") + return } bffrReq := make([]byte, 0) - for i := uint16(address); i < address+totalLength; i = i + blockSize { - bffrReq = append(bffrReq, byte(totalLength)) - bffrReq = append(bffrReq, wFileRecReferenceType...) - bffrReq = append(bffrReq, wFileRecUpdateFileNumber...) - recordFilenumber := []byte{byte(i >> 8), byte(i & 0xFF)} - bffrReq = append(bffrReq, recordFilenumber...) - bffrReq = append(bffrReq, byte(blockSize/2)) - valueNext := value[(i * blockSize):((i + 1) * blockSize)] + bffrReq = append(bffrReq, byte(totalLength-1)) + for i := address; i < address+dataFieldsLength; i += subReqSize { + bffrReq = append(bffrReq, 6) //Sub Request reference type + bffrReq = append(bffrReq, recUpdateFileNum...) + recordNumber := []byte{byte(i >> 8), byte(i & 0xFF)} + bffrReq = append(bffrReq, recordNumber...) + bSize := []byte{byte(subReqSize / 2 >> 8), byte(subReqSize / 2 & 0xFF)} + bffrReq = append(bffrReq, bSize...) + valueNext := value[(i * subReqSize):((i + 1) * subReqSize)] bffrReq = append(bffrReq, valueNext...) } request := ProtocolDataUnit{ @@ -354,16 +356,15 @@ func (mb *client) WriteFileRecord(address, quantity uint16, blockSize uint16, va } response, err := mb.send(&request) if err != nil { - return err + return } - responseLength += len(response.Data) - // Fixed response length - if responseLength != int(quantity*blockSize) { - err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", responseLength, (quantity * blockSize)) - return err + if len(response.Data) != int(totalLength) { + err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), int(totalLength)) + return } - return nil + results = response.Data[2:] + return } // Request: diff --git a/rtuclient.go b/rtuclient.go index ad6a31a..5860f7c 100644 --- a/rtuclient.go +++ b/rtuclient.go @@ -204,6 +204,8 @@ func calculateResponseLength(adu []byte) int { length += 6 case FuncCodeReadFIFOQueue: // undetermined + case FuncCodeWriteFileRecord: + length = len(adu) default: } return length From 27d3dfff5bfef81b3c3b7532d783191906f9a840 Mon Sep 17 00:00:00 2001 From: cgerman Date: Thu, 5 Mar 2020 12:15:08 +0100 Subject: [PATCH 06/13] Added check to ensure valueNext creation never is out of value input buffer boundaries --- client.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client.go b/client.go index 3f89221..439f679 100644 --- a/client.go +++ b/client.go @@ -338,6 +338,10 @@ func (mb *client) WriteFileRecord(address, quantity uint16, subReqSize uint16, r err = fmt.Errorf("modbus: sub request file number should be a 2 byte value") return } + if len(value) < int(address+dataFieldsLength) { + err = fmt.Errorf("modbus: value input parameter length is not enough given address, quantity and subReqSize requested") + return + } bffrReq := make([]byte, 0) bffrReq = append(bffrReq, byte(totalLength-1)) for i := address; i < address+dataFieldsLength; i += subReqSize { From dbf18a5b9698ac299268899484bad59279e074dd Mon Sep 17 00:00:00 2001 From: cgerman-circ <53077375+cgerman-circ@users.noreply.github.com> Date: Fri, 6 Mar 2020 12:08:40 +0100 Subject: [PATCH 07/13] Update client.go --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index 439f679..63d6bf2 100644 --- a/client.go +++ b/client.go @@ -351,7 +351,7 @@ func (mb *client) WriteFileRecord(address, quantity uint16, subReqSize uint16, r bffrReq = append(bffrReq, recordNumber...) bSize := []byte{byte(subReqSize / 2 >> 8), byte(subReqSize / 2 & 0xFF)} bffrReq = append(bffrReq, bSize...) - valueNext := value[(i * subReqSize):((i + 1) * subReqSize)] + valueNext := value[int(i * subReqSize):int((i + 1) * subReqSize)] bffrReq = append(bffrReq, valueNext...) } request := ProtocolDataUnit{ From 5efe5a4f552212a692082de01ecbc947199aadce Mon Sep 17 00:00:00 2001 From: cgerman-circ <53077375+cgerman-circ@users.noreply.github.com> Date: Fri, 6 Mar 2020 12:22:11 +0100 Subject: [PATCH 08/13] Added int explicit conversion to avoid overflows in valueNext assignation --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index 63d6bf2..f4dd748 100644 --- a/client.go +++ b/client.go @@ -344,7 +344,7 @@ func (mb *client) WriteFileRecord(address, quantity uint16, subReqSize uint16, r } bffrReq := make([]byte, 0) bffrReq = append(bffrReq, byte(totalLength-1)) - for i := address; i < address+dataFieldsLength; i += subReqSize { + for i := int(address); i < int(address+dataFieldsLength); i += int(subReqSize) { bffrReq = append(bffrReq, 6) //Sub Request reference type bffrReq = append(bffrReq, recUpdateFileNum...) recordNumber := []byte{byte(i >> 8), byte(i & 0xFF)} From 7d8bf471a772187228868314130a8b3ae1bb2ebf Mon Sep 17 00:00:00 2001 From: cgerman Date: Fri, 6 Mar 2020 13:06:15 +0100 Subject: [PATCH 09/13] Fixed wrong cast --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index f4dd748..92a1b85 100644 --- a/client.go +++ b/client.go @@ -351,7 +351,7 @@ func (mb *client) WriteFileRecord(address, quantity uint16, subReqSize uint16, r bffrReq = append(bffrReq, recordNumber...) bSize := []byte{byte(subReqSize / 2 >> 8), byte(subReqSize / 2 & 0xFF)} bffrReq = append(bffrReq, bSize...) - valueNext := value[int(i * subReqSize):int((i + 1) * subReqSize)] + valueNext := value[i*int(subReqSize) : (i+1)*int(subReqSize)] bffrReq = append(bffrReq, valueNext...) } request := ProtocolDataUnit{ From 298e617390d7ec0cdb68781a41d0e60e22ad735d Mon Sep 17 00:00:00 2001 From: Arantxa Date: Tue, 16 Jun 2020 09:23:35 +0200 Subject: [PATCH 10/13] Function NewTCPConnClientHandler allocates a new TCPClientHandler with a connection and an address --- tcpclient.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tcpclient.go b/tcpclient.go index 4e53c73..d8bf920 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -41,6 +41,16 @@ func NewTCPClientHandler(address string) *TCPClientHandler { return h } +// NewTCPConnClientHandler allocates a new TCPClientHandler. +func NewTCPConnClientHandler(address string, conn net.Conn) *TCPClientHandler { + h := &TCPClientHandler{} + h.Address = address + h.Timeout = tcpTimeout + h.IdleTimeout = tcpIdleTimeout + h.conn = conn + return h +} + // TCPClient creates TCP client with default handler and given connect string. func TCPClient(address string) Client { handler := NewTCPClientHandler(address) From 0856a83cfa61b0d3078c901e0a6cfef2a8572873 Mon Sep 17 00:00:00 2001 From: Edu Galvez Date: Fri, 14 May 2021 09:09:33 +0200 Subject: [PATCH 11/13] Fix "closing connection due to idle timeout" message even after closing connection manually. --- serial.go | 10 ++++++++-- tcpclient.go | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/serial.go b/serial.go index 7d14c67..9eecf14 100644 --- a/serial.go +++ b/serial.go @@ -53,11 +53,17 @@ func (mb *serialPort) connect() error { return nil } -func (mb *serialPort) Close() (err error) { +func (mb *serialPort) Close() error { mb.mu.Lock() defer mb.mu.Unlock() - return mb.close() + err := mb.close() + if err == nil { + if mb.closeTimer != nil { + mb.closeTimer.Stop() + } + } + return err } // close closes the serial port if it is connected. Caller must hold the mutex. diff --git a/tcpclient.go b/tcpclient.go index d8bf920..849a641 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -243,7 +243,13 @@ func (mb *tcpTransporter) Close() error { mb.mu.Lock() defer mb.mu.Unlock() - return mb.close() + err := mb.close() + if err == nil { + if mb.closeTimer != nil { + mb.closeTimer.Stop() + } + } + return err } // flush flushes pending data in the connection, From caa777fad88e300176442b182ae7010f8b9557ea Mon Sep 17 00:00:00 2001 From: cgerman Date: Wed, 26 Apr 2023 11:34:13 +0200 Subject: [PATCH 12/13] Added protection to return error when quantity requested and count field do not match --- client.go | 226 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 141 insertions(+), 85 deletions(-) diff --git a/client.go b/client.go index 92a1b85..d6db330 100644 --- a/client.go +++ b/client.go @@ -31,13 +31,16 @@ func NewClient2(packager Packager, transporter Transporter) Client { } // Request: -// Function code : 1 byte (0x01) -// Starting address : 2 bytes -// Quantity of coils : 2 bytes +// +// Function code : 1 byte (0x01) +// Starting address : 2 bytes +// Quantity of coils : 2 bytes +// // Response: -// Function code : 1 byte (0x01) -// Byte count : 1 byte -// Coil status : N* bytes (=N or N+1) +// +// Function code : 1 byte (0x01) +// Byte count : 1 byte +// Coil status : N* bytes (=N or N+1) func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error) { if quantity < 1 || quantity > 2000 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 2000) @@ -57,18 +60,25 @@ func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) return } + if count != int(quantity) { + err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, quantity) + return + } results = response.Data[1:] return } // Request: -// Function code : 1 byte (0x02) -// Starting address : 2 bytes -// Quantity of inputs : 2 bytes +// +// Function code : 1 byte (0x02) +// Starting address : 2 bytes +// Quantity of inputs : 2 bytes +// // Response: -// Function code : 1 byte (0x02) -// Byte count : 1 byte -// Input status : N* bytes (=N or N+1) +// +// Function code : 1 byte (0x02) +// Byte count : 1 byte +// Input status : N* bytes (=N or N+1) func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, err error) { if quantity < 1 || quantity > 2000 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 2000) @@ -88,18 +98,25 @@ func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) return } + if count != int(quantity) { + err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, quantity) + return + } results = response.Data[1:] return } // Request: -// Function code : 1 byte (0x03) -// Starting address : 2 bytes -// Quantity of registers : 2 bytes +// +// Function code : 1 byte (0x03) +// Starting address : 2 bytes +// Quantity of registers : 2 bytes +// // Response: -// Function code : 1 byte (0x03) -// Byte count : 1 byte -// Register value : Nx2 bytes +// +// Function code : 1 byte (0x03) +// Byte count : 1 byte +// Register value : Nx2 bytes func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte, err error) { if quantity < 1 || quantity > 125 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 125) @@ -119,18 +136,25 @@ func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) return } + if count != int(quantity)*2 { + err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, quantity) + return + } results = response.Data[1:] return } // Request: -// Function code : 1 byte (0x04) -// Starting address : 2 bytes -// Quantity of registers : 2 bytes +// +// Function code : 1 byte (0x04) +// Starting address : 2 bytes +// Quantity of registers : 2 bytes +// // Response: -// Function code : 1 byte (0x04) -// Byte count : 1 byte -// Input registers : N bytes +// +// Function code : 1 byte (0x04) +// Byte count : 1 byte +// Input registers : N bytes func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, err error) { if quantity < 1 || quantity > 125 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 125) @@ -150,18 +174,25 @@ func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) return } + if count != int(quantity)*2 { + err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, quantity) + return + } results = response.Data[1:] return } // Request: -// Function code : 1 byte (0x05) -// Output address : 2 bytes -// Output value : 2 bytes +// +// Function code : 1 byte (0x05) +// Output address : 2 bytes +// Output value : 2 bytes +// // Response: -// Function code : 1 byte (0x05) -// Output address : 2 bytes -// Output value : 2 bytes +// +// Function code : 1 byte (0x05) +// Output address : 2 bytes +// Output value : 2 bytes func (mb *client) WriteSingleCoil(address, value uint16) (results []byte, err error) { // The requested ON/OFF state can only be 0xFF00 and 0x0000 if value != 0xFF00 && value != 0x0000 { @@ -196,13 +227,16 @@ func (mb *client) WriteSingleCoil(address, value uint16) (results []byte, err er } // Request: -// Function code : 1 byte (0x06) -// Register address : 2 bytes -// Register value : 2 bytes +// +// Function code : 1 byte (0x06) +// Register address : 2 bytes +// Register value : 2 bytes +// // Response: -// Function code : 1 byte (0x06) -// Register address : 2 bytes -// Register value : 2 bytes +// +// Function code : 1 byte (0x06) +// Register address : 2 bytes +// Register value : 2 bytes func (mb *client) WriteSingleRegister(address, value uint16) (results []byte, err error) { request := ProtocolDataUnit{ FunctionCode: FuncCodeWriteSingleRegister, @@ -232,15 +266,18 @@ func (mb *client) WriteSingleRegister(address, value uint16) (results []byte, er } // Request: -// Function code : 1 byte (0x0F) -// Starting address : 2 bytes -// Quantity of outputs : 2 bytes -// Byte count : 1 byte -// Outputs value : N* bytes +// +// Function code : 1 byte (0x0F) +// Starting address : 2 bytes +// Quantity of outputs : 2 bytes +// Byte count : 1 byte +// Outputs value : N* bytes +// // Response: -// Function code : 1 byte (0x0F) -// Starting address : 2 bytes -// Quantity of outputs : 2 bytes +// +// Function code : 1 byte (0x0F) +// Starting address : 2 bytes +// Quantity of outputs : 2 bytes func (mb *client) WriteMultipleCoils(address, quantity uint16, value []byte) (results []byte, err error) { if quantity < 1 || quantity > 1968 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 1968) @@ -274,15 +311,18 @@ func (mb *client) WriteMultipleCoils(address, quantity uint16, value []byte) (re } // Request: -// Function code : 1 byte (0x10) -// Starting address : 2 bytes -// Quantity of outputs : 2 bytes -// Byte count : 1 byte -// Registers value : N* bytes +// +// Function code : 1 byte (0x10) +// Starting address : 2 bytes +// Quantity of outputs : 2 bytes +// Byte count : 1 byte +// Registers value : N* bytes +// // Response: -// Function code : 1 byte (0x10) -// Starting address : 2 bytes -// Quantity of registers : 2 bytes +// +// Function code : 1 byte (0x10) +// Starting address : 2 bytes +// Quantity of registers : 2 bytes func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) (results []byte, err error) { if quantity < 1 || quantity > 123 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 123) @@ -316,15 +356,18 @@ func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) } // Request: -// Function code : 1 byte (0x15) -// Starting address : 2 bytes -// Quantity of outputs : 2 bytes -// Byte count : 1 byte -// Registers value : N* bytes +// +// Function code : 1 byte (0x15) +// Starting address : 2 bytes +// Quantity of outputs : 2 bytes +// Byte count : 1 byte +// Registers value : N* bytes +// // Response: -// Function code : 1 byte (0x15) -// Starting address : 2 bytes -// Quantity of registers : 2 bytes +// +// Function code : 1 byte (0x15) +// Starting address : 2 bytes +// Quantity of registers : 2 bytes func (mb *client) WriteFileRecord(address, quantity uint16, subReqSize uint16, recUpdateFileNum []byte, value []byte) (results []byte, err error) { wRecControlFieldsLength := uint16(7) @@ -372,15 +415,18 @@ func (mb *client) WriteFileRecord(address, quantity uint16, subReqSize uint16, r } // Request: -// Function code : 1 byte (0x16) -// Reference address : 2 bytes -// AND-mask : 2 bytes -// OR-mask : 2 bytes +// +// Function code : 1 byte (0x16) +// Reference address : 2 bytes +// AND-mask : 2 bytes +// OR-mask : 2 bytes +// // Response: -// Function code : 1 byte (0x16) -// Reference address : 2 bytes -// AND-mask : 2 bytes -// OR-mask : 2 bytes +// +// Function code : 1 byte (0x16) +// Reference address : 2 bytes +// AND-mask : 2 bytes +// OR-mask : 2 bytes func (mb *client) MaskWriteRegister(address, andMask, orMask uint16) (results []byte, err error) { request := ProtocolDataUnit{ FunctionCode: FuncCodeMaskWriteRegister, @@ -415,17 +461,20 @@ func (mb *client) MaskWriteRegister(address, andMask, orMask uint16) (results [] } // Request: -// Function code : 1 byte (0x17) -// Read starting address : 2 bytes -// Quantity to read : 2 bytes -// Write starting address: 2 bytes -// Quantity to write : 2 bytes -// Write byte count : 1 byte -// Write registers value : N* bytes +// +// Function code : 1 byte (0x17) +// Read starting address : 2 bytes +// Quantity to read : 2 bytes +// Write starting address: 2 bytes +// Quantity to write : 2 bytes +// Write byte count : 1 byte +// Write registers value : N* bytes +// // Response: -// Function code : 1 byte (0x17) -// Byte count : 1 byte -// Read registers value : Nx2 bytes +// +// Function code : 1 byte (0x17) +// Byte count : 1 byte +// Read registers value : Nx2 bytes func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) { if readQuantity < 1 || readQuantity > 125 { err = fmt.Errorf("modbus: quantity to read '%v' must be between '%v' and '%v',", readQuantity, 1, 125) @@ -448,19 +497,26 @@ func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAdd err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", len(response.Data)-1, count) return } + if count != int(readQuantity)*2 { + err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, readQuantity) + return + } results = response.Data[1:] return } // Request: -// Function code : 1 byte (0x18) -// FIFO pointer address : 2 bytes +// +// Function code : 1 byte (0x18) +// FIFO pointer address : 2 bytes +// // Response: -// Function code : 1 byte (0x18) -// Byte count : 2 bytes -// FIFO count : 2 bytes -// FIFO count : 2 bytes (<=31) -// FIFO value register : Nx2 bytes +// +// Function code : 1 byte (0x18) +// Byte count : 2 bytes +// FIFO count : 2 bytes +// FIFO count : 2 bytes (<=31) +// FIFO value register : Nx2 bytes func (mb *client) ReadFIFOQueue(address uint16) (results []byte, err error) { request := ProtocolDataUnit{ FunctionCode: FuncCodeReadFIFOQueue, From cf1a34025be73e99506f74f4f2752451a4b84876 Mon Sep 17 00:00:00 2001 From: cgerman Date: Wed, 26 Apr 2023 11:41:09 +0200 Subject: [PATCH 13/13] Fixed new error message --- client.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client.go b/client.go index d6db330..c0d3606 100644 --- a/client.go +++ b/client.go @@ -61,7 +61,7 @@ func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error return } if count != int(quantity) { - err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, quantity) + err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, quantity) return } results = response.Data[1:] @@ -99,7 +99,7 @@ func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, return } if count != int(quantity) { - err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, quantity) + err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, quantity) return } results = response.Data[1:] @@ -137,7 +137,7 @@ func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte return } if count != int(quantity)*2 { - err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, quantity) + err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, quantity) return } results = response.Data[1:] @@ -175,7 +175,7 @@ func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, return } if count != int(quantity)*2 { - err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, quantity) + err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, quantity) return } results = response.Data[1:] @@ -498,7 +498,7 @@ func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAdd return } if count != int(readQuantity)*2 { - err = fmt.Errorf("modbus: response data size '%v' does not request quantity '%v'", count, readQuantity) + err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, readQuantity) return } results = response.Data[1:]