You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2021/04/10 11:43:23 UTC

[plc4x] branch develop updated: plc4go: fixed several bugs concerning type conversions

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

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


The following commit(s) were added to refs/heads/develop by this push:
     new b4b2a63  plc4go: fixed several bugs concerning type conversions
b4b2a63 is described below

commit b4b2a63e2993a7a29554db49b0e77ed756783db1
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Sat Apr 10 13:43:10 2021 +0200

    plc4go: fixed several bugs concerning type conversions
    
    + ManualTestRunner.go uses *testing.T now to better report failures
    + Implemented first draft of date/time
    + Fixed int and bit strings
    + Fixed PlcValueAdapter.go falsely having a plcValue child (was meant to be a "implements")
---
 .../main/drivers/tests/manual_ads_driver_test.go   |   2 +-
 plc4go/internal/plc4go/ads/Reader.go               |   2 +-
 plc4go/internal/plc4go/spi/MessageCodec.go         |   3 +
 .../plc4go/spi/testutils/ManualTestRunner.go       | 192 +++++++++++----------
 plc4go/internal/plc4go/spi/values/DATE.go          |  10 +-
 plc4go/internal/plc4go/spi/values/DATE_AND_TIME.go |  10 +-
 plc4go/internal/plc4go/spi/values/INT.go           |   8 +-
 plc4go/internal/plc4go/spi/values/LTIME.go         |   9 +
 plc4go/internal/plc4go/spi/values/PlcBitString.go  |   8 +-
 .../internal/plc4go/spi/values/PlcValueAdapter.go  |  18 +-
 plc4go/internal/plc4go/spi/values/TIME.go          |   9 +
 plc4go/internal/plc4go/spi/values/TIME_OF_DAY.go   |   4 +
 plc4go/pkg/plc4go/values/plc_value.go              |   4 +-
 13 files changed, 166 insertions(+), 113 deletions(-)

diff --git a/plc4go/cmd/main/drivers/tests/manual_ads_driver_test.go b/plc4go/cmd/main/drivers/tests/manual_ads_driver_test.go
index 24a4698..92b2634 100644
--- a/plc4go/cmd/main/drivers/tests/manual_ads_driver_test.go
+++ b/plc4go/cmd/main/drivers/tests/manual_ads_driver_test.go
@@ -85,7 +85,7 @@ func TestManualAds(t *testing.T) {
 	driverManager := plc4go.NewPlcDriverManager()
 	driverManager.RegisterDriver(ads.NewDriver())
 	driverManager.RegisterTransport(tcp.NewTransport())
-	test := testutils.NewManualTestSuite(connectionString, driverManager)
+	test := testutils.NewManualTestSuite(connectionString, driverManager, t)
 	test.AddTestCase("main.hurz_BOOL:BOOL", true)
 	test.AddTestCase("main.hurz_BYTE:BYTE", []bool{false, false, true, false, true, false, true, false})
 	test.AddTestCase("main.hurz_WORD:WORD", []bool{true, false, true, false, false, true, false, true, true, false, true, true, true, false, false, false})
diff --git a/plc4go/internal/plc4go/ads/Reader.go b/plc4go/internal/plc4go/ads/Reader.go
index b1858f8..8a985d0 100644
--- a/plc4go/internal/plc4go/ads/Reader.go
+++ b/plc4go/internal/plc4go/ads/Reader.go
@@ -407,7 +407,7 @@ func (m *Reader) ToPlc4xReadResponse(amsTcpPaket readWriteModel.AmsTCPPacket, re
 	plcValues := map[string]values.PlcValue{}
 	// Get the field from the request
 	for _, fieldName := range readRequest.GetFieldNames() {
-		log.Trace().Msg("get a field from request")
+		log.Debug().Msgf("get a field from request with name %s", fieldName)
 		field, err := castToAdsFieldFromPlcField(readRequest.GetField(fieldName))
 		if err != nil {
 			return nil, errors.Wrap(err, "error casting to ads-field")
diff --git a/plc4go/internal/plc4go/spi/MessageCodec.go b/plc4go/internal/plc4go/spi/MessageCodec.go
index 39e841a..bcb86c5 100644
--- a/plc4go/internal/plc4go/spi/MessageCodec.go
+++ b/plc4go/internal/plc4go/spi/MessageCodec.go
@@ -173,6 +173,7 @@ func (m *DefaultCodec) TimeoutExpectations(now time.Time) {
 			// Remove this expectation from the list.
 			m.Expectations = append(m.Expectations[:index], m.Expectations[index+1:]...)
 			// Call the error handler.
+			// TODO: decouple from worker thread
 			err := expectation.GetHandleError()(plcerrors.NewTimeoutError(now.Sub(expectation.GetExpiration())))
 			if err != nil {
 				log.Error().Err(err).Msg("Got an error handling error on expectation")
@@ -188,9 +189,11 @@ func (m *DefaultCodec) HandleMessages(message interface{}) bool {
 		// If it does, let it handle the message.
 		if accepts := expectation.GetAcceptsMessage()(message); accepts {
 			log.Debug().Stringer("expectation", expectation).Msg("accepts message")
+			// TODO: decouple from worker thread
 			err := expectation.GetHandleMessage()(message)
 			if err != nil {
 				// Pass the error to the error handler.
+				// TODO: decouple from worker thread
 				err := expectation.GetHandleError()(err)
 				if err != nil {
 					log.Error().Err(err).Msg("Got an error handling error on expectation")
diff --git a/plc4go/internal/plc4go/spi/testutils/ManualTestRunner.go b/plc4go/internal/plc4go/spi/testutils/ManualTestRunner.go
index 6a0ad52..5c0b522 100644
--- a/plc4go/internal/plc4go/spi/testutils/ManualTestRunner.go
+++ b/plc4go/internal/plc4go/spi/testutils/ManualTestRunner.go
@@ -21,11 +21,14 @@ package testutils
 
 import (
 	"fmt"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
 	"github.com/apache/plc4x/plc4go/pkg/plc4go"
 	"github.com/apache/plc4x/plc4go/pkg/plc4go/model"
 	"github.com/rs/zerolog/log"
 	"math/rand"
+	"reflect"
 	"strings"
+	"testing"
 	"time"
 )
 
@@ -39,12 +42,14 @@ type ManualTestSuite struct {
 	ConnectionString string
 	DriverManager    plc4go.PlcDriverManager
 	TestCases        []ManualTestCase
+	t                *testing.T
 }
 
-func NewManualTestSuite(connectionString string, driverManager plc4go.PlcDriverManager) *ManualTestSuite {
+func NewManualTestSuite(connectionString string, driverManager plc4go.PlcDriverManager, t *testing.T) *ManualTestSuite {
 	return &ManualTestSuite{
 		ConnectionString: connectionString,
 		DriverManager:    driverManager,
+		t:                t,
 	}
 }
 
@@ -62,111 +67,118 @@ func (m *ManualTestSuite) Run() {
 	// Run all entries separately:
 	for _, testCase := range m.TestCases {
 		fieldName := testCase.Address
+		m.t.Run(fieldName, func(t *testing.T) {
+			m.runSingleTest(t, connection, fieldName, testCase)
+		})
+	}
+	m.t.Run("combinedTest", func(t *testing.T) {
+		m.runBurstTest(t, connection)
+	})
+}
 
-		{
-			// Prepare the read-request
-			readRequestBuilder := connection.Connection.ReadRequestBuilder()
-			readRequestBuilder.AddQuery(fieldName, testCase.Address)
-			readRequest, err := readRequestBuilder.Build()
-			if err != nil {
-				panic(err)
-			}
-
-			// Execute the read request
-			readResponseResult := <-readRequest.Execute()
-			if readResponseResult.Err != nil {
-				panic(err)
-			}
-			readResponse := readResponseResult.Response
-
-			// Check the result
-			assertEquals(1, len(readResponse.GetFieldNames()), fieldName)
-			assertEquals(fieldName, readResponse.GetFieldNames()[0], fieldName)
-			assertEquals(model.PlcResponseCode_OK, readResponse.GetResponseCode(fieldName), fieldName)
-			assertNotNil(readResponse.GetValue(fieldName), fieldName)
-			// TODO: is list is not the right call here. There is also a bug which breaks the field on the get list call which should be fixed
-			if readResponse.GetValue(fieldName).IsList() && false {
-				plcList := readResponse.GetValue(fieldName).GetList()
-				expectedValues := testCase.ExpectedReadValue.([]interface{})
-				for j := 0; j < len(expectedValues); j++ {
-					assertEquals(expectedValues[j], plcList[j].GetString(), fmt.Sprintf("%s[%d]", fieldName, j))
-				}
-			} else {
-				assertEquals(fmt.Sprint(testCase.ExpectedReadValue), readResponse.GetValue(fieldName).GetString(), fieldName)
-			}
+func (m *ManualTestSuite) runSingleTest(t *testing.T, connection plc4go.PlcConnectionConnectResult, fieldName string, testCase ManualTestCase) {
+	// Prepare the read-request
+	readRequestBuilder := connection.Connection.ReadRequestBuilder()
+	readRequestBuilder.AddQuery(fieldName, testCase.Address)
+	readRequest, err := readRequestBuilder.Build()
+	if err != nil {
+		panic(err)
+	}
 
-			log.Info().Msg("Success")
+	// Execute the read request
+	readResponseResult := <-readRequest.Execute()
+	if readResponseResult.Err != nil {
+		panic(err)
+	}
+	readResponse := readResponseResult.Response
+
+	// Check the result
+	assertEquals(t, 1, len(readResponse.GetFieldNames()), fieldName)
+	assertEquals(t, fieldName, readResponse.GetFieldNames()[0], fieldName)
+	assertEquals(t, model.PlcResponseCode_OK, readResponse.GetResponseCode(fieldName), fieldName)
+	assertNotNil(t, readResponse.GetValue(fieldName), fieldName)
+	expectation := reflect.ValueOf(testCase.ExpectedReadValue)
+	if readResponse.GetValue(fieldName).IsList() && (expectation.Kind() == reflect.Slice || expectation.Kind() == reflect.Array) {
+		plcList := readResponse.GetValue(fieldName).GetList()
+		for j := 0; j < expectation.Len(); j++ {
+			assertEquals(t, expectation.Index(j).Interface(), plcList[j], fmt.Sprintf("%s[%d]", fieldName, j))
 		}
+	} else {
+		assertEquals(t, fmt.Sprint(testCase.ExpectedReadValue), readResponse.GetValue(fieldName).GetString(), fieldName)
+	}
+}
 
-		{
-			// Read all items in one big request.
-			// Shuffle the list of test cases and run the test 10 times.
-			log.Info().Msg("Reading all items together in random order")
-			for i := 0; i < 100; i++ {
-				log.Info().Msgf(" - run number %d of %d", i, 100)
-				shuffledTestcases := append(make([]ManualTestCase, 0), m.TestCases...)
-				rand.Seed(time.Now().UnixNano())
-				rand.Shuffle(len(shuffledTestcases), func(i, j int) {
-					shuffledTestcases[i], shuffledTestcases[j] = shuffledTestcases[j], shuffledTestcases[i]
-				})
-
-				sb := strings.Builder{}
-				for _, testCase := range shuffledTestcases {
-					sb.WriteString(testCase.Address)
-					sb.WriteString(", ")
-				}
-				log.Info().Msgf("       using order: %s", sb.String())
+func (m *ManualTestSuite) runBurstTest(t *testing.T, connection plc4go.PlcConnectionConnectResult) {
+	// Read all items in one big request.
+	// Shuffle the list of test cases and run the test 10 times.
+	log.Info().Msg("Reading all items together in random order")
+	for i := 0; i < 100; i++ {
+		log.Info().Msgf(" - run number %d of %d", i, 100)
+		shuffledTestcases := append(make([]ManualTestCase, 0), m.TestCases...)
+		rand.Seed(time.Now().UnixNano())
+		rand.Shuffle(len(shuffledTestcases), func(i, j int) {
+			shuffledTestcases[i], shuffledTestcases[j] = shuffledTestcases[j], shuffledTestcases[i]
+		})
+
+		sb := strings.Builder{}
+		for _, testCase := range shuffledTestcases {
+			sb.WriteString(testCase.Address)
+			sb.WriteString(", ")
+		}
+		log.Info().Msgf("       using order: %s", sb.String())
 
-				builder := connection.Connection.ReadRequestBuilder()
-				for _, testCase := range shuffledTestcases {
-					fieldName := testCase.Address
-					builder.AddQuery(fieldName, testCase.Address)
-				}
-				readRequest, err := builder.Build()
-				if err != nil {
-					panic(err)
-				}
+		builder := connection.Connection.ReadRequestBuilder()
+		for _, testCase := range shuffledTestcases {
+			fieldName := testCase.Address
+			builder.AddQuery(fieldName, testCase.Address)
+		}
+		readRequest, err := builder.Build()
+		if err != nil {
+			t.Errorf("Error building request %v", err)
+			return
+		}
 
-				// Execute the read request
-				readResponseResult := <-readRequest.Execute()
-				if readResponseResult.Err != nil {
-					panic(err)
-				}
-				readResponse := readResponseResult.Response
-
-				// Check the result
-				assertEquals(len(shuffledTestcases), len(readResponse.GetFieldNames()))
-				for _, testCase := range shuffledTestcases {
-					fieldName := testCase.Address
-					assertEquals(model.PlcResponseCode_OK, readResponse.GetResponseCode(fieldName))
-					assertNotNil(readResponse.GetValue(fieldName))
-					// TODO: is list is not the right call here. There is also a bug which breaks the field on the get list call which should be fixed
-					if readResponse.GetValue(fieldName).IsList() && false {
-						plcList := readResponse.GetValue(fieldName).GetList()
-						expectedValues := testCase.ExpectedReadValue.([]interface{})
-						for j := 0; j < len(expectedValues); j++ {
-							assertEquals(expectedValues[j], plcList[j].GetString(), fmt.Sprintf("%s[%d]", fieldName, j))
-						}
-					} else {
-						assertEquals(fmt.Sprint(testCase.ExpectedReadValue), readResponse.GetValue(fieldName).GetString(), fieldName)
-					}
+		// Execute the read request
+		readResponseResult := <-readRequest.Execute()
+		if readResponseResult.Err != nil {
+			t.Errorf("Error getting response %v", err)
+			return
+		}
+		readResponse := readResponseResult.Response
+
+		// Check the result
+		assertEquals(t, len(shuffledTestcases), len(readResponse.GetFieldNames()))
+		for _, testCase := range shuffledTestcases {
+			fieldName := testCase.Address
+			assertEquals(t, model.PlcResponseCode_OK, readResponse.GetResponseCode(fieldName))
+			assertNotNil(t, readResponse.GetValue(fieldName))
+			expectation := reflect.ValueOf(testCase.ExpectedReadValue)
+			if readResponse.GetValue(fieldName).IsList() && (expectation.Kind() == reflect.Slice || expectation.Kind() == reflect.Array) {
+				plcList := readResponse.GetValue(fieldName).GetList()
+				for j := 0; j < expectation.Len(); j++ {
+					assertEquals(t, expectation.Index(j).Interface(), plcList[j], fmt.Sprintf("%s[%d]", fieldName, j))
 				}
+			} else {
+				assertEquals(t, fmt.Sprint(testCase.ExpectedReadValue), readResponse.GetValue(fieldName).GetString(), fieldName)
 			}
-			log.Info().Msg("Success")
 		}
 	}
 }
 
-func assertEquals(expected interface{}, actual interface{}, message ...string) {
+func assertEquals(t *testing.T, expected interface{}, actual interface{}, message ...string) {
+	switch actual.(type) {
+	case values.PlcBOOL:
+		actual = actual.(values.PlcBOOL).GetBool()
+	case values.PlcWORD:
+		actual = actual.(values.PlcWORD).GetInt8()
+	}
 	if expected != actual {
-		log.Error().Msgf("actual %v doesn't match expected %v\nmessage: %s", actual, expected, message)
-		panic("Assertion failed")
+		t.Errorf("actual %v doesn't match expected %v\nmessage: %s", actual, expected, message)
 	}
 }
 
-func assertNotNil(actual interface{}, message ...string) {
+func assertNotNil(t *testing.T, actual interface{}, message ...string) {
 	if actual == nil {
-		log.Error().Msgf("actual %v is nil\nmessage: %v", actual, message)
-		panic("Assertion failed")
+		t.Errorf("actual %v is nil\nmessage: %v", actual, message)
 	}
 }
diff --git a/plc4go/internal/plc4go/spi/values/DATE.go b/plc4go/internal/plc4go/spi/values/DATE.go
index 7dc2da2..fdc19a4 100644
--- a/plc4go/internal/plc4go/spi/values/DATE.go
+++ b/plc4go/internal/plc4go/spi/values/DATE.go
@@ -48,11 +48,15 @@ func NewPlcDATE(value interface{}) PlcDATE {
 	}
 }
 
-func (m PlcDATE) IsTime() bool {
+func (m PlcDATE) IsDate() bool {
 	return true
 }
-func (m PlcDATE) GetTime() time.Time {
-	return m.value
+func (m PlcDATE) GetDate() time.Time {
+	return time.Date(m.value.Year(), m.value.Month(), m.value.Day(), 0, 0, 0, 0, time.UTC)
+}
+
+func (m PlcDATE) GetString() string {
+	return m.GetDate().Format("2006-01-02")
 }
 
 func (m PlcDATE) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
diff --git a/plc4go/internal/plc4go/spi/values/DATE_AND_TIME.go b/plc4go/internal/plc4go/spi/values/DATE_AND_TIME.go
index fc7cb0c..2c39601 100644
--- a/plc4go/internal/plc4go/spi/values/DATE_AND_TIME.go
+++ b/plc4go/internal/plc4go/spi/values/DATE_AND_TIME.go
@@ -20,6 +20,7 @@ package values
 
 import (
 	"encoding/xml"
+	"fmt"
 	"time"
 )
 
@@ -34,12 +35,15 @@ func NewPlcDATE_AND_TIME(value time.Time) PlcDATE_AND_TIME {
 	}
 }
 
-func (m PlcDATE_AND_TIME) IsTime() bool {
+func (m PlcDATE_AND_TIME) IsDateTime() bool {
 	return true
 }
+func (m PlcDATE_AND_TIME) GetDateTime() time.Time {
+	return time.Time{}.Add(m.GetDuration())
+}
 
-func (m PlcDATE_AND_TIME) GetTime() time.Time {
-	return m.value
+func (m PlcDATE_AND_TIME) GetString() string {
+	return fmt.Sprintf("%v", m.GetDateTime())
 }
 
 func (m PlcDATE_AND_TIME) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
diff --git a/plc4go/internal/plc4go/spi/values/INT.go b/plc4go/internal/plc4go/spi/values/INT.go
index af1c73f..cbfea8a 100644
--- a/plc4go/internal/plc4go/spi/values/INT.go
+++ b/plc4go/internal/plc4go/spi/values/INT.go
@@ -102,21 +102,21 @@ func (m PlcINT) GetInt16() int16 {
 }
 
 func (m PlcINT) GetInt32() int32 {
-	return int32(m.GetInt8())
+	return int32(m.GetInt16())
 }
 
 func (m PlcINT) GetInt64() int64 {
-	return int64(m.GetInt8())
+	return int64(m.GetInt16())
 }
 
 func (m PlcINT) GetFloat32() float32 {
 	//TODO: Check if this is ok
-	return float32(m.GetInt8())
+	return float32(m.GetInt16())
 }
 
 func (m PlcINT) GetFloat64() float64 {
 	//TODO: Check if this is ok
-	return float64(m.GetInt8())
+	return float64(m.GetInt16())
 }
 
 func (m PlcINT) GetString() string {
diff --git a/plc4go/internal/plc4go/spi/values/LTIME.go b/plc4go/internal/plc4go/spi/values/LTIME.go
index 738fd18..23f02d9 100644
--- a/plc4go/internal/plc4go/spi/values/LTIME.go
+++ b/plc4go/internal/plc4go/spi/values/LTIME.go
@@ -20,6 +20,7 @@ package values
 
 import (
 	"encoding/xml"
+	"fmt"
 	"time"
 )
 
@@ -42,6 +43,14 @@ func (m PlcLTIME) GetDuration() time.Duration {
 	return time.Duration(m.value)
 }
 
+func (m PlcLTIME) IsString() bool {
+	return true
+}
+
+func (m PlcLTIME) GetString() string {
+	return fmt.Sprintf("PT%0.fS", m.GetDuration().Seconds())
+}
+
 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
diff --git a/plc4go/internal/plc4go/spi/values/PlcBitString.go b/plc4go/internal/plc4go/spi/values/PlcBitString.go
index ecb5503..a18a602 100644
--- a/plc4go/internal/plc4go/spi/values/PlcBitString.go
+++ b/plc4go/internal/plc4go/spi/values/PlcBitString.go
@@ -34,22 +34,22 @@ func NewPlcBitString(value interface{}) PlcBitString {
 	case uint8:
 		bools = make([]api.PlcValue, 8)
 		for i := 0; i < 8; i++ {
-			bools[0] = NewPlcBOOL(((value.(uint8) >> uint8((8-1)-i)) & 0x01) == 0x01)
+			bools[i] = NewPlcBOOL(((value.(uint8) >> uint8((8-1)-i)) & 0x01) == 0x01)
 		}
 	case uint16:
 		bools = make([]api.PlcValue, 16)
 		for i := 0; i < 16; i++ {
-			bools[0] = NewPlcBOOL(((value.(uint16) >> uint8((16-1)-i)) & 0x01) == 0x01)
+			bools[i] = NewPlcBOOL(((value.(uint16) >> uint8((16-1)-i)) & 0x01) == 0x01)
 		}
 	case uint32:
 		bools = make([]api.PlcValue, 32)
 		for i := 0; i < 32; i++ {
-			bools[0] = NewPlcBOOL(((value.(uint32) >> uint8((32-1)-i)) & 0x01) == 0x01)
+			bools[i] = NewPlcBOOL(((value.(uint32) >> uint8((32-1)-i)) & 0x01) == 0x01)
 		}
 	case uint64:
 		bools = make([]api.PlcValue, 64)
 		for i := 0; i < 64; i++ {
-			bools[0] = NewPlcBOOL(((value.(uint64) >> uint8((64-1)-i)) & 0x01) == 0x01)
+			bools[i] = NewPlcBOOL(((value.(uint64) >> uint8((64-1)-i)) & 0x01) == 0x01)
 		}
 	}
 
diff --git a/plc4go/internal/plc4go/spi/values/PlcValueAdapter.go b/plc4go/internal/plc4go/spi/values/PlcValueAdapter.go
index 89da493..a19916b 100644
--- a/plc4go/internal/plc4go/spi/values/PlcValueAdapter.go
+++ b/plc4go/internal/plc4go/spi/values/PlcValueAdapter.go
@@ -25,7 +25,6 @@ import (
 
 // Dummy structure
 type PlcValueAdapter struct {
-	api.PlcValue
 }
 
 // Simple Types
@@ -158,13 +157,10 @@ func (m PlcValueAdapter) GetLength() uint32 {
 	return 1
 }
 func (m PlcValueAdapter) GetIndex(i uint32) api.PlcValue {
-	if i == 0 {
-		return m
-	}
 	return nil
 }
 func (m PlcValueAdapter) GetList() []api.PlcValue {
-	return []api.PlcValue{m}
+	return []api.PlcValue{}
 }
 
 // Struct Methods
@@ -183,3 +179,15 @@ func (m PlcValueAdapter) GetValue(key string) api.PlcValue {
 func (m PlcValueAdapter) GetStruct() map[string]api.PlcValue {
 	return nil
 }
+func (m PlcValueAdapter) IsDate() bool {
+	return false
+}
+func (m PlcValueAdapter) GetDate() time.Time {
+	return time.Time{}
+}
+func (m PlcValueAdapter) IsDateTime() bool {
+	return false
+}
+func (m PlcValueAdapter) GetDateTime() time.Time {
+	return time.Time{}
+}
diff --git a/plc4go/internal/plc4go/spi/values/TIME.go b/plc4go/internal/plc4go/spi/values/TIME.go
index 38c54c6..a472e54 100644
--- a/plc4go/internal/plc4go/spi/values/TIME.go
+++ b/plc4go/internal/plc4go/spi/values/TIME.go
@@ -20,6 +20,7 @@ package values
 
 import (
 	"encoding/xml"
+	"fmt"
 	"time"
 )
 
@@ -42,6 +43,14 @@ func (m PlcTIME) GetDuration() time.Duration {
 	return time.Duration(m.value)
 }
 
+func (m PlcTIME) IsString() bool {
+	return true
+}
+
+func (m PlcTIME) GetString() string {
+	return fmt.Sprintf("PT%0.fS", m.GetDuration().Seconds())
+}
+
 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
diff --git a/plc4go/internal/plc4go/spi/values/TIME_OF_DAY.go b/plc4go/internal/plc4go/spi/values/TIME_OF_DAY.go
index 6d7b0d6..f6ba42b 100644
--- a/plc4go/internal/plc4go/spi/values/TIME_OF_DAY.go
+++ b/plc4go/internal/plc4go/spi/values/TIME_OF_DAY.go
@@ -58,6 +58,10 @@ func (m PlcTIME_OF_DAY) GetTime() time.Time {
 	return m.value
 }
 
+func (m PlcTIME_OF_DAY) GetString() string {
+	return m.value.Format("15:04:05.000")
+}
+
 func (m PlcTIME_OF_DAY) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
 	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcTIME_OF_DAY"}}); err != nil {
 		return err
diff --git a/plc4go/pkg/plc4go/values/plc_value.go b/plc4go/pkg/plc4go/values/plc_value.go
index d741909..bdbb10e 100644
--- a/plc4go/pkg/plc4go/values/plc_value.go
+++ b/plc4go/pkg/plc4go/values/plc_value.go
@@ -66,9 +66,9 @@ type PlcValue interface {
 	IsTime() bool
 	GetTime() time.Time
 	IsDate() bool
-	GetDate()
+	GetDate() time.Time
 	IsDateTime() bool
-	GetDateTime()
+	GetDateTime() time.Time
 
 	// Raw Access
 	GetRaw() []byte