Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plc4go): Implementing the correct reading of BOOL types #545

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions plc4go/examples/read/hello_world_plc4go_read.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func main() {
drivers.RegisterModbusTcpDriver(driverManager)

// Get a connection to a remote PLC
crc := driverManager.GetConnection("modbus-tcp://192.168.23.30")
crc := driverManager.GetConnection("modbus-tcp://192.168.10.180")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably externalize this because every one is probably going to have the device at a different IP


// Wait for the driver to connect (or not)
connectionResult := <-crc
Expand All @@ -47,6 +47,8 @@ func main() {
// Prepare a read-request
readRequest, err := connection.ReadRequestBuilder().
AddQuery("field", "holding-register:26:REAL").
AddQuery("field_bool_single", "holding-register:1:BOOL[1]").
AddQuery("field_bool_list", "holding-register:1.10:BOOL[20]").
Build()
if err != nil {
fmt.Printf("error preparing read-request: %s", connectionResult.GetErr().Error())
Expand All @@ -69,6 +71,23 @@ func main() {
return
}

if rrr.GetResponse().GetResponseCode("field_bool_single") != model.PlcResponseCode_OK {
fmt.Printf("error an non-ok return code: %s", rrr.GetResponse().GetResponseCode("field_bool_single").GetName())
return
}

if rrr.GetResponse().GetResponseCode("field_bool_list") != model.PlcResponseCode_OK {
fmt.Printf("error an non-ok return code: %s", rrr.GetResponse().GetResponseCode("field_bool_list").GetName())
return
}

value := rrr.GetResponse().GetValue("field")
fmt.Printf("Got result %f", value.GetFloat32())
fmt.Printf("Got result of field: %f\n", value.GetFloat32())

valueBoolSingle := rrr.GetResponse().GetValue("field_bool_single")
fmt.Printf("Got result of field_bool_single: %t\n", valueBoolSingle.GetBool())

valueBoolList := rrr.GetResponse().GetValue("field_bool_list")
array := valueBoolList.GetList()
fmt.Printf("Got result of field_bool_list: %v\n", array)
}
17 changes: 14 additions & 3 deletions plc4go/internal/modbus/Field.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,35 @@ const (
type PlcField struct {
FieldType FieldType
Address uint16
Offset uint16
Quantity uint16
Datatype model2.ModbusDataType
}

func NewField(fieldType FieldType, address uint16, quantity uint16, datatype model2.ModbusDataType) PlcField {
func NewField(fieldType FieldType, address uint16, offset uint16, quantity uint16, datatype model2.ModbusDataType) PlcField {
return PlcField{
FieldType: fieldType,
Address: address - AddressOffset,
Offset: offset,
Quantity: quantity,
Datatype: datatype,
}
}

func NewModbusPlcFieldFromStrings(fieldType FieldType, addressString string, quantityString string, datatype model2.ModbusDataType) (model.PlcField, error) {
func NewModbusPlcFieldFromStrings(fieldType FieldType, addressString string, offsetString string, quantityString string, datatype model2.ModbusDataType) (model.PlcField, 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"
Expand All @@ -63,7 +74,7 @@ func NewModbusPlcFieldFromStrings(fieldType FieldType, addressString string, qua
log.Warn().Err(err).Msgf("Error during parsing for %s. Falling back to 1", quantityString)
quantity = 1
}
return NewField(fieldType, uint16(address), uint16(quantity), datatype), nil
return NewField(fieldType, uint16(address), uint16(offset), uint16(quantity), datatype), nil
}

func (m PlcField) GetAddressString() string {
Expand Down
24 changes: 12 additions & 12 deletions plc4go/internal/modbus/FieldHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ type FieldHandler struct {
}

func NewFieldHandler() FieldHandler {
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 FieldHandler{
plc4xCoilPattern: regexp.MustCompile("^coil:" + generalAddressPattern),
numericCoilPattern: regexp.MustCompile("^0[xX]?" + generalFixedDigitAddressPattern),
Expand All @@ -79,61 +79,61 @@ func (m FieldHandler) ParseQuery(query string) (model.PlcField, error) {
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(Coil, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(Coil, match["address"], match["offset"], match["quantity"], typeByName)
} else if match := utils.GetSubgroupMatches(m.numericCoilPattern, query); match != nil {
typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(Coil, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(Coil, match["address"], match["offset"], match["quantity"], typeByName)
} else if match := utils.GetSubgroupMatches(m.plc4xDiscreteInputPattern, query); match != nil {
typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(DiscreteInput, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(DiscreteInput, match["address"], match["offset"], match["quantity"], typeByName)
} else if match := utils.GetSubgroupMatches(m.numericDiscreteInputPattern, query); match != nil {
typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(DiscreteInput, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(DiscreteInput, match["address"], match["offset"], match["quantity"], typeByName)
} else if match := utils.GetSubgroupMatches(m.plc4xInputRegisterPattern, query); match != nil {
typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(InputRegister, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(InputRegister, match["address"], match["offset"], match["quantity"], typeByName)
} else if match := utils.GetSubgroupMatches(m.numericInputRegisterPattern, query); match != nil {
typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(InputRegister, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(InputRegister, match["address"], match["offset"], match["quantity"], typeByName)
} else if match := utils.GetSubgroupMatches(m.plc4xHoldingRegisterPattern, query); match != nil {
typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(HoldingRegister, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(HoldingRegister, match["address"], match["offset"], match["quantity"], typeByName)
} else if match := utils.GetSubgroupMatches(m.numericHoldingRegisterPattern, query); match != nil {
typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(HoldingRegister, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(HoldingRegister, match["address"], match["offset"], match["quantity"], typeByName)
} else if match := utils.GetSubgroupMatches(m.plc4xExtendedRegisterPattern, query); match != nil {
typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(ExtendedRegister, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(ExtendedRegister, match["address"], match["offset"], match["quantity"], typeByName)
} else if match := utils.GetSubgroupMatches(m.numericExtendedRegisterPattern, query); match != nil {
typeByName, ok := model2.ModbusDataTypeByName(match["datatype"])
if !ok {
return nil, errors.Errorf("Unknown type %s", match["datatype"])
}
return NewModbusPlcFieldFromStrings(ExtendedRegister, match["address"], match["quantity"], typeByName)
return NewModbusPlcFieldFromStrings(ExtendedRegister, match["address"], match["offset"], match["quantity"], typeByName)
}
return nil, errors.Errorf("Invalid address format for address '%s'", query)
}
6 changes: 5 additions & 1 deletion plc4go/internal/modbus/Reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ func (m *Reader) Read(ctx context.Context, readRequest model.PlcReadRequest) <-c
return
}
numWords := uint16(math.Ceil(float64(modbusField.Quantity*uint16(modbusField.Datatype.DataTypeSize())) / float64(2)))
// BOOL type numWords = Ceil((Offset + Quantity) / 16)
if modbusField.Datatype == readWriteModel.ModbusDataType_BOOL {
numWords = uint16(math.Ceil(float64(modbusField.Offset+modbusField.Quantity) / float64(16)))
}
log.Debug().Msgf("Working with %d words", numWords)
var pdu readWriteModel.ModbusPDU = nil
switch modbusField.FieldType {
Expand Down Expand Up @@ -191,7 +195,7 @@ func (m *Reader) ToPlc4xReadResponse(responseAdu readWriteModel.ModbusTcpADU, re
// Decode the data according to the information from the request
log.Trace().Msg("decode data")
rb := utils.NewReadBufferByteBased(data)
value, err := readWriteModel.DataItemParse(rb, field.Datatype, field.Quantity)
value, err := readWriteModel.DataItemParse(rb, field.Datatype, field.Quantity, field.Offset)
if err != nil {
return nil, errors.Wrap(err, "Error parsing data item")
}
Expand Down
2 changes: 1 addition & 1 deletion plc4go/protocols/modbus/readwrite/ParserHelper.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion plc4go/protocols/modbus/readwrite/XmlParserHelper.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 29 additions & 11 deletions plc4go/protocols/modbus/readwrite/model/DataItem.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.