You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2020/10/30 09:53:54 UTC
[plc4x] branch feature/plc4go updated: - Finished implementing a
first fully working version of the integration-test-framework for plc4go
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch feature/plc4go
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/feature/plc4go by this push:
new 1b33b3f - Finished implementing a first fully working version of the integration-test-framework for plc4go
1b33b3f is described below
commit 1b33b3f816b087c2a135d65c72365d53bc540fe9
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Fri Oct 30 10:53:19 2020 +0100
- Finished implementing a first fully working version of the integration-test-framework for plc4go
---
.../plc4go/internal/plc4go/modbus/ModbusField.go | 40 +-
.../internal/plc4go/modbus/ModbusFieldHandler.go | 52 +-
.../plc4go/internal/plc4go/modbus/ModbusReader.go | 325 ++++-----
.../internal/plc4go/model/DefaultPlcReadRequest.go | 48 +-
.../plc4go/model/DefaultPlcReadResponse.go | 70 +-
.../plc4go/model/DefaultPlcWriteRequest.go | 30 +
.../plc4go/internal/plc4go/model/values/BOOL.go | 11 +-
.../plc4go/internal/plc4go/model/values/BYTE.go | 11 +-
.../plc4go/internal/plc4go/model/values/CHAR.go | 11 +-
.../plc4go/internal/plc4go/model/values/DATE.go | 12 +-
.../internal/plc4go/model/values/DATE_AND_TIME.go | 13 +-
.../plc4go/internal/plc4go/model/values/DINT.go | 12 +-
.../plc4go/internal/plc4go/model/values/DWORD.go | 11 +-
sandbox/plc4go/internal/plc4go/model/values/INT.go | 12 +-
.../plc4go/internal/plc4go/model/values/LINT.go | 12 +-
.../plc4go/internal/plc4go/model/values/LREAL.go | 12 +-
.../plc4go/internal/plc4go/model/values/LTIME.go | 13 +-
.../plc4go/internal/plc4go/model/values/LWORD.go | 11 +-
.../plc4go/internal/plc4go/model/values/NULL.go | 14 +-
.../plc4go/internal/plc4go/model/values/PlcList.go | 22 +-
.../internal/plc4go/model/values/PlcStruct.go | 22 +-
.../plc4go/internal/plc4go/model/values/REAL.go | 12 +-
.../plc4go/internal/plc4go/model/values/SINT.go | 11 +-
.../plc4go/internal/plc4go/model/values/STRING.go | 11 +-
.../plc4go/internal/plc4go/model/values/TIME.go | 13 +-
.../internal/plc4go/model/values/TIME_OF_DAY.go | 13 +-
.../plc4go/internal/plc4go/model/values/UDINT.go | 12 +-
.../plc4go/internal/plc4go/model/values/UINT.go | 12 +-
.../plc4go/internal/plc4go/model/values/ULINT.go | 12 +-
.../plc4go/internal/plc4go/model/values/USINT.go | 12 +-
.../plc4go/internal/plc4go/model/values/WCHAR.go | 12 +-
.../plc4go/internal/plc4go/model/values/WORD.go | 11 +-
.../plc4go/internal/plc4go/model/values/WSTRING.go | 12 +-
.../internal/plc4go/testutils/DriverTestRunner.go | 775 +++++++++++----------
.../plc4go/testutils/ParserSerializerTestRunner.go | 295 ++++----
.../plc4go/internal/plc4go/testutils/TestUtils.go | 52 ++
.../plc4go/pkg/plc4go/model/plc_read_response.go | 8 +-
.../plc4go/pkg/plc4go/model/plc_response_code.go | 63 ++
38 files changed, 1310 insertions(+), 790 deletions(-)
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusField.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusField.go
index 65a31df..d1f978a 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusField.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusField.go
@@ -19,23 +19,24 @@
package modbus
import (
+ "encoding/xml"
"errors"
"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
"strconv"
)
const (
- MODBUS_PROTOCOL_ADDRESS_OFFSET = 1
+ MODBUS_PROTOCOL_ADDRESS_OFFSET = 1
)
type ModbusPlcField struct {
- FieldType uint8
+ FieldType ModbusFieldType
Address uint16
Quantity uint16
Datatype string
}
-func NewModbusPlcField(fieldType uint8, address uint16, quantity uint16, datatype string) ModbusPlcField {
+func NewModbusPlcField(fieldType ModbusFieldType, address uint16, quantity uint16, datatype string) ModbusPlcField {
return ModbusPlcField{
FieldType: fieldType,
Address: address - MODBUS_PROTOCOL_ADDRESS_OFFSET,
@@ -44,7 +45,7 @@ func NewModbusPlcField(fieldType uint8, address uint16, quantity uint16, datatyp
}
}
-func NewModbusPlcFieldFromStrings(fieldType uint8, addressString string, quantityString string, datatype string) (model.PlcField, error) {
+func NewModbusPlcFieldFromStrings(fieldType ModbusFieldType, addressString string, quantityString string, datatype string) (model.PlcField, error) {
address, err := strconv.Atoi(addressString)
if err != nil {
return nil, errors.New("Couldn't parse address string '" + addressString + "' into an int")
@@ -57,12 +58,33 @@ func NewModbusPlcFieldFromStrings(fieldType uint8, addressString string, quantit
}
func (m ModbusPlcField) GetTypeName() string {
- return m.Datatype
+ return m.Datatype
}
func CastFromPlcField(plcField model.PlcField) (ModbusPlcField, error) {
- if modbusField, ok := plcField.(ModbusPlcField); ok {
- return modbusField, nil
- }
- return ModbusPlcField{}, errors.New("coudln't cast to ModbusPlcField")
+ if modbusField, ok := plcField.(ModbusPlcField); ok {
+ return modbusField, nil
+ }
+ return ModbusPlcField{}, errors.New("couldn't cast to ModbusPlcField")
+}
+
+func (m ModbusPlcField) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: m.FieldType.GetName()}}); err != nil {
+ return err
+ }
+
+ if err := e.EncodeElement(m.Address, xml.StartElement{Name: xml.Name{Local: "address"}}); err != nil {
+ return err
+ }
+ if err := e.EncodeElement(m.Quantity, xml.StartElement{Name: xml.Name{Local: "numberOfElements"}}); err != nil {
+ return err
+ }
+ if err := e.EncodeElement(m.Datatype, xml.StartElement{Name: xml.Name{Local: "dataType"}}); err != nil {
+ return err
+ }
+
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: m.FieldType.GetName()}}); err != nil {
+ return err
+ }
+ return nil
}
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusFieldHandler.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusFieldHandler.go
index 746b6a2..8147439 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusFieldHandler.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusFieldHandler.go
@@ -21,19 +21,37 @@ package modbus
import (
"errors"
"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
- "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
- "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
"regexp"
)
+type ModbusFieldType uint8
+
const (
- MODBUS_FIELD_COIL = uint8(0)
- MODBUS_FIELD_DISCRETE_INPUT = uint8(1)
- MODBUS_FIELD_INPUT_REGISTER = uint8(3)
- MODBUS_FIELD_HOLDING_REGISTER = uint8(4)
- MODBUS_FIELD_EXTENDED_REGISTER = uint8(6)
+ MODBUS_FIELD_COIL ModbusFieldType = 0x00
+ MODBUS_FIELD_DISCRETE_INPUT ModbusFieldType = 0x01
+ MODBUS_FIELD_INPUT_REGISTER ModbusFieldType = 0x03
+ MODBUS_FIELD_HOLDING_REGISTER ModbusFieldType = 0x04
+ MODBUS_FIELD_EXTENDED_REGISTER ModbusFieldType = 0x06
)
+func (m ModbusFieldType) GetName() string {
+ switch m {
+ case MODBUS_FIELD_COIL:
+ return "ModbusFieldHoldingRegister"
+ case MODBUS_FIELD_DISCRETE_INPUT:
+ return "ModbusFieldDiscreteInput"
+ case MODBUS_FIELD_INPUT_REGISTER:
+ return "ModbusFieldInputRegister"
+ case MODBUS_FIELD_HOLDING_REGISTER:
+ return "ModbusFieldHoldingRegister"
+ case MODBUS_FIELD_EXTENDED_REGISTER:
+ return "ModbusFieldExtendedRegister"
+ }
+ return ""
+}
+
type FieldHandler struct {
plc4xCoilPattern *regexp.Regexp
numericCoilPattern *regexp.Regexp
@@ -67,25 +85,25 @@ func NewFieldHandler() FieldHandler {
func (m FieldHandler) ParseQuery(query string) (model.PlcField, error) {
if match := utils.GetSubgropMatches(m.plc4xCoilPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_COIL, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_COIL, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
} else if match := utils.GetSubgropMatches(m.numericCoilPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_COIL, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_COIL, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
} else if match := utils.GetSubgropMatches(m.plc4xDiscreteInputPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_DISCRETE_INPUT, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_DISCRETE_INPUT, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
} else if match := utils.GetSubgropMatches(m.numericDiscreteInputPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_DISCRETE_INPUT, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_DISCRETE_INPUT, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
} else if match := utils.GetSubgropMatches(m.plc4xInputRegisterPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_INPUT_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_INPUT_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
} else if match := utils.GetSubgropMatches(m.numericInputRegisterPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_INPUT_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_INPUT_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
} else if match := utils.GetSubgropMatches(m.plc4xHoldingRegisterPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_HOLDING_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_HOLDING_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
} else if match := utils.GetSubgropMatches(m.numericHoldingRegisterPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_HOLDING_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_HOLDING_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
} else if match := utils.GetSubgropMatches(m.plc4xExtendedRegisterPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_EXTENDED_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_EXTENDED_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
} else if match := utils.GetSubgropMatches(m.numericExtendedRegisterPattern, query); match != nil {
- return NewModbusPlcFieldFromStrings(MODBUS_FIELD_EXTENDED_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+ return NewModbusPlcFieldFromStrings(MODBUS_FIELD_EXTENDED_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
}
return nil, errors.New("Invalid address format for address '" + query + "'")
}
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go
index 74c0cdb..fcf3edd 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go
@@ -19,188 +19,189 @@
package modbus
import (
- "errors"
- "fmt"
- "math"
- modbusModel "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
- plc4goModel "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/model"
- "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
- "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
- "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
- "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
- "sync/atomic"
+ "errors"
+ "fmt"
+ "math"
+ modbusModel "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
+ plc4goModel "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/model"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+ "sync/atomic"
)
type ModbusReader struct {
- transactionIdentifier uint16
- unitIdentifier int32
- messageCodec spi.MessageCodec
+ transactionIdentifier uint16
+ unitIdentifier int32
+ messageCodec spi.MessageCodec
spi.PlcReader
}
func NewModbusReader(transactionIdentifier uint16, messageCodec spi.MessageCodec) ModbusReader {
return ModbusReader{
- transactionIdentifier: transactionIdentifier,
- unitIdentifier: 0,
- messageCodec: messageCodec,
- }
+ transactionIdentifier: transactionIdentifier,
+ unitIdentifier: 0,
+ messageCodec: messageCodec,
+ }
}
func (m ModbusReader) Read(readRequest model.PlcReadRequest) <-chan model.PlcReadRequestResult {
- result := make(chan model.PlcReadRequestResult)
- // If we are requesting only one field, use a
- if len(readRequest.GetFieldNames()) == 1 {
- fieldName := readRequest.GetFieldNames()[0]
- field := readRequest.GetField(fieldName)
- modbusField, err := CastFromPlcField(field)
- if err != nil {
- result <- model.PlcReadRequestResult{
- Request: readRequest,
- Response: nil,
- Err: errors.New("invalid field item type"),
- }
- return result
- }
- numWords := uint16(math.Ceil(float64(modbusField.Quantity * uint16(modbusModel.ModbusDataTypeSizesValueOf(modbusField.Datatype).DataTypeSize())) / float64(2)))
- var pdu modbusModel.IModbusPDU = nil
- switch modbusField.FieldType {
- case MODBUS_FIELD_COIL:
- pdu = modbusModel.ModbusPDUReadCoilsRequest{
- StartingAddress: modbusField.Address,
- Quantity: modbusField.Quantity,
- }
- case MODBUS_FIELD_DISCRETE_INPUT:
- pdu = modbusModel.ModbusPDUReadDiscreteInputsRequest{
- StartingAddress: modbusField.Address,
- Quantity: modbusField.Quantity,
- }
- case MODBUS_FIELD_INPUT_REGISTER:
- pdu = modbusModel.ModbusPDUReadInputRegistersRequest{
- StartingAddress: modbusField.Address,
- Quantity: numWords,
- }
- case MODBUS_FIELD_HOLDING_REGISTER:
- pdu = modbusModel.ModbusPDUReadHoldingRegistersRequest{
- StartingAddress: modbusField.Address,
- Quantity: numWords,
- }
- case MODBUS_FIELD_EXTENDED_REGISTER:
- result <- model.PlcReadRequestResult{
- Request: readRequest,
- Response: nil,
- Err: errors.New("modbus currently doesn't support extended register requests"),
- }
- return result
- default:
- result <- model.PlcReadRequestResult{
- Request: readRequest,
- Response: nil,
- Err: errors.New("unsupported field type"),
- }
- return result
- }
+ result := make(chan model.PlcReadRequestResult)
+ // If we are requesting only one field, use a
+ if len(readRequest.GetFieldNames()) == 1 {
+ fieldName := readRequest.GetFieldNames()[0]
+ field := readRequest.GetField(fieldName)
+ modbusField, err := CastFromPlcField(field)
+ if err != nil {
+ result <- model.PlcReadRequestResult{
+ Request: readRequest,
+ Response: nil,
+ Err: errors.New("invalid field item type"),
+ }
+ return result
+ }
+ numWords := uint16(math.Ceil(float64(modbusField.Quantity*uint16(modbusModel.ModbusDataTypeSizesValueOf(modbusField.Datatype).DataTypeSize())) / float64(2)))
+ var pdu modbusModel.IModbusPDU = nil
+ switch modbusField.FieldType {
+ case MODBUS_FIELD_COIL:
+ pdu = modbusModel.ModbusPDUReadCoilsRequest{
+ StartingAddress: modbusField.Address,
+ Quantity: modbusField.Quantity,
+ }
+ case MODBUS_FIELD_DISCRETE_INPUT:
+ pdu = modbusModel.ModbusPDUReadDiscreteInputsRequest{
+ StartingAddress: modbusField.Address,
+ Quantity: modbusField.Quantity,
+ }
+ case MODBUS_FIELD_INPUT_REGISTER:
+ pdu = modbusModel.ModbusPDUReadInputRegistersRequest{
+ StartingAddress: modbusField.Address,
+ Quantity: numWords,
+ }
+ case MODBUS_FIELD_HOLDING_REGISTER:
+ pdu = modbusModel.ModbusPDUReadHoldingRegistersRequest{
+ StartingAddress: modbusField.Address,
+ Quantity: numWords,
+ }
+ case MODBUS_FIELD_EXTENDED_REGISTER:
+ result <- model.PlcReadRequestResult{
+ Request: readRequest,
+ Response: nil,
+ Err: errors.New("modbus currently doesn't support extended register requests"),
+ }
+ return result
+ default:
+ result <- model.PlcReadRequestResult{
+ Request: readRequest,
+ Response: nil,
+ Err: errors.New("unsupported field type"),
+ }
+ return result
+ }
- // Calculate a new unit identifier
- unitIdentifier := atomic.AddInt32(&m.unitIdentifier, 1)
- if unitIdentifier > math.MaxUint8 {
- unitIdentifier = 0
- atomic.StoreInt32(&m.unitIdentifier, 0)
- }
+ // Calculate a new unit identifier
+ unitIdentifier := atomic.AddInt32(&m.unitIdentifier, 1)
+ if unitIdentifier > math.MaxUint8 {
+ unitIdentifier = 0
+ atomic.StoreInt32(&m.unitIdentifier, 0)
+ }
- // Assemble the finished ADU
- requestAdu := modbusModel.ModbusTcpADU{
- TransactionIdentifier: m.transactionIdentifier,
- UnitIdentifier: uint8(unitIdentifier),
- Pdu: pdu,
- }
+ // Assemble the finished ADU
+ requestAdu := modbusModel.ModbusTcpADU{
+ TransactionIdentifier: m.transactionIdentifier,
+ UnitIdentifier: uint8(unitIdentifier),
+ Pdu: pdu,
+ }
- // Send the ADU over the wire
- err = m.messageCodec.Send(requestAdu)
- if err != nil {
- result <- model.PlcReadRequestResult{
- Request: readRequest,
- Response: nil,
- Err: errors.New("error sending message: " + err.Error()),
- }
- }
+ // Send the ADU over the wire
+ err = m.messageCodec.Send(requestAdu)
+ if err != nil {
+ result <- model.PlcReadRequestResult{
+ Request: readRequest,
+ Response: nil,
+ Err: errors.New("error sending message: " + err.Error()),
+ }
+ }
- // Register an expected response
- check := func(response interface{})bool {
- responseAdu := modbusModel.CastModbusTcpADU(response)
- return responseAdu.TransactionIdentifier == m.transactionIdentifier &&
- responseAdu.UnitIdentifier == requestAdu.UnitIdentifier
- }
- // Register a callback to handle the response
- responseChan := m.messageCodec.Expect(check)
- go func() {
- response := <-responseChan
- // Convert the response into an ADU
- responseAdu := modbusModel.CastModbusTcpADU(response)
- // Convert the modbus response into a PLC4X response
- readResponse, err := toPlc4xResponse(requestAdu, responseAdu, readRequest)
+ // Register an expected response
+ check := func(response interface{}) bool {
+ responseAdu := modbusModel.CastModbusTcpADU(response)
+ return responseAdu.TransactionIdentifier == m.transactionIdentifier &&
+ responseAdu.UnitIdentifier == requestAdu.UnitIdentifier
+ }
+ // Register a callback to handle the response
+ responseChan := m.messageCodec.Expect(check)
+ go func() {
+ response := <-responseChan
+ // Convert the response into an ADU
+ responseAdu := modbusModel.CastModbusTcpADU(response)
+ // Convert the modbus response into a PLC4X response
+ readResponse, err := toPlc4xResponse(requestAdu, responseAdu, readRequest)
- if err != nil {
- result <- model.PlcReadRequestResult{
- Request: readRequest,
- Err: errors.New("Error decoding response: " + err.Error()),
- }
- } else {
- result <- model.PlcReadRequestResult{
- Request: readRequest,
- Response: readResponse,
- }
- }
- }()
- } else {
- result <- model.PlcReadRequestResult{
- Request: readRequest,
- Response: nil,
- Err: errors.New("modbus only supports single-item requests"),
- }
- }
- fmt.Printf("Read Request %s", readRequest)
+ if err != nil {
+ result <- model.PlcReadRequestResult{
+ Request: readRequest,
+ Err: errors.New("Error decoding response: " + err.Error()),
+ }
+ } else {
+ result <- model.PlcReadRequestResult{
+ Request: readRequest,
+ Response: readResponse,
+ }
+ }
+ }()
+ } else {
+ result <- model.PlcReadRequestResult{
+ Request: readRequest,
+ Response: nil,
+ Err: errors.New("modbus only supports single-item requests"),
+ }
+ }
+ fmt.Printf("Read Request %s", readRequest)
return result
}
-func toPlc4xResponse(requestAdu modbusModel.ModbusTcpADU, responseAdu modbusModel.ModbusTcpADU, readRequest model.PlcReadRequest) (model.PlcReadResponse,error) {
- var data []uint8
- switch responseAdu.Pdu.(type) {
- case modbusModel.ModbusPDUReadDiscreteInputsResponse:
- pdu := modbusModel.CastModbusPDUReadDiscreteInputsResponse(responseAdu.Pdu)
- data = utils.Int8ToUint8(pdu.Value)
- // Pure Boolean ...
- case modbusModel.ModbusPDUReadCoilsResponse:
- pdu := modbusModel.CastModbusPDUReadCoilsResponse(&responseAdu.Pdu)
- data = utils.Int8ToUint8(pdu.Value)
- // Pure Boolean ...
- case modbusModel.ModbusPDUReadInputRegistersResponse:
- pdu := modbusModel.CastModbusPDUReadInputRegistersResponse(responseAdu.Pdu)
- data = utils.Int8ToUint8(pdu.Value)
- // DataIo ...
- case modbusModel.ModbusPDUReadHoldingRegistersResponse:
- pdu := modbusModel.CastModbusPDUReadHoldingRegistersResponse(responseAdu.Pdu)
- data = utils.Int8ToUint8(pdu.Value)
- default:
- return nil, errors.New("unsupported response type")
- }
+func toPlc4xResponse(requestAdu modbusModel.ModbusTcpADU, responseAdu modbusModel.ModbusTcpADU, readRequest model.PlcReadRequest) (model.PlcReadResponse, error) {
+ var data []uint8
+ switch responseAdu.Pdu.(type) {
+ case modbusModel.ModbusPDUReadDiscreteInputsResponse:
+ pdu := modbusModel.CastModbusPDUReadDiscreteInputsResponse(responseAdu.Pdu)
+ data = utils.Int8ToUint8(pdu.Value)
+ // Pure Boolean ...
+ case modbusModel.ModbusPDUReadCoilsResponse:
+ pdu := modbusModel.CastModbusPDUReadCoilsResponse(&responseAdu.Pdu)
+ data = utils.Int8ToUint8(pdu.Value)
+ // Pure Boolean ...
+ case modbusModel.ModbusPDUReadInputRegistersResponse:
+ pdu := modbusModel.CastModbusPDUReadInputRegistersResponse(responseAdu.Pdu)
+ data = utils.Int8ToUint8(pdu.Value)
+ // DataIo ...
+ case modbusModel.ModbusPDUReadHoldingRegistersResponse:
+ pdu := modbusModel.CastModbusPDUReadHoldingRegistersResponse(responseAdu.Pdu)
+ data = utils.Int8ToUint8(pdu.Value)
+ default:
+ return nil, errors.New("unsupported response type")
+ }
- // Get the field from the request
- fieldName := readRequest.GetFieldNames()[0]
- field, err := CastFromPlcField(readRequest.GetField(fieldName))
- if err != nil {
- return nil, errors.New("error casting to modbus-field")
- }
+ // Get the field from the request
+ fieldName := readRequest.GetFieldNames()[0]
+ field, err := CastFromPlcField(readRequest.GetField(fieldName))
+ if err != nil {
+ return nil, errors.New("error casting to modbus-field")
+ }
- // Decode the data according to the information from the request
- rb := utils.ReadBufferNew(data)
- value, err := modbusModel.DataItemParse(rb, field.Datatype, field.Quantity)
- if err != nil {
- return nil, err
- }
- values := map[string]values.PlcValue{}
- values[fieldName] = value
+ // Decode the data according to the information from the request
+ rb := utils.ReadBufferNew(data)
+ value, err := modbusModel.DataItemParse(rb, field.Datatype, field.Quantity)
+ if err != nil {
+ return nil, err
+ }
+ responseCodes := map[string]model.PlcResponseCode{}
+ values := map[string]values.PlcValue{}
+ values[fieldName] = value
+ responseCodes[fieldName] = model.PlcResponseCode_OK
- // Return the response
- return plc4goModel.NewDefaultPlcReadResponse(values), nil
+ // Return the response
+ return plc4goModel.NewDefaultPlcReadResponse(readRequest, responseCodes, values), nil
}
-
diff --git a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go
index b33002d..5a747be 100644
--- a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go
+++ b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go
@@ -19,6 +19,7 @@
package model
import (
+ "encoding/xml"
"errors"
"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
@@ -70,16 +71,45 @@ func (m DefaultPlcReadRequest) Execute() <-chan model.PlcReadRequestResult {
}
func (m DefaultPlcReadRequest) GetFieldNames() []string {
- fieldNames := []string{}
- for name := range m.fields {
- fieldNames = append(fieldNames, name)
- }
- return fieldNames
+ fieldNames := []string{}
+ for name := range m.fields {
+ fieldNames = append(fieldNames, name)
+ }
+ return fieldNames
}
func (m DefaultPlcReadRequest) GetField(name string) model.PlcField {
- if field, ok := m.fields[name]; ok {
- return field
- }
- return nil
+ if field, ok := m.fields[name]; ok {
+ return field
+ }
+ return nil
+}
+
+func (m DefaultPlcReadRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcReadRequest"}}); err != nil {
+ return err
+ }
+
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "fields"}}); err != nil {
+ return err
+ }
+ for fieldName, field := range m.fields {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: fieldName}}); err != nil {
+ return err
+ }
+ if err := e.EncodeElement(field, xml.StartElement{Name: xml.Name{Local: "field"}}); err != nil {
+ return err
+ }
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: fieldName}}); err != nil {
+ return err
+ }
+ }
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "fields"}}); err != nil {
+ return err
+ }
+
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcReadRequest"}}); err != nil {
+ return err
+ }
+ return nil
}
diff --git a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadResponse.go b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadResponse.go
index 2a68bfb..a7a2ca6 100644
--- a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadResponse.go
+++ b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadResponse.go
@@ -19,22 +19,78 @@
package model
import (
+ "encoding/xml"
"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
- "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
)
type DefaultPlcReadResponse struct {
- values map[string]values.PlcValue
+ request model.PlcReadRequest
+ responseCodes map[string]model.PlcResponseCode
+ values map[string]values.PlcValue
model.PlcReadResponse
}
-func NewDefaultPlcReadResponse(values map[string]values.PlcValue) DefaultPlcReadResponse {
- return DefaultPlcReadResponse {
- values: values,
- }
+func NewDefaultPlcReadResponse(request model.PlcReadRequest, responseCodes map[string]model.PlcResponseCode, values map[string]values.PlcValue) DefaultPlcReadResponse {
+ return DefaultPlcReadResponse{
+ request: request,
+ responseCodes: responseCodes,
+ values: values,
+ }
+}
+
+func (m DefaultPlcReadResponse) GetFieldNames() []string {
+ var fieldNames []string
+ for fieldName, _ := range m.values {
+ fieldNames = append(fieldNames, fieldName)
+ }
+ return fieldNames
+}
+
+func (m DefaultPlcReadResponse) GetRequest() model.PlcReadRequest {
+ return m.request
+}
+
+func (m DefaultPlcReadResponse) GetResponseCode(name string) model.PlcResponseCode {
+ return m.responseCodes[name]
}
func (m DefaultPlcReadResponse) GetValue(name string) values.PlcValue {
- return m.values[name]
+ return m.values[name]
}
+func (m DefaultPlcReadResponse) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcReadResponse"}}); err != nil {
+ return err
+ }
+
+ if err := e.EncodeElement(m.request, xml.StartElement{Name: xml.Name{Local: "PlcReadRequest"}}); err != nil {
+ return err
+ }
+
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "values"}}); err != nil {
+ return err
+ }
+ for _, fieldName := range m.GetFieldNames() {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: fieldName},
+ Attr: []xml.Attr{
+ {Name: xml.Name{Local: "result"}, Value: m.GetResponseCode(fieldName).GetName()},
+ }}); err != nil {
+ return err
+ }
+ if err := e.EncodeElement(m.GetValue(fieldName), xml.StartElement{Name: xml.Name{Local: "field"}}); err != nil {
+ return err
+ }
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: fieldName}}); err != nil {
+ return err
+ }
+ }
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "values"}}); err != nil {
+ return err
+ }
+
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcReadResponse"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go b/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go
index 51845b4..91e5a63 100644
--- a/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go
+++ b/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go
@@ -19,6 +19,7 @@
package model
import (
+ "encoding/xml"
"errors"
"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
@@ -81,3 +82,32 @@ type DefaultPlcWriteRequest struct {
func (m DefaultPlcWriteRequest) Execute() <-chan model.PlcWriteRequestResult {
return m.writer.Write(m)
}
+
+func (m DefaultPlcWriteRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcWriteRequest"}}); err != nil {
+ return err
+ }
+
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "fields"}}); err != nil {
+ return err
+ }
+ for fieldName, field := range m.fields {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: fieldName}}); err != nil {
+ return err
+ }
+ if err := e.EncodeElement(field, xml.StartElement{Name: xml.Name{Local: "field"}}); err != nil {
+ return err
+ }
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: fieldName}}); err != nil {
+ return err
+ }
+ }
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "fields"}}); err != nil {
+ return err
+ }
+
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcWriteRequest"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/BOOL.go b/sandbox/plc4go/internal/plc4go/model/values/BOOL.go
index 48c1a6b..8b13824 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/BOOL.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/BOOL.go
@@ -18,9 +18,11 @@
//
package values
+import "encoding/xml"
+
type PlcBOOL struct {
value bool
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcBOOL(value bool) PlcBOOL {
@@ -51,3 +53,10 @@ func (m PlcBOOL) GetBooleanAt(index uint32) bool {
func (m PlcBOOL) GetBooleanArray() []bool {
return []bool{m.value}
}
+
+func (m PlcBOOL) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcBOOL"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/BYTE.go b/sandbox/plc4go/internal/plc4go/model/values/BYTE.go
index 41e9ea6..bf01ff4 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/BYTE.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/BYTE.go
@@ -18,9 +18,11 @@
//
package values
+import "encoding/xml"
+
type PlcBYTE struct {
value uint8
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcBYTE(value uint8) PlcBYTE {
@@ -54,3 +56,10 @@ func (m PlcBYTE) GetBooleanArray() []bool {
m.value>>4&1 == 1, m.value>>5&1 == 1,
m.value>>6&1 == 1, m.value>>7&1 == 1}
}
+
+func (m PlcBYTE) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcBYTE"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/CHAR.go b/sandbox/plc4go/internal/plc4go/model/values/CHAR.go
index 37c0751..27e785f 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/CHAR.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/CHAR.go
@@ -18,9 +18,11 @@
//
package values
+import "encoding/xml"
+
type PlcCHAR struct {
value []byte
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcCHAR(value uint8) PlcCHAR {
@@ -36,3 +38,10 @@ func (m PlcCHAR) IsString() bool {
func (m PlcCHAR) GetString() string {
return string(m.value)
}
+
+func (m PlcCHAR) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcCHAR"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/DATE.go b/sandbox/plc4go/internal/plc4go/model/values/DATE.go
index f6274a6..be64679 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/DATE.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/DATE.go
@@ -19,12 +19,13 @@
package values
import (
- "time"
+ "encoding/xml"
+ "time"
)
type PlcDATE struct {
value time.Time
- PlcValueAdapter
+ PlcValueAdapter
}
func NewPlcDATE(value time.Time) PlcDATE {
@@ -40,3 +41,10 @@ func (m PlcDATE) IsTime() bool {
func (m PlcDATE) GetTime() time.Time {
return m.value
}
+
+func (m PlcDATE) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcDATE"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/DATE_AND_TIME.go b/sandbox/plc4go/internal/plc4go/model/values/DATE_AND_TIME.go
index 665a941..6396b1e 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/DATE_AND_TIME.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/DATE_AND_TIME.go
@@ -19,12 +19,13 @@
package values
import (
- "time"
+ "encoding/xml"
+ "time"
)
type PlcDATEANDTIME struct {
value time.Time
- PlcValueAdapter
+ PlcValueAdapter
}
func NewPlcDATEANDTIME(value time.Time) PlcDATEANDTIME {
@@ -36,6 +37,14 @@ func NewPlcDATEANDTIME(value time.Time) PlcDATEANDTIME {
func (m PlcDATEANDTIME) IsTime() bool {
return true
}
+
func (m PlcDATEANDTIME) GetTime() time.Time {
return m.value
}
+
+func (m PlcDATEANDTIME) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcDATEANDTIME"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/DINT.go b/sandbox/plc4go/internal/plc4go/model/values/DINT.go
index 80ff2cb..2405412 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/DINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/DINT.go
@@ -19,12 +19,13 @@
package values
import (
- "math"
+ "encoding/xml"
+ "math"
)
type PlcDINT struct {
value int32
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcDINT(value int32) PlcDINT {
@@ -123,3 +124,10 @@ func (m PlcDINT) GetFloat64() float64 {
//TODO: Check if this is ok
return float64(m.GetInt32())
}
+
+func (m PlcDINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcDINT"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/DWORD.go b/sandbox/plc4go/internal/plc4go/model/values/DWORD.go
index e50ad54..c01adab 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/DWORD.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/DWORD.go
@@ -18,9 +18,11 @@
//
package values
+import "encoding/xml"
+
type PlcDWORD struct {
value uint32
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcDWORD(value uint32) PlcDWORD {
@@ -66,3 +68,10 @@ func (m PlcDWORD) GetBooleanArray() []bool {
m.value>>28&1 == 1, m.value>>29&1 == 1,
m.value>>30&1 == 1, m.value>>31&1 == 1}
}
+
+func (m PlcDWORD) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcDWORD"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/INT.go b/sandbox/plc4go/internal/plc4go/model/values/INT.go
index 27faf7e..f9796c7 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/INT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/INT.go
@@ -19,12 +19,13 @@
package values
import (
- "math"
+ "encoding/xml"
+ "math"
)
type PlcINT struct {
value int16
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcINT(value int16) PlcINT {
@@ -116,3 +117,10 @@ func (m PlcINT) GetFloat64() float64 {
//TODO: Check if this is ok
return float64(m.GetInt8())
}
+
+func (m PlcINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcINT"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/LINT.go b/sandbox/plc4go/internal/plc4go/model/values/LINT.go
index 62d3f51..c7e13b0 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/LINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/LINT.go
@@ -19,12 +19,13 @@
package values
import (
- "math"
+ "encoding/xml"
+ "math"
)
type PlcLINT struct {
value int64
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcLINT(value int64) PlcLINT {
@@ -130,3 +131,10 @@ func (m PlcLINT) GetFloat64() float64 {
//TODO: Check if this is ok
return float64(m.GetInt64())
}
+
+func (m PlcLINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcLINT"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/LREAL.go b/sandbox/plc4go/internal/plc4go/model/values/LREAL.go
index 3ed9ffa..84914f3 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/LREAL.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/LREAL.go
@@ -19,12 +19,13 @@
package values
import (
- "math"
+ "encoding/xml"
+ "math"
)
type PlcLREAL struct {
value float64
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcLREAL(value float64) PlcLREAL {
@@ -142,3 +143,10 @@ func (m PlcLREAL) GetFloat32() float32 {
func (m PlcLREAL) GetFloat64() float64 {
return m.value
}
+
+func (m PlcLREAL) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcLREAL"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/LTIME.go b/sandbox/plc4go/internal/plc4go/model/values/LTIME.go
index 0125f0f..738fd18 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/LTIME.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/LTIME.go
@@ -19,12 +19,13 @@
package values
import (
- "time"
+ "encoding/xml"
+ "time"
)
type PlcLTIME struct {
value uint64
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcLTIME(value uint64) PlcLTIME {
@@ -36,6 +37,14 @@ func NewPlcLTIME(value uint64) PlcLTIME {
func (m PlcLTIME) IsDuration() bool {
return true
}
+
func (m PlcLTIME) GetDuration() time.Duration {
return time.Duration(m.value)
}
+
+func (m PlcLTIME) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcLTIME"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/LWORD.go b/sandbox/plc4go/internal/plc4go/model/values/LWORD.go
index f43bb50..072c740 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/LWORD.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/LWORD.go
@@ -18,9 +18,11 @@
//
package values
+import "encoding/xml"
+
type PlcLWORD struct {
value uint64
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcLWORD(value uint64) PlcLWORD {
@@ -82,3 +84,10 @@ func (m PlcLWORD) GetBooleanArray() []bool {
m.value>>60&1 == 1, m.value>>61&1 == 1,
m.value>>62&1 == 1, m.value>>63&1 == 1}
}
+
+func (m PlcLWORD) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcLWORD"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/NULL.go b/sandbox/plc4go/internal/plc4go/model/values/NULL.go
index eb45ac6..df25ba4 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/NULL.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/NULL.go
@@ -18,10 +18,22 @@
//
package values
+import "encoding/xml"
+
type PlcNULL struct {
- PlcValueAdapter
+ PlcValueAdapter
}
func NewPlcNULL() PlcNULL {
return PlcNULL{}
}
+
+func (m PlcNULL) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcNULL"}}); err != nil {
+ return err
+ }
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcNULL"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/PlcList.go b/sandbox/plc4go/internal/plc4go/model/values/PlcList.go
index 0831e20..daec134 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/PlcList.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/PlcList.go
@@ -18,7 +18,10 @@
//
package values
-import api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+import (
+ "encoding/xml"
+ api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+)
type PlcList struct {
values []api.PlcValue
@@ -46,3 +49,20 @@ func (m PlcList) GetIndex(i uint32) api.PlcValue {
func (m PlcList) GetList() []api.PlcValue {
return m.values
}
+
+func (m PlcList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcList"}}); err != nil {
+ return err
+ }
+
+ for _, value := range m.values {
+ if err := e.EncodeElement(value, xml.StartElement{Name: xml.Name{Local: "-set-by-element-"}}); err != nil {
+ return err
+ }
+ }
+
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcList"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/PlcStruct.go b/sandbox/plc4go/internal/plc4go/model/values/PlcStruct.go
index 3266d67..6670969 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/PlcStruct.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/PlcStruct.go
@@ -18,7 +18,10 @@
//
package values
-import api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+import (
+ "encoding/xml"
+ api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+)
type PlcStruct struct {
values map[string]api.PlcValue
@@ -60,3 +63,20 @@ func (m PlcStruct) GetValue(key string) api.PlcValue {
func (m PlcStruct) GetStruct() map[string]api.PlcValue {
return m.values
}
+
+func (m PlcStruct) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcNULL"}}); err != nil {
+ return err
+ }
+
+ for fieldName, fieldValue := range m.values {
+ if err := e.EncodeElement(fieldValue, xml.StartElement{Name: xml.Name{Local: fieldName}}); err != nil {
+ return err
+ }
+ }
+
+ if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcNULL"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/REAL.go b/sandbox/plc4go/internal/plc4go/model/values/REAL.go
index a662446..7c641a5 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/REAL.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/REAL.go
@@ -19,12 +19,13 @@
package values
import (
- "math"
+ "encoding/xml"
+ "math"
)
type PlcREAL struct {
value float32
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcREAL(value float32) PlcREAL {
@@ -136,3 +137,10 @@ func (m PlcREAL) GetFloat64() float64 {
//TODO: Check if this is ok
return float64(m.GetFloat32())
}
+
+func (m PlcREAL) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcREAL"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/SINT.go b/sandbox/plc4go/internal/plc4go/model/values/SINT.go
index ba57a7f..9bcbc67 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/SINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/SINT.go
@@ -18,9 +18,11 @@
//
package values
+import "encoding/xml"
+
type PlcSINT struct {
value int8
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcSINT(value int8) PlcSINT {
@@ -105,3 +107,10 @@ func (m PlcSINT) GetFloat64() float64 {
//TODO: Check if this is ok
return float64(m.GetInt8())
}
+
+func (m PlcSINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcSINT"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/STRING.go b/sandbox/plc4go/internal/plc4go/model/values/STRING.go
index ab7f598..48fe1b4 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/STRING.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/STRING.go
@@ -18,9 +18,11 @@
//
package values
+import "encoding/xml"
+
type PlcSTRING struct {
value string
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcSTRING(value string) PlcSTRING {
@@ -36,3 +38,10 @@ func (m PlcSTRING) IsString() bool {
func (m PlcSTRING) GetString() string {
return m.value
}
+
+func (m PlcSTRING) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcSTRING"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/TIME.go b/sandbox/plc4go/internal/plc4go/model/values/TIME.go
index 54c5ad4..38c54c6 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/TIME.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/TIME.go
@@ -19,12 +19,13 @@
package values
import (
- "time"
+ "encoding/xml"
+ "time"
)
type PlcTIME struct {
value uint32
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcTIME(value uint32) PlcTIME {
@@ -36,6 +37,14 @@ func NewPlcTIME(value uint32) PlcTIME {
func (m PlcTIME) IsDuration() bool {
return true
}
+
func (m PlcTIME) GetDuration() time.Duration {
return time.Duration(m.value)
}
+
+func (m PlcTIME) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcTIME"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/TIME_OF_DAY.go b/sandbox/plc4go/internal/plc4go/model/values/TIME_OF_DAY.go
index e9b73e6..1cd136f 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/TIME_OF_DAY.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/TIME_OF_DAY.go
@@ -19,12 +19,13 @@
package values
import (
- "time"
+ "encoding/xml"
+ "time"
)
type PlcTIMEOFDAY struct {
value time.Time
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcTIMEOFDAY(value time.Time) PlcTIMEOFDAY {
@@ -37,6 +38,14 @@ func NewPlcTIMEOFDAY(value time.Time) PlcTIMEOFDAY {
func (m PlcTIMEOFDAY) IsTime() bool {
return true
}
+
func (m PlcTIMEOFDAY) GetTime() time.Time {
return m.value
}
+
+func (m PlcTIMEOFDAY) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcTIMEOFDAY"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/UDINT.go b/sandbox/plc4go/internal/plc4go/model/values/UDINT.go
index 3104c18..6b78443 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/UDINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/UDINT.go
@@ -19,12 +19,13 @@
package values
import (
- "math"
+ "encoding/xml"
+ "math"
)
type PlcUDINT struct {
value uint32
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcUDINT(value uint32) PlcUDINT {
@@ -116,3 +117,10 @@ func (m PlcUDINT) GetFloat64() float64 {
//TODO: Check if this is ok
return float64(m.GetUint32())
}
+
+func (m PlcUDINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcUDINT"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/UINT.go b/sandbox/plc4go/internal/plc4go/model/values/UINT.go
index 8b59e47..3e35ff9 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/UINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/UINT.go
@@ -19,12 +19,13 @@
package values
import (
- "math"
+ "encoding/xml"
+ "math"
)
type PlcUINT struct {
value uint16
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcUINT(value uint16) PlcUINT {
@@ -102,3 +103,10 @@ func (m PlcUINT) GetFloat64() float64 {
//TODO: Check if this is ok
return float64(m.GetUint16())
}
+
+func (m PlcUINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcUINT"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/ULINT.go b/sandbox/plc4go/internal/plc4go/model/values/ULINT.go
index d4f9a63..2e7def5 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/ULINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/ULINT.go
@@ -19,12 +19,13 @@
package values
import (
- "math"
+ "encoding/xml"
+ "math"
)
type PlcULINT struct {
value uint64
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcULINT(value uint64) PlcULINT {
@@ -130,3 +131,10 @@ func (m PlcULINT) GetFloat64() float64 {
//TODO: Check if this is ok
return float64(m.GetUint32())
}
+
+func (m PlcULINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcULINT"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/USINT.go b/sandbox/plc4go/internal/plc4go/model/values/USINT.go
index 25a935e..9015c90 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/USINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/USINT.go
@@ -19,12 +19,13 @@
package values
import (
- "math"
+ "encoding/xml"
+ "math"
)
type PlcUSINT struct {
value uint8
- PlcSimpleNumericValueAdapter
+ PlcSimpleNumericValueAdapter
}
func NewPlcUSINT(value uint8) PlcUSINT {
@@ -88,3 +89,10 @@ func (m PlcUSINT) GetFloat64() float64 {
//TODO: Check if this is ok
return float64(m.GetUint8())
}
+
+func (m PlcUSINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcUSINT"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/WCHAR.go b/sandbox/plc4go/internal/plc4go/model/values/WCHAR.go
index f8e9186..4439062 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/WCHAR.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/WCHAR.go
@@ -19,12 +19,13 @@
package values
import (
- "unicode/utf16"
+ "encoding/xml"
+ "unicode/utf16"
)
type PlcWCHAR struct {
value []rune
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcWCHAR(value uint16) PlcWCHAR {
@@ -40,3 +41,10 @@ func (m PlcWCHAR) IsString() bool {
func (m PlcWCHAR) GetString() string {
return string(m.value)
}
+
+func (m PlcWCHAR) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcWCHAR"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/WORD.go b/sandbox/plc4go/internal/plc4go/model/values/WORD.go
index f53eef1..2e947c1 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/WORD.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/WORD.go
@@ -18,9 +18,11 @@
//
package values
+import "encoding/xml"
+
type PlcWORD struct {
value uint16
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcWORD(value uint16) PlcWORD {
@@ -58,3 +60,10 @@ func (m PlcWORD) GetBooleanArray() []bool {
m.value>>12&1 == 1, m.value>>13&1 == 1,
m.value>>14&1 == 1, m.value>>15&1 == 1}
}
+
+func (m PlcWORD) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcWORD"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/WSTRING.go b/sandbox/plc4go/internal/plc4go/model/values/WSTRING.go
index ed631de..f8e65c9 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/WSTRING.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/WSTRING.go
@@ -19,12 +19,13 @@
package values
import (
- "unicode/utf16"
+ "encoding/xml"
+ "unicode/utf16"
)
type PlcWSTRING struct {
value []rune
- PlcSimpleValueAdapter
+ PlcSimpleValueAdapter
}
func NewPlcWSTRING(value []uint16) PlcWSTRING {
@@ -40,3 +41,10 @@ func (m PlcWSTRING) IsString() bool {
func (m PlcWSTRING) GetString() string {
return string(m.value)
}
+
+func (m PlcWSTRING) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcWSTRING"}}); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/testutils/DriverTestRunner.go b/sandbox/plc4go/internal/plc4go/testutils/DriverTestRunner.go
index dc7cda3..aea4eda 100644
--- a/sandbox/plc4go/internal/plc4go/testutils/DriverTestRunner.go
+++ b/sandbox/plc4go/internal/plc4go/testutils/DriverTestRunner.go
@@ -19,415 +19,436 @@
package testutils
import (
- "encoding/hex"
- "errors"
- "fmt"
- "github.com/subchen/go-xmldom"
- "os"
- "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
- "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
- "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/transports/test"
- "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
- "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
- api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
- "strconv"
- "testing"
- "time"
+ "encoding/hex"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "github.com/subchen/go-xmldom"
+ "os"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/transports/test"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
+ api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
+ "strconv"
+ "testing"
+ "time"
)
type DriverTestsuite struct {
- name string
- driverName string
- driverParameters map[string]string
- setupSteps []TestStep
- teardownSteps []TestStep
- testcases []Testcase
+ name string
+ driverName string
+ driverParameters map[string]string
+ setupSteps []TestStep
+ teardownSteps []TestStep
+ testcases []Testcase
}
func (m DriverTestsuite) Run(driverManager plc4go.PlcDriverManager, testcase Testcase) error {
- // Get a connection
- connectionChan := driverManager.GetConnection(m.driverName + ":test://hurz")
- connectionResult := <-connectionChan
-
- if connectionResult.Err != nil {
- return errors.New("error getting a connection: " + connectionResult.Err.Error())
- }
-
- // Run the setup steps
- for _, testStep := range m.setupSteps {
- m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
- }
-
- // Run the actual scenario steps
- for _, testStep := range testcase.steps {
- m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
- }
-
- // Run the teardown steps
- for _, testStep := range m.teardownSteps {
- m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
- }
-
- return nil
+ // Get a connection
+ connectionChan := driverManager.GetConnection(m.driverName + ":test://hurz")
+ connectionResult := <-connectionChan
+
+ if connectionResult.Err != nil {
+ return errors.New("error getting a connection: " + connectionResult.Err.Error())
+ }
+
+ // Run the setup steps
+ for _, testStep := range m.setupSteps {
+ m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
+ }
+
+ // Run the actual scenario steps
+ for _, testStep := range testcase.steps {
+ m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
+ }
+
+ // Run the teardown steps
+ for _, testStep := range m.teardownSteps {
+ m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
+ }
+
+ return nil
}
func (m DriverTestsuite) ExecuteStep(connection plc4go.PlcConnection, testcase *Testcase, step TestStep) error {
- mc, ok := connection.(spi.TransportInstanceExposer)
- if !ok {
- return errors.New("couldn't access connections transport instance")
- }
- transportInstance := mc.GetTransportInstance()
-
- switch step.stepType {
- case StepType_API_REQUEST:
- switch step.payload.Name {
- case "TestReadRequest":
- // Assemble a read-request according to the information in the test xml
- rrb := connection.ReadRequestBuilder()
- for _, fieldNode := range step.payload.GetChild("fields").GetChildren("field") {
- fieldName := fieldNode.GetChild("name").Text
- fieldAddress := fieldNode.GetChild("address").Text
- rrb.AddItem(fieldName, fieldAddress)
- }
- readRequest, err := rrb.Build()
- if err != nil {
- return errors.New("Error creating read-request: " + err.Error())
- }
-
- // Execute the read-request and store the response-channel in the testcase.
- if testcase.readRequestResultChannel != nil {
- return errors.New("testcase read-request result channel already occupied")
- }
- testcase.readRequestResultChannel = readRequest.Execute()
- case "TestWriteRequest":
- wrb := connection.WriteRequestBuilder()
- for _, fieldNode := range step.payload.GetChild("fields").GetChildren("field") {
- fieldName := fieldNode.GetChild("name").Text
- fieldAddress := fieldNode.GetChild("address").Text
- fieldValue := fieldNode.GetChild("value").Text
- wrb.AddItem(fieldName, fieldAddress, fieldValue)
- }
- writeRequest, err := wrb.Build()
- if err != nil {
- return errors.New("Error creating write-request: " + err.Error())
- }
- if testcase.writeRequestResultChannel != nil {
- return errors.New("testcase write-request result channel already occupied")
- }
- testcase.writeRequestResultChannel = writeRequest.Execute()
- }
- case StepType_API_RESPONSE:
- switch step.payload.Name {
- case "DefaultPlcReadResponse":
- if testcase.readRequestResultChannel == nil {
- return errors.New("no read response expected")
- }
- readResponse := <- testcase.readRequestResultChannel
- // TODO: Implement comparing of the results ...
- fmt.Println(readResponse)
- case "DefaultPlcWriteResponse":
- if testcase.writeRequestResultChannel == nil {
- return errors.New("no write response expected")
- }
- writeResponse := <- testcase.writeRequestResultChannel
- // TODO: Implement comparing of the results ...
- fmt.Println(writeResponse)
- }
- case StepType_OUTGOING_PLC_MESSAGE:
- typeName := step.payload.Name
- payloadString := step.payload.XML()
-
- // Parse the xml into a real model
- message, err := model.ModbusXmlParserHelper{}.Parse(typeName, payloadString)
- if err != nil {
- return errors.New("error parsing xml: " + err.Error())
- }
-
- // Serialize the model into bytes
- ser, ok := message.(utils.Serializable)
- if !ok {
- return errors.New("error converting type into Serializable type: " + err.Error())
- }
- wb := utils.WriteBufferNew()
- err = ser.Serialize(*wb)
- if err != nil {
- return errors.New("error serializing message: " + err.Error())
- }
- expectedRawOutput := wb.GetBytes()
-
- // Read exactly this amount of bytes from the transport
- rawOutput, err := transportInstance.Read(uint32(len(expectedRawOutput)))
- if err != nil {
- return errors.New("error getting bytes from transport: " + err.Error())
- }
-
- // Compare the bytes read with the ones we expect
- for i := range expectedRawOutput {
- if expectedRawOutput[i] != rawOutput[i] {
- return errors.New("actual output doesn't match expected output")
- }
- }
- // If there's a difference, parse the input and display it to simplify debugging
- case StepType_OUTGOING_PLC_BYTES:
- // Read exactly this amount of bytes from the transport
- expectedRawInput, err := hex.DecodeString(step.payload.Text)
- if err != nil {
- return errors.New("error decoding hex-encoded byte data: " + err.Error())
- }
- rawInput, err := transportInstance.Read(uint32(len(expectedRawInput)))
- if err != nil {
- return errors.New("error getting bytes from transport: " + err.Error())
- }
-
- // Compare the bytes read with the ones we expect
- for i := range expectedRawInput {
- if expectedRawInput[i] != rawInput[i] {
- return errors.New("actual output doesn't match expected output")
- }
- }
- // If there's a difference, parse the input and display it to simplify debugging
- case StepType_INCOMING_PLC_MESSAGE:
- typeName := step.payload.Name
- payloadString := step.payload.XML()
-
- // Parse the xml into a real model
- message, err := model.ModbusXmlParserHelper{}.Parse(typeName, payloadString)
- if err != nil {
- return errors.New("error parsing xml: " + err.Error())
- }
-
- // Serialize the model into bytes
- ser, ok := message.(utils.Serializable)
- if !ok {
- return errors.New("error converting type into Serializable type: " + err.Error())
- }
- wb := utils.WriteBufferNew()
- err = ser.Serialize(*wb)
- if err != nil {
- return errors.New("error serializing message: " + err.Error())
- }
-
- // Send these bytes to the transport
- err = transportInstance.Write(wb.GetBytes())
- if err != nil {
- return errors.New("error writing data to transport: " + err.Error())
- }
- case StepType_INCOMING_PLC_BYTES:
- // Get the raw hex-data.
- rawInput, err := hex.DecodeString(step.payload.Text)
- if err != nil {
- return errors.New("error decoding hex-encoded byte data: " + err.Error())
- }
-
- // Send these bytes to the transport
- err = transportInstance.Write(rawInput)
- if err != nil {
- return errors.New("error writing data to transport: " + err.Error())
- }
- case StepType_DELAY:
- // Get the number of milliseconds
- delay, err := strconv.Atoi(step.payload.Text)
- if err != nil {
- return errors.New("invalid delay format: " + err.Error())
- }
- // Sleep for that long
- time.Sleep(time.Duration(delay))
- case StepType_TERMINATE:
- // Simply close the transport connection
- err := transportInstance.Close()
- if err != nil {
- return errors.New("error closing transport: " + err.Error())
- }
- }
- return nil
+ mc, ok := connection.(spi.TransportInstanceExposer)
+ if !ok {
+ return errors.New("couldn't access connections transport instance")
+ }
+ transportInstance := mc.GetTransportInstance()
+
+ switch step.stepType {
+ case StepType_API_REQUEST:
+ switch step.payload.Name {
+ case "TestReadRequest":
+ // Assemble a read-request according to the information in the test xml
+ rrb := connection.ReadRequestBuilder()
+ for _, fieldNode := range step.payload.GetChild("fields").GetChildren("field") {
+ fieldName := fieldNode.GetChild("name").Text
+ fieldAddress := fieldNode.GetChild("address").Text
+ rrb.AddItem(fieldName, fieldAddress)
+ }
+ readRequest, err := rrb.Build()
+ if err != nil {
+ return errors.New("Error creating read-request: " + err.Error())
+ }
+
+ // Execute the read-request and store the response-channel in the testcase.
+ if testcase.readRequestResultChannel != nil {
+ return errors.New("testcase read-request result channel already occupied")
+ }
+ testcase.readRequestResultChannel = readRequest.Execute()
+ case "TestWriteRequest":
+ wrb := connection.WriteRequestBuilder()
+ for _, fieldNode := range step.payload.GetChild("fields").GetChildren("field") {
+ fieldName := fieldNode.GetChild("name").Text
+ fieldAddress := fieldNode.GetChild("address").Text
+ fieldValue := fieldNode.GetChild("value").Text
+ wrb.AddItem(fieldName, fieldAddress, fieldValue)
+ }
+ writeRequest, err := wrb.Build()
+ if err != nil {
+ return errors.New("Error creating write-request: " + err.Error())
+ }
+ if testcase.writeRequestResultChannel != nil {
+ return errors.New("testcase write-request result channel already occupied")
+ }
+ testcase.writeRequestResultChannel = writeRequest.Execute()
+ }
+ case StepType_API_RESPONSE:
+ switch step.payload.Name {
+ case "PlcReadResponse":
+ if testcase.readRequestResultChannel == nil {
+ return errors.New("no read response expected")
+ }
+ readRequestResult := <-testcase.readRequestResultChannel
+ // Serialize the response to XML
+ actualResponse, err := xml.Marshal(readRequestResult.Response)
+ if err != nil {
+ return errors.New("error serializing response: " + err.Error())
+ }
+ // Get the reference XML
+ referenceSerialized := step.payload.XML()
+ // Compare the results
+ err = CompareResults(actualResponse, []byte(referenceSerialized))
+ if err != nil {
+ return errors.New("Error comparing the results: " + err.Error())
+ }
+ case "PlcWriteResponse":
+ if testcase.writeRequestResultChannel == nil {
+ return errors.New("no write response expected")
+ }
+ writeResponseResult := <-testcase.writeRequestResultChannel
+ // Serialize the response to XML
+ actualResponse, err := xml.Marshal(writeResponseResult.Response)
+ if err != nil {
+ return errors.New("error serializing response: " + err.Error())
+ }
+ // Get the reference XML
+ referenceSerialized := step.payload.XML()
+ // Compare the results
+ err = CompareResults(actualResponse, []byte(referenceSerialized))
+ if err != nil {
+ return errors.New("Error comparing the results: " + err.Error())
+ }
+ }
+ case StepType_OUTGOING_PLC_MESSAGE:
+ typeName := step.payload.Name
+ payloadString := step.payload.XML()
+
+ // Parse the xml into a real model
+ message, err := model.ModbusXmlParserHelper{}.Parse(typeName, payloadString)
+ if err != nil {
+ return errors.New("error parsing xml: " + err.Error())
+ }
+
+ // Serialize the model into bytes
+ ser, ok := message.(utils.Serializable)
+ if !ok {
+ return errors.New("error converting type into Serializable type: " + err.Error())
+ }
+ wb := utils.WriteBufferNew()
+ err = ser.Serialize(*wb)
+ if err != nil {
+ return errors.New("error serializing message: " + err.Error())
+ }
+ expectedRawOutput := wb.GetBytes()
+
+ // Read exactly this amount of bytes from the transport
+ rawOutput, err := transportInstance.Read(uint32(len(expectedRawOutput)))
+ if err != nil {
+ return errors.New("error getting bytes from transport: " + err.Error())
+ }
+
+ // Compare the bytes read with the ones we expect
+ for i := range expectedRawOutput {
+ if expectedRawOutput[i] != rawOutput[i] {
+ return errors.New("actual output doesn't match expected output")
+ }
+ }
+ // If there's a difference, parse the input and display it to simplify debugging
+ case StepType_OUTGOING_PLC_BYTES:
+ // Read exactly this amount of bytes from the transport
+ expectedRawInput, err := hex.DecodeString(step.payload.Text)
+ if err != nil {
+ return errors.New("error decoding hex-encoded byte data: " + err.Error())
+ }
+ rawInput, err := transportInstance.Read(uint32(len(expectedRawInput)))
+ if err != nil {
+ return errors.New("error getting bytes from transport: " + err.Error())
+ }
+
+ // Compare the bytes read with the ones we expect
+ for i := range expectedRawInput {
+ if expectedRawInput[i] != rawInput[i] {
+ return errors.New("actual output doesn't match expected output")
+ }
+ }
+ // If there's a difference, parse the input and display it to simplify debugging
+ case StepType_INCOMING_PLC_MESSAGE:
+ typeName := step.payload.Name
+ payloadString := step.payload.XML()
+
+ // Parse the xml into a real model
+ message, err := model.ModbusXmlParserHelper{}.Parse(typeName, payloadString)
+ if err != nil {
+ return errors.New("error parsing xml: " + err.Error())
+ }
+
+ // Serialize the model into bytes
+ ser, ok := message.(utils.Serializable)
+ if !ok {
+ return errors.New("error converting type into Serializable type: " + err.Error())
+ }
+ wb := utils.WriteBufferNew()
+ err = ser.Serialize(*wb)
+ if err != nil {
+ return errors.New("error serializing message: " + err.Error())
+ }
+
+ // Send these bytes to the transport
+ err = transportInstance.Write(wb.GetBytes())
+ if err != nil {
+ return errors.New("error writing data to transport: " + err.Error())
+ }
+ case StepType_INCOMING_PLC_BYTES:
+ // Get the raw hex-data.
+ rawInput, err := hex.DecodeString(step.payload.Text)
+ if err != nil {
+ return errors.New("error decoding hex-encoded byte data: " + err.Error())
+ }
+
+ // Send these bytes to the transport
+ err = transportInstance.Write(rawInput)
+ if err != nil {
+ return errors.New("error writing data to transport: " + err.Error())
+ }
+ case StepType_DELAY:
+ // Get the number of milliseconds
+ delay, err := strconv.Atoi(step.payload.Text)
+ if err != nil {
+ return errors.New("invalid delay format: " + err.Error())
+ }
+ // Sleep for that long
+ time.Sleep(time.Duration(delay))
+ case StepType_TERMINATE:
+ // Simply close the transport connection
+ err := transportInstance.Close()
+ if err != nil {
+ return errors.New("error closing transport: " + err.Error())
+ }
+ }
+ return nil
}
-func (m DriverTestsuite) ParseXml(referenceXml *xmldom.Node, parserArguments []string) {
- normalizeXml(referenceXml)
- //referenceSerialized := referenceXml.FirstChild().XML()
+func (m DriverTestsuite) ParseXml(referenceXml *xmldom.Node, parserArguments []string) {
+ normalizeXml(referenceXml)
+ //referenceSerialized := referenceXml.FirstChild().XML()
}
type Testcase struct {
- name string
- steps []TestStep
- readRequestResultChannel <-chan api.PlcReadRequestResult
- writeRequestResultChannel <-chan api.PlcWriteRequestResult
+ name string
+ steps []TestStep
+ readRequestResultChannel <-chan api.PlcReadRequestResult
+ writeRequestResultChannel <-chan api.PlcWriteRequestResult
}
type TestStep struct {
- name string
- stepType StepType
- parserArguments []string
- payload xmldom.Node
+ name string
+ stepType StepType
+ parserArguments []string
+ payload xmldom.Node
}
type StepType uint8
const (
- StepType_OUTGOING_PLC_MESSAGE StepType = 0x01
- StepType_OUTGOING_PLC_BYTES StepType = 0x02
- StepType_INCOMING_PLC_MESSAGE StepType = 0x03
- StepType_INCOMING_PLC_BYTES StepType = 0x04
- StepType_API_REQUEST StepType = 0x05
- StepType_API_RESPONSE StepType = 0x06
- StepType_DELAY StepType = 0x07
- StepType_TERMINATE StepType = 0x08
+ StepType_OUTGOING_PLC_MESSAGE StepType = 0x01
+ StepType_OUTGOING_PLC_BYTES StepType = 0x02
+ StepType_INCOMING_PLC_MESSAGE StepType = 0x03
+ StepType_INCOMING_PLC_BYTES StepType = 0x04
+ StepType_API_REQUEST StepType = 0x05
+ StepType_API_RESPONSE StepType = 0x06
+ StepType_DELAY StepType = 0x07
+ StepType_TERMINATE StepType = 0x08
)
func RunDriverTestsuite(t *testing.T, driver plc4go.PlcDriver, testPath string) {
- // Read the test-specification as XML file
- rootNode, err := ParseDriverTestsuiteXml(testPath)
- if err != nil {
- t.Error(err.Error())
- t.Fail()
- return
- }
-
- // Parse the contents of the test-specification
- testsuite, err := ParseDriverTestsuite(*rootNode)
- if err != nil {
- t.Error(err.Error())
- t.Fail()
- return
- }
-
- // Initialize the driver manager
- driverManager := plc4go.NewPlcDriverManager()
- driverManager.RegisterTransport(test.NewTestTransport())
- driverManager.RegisterDriver(driver)
-
- for _, testcase := range testsuite.testcases {
- testsuite.Run(driverManager, testcase)
- }
- // Execute the tests in the testsuite
- fmt.Printf(testsuite.name)
+ // Read the test-specification as XML file
+ rootNode, err := ParseDriverTestsuiteXml(testPath)
+ if err != nil {
+ t.Error(err.Error())
+ t.Fail()
+ return
+ }
+
+ // Parse the contents of the test-specification
+ testsuite, err := ParseDriverTestsuite(*rootNode)
+ if err != nil {
+ t.Error(err.Error())
+ t.Fail()
+ return
+ }
+
+ // Initialize the driver manager
+ driverManager := plc4go.NewPlcDriverManager()
+ driverManager.RegisterTransport(test.NewTestTransport())
+ driverManager.RegisterDriver(driver)
+
+ for _, testcase := range testsuite.testcases {
+ testsuite.Run(driverManager, testcase)
+ }
+ // Execute the tests in the testsuite
+ fmt.Printf(testsuite.name)
}
func ParseDriverTestsuiteXml(testPath string) (*xmldom.Node, error) {
- // Get the current working directory
- path, err := os.Getwd()
- if err != nil {
- return nil, err
- }
-
- // Check if the test-file is available
- info, err := os.Stat(path + "/../../../../" + testPath)
- if os.IsNotExist(err) {
- return nil, errors.New("test-File doesn't exist")
- }
- if info.IsDir() {
- return nil, errors.New("test-file refers to a directory")
- }
-
- // Open a reader for this file
- dat, err := os.Open(path + "/../../../../" + testPath)
- if err != nil {
- return nil, errors.New("error opening file")
- }
-
- // Read the xml
- node := xmldom.Must(xmldom.Parse(dat)).Root
- return node, nil
+ // Get the current working directory
+ path, err := os.Getwd()
+ if err != nil {
+ return nil, err
+ }
+
+ // Check if the test-file is available
+ info, err := os.Stat(path + "/../../../../" + testPath)
+ if os.IsNotExist(err) {
+ return nil, errors.New("test-File doesn't exist")
+ }
+ if info.IsDir() {
+ return nil, errors.New("test-file refers to a directory")
+ }
+
+ // Open a reader for this file
+ dat, err := os.Open(path + "/../../../../" + testPath)
+ if err != nil {
+ return nil, errors.New("error opening file")
+ }
+
+ // Read the xml
+ node := xmldom.Must(xmldom.Parse(dat)).Root
+ return node, nil
}
func ParseDriverTestsuite(node xmldom.Node) (*DriverTestsuite, error) {
- if node.Name != "driver-testsuite" {
- return nil, errors.New("invalid document structure")
- }
- var testsuiteName string
- var driverName string
- var setupSteps []TestStep
- var teardownSteps []TestStep
- var testcases []Testcase
- for _, childPtr := range node.Children {
- child := *childPtr
- if child.Name == "name" {
- testsuiteName = child.Text
- } else if child.Name == "driver-name" {
- driverName = child.Text
- } else if child.Name == "setup" {
- steps, err := ParseDriverTestsuiteSteps(child)
- if err != nil {
- return nil, errors.New("error parsing setup steps")
- }
- setupSteps = steps
- } else if child.Name == "teardown" {
- steps, err := ParseDriverTestsuiteSteps(child)
- if err != nil {
- return nil, errors.New("error teardown setup steps")
- }
- teardownSteps = steps
- } else if child.Name == "testcase" {
- testcaseName := child.FindOneByName("name").Text
- stepsNode := child.FindOneByName("steps")
- steps, err := ParseDriverTestsuiteSteps(*stepsNode)
- if err != nil {
- return nil, errors.New("error parsing testcase " + testcaseName + ": " + err.Error())
- }
- testcase := Testcase{
- name: testcaseName,
- steps: steps,
- }
- testcases = append(testcases, testcase)
- } else {
- return nil, errors.New("invalid document structure")
- }
- }
-
- return &DriverTestsuite{
- name: testsuiteName,
- driverName: driverName,
- setupSteps: setupSteps,
- teardownSteps: teardownSteps,
- testcases: testcases,
- }, nil
+ if node.Name != "driver-testsuite" {
+ return nil, errors.New("invalid document structure")
+ }
+ var testsuiteName string
+ var driverName string
+ var setupSteps []TestStep
+ var teardownSteps []TestStep
+ var testcases []Testcase
+ for _, childPtr := range node.Children {
+ child := *childPtr
+ if child.Name == "name" {
+ testsuiteName = child.Text
+ } else if child.Name == "driver-name" {
+ driverName = child.Text
+ } else if child.Name == "setup" {
+ steps, err := ParseDriverTestsuiteSteps(child)
+ if err != nil {
+ return nil, errors.New("error parsing setup steps")
+ }
+ setupSteps = steps
+ } else if child.Name == "teardown" {
+ steps, err := ParseDriverTestsuiteSteps(child)
+ if err != nil {
+ return nil, errors.New("error teardown setup steps")
+ }
+ teardownSteps = steps
+ } else if child.Name == "testcase" {
+ testcaseName := child.FindOneByName("name").Text
+ stepsNode := child.FindOneByName("steps")
+ steps, err := ParseDriverTestsuiteSteps(*stepsNode)
+ if err != nil {
+ return nil, errors.New("error parsing testcase " + testcaseName + ": " + err.Error())
+ }
+ testcase := Testcase{
+ name: testcaseName,
+ steps: steps,
+ }
+ testcases = append(testcases, testcase)
+ } else {
+ return nil, errors.New("invalid document structure")
+ }
+ }
+
+ return &DriverTestsuite{
+ name: testsuiteName,
+ driverName: driverName,
+ setupSteps: setupSteps,
+ teardownSteps: teardownSteps,
+ testcases: testcases,
+ }, nil
}
func ParseDriverTestsuiteSteps(node xmldom.Node) ([]TestStep, error) {
- var testSteps []TestStep
- for _, step := range node.Children {
- name := step.GetAttributeValue("name")
- var stepType StepType
- switch step.Name {
- case "api-request":
- stepType = StepType_API_REQUEST
- case "api-response":
- stepType = StepType_API_RESPONSE
- case "outgoing-plc-message":
- stepType = StepType_OUTGOING_PLC_MESSAGE
- case "incoming-plc-message":
- stepType = StepType_INCOMING_PLC_MESSAGE
- case "outgoing-plc-bytes":
- stepType = StepType_OUTGOING_PLC_BYTES
- case "incoming-plc-bytes":
- stepType = StepType_INCOMING_PLC_BYTES
- case "delay":
- stepType = StepType_DELAY
- case "terminate":
- stepType = StepType_TERMINATE
- }
- var parserArguments []string
- var payload *xmldom.Node
- for _, childNode := range step.Children {
- if childNode.Name == "parser-arguments" {
- for _, parserArgumentNode := range childNode.Children {
- parserArguments = append(parserArguments, parserArgumentNode.Text)
- }
- } else if payload == nil {
- payload = childNode
- } else {
- return nil, errors.New("test step can only contain a single payload element")
- }
- }
- if payload == nil {
- return nil, errors.New("missing payload element")
- }
- testSteps = append(testSteps, TestStep{
- name: name,
- stepType: stepType,
- parserArguments: parserArguments,
- payload: *payload,
- })
- }
- return testSteps, nil
+ var testSteps []TestStep
+ for _, step := range node.Children {
+ name := step.GetAttributeValue("name")
+ var stepType StepType
+ switch step.Name {
+ case "api-request":
+ stepType = StepType_API_REQUEST
+ case "api-response":
+ stepType = StepType_API_RESPONSE
+ case "outgoing-plc-message":
+ stepType = StepType_OUTGOING_PLC_MESSAGE
+ case "incoming-plc-message":
+ stepType = StepType_INCOMING_PLC_MESSAGE
+ case "outgoing-plc-bytes":
+ stepType = StepType_OUTGOING_PLC_BYTES
+ case "incoming-plc-bytes":
+ stepType = StepType_INCOMING_PLC_BYTES
+ case "delay":
+ stepType = StepType_DELAY
+ case "terminate":
+ stepType = StepType_TERMINATE
+ }
+ var parserArguments []string
+ var payload *xmldom.Node
+ for _, childNode := range step.Children {
+ if childNode.Name == "parser-arguments" {
+ for _, parserArgumentNode := range childNode.Children {
+ parserArguments = append(parserArguments, parserArgumentNode.Text)
+ }
+ } else if payload == nil {
+ payload = childNode
+ } else {
+ return nil, errors.New("test step can only contain a single payload element")
+ }
+ }
+ if payload == nil {
+ return nil, errors.New("missing payload element")
+ }
+ testSteps = append(testSteps, TestStep{
+ name: name,
+ stepType: stepType,
+ parserArguments: parserArguments,
+ payload: *payload,
+ })
+ }
+ return testSteps, nil
}
diff --git a/sandbox/plc4go/internal/plc4go/testutils/ParserSerializerTestRunner.go b/sandbox/plc4go/internal/plc4go/testutils/ParserSerializerTestRunner.go
index 20de594..8f0ba09 100644
--- a/sandbox/plc4go/internal/plc4go/testutils/ParserSerializerTestRunner.go
+++ b/sandbox/plc4go/internal/plc4go/testutils/ParserSerializerTestRunner.go
@@ -19,185 +19,156 @@
package testutils
import (
- "encoding/hex"
- "encoding/xml"
- "errors"
- "fmt"
- "github.com/ajankovic/xdiff"
- "github.com/ajankovic/xdiff/parser"
- "github.com/subchen/go-xmldom"
+ "encoding/hex"
+ "encoding/xml"
+ "fmt"
+ "github.com/subchen/go-xmldom"
"os"
- "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
- "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
- "strconv"
- "strings"
- "testing"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
+ "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
+ "strconv"
+ "strings"
+ "testing"
)
func RunParserSerializerTestsuite(t *testing.T, testPath string) {
- // Get the current working directory
- path, err := os.Getwd()
- if err != nil {
- t.Error(err)
- }
+ // Get the current working directory
+ path, err := os.Getwd()
+ if err != nil {
+ t.Error(err)
+ }
- // Check if the test-file is available
- info, err := os.Stat(path + "/../../../../" + testPath)
- if os.IsNotExist(err) {
- t.Error("Test-File doesn't exist")
- }
- if info.IsDir() {
- t.Error("Test-File refers to a directory")
- }
+ // Check if the test-file is available
+ info, err := os.Stat(path + "/../../../../" + testPath)
+ if os.IsNotExist(err) {
+ t.Error("Test-File doesn't exist")
+ }
+ if info.IsDir() {
+ t.Error("Test-File refers to a directory")
+ }
- // Open a reader for this file
- dat, err := os.Open(path + "/../../../../" + testPath)
- if err != nil {
- t.Error("Error opening file")
- }
+ // Open a reader for this file
+ dat, err := os.Open(path + "/../../../../" + testPath)
+ if err != nil {
+ t.Error("Error opening file")
+ }
- // Read the xml
- node := xmldom.Must(xmldom.Parse(dat)).Root
+ // Read the xml
+ node := xmldom.Must(xmldom.Parse(dat)).Root
- if node.Name != "testsuite" {
- t.Error("Invalid document structure")
- }
- var testsuiteName string
- for _, childPtr := range node.Children {
- curFailed := false
- child := *childPtr
- if child.Name == "name" {
- testsuiteName = child.Text
- } else if child.Name != "testcase" {
- t.Error("Invalid document structure")
- curFailed = true
- } else {
- t.Logf("running testsuite: %s test: %s", testsuiteName, (*(child.FindOneByName("name"))).Text)
- rawInputText := (*(child.FindOneByName("raw"))).Text
- rootType := (*(child.FindOneByName("root-type"))).Text
- parserArgumentsXml := child.FindOneByName("parser-arguments")
- var parserArguments []string
- if parserArgumentsXml != nil {
- for _, parserArgumentXml := range parserArgumentsXml.Children {
- parserArguments = append(parserArguments, parserArgumentXml.Text)
- }
- }
- referenceXml := child.FindOneByName("xml")
- normalizeXml(referenceXml)
- referenceSerialized := referenceXml.FirstChild().XML()
+ if node.Name != "testsuite" {
+ t.Error("Invalid document structure")
+ }
+ var testsuiteName string
+ for _, childPtr := range node.Children {
+ curFailed := false
+ child := *childPtr
+ if child.Name == "name" {
+ testsuiteName = child.Text
+ } else if child.Name != "testcase" {
+ t.Error("Invalid document structure")
+ curFailed = true
+ } else {
+ t.Logf("running testsuite: %s test: %s", testsuiteName, (*(child.FindOneByName("name"))).Text)
+ rawInputText := (*(child.FindOneByName("raw"))).Text
+ rootType := (*(child.FindOneByName("root-type"))).Text
+ parserArgumentsXml := child.FindOneByName("parser-arguments")
+ var parserArguments []string
+ if parserArgumentsXml != nil {
+ for _, parserArgumentXml := range parserArgumentsXml.Children {
+ parserArguments = append(parserArguments, parserArgumentXml.Text)
+ }
+ }
+ referenceXml := child.FindOneByName("xml")
+ normalizeXml(referenceXml)
+ referenceSerialized := referenceXml.FirstChild().XML()
- // Get the raw input by decoding the hex-encoded binary input
- rawInput, err := hex.DecodeString(rawInputText)
- if err != nil {
- t.Errorf("Error decoding test input")
- t.Fail()
- curFailed = true
- }
- readBuffer := utils.ReadBufferNew(rawInput)
+ // Get the raw input by decoding the hex-encoded binary input
+ rawInput, err := hex.DecodeString(rawInputText)
+ if err != nil {
+ t.Errorf("Error decoding test input")
+ t.Fail()
+ curFailed = true
+ }
+ readBuffer := utils.ReadBufferNew(rawInput)
- // Parse the input according to the settings of the testcase
- helper := new(model.ModbusParserHelper)
- msg, err := helper.Parse(rootType, parserArguments, readBuffer)
- if err != nil {
- t.Error("Error parsing input data: " + err.Error())
- t.Fail()
- curFailed = true
- }
+ // Parse the input according to the settings of the testcase
+ helper := new(model.ModbusParserHelper)
+ msg, err := helper.Parse(rootType, parserArguments, readBuffer)
+ if err != nil {
+ t.Error("Error parsing input data: " + err.Error())
+ t.Fail()
+ curFailed = true
+ }
- // Serialize the parsed object to XML
- actualSerialized, err := xml.Marshal(msg)
- if err != nil {
- t.Error("Error serializing the actual message: " + err.Error())
- t.Fail()
- curFailed = true
- }
+ // Serialize the parsed object to XML
+ actualSerialized, err := xml.Marshal(msg)
+ if err != nil {
+ t.Error("Error serializing the actual message: " + err.Error())
+ t.Fail()
+ curFailed = true
+ }
- // Compare the actual and the expected xml
- err = compareResults(actualSerialized, []byte(referenceSerialized))
- if err != nil {
- t.Error("Error comparing the results: " + err.Error())
- t.Fail()
- curFailed = true
- }
+ // Compare the actual and the expected xml
+ err = CompareResults(actualSerialized, []byte(referenceSerialized))
+ if err != nil {
+ t.Error("Error comparing the results: " + err.Error())
+ t.Fail()
+ curFailed = true
+ }
- // If all was ok, serialize the object again
- s, ok := msg.(utils.Serializable)
- if !ok {
- t.Error("Couldn't cast message to Serializable")
- t.Fail()
- curFailed = true
- }
- writeBuffer := utils.WriteBufferNew()
- err = s.Serialize(*writeBuffer)
- if !ok {
- t.Error("Couldn't serialize message back to byte array")
- t.Fail()
- curFailed = true
- }
+ // If all was ok, serialize the object again
+ s, ok := msg.(utils.Serializable)
+ if !ok {
+ t.Error("Couldn't cast message to Serializable")
+ t.Fail()
+ curFailed = true
+ }
+ writeBuffer := utils.WriteBufferNew()
+ err = s.Serialize(*writeBuffer)
+ if !ok {
+ t.Error("Couldn't serialize message back to byte array")
+ t.Fail()
+ curFailed = true
+ }
- // Check if the output matches in size and content
- rawOutput := writeBuffer.GetBytes()
- if len(rawInput) != len(rawOutput) {
- t.Error("Couldn't serialize message back to byte array")
- t.Fail()
- curFailed = true
- }
- for i, val := range rawInput {
- if rawOutput[i] != val {
- t.Error("Raw output doesn't match input at position: " + strconv.Itoa(i))
- t.Fail()
- curFailed = true
- }
- }
+ // Check if the output matches in size and content
+ rawOutput := writeBuffer.GetBytes()
+ if len(rawInput) != len(rawOutput) {
+ t.Error("Couldn't serialize message back to byte array")
+ t.Fail()
+ curFailed = true
+ }
+ for i, val := range rawInput {
+ if rawOutput[i] != val {
+ t.Error("Raw output doesn't match input at position: " + strconv.Itoa(i))
+ t.Fail()
+ curFailed = true
+ }
+ }
- if curFailed {
- // All worked
- t.Logf("FAILED")
- } else {
- // All worked
- t.Logf("SUCCESS")
- }
- }
- }
- fmt.Printf("name = %v\n", node.Name)
+ if curFailed {
+ // All worked
+ t.Logf("FAILED")
+ } else {
+ // All worked
+ t.Logf("SUCCESS")
+ }
+ }
+ }
+ fmt.Printf("name = %v\n", node.Name)
}
// Mainly remove linebreaks from text content.
func normalizeXml(input *xmldom.Node) {
- if len(input.Children) > 0 {
- for _, child := range input.Children {
- normalizeXml(child)
- }
- }
- if len(input.Text) > 0 {
- if strings.Contains(input.Text, "\n") {
- input.Text = strings.Replace(input.Text, "\n", "", -1)
- }
- }
+ if len(input.Children) > 0 {
+ for _, child := range input.Children {
+ normalizeXml(child)
+ }
+ }
+ if len(input.Text) > 0 {
+ if strings.Contains(input.Text, "\n") {
+ input.Text = strings.Replace(input.Text, "\n", "", -1)
+ }
+ }
}
-
-func compareResults(actualString []byte, referenceString []byte) error {
- // Now parse the xml strings of the actual and the reference in xdiff's dom
- p := parser.New()
- actual, err := p.ParseBytes(actualString)
- if err != nil {
- return errors.New("Error parsing actual input: " + err.Error())
- }
- reference, err := p.ParseBytes(referenceString)
- if err != nil {
- return errors.New("Error parsing reference input: " + err.Error())
- }
- // Use XDiff to actually do the comparison
- diff, err := xdiff.Compare(actual, reference)
- if err != nil {
- return errors.New("Error comparing xml trees: " + err.Error())
- }
- if diff != nil {
- enc := xdiff.NewTextEncoder(os.Stdout)
- if err := enc.Encode(diff); err != nil {
- return errors.New("Error outputting results: " + err.Error())
- }
- return errors.New("there were differences")
- }
- return nil
-}
\ No newline at end of file
diff --git a/sandbox/plc4go/internal/plc4go/testutils/TestUtils.go b/sandbox/plc4go/internal/plc4go/testutils/TestUtils.go
new file mode 100644
index 0000000..a3e21ff
--- /dev/null
+++ b/sandbox/plc4go/internal/plc4go/testutils/TestUtils.go
@@ -0,0 +1,52 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+package testutils
+
+import (
+ "errors"
+ "github.com/ajankovic/xdiff"
+ "github.com/ajankovic/xdiff/parser"
+ "os"
+)
+
+func CompareResults(actualString []byte, referenceString []byte) error {
+ // Now parse the xml strings of the actual and the reference in xdiff's dom
+ p := parser.New()
+ actual, err := p.ParseBytes(actualString)
+ if err != nil {
+ return errors.New("Error parsing actual input: " + err.Error())
+ }
+ reference, err := p.ParseBytes(referenceString)
+ if err != nil {
+ return errors.New("Error parsing reference input: " + err.Error())
+ }
+ // Use XDiff to actually do the comparison
+ diff, err := xdiff.Compare(actual, reference)
+ if err != nil {
+ return errors.New("Error comparing xml trees: " + err.Error())
+ }
+ if diff != nil {
+ enc := xdiff.NewTextEncoder(os.Stdout)
+ if err := enc.Encode(diff); err != nil {
+ return errors.New("Error outputting results: " + err.Error())
+ }
+ return errors.New("there were differences")
+ }
+ return nil
+}
diff --git a/sandbox/plc4go/pkg/plc4go/model/plc_read_response.go b/sandbox/plc4go/pkg/plc4go/model/plc_read_response.go
index f8ed246..b265766 100644
--- a/sandbox/plc4go/pkg/plc4go/model/plc_read_response.go
+++ b/sandbox/plc4go/pkg/plc4go/model/plc_read_response.go
@@ -21,8 +21,8 @@ package model
import "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
type PlcReadResponse interface {
-
- GetValue(name string) values.PlcValue
- PlcResponse
-
+ GetRequest() PlcReadRequest
+ GetResponseCode(name string) PlcResponseCode
+ GetValue(name string) values.PlcValue
+ PlcResponse
}
diff --git a/sandbox/plc4go/pkg/plc4go/model/plc_response_code.go b/sandbox/plc4go/pkg/plc4go/model/plc_response_code.go
new file mode 100644
index 0000000..be5ec40
--- /dev/null
+++ b/sandbox/plc4go/pkg/plc4go/model/plc_response_code.go
@@ -0,0 +1,63 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+package model
+
+type PlcResponseCode uint8
+
+const (
+ PlcResponseCode_OK PlcResponseCode = 0x01
+ PlcResponseCode_NOT_FOUND PlcResponseCode = 0x02
+ PlcResponseCode_ACCESS_DENIED PlcResponseCode = 0x03
+ PlcResponseCode_INVALID_ADDRESS PlcResponseCode = 0x04
+ PlcResponseCode_INVALID_DATATYPE PlcResponseCode = 0x05
+ PlcResponseCode_INVALID_DATA PlcResponseCode = 0x06
+ PlcResponseCode_INTERNAL_ERROR PlcResponseCode = 0x07
+ PlcResponseCode_REMOTE_BUSY PlcResponseCode = 0x08
+ PlcResponseCode_REMOTE_ERROR PlcResponseCode = 0x09
+ PlcResponseCode_UNSUPPORTED PlcResponseCode = 0x010
+ PlcResponseCode_RESPONSE_PENDING PlcResponseCode = 0x11
+)
+
+func (m PlcResponseCode) GetName() string {
+ switch m {
+ case PlcResponseCode_OK:
+ return "OK"
+ case PlcResponseCode_NOT_FOUND:
+ return "NOT_FOUND"
+ case PlcResponseCode_ACCESS_DENIED:
+ return "ACCESS_DENIED"
+ case PlcResponseCode_INVALID_ADDRESS:
+ return "INVALID_ADDRESS"
+ case PlcResponseCode_INVALID_DATATYPE:
+ return "INVALID_DATATYPE"
+ case PlcResponseCode_INVALID_DATA:
+ return "INVALID_DATA"
+ case PlcResponseCode_INTERNAL_ERROR:
+ return "INTERNAL_ERROR"
+ case PlcResponseCode_REMOTE_BUSY:
+ return "REMOTE_BUSY"
+ case PlcResponseCode_REMOTE_ERROR:
+ return "REMOTE_ERROR"
+ case PlcResponseCode_UNSUPPORTED:
+ return "UNSUPPORTED"
+ case PlcResponseCode_RESPONSE_PENDING:
+ return "RESPONSE_PENDING"
+ }
+ return ""
+}