You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by ho...@apache.org on 2023/01/07 02:32:29 UTC

[plc4x] branch develop updated: feat(plc4go/modbus): Implementing the correct reading of BOOL type

This is an automated email from the ASF dual-hosted git repository.

hongjinlin pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/develop by this push:
     new 17d7f765c6 feat(plc4go/modbus): Implementing the correct reading of BOOL type
17d7f765c6 is described below

commit 17d7f765c670f86c3fd110f010a3faafe8ee1c5a
Author: Jinlin Hong <89...@qq.com>
AuthorDate: Sat Jan 7 10:31:49 2023 +0800

    feat(plc4go/modbus): Implementing the correct reading of BOOL type
---
 plc4go/examples/read/hello_world_plc4go_read.go    | 16 ++++----
 plc4go/internal/modbus/Reader.go                   |  6 ++-
 plc4go/internal/modbus/Tag.go                      | 17 +++++++--
 plc4go/internal/modbus/TagHandler.go               | 24 ++++++------
 plc4go/protocols/modbus/readwrite/ParserHelper.go  |  2 +-
 .../protocols/modbus/readwrite/XmlParserHelper.go  |  2 +-
 .../protocols/modbus/readwrite/model/DataItem.go   | 44 +++++++++++++++-------
 7 files changed, 71 insertions(+), 40 deletions(-)

diff --git a/plc4go/examples/read/hello_world_plc4go_read.go b/plc4go/examples/read/hello_world_plc4go_read.go
index 465a7ff6c7..4ab56f6d54 100644
--- a/plc4go/examples/read/hello_world_plc4go_read.go
+++ b/plc4go/examples/read/hello_world_plc4go_read.go
@@ -24,7 +24,6 @@ import (
 
 	"github.com/apache/plc4x/plc4go/pkg/api"
 	"github.com/apache/plc4x/plc4go/pkg/api/drivers"
-	"github.com/apache/plc4x/plc4go/pkg/api/model"
 )
 
 func main() {
@@ -47,7 +46,9 @@ func main() {
 
 	// Prepare a read-request
 	readRequest, err := connection.ReadRequestBuilder().
-		AddTagAddress("tag", "holding-register:26:REAL").
+		AddTagAddress("field", "holding-register:26:INT").
+		AddTagAddress("field_bool_single", "holding-register:1:BOOL[1]").
+		AddTagAddress("field_bool_list", "holding-register:1.10:BOOL[20]").
 		Build()
 	if err != nil {
 		fmt.Printf("error preparing read-request: %s", connectionResult.GetErr().Error())
@@ -64,12 +65,9 @@ func main() {
 		return
 	}
 
-	// Do something with the response
-	if rrr.GetResponse().GetResponseCode("tag") != model.PlcResponseCode_OK {
-		fmt.Printf("error an non-ok return code: %s", rrr.GetResponse().GetResponseCode("tag").GetName())
-		return
+	readResponse := rrr.GetResponse()
+	for _, tagName := range readResponse.GetTagNames() {
+		plcValue := readResponse.GetValue(tagName)
+		fmt.Printf("%v\n", plcValue)
 	}
-
-	value := rrr.GetResponse().GetValue("tag")
-	fmt.Printf("Got result %f", value.GetFloat32())
 }
diff --git a/plc4go/internal/modbus/Reader.go b/plc4go/internal/modbus/Reader.go
index 482cc30750..b29c772fc9 100644
--- a/plc4go/internal/modbus/Reader.go
+++ b/plc4go/internal/modbus/Reader.go
@@ -76,6 +76,10 @@ func (m *Reader) Read(ctx context.Context, readRequest model.PlcReadRequest) <-c
 			return
 		}
 		numWords := uint16(math.Ceil(float64(modbusTagVar.Quantity*uint16(modbusTagVar.Datatype.DataTypeSize())) / float64(2)))
+		// BOOL type numWords = Ceil((Offset + Quantity) / 16)
+		if modbusTagVar.Datatype == readWriteModel.ModbusDataType_BOOL {
+			numWords = uint16(math.Ceil(float64(modbusTagVar.Offset+modbusTagVar.Quantity) / float64(16)))
+		}
 		log.Debug().Msgf("Working with %d words", numWords)
 		var pdu readWriteModel.ModbusPDU = nil
 		switch modbusTagVar.TagType {
@@ -190,7 +194,7 @@ func (m *Reader) ToPlc4xReadResponse(responseAdu readWriteModel.ModbusTcpADU, re
 
 	// Decode the data according to the information from the request
 	log.Trace().Msg("decode data")
-	value, err := readWriteModel.DataItemParse(data, tag.Datatype, tag.Quantity)
+	value, err := readWriteModel.DataItemParse(data, tag.Datatype, tag.Quantity, tag.Offset)
 	if err != nil {
 		return nil, errors.Wrap(err, "Error parsing data item")
 	}
diff --git a/plc4go/internal/modbus/Tag.go b/plc4go/internal/modbus/Tag.go
index 8958d18c4f..76cd1859be 100644
--- a/plc4go/internal/modbus/Tag.go
+++ b/plc4go/internal/modbus/Tag.go
@@ -42,24 +42,35 @@ type modbusTag struct {
 
 	TagType  TagType
 	Address  uint16
+	Offset   uint16
 	Quantity uint16
 	Datatype model2.ModbusDataType
 }
 
-func NewTag(tagType TagType, address uint16, quantity uint16, datatype model2.ModbusDataType) modbusTag {
+func NewTag(tagType TagType, address uint16, offset uint16, quantity uint16, datatype model2.ModbusDataType) modbusTag {
 	return modbusTag{
 		TagType:  tagType,
 		Address:  address - AddressOffset,
+		Offset:   offset,
 		Quantity: quantity,
 		Datatype: datatype,
 	}
 }
 
-func NewModbusPlcTagFromStrings(tagType TagType, addressString string, quantityString string, datatype model2.ModbusDataType) (model.PlcTag, error) {
+func NewModbusPlcTagFromStrings(tagType TagType, addressString string, offsetString string, quantityString string, datatype model2.ModbusDataType) (model.PlcTag, error) {
 	address, err := strconv.ParseUint(addressString, 10, 16)
 	if err != nil {
 		return nil, errors.Errorf("Couldn't parse address string '%s' into an int", addressString)
 	}
+	if offsetString == "" {
+		log.Debug().Msg("No offset supplied, assuming 0")
+		offsetString = "0"
+	}
+	offset, err := strconv.ParseUint(offsetString, 10, 16)
+	if err != nil {
+		log.Warn().Err(err).Msgf("Error during parsing for %s. Falling back to 1", offsetString)
+		offset = 0
+	}
 	if quantityString == "" {
 		log.Debug().Msg("No quantity supplied, assuming 1")
 		quantityString = "1"
@@ -69,7 +80,7 @@ func NewModbusPlcTagFromStrings(tagType TagType, addressString string, quantityS
 		log.Warn().Err(err).Msgf("Error during parsing for %s. Falling back to 1", quantityString)
 		quantity = 1
 	}
-	return NewTag(tagType, uint16(address), uint16(quantity), datatype), nil
+	return NewTag(tagType, uint16(address), uint16(offset), uint16(quantity), datatype), nil
 }
 
 func (m modbusTag) GetAddressString() string {
diff --git a/plc4go/internal/modbus/TagHandler.go b/plc4go/internal/modbus/TagHandler.go
index 9fa496e7e7..aac0bc0e70 100644
--- a/plc4go/internal/modbus/TagHandler.go
+++ b/plc4go/internal/modbus/TagHandler.go
@@ -58,8 +58,8 @@ type TagHandler struct {
 }
 
 func NewTagHandler() TagHandler {
-	generalAddressPattern := `(?P<address>\d+)(:(?P<datatype>[a-zA-Z_]+))?(\[(?P<quantity>\d+)])?$`
-	generalFixedDigitAddressPattern := `(?P<address>\d{4,5})?(:(?P<datatype>[a-zA-Z_]+))?(\[(?P<quantity>\d+)])?$`
+	generalAddressPattern := `(?P<address>\d+)\.?(?P<offset>\d+)?(:(?P<datatype>[a-zA-Z_]+))?(\[(?P<quantity>\d+)])?$`
+	generalFixedDigitAddressPattern := `(?P<address>\d{4,5})?\.?(?P<offset>\d+)?(:(?P<datatype>[a-zA-Z_]+))?(\[(?P<quantity>\d+)])?$`
 	return TagHandler{
 		plc4xCoilPattern:               regexp.MustCompile("^coil:" + generalAddressPattern),
 		numericCoilPattern:             regexp.MustCompile("^0[xX]?" + generalFixedDigitAddressPattern),
@@ -80,61 +80,61 @@ func (m TagHandler) ParseTag(tagAddress string) (model.PlcTag, error) {
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(Coil, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(Coil, match["address"], match["offset"], match["quantity"], typeByName)
 	} else if match := utils.GetSubgroupMatches(m.numericCoilPattern, tagAddress); match != nil {
 		typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(Coil, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(Coil, match["address"], match["offset"], match["quantity"], typeByName)
 	} else if match := utils.GetSubgroupMatches(m.plc4xDiscreteInputPattern, tagAddress); match != nil {
 		typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(DiscreteInput, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(DiscreteInput, match["address"], match["offset"], match["quantity"], typeByName)
 	} else if match := utils.GetSubgroupMatches(m.numericDiscreteInputPattern, tagAddress); match != nil {
 		typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(DiscreteInput, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(DiscreteInput, match["address"], match["offset"], match["quantity"], typeByName)
 	} else if match := utils.GetSubgroupMatches(m.plc4xInputRegisterPattern, tagAddress); match != nil {
 		typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(InputRegister, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(InputRegister, match["address"], match["offset"], match["quantity"], typeByName)
 	} else if match := utils.GetSubgroupMatches(m.numericInputRegisterPattern, tagAddress); match != nil {
 		typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(InputRegister, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(InputRegister, match["address"], match["offset"], match["quantity"], typeByName)
 	} else if match := utils.GetSubgroupMatches(m.plc4xHoldingRegisterPattern, tagAddress); match != nil {
 		typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(HoldingRegister, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(HoldingRegister, match["address"], match["offset"], match["quantity"], typeByName)
 	} else if match := utils.GetSubgroupMatches(m.numericHoldingRegisterPattern, tagAddress); match != nil {
 		typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(HoldingRegister, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(HoldingRegister, match["address"], match["offset"], match["quantity"], typeByName)
 	} else if match := utils.GetSubgroupMatches(m.plc4xExtendedRegisterPattern, tagAddress); match != nil {
 		typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(ExtendedRegister, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(ExtendedRegister, match["address"], match["offset"], match["quantity"], typeByName)
 	} else if match := utils.GetSubgroupMatches(m.numericExtendedRegisterPattern, tagAddress); match != nil {
 		typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
 		if !ok {
 			return nil, errors.Errorf("Unknown type %s", match["datatype"])
 		}
-		return NewModbusPlcTagFromStrings(ExtendedRegister, match["address"], match["quantity"], typeByName)
+		return NewModbusPlcTagFromStrings(ExtendedRegister, match["address"], match["offset"], match["quantity"], typeByName)
 	}
 	return nil, errors.Errorf("Invalid address format for address '%s'", tagAddress)
 }
diff --git a/plc4go/protocols/modbus/readwrite/ParserHelper.go b/plc4go/protocols/modbus/readwrite/ParserHelper.go
index 83ccafbbd4..38bb3f1994 100644
--- a/plc4go/protocols/modbus/readwrite/ParserHelper.go
+++ b/plc4go/protocols/modbus/readwrite/ParserHelper.go
@@ -40,7 +40,7 @@ func (m ModbusParserHelper) Parse(typeName string, arguments []string, io utils.
 		if err != nil {
 			return nil, errors.Wrap(err, "Error parsing")
 		}
-		return model.DataItemParseWithBuffer(io, dataType, numberOfValues)
+		return model.DataItemParseWithBuffer(io, dataType, numberOfValues, 0)
 	case "ModbusPDUReadFileRecordResponseItem":
 		return model.ModbusPDUReadFileRecordResponseItemParseWithBuffer(io)
 	case "ModbusDeviceInformationObject":
diff --git a/plc4go/protocols/modbus/readwrite/XmlParserHelper.go b/plc4go/protocols/modbus/readwrite/XmlParserHelper.go
index 9790ce62ef..19ccbfdd73 100644
--- a/plc4go/protocols/modbus/readwrite/XmlParserHelper.go
+++ b/plc4go/protocols/modbus/readwrite/XmlParserHelper.go
@@ -51,7 +51,7 @@ func (m ModbusXmlParserHelper) Parse(typeName string, xmlString string, parserAr
 			return nil, err
 		}
 		numberOfValues := uint16(parsedUint1)
-		return model.DataItemParseWithBuffer(utils.NewXmlReadBuffer(strings.NewReader(xmlString)), dataType, numberOfValues)
+		return model.DataItemParseWithBuffer(utils.NewXmlReadBuffer(strings.NewReader(xmlString)), dataType, numberOfValues, 0)
 	case "ModbusPDUReadFileRecordResponseItem":
 		return model.ModbusPDUReadFileRecordResponseItemParseWithBuffer(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "ModbusDeviceInformationObject":
diff --git a/plc4go/protocols/modbus/readwrite/model/DataItem.go b/plc4go/protocols/modbus/readwrite/model/DataItem.go
index 135c89fdc6..2742c8ac1b 100644
--- a/plc4go/protocols/modbus/readwrite/model/DataItem.go
+++ b/plc4go/protocols/modbus/readwrite/model/DataItem.go
@@ -24,34 +24,52 @@ import (
 	"github.com/apache/plc4x/plc4go/spi/utils"
 	"github.com/apache/plc4x/plc4go/spi/values"
 	"github.com/pkg/errors"
+	"math"
 )
 
 // Code generated by code-generation. DO NOT EDIT.
 
-func DataItemParse(theBytes []byte, dataType ModbusDataType, numberOfValues uint16) (api.PlcValue, error) {
-	return DataItemParseWithBuffer(utils.NewReadBufferByteBased(theBytes), dataType, numberOfValues)
+func DataItemParse(theBytes []byte, dataType ModbusDataType, numberOfValues uint16, offset uint16) (api.PlcValue, error) {
+	return DataItemParseWithBuffer(utils.NewReadBufferByteBased(theBytes), dataType, numberOfValues, offset)
 }
 
-func DataItemParseWithBuffer(readBuffer utils.ReadBuffer, dataType ModbusDataType, numberOfValues uint16) (api.PlcValue, error) {
+func DataItemParseWithBuffer(readBuffer utils.ReadBuffer, dataType ModbusDataType, numberOfValues uint16, offset uint16) (api.PlcValue, error) {
 	readBuffer.PullContext("DataItem")
 	switch {
 	case dataType == ModbusDataType_BOOL && numberOfValues == uint16(1): // BOOL
-		// Reserved Field (Just skip the bytes)
-		if _, _err := readBuffer.ReadUint16("reserved", 15); _err != nil {
-			return nil, errors.Wrap(_err, "Error parsing reserved field")
-		}
-
-		// Simple Field (value)
-		value, _valueErr := readBuffer.ReadBit("value")
-		if _valueErr != nil {
-			return nil, errors.Wrap(_valueErr, "Error parsing 'value' field")
+		_numberOfValues := uint16(math.Ceil(float64((offset + numberOfValues)) / float64(16)))
+		var value bool
+		var _valueErr error
+		// find the offset bit
+		for i := 0; i < int(_numberOfValues*16); i++ {
+			// skip offset
+			if i != int(offset) {
+				readBuffer.ReadBit("value")
+				continue
+			}
+			// Simple Field (value)
+			value, _valueErr = readBuffer.ReadBit("value")
+			if _valueErr != nil {
+				return nil, errors.Wrap(_valueErr, "Error parsing 'value' field")
+			}
+			break
 		}
 		readBuffer.CloseContext("DataItem")
 		return values.NewPlcBOOL(value), nil
 	case dataType == ModbusDataType_BOOL: // List
+		_numberOfValues := uint16(math.Ceil(float64((offset + numberOfValues)) / float64(16)))
 		// Array Field (value)
 		var value []api.PlcValue
-		for i := 0; i < int(numberOfValues); i++ {
+		for i := 0; i < int(_numberOfValues*16); i++ {
+			// skip offset
+			if i < int(offset) {
+				readBuffer.ReadBit("value")
+				continue
+			}
+			// read end
+			if i == int(offset+numberOfValues) {
+				break
+			}
 			_item, _itemErr := readBuffer.ReadBit("value")
 			if _itemErr != nil {
 				return nil, errors.Wrap(_itemErr, "Error parsing 'value' field")