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 2021/03/04 11:44:19 UTC

[plc4x] branch develop updated: - Added a "RawPlcValue" type which allows lazy decoding of the payload if the type-information is not known at the time it's processed by the driver.

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

cdutz 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 281d5d1  - Added a "RawPlcValue" type which allows lazy decoding of the payload if the type-information is not known at the time it's processed by the driver.
281d5d1 is described below

commit 281d5d18726a394392b8ac5fec7b1c59547fdfb9
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Thu Mar 4 12:44:09 2021 +0100

    - Added a "RawPlcValue" type which allows lazy decoding of the payload if the type-information is not known at the time it's processed by the driver.
---
 .../hello_world_plc4go_knx_subscription.go         | 34 ++++++++-
 .../internal/plc4go/knxnetip/KnxNetIpConnection.go |  1 -
 .../internal/plc4go/knxnetip/KnxNetIpSubscriber.go | 62 +++++----------
 .../plc4go/knxnetip/KnxNetIpValueDecoder.go        | 44 +++++++++++
 plc4go/internal/plc4go/spi/values/RawPlcValue.go   | 87 ++++++++++++++++++++++
 plc4go/pkg/plc4go/values/plc_value.go              | 11 +++
 6 files changed, 191 insertions(+), 48 deletions(-)

diff --git a/plc4go/examples/knx/subscribe/hello_world_plc4go_knx_subscription.go b/plc4go/examples/knx/subscribe/hello_world_plc4go_knx_subscription.go
index 6b9b645..80c90bb 100644
--- a/plc4go/examples/knx/subscribe/hello_world_plc4go_knx_subscription.go
+++ b/plc4go/examples/knx/subscribe/hello_world_plc4go_knx_subscription.go
@@ -24,6 +24,8 @@ import (
 	"github.com/apache/plc4x/plc4go/pkg/plc4go/drivers"
 	"github.com/apache/plc4x/plc4go/pkg/plc4go/model"
 	"github.com/apache/plc4x/plc4go/pkg/plc4go/transports"
+	"github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+	"strings"
 	"time"
 )
 
@@ -48,11 +50,37 @@ func main() {
 
 	// Prepare a subscription-request
 	srb := connection.SubscriptionRequestBuilder()
-	//	srb.AddChangeOfStateQuery("all", "*/*/*")
+	// Intentionally catching all without datatype and the temperature values of the first floor with type
+	srb.AddChangeOfStateQuery("all", "*/*/*")
 	srb.AddChangeOfStateQuery("firstFlorTemperatures", "2/[1,2,4,6]/10:DPT_Value_Temp")
-	srb.AddChangeOfStateQuery("secondFlorTemperatures", "3/[2,3,4,6]/10:DPT_Value_Temp")
 	srb.AddItemHandler(func(event model.PlcSubscriptionEvent) {
-		fmt.Printf("Got event %v\n", event)
+		// Iterate over all fields that were triggered in the current event.
+		for _, fieldName := range event.GetFieldNames() {
+			if event.GetResponseCode(fieldName) == model.PlcResponseCode_OK {
+				address := event.GetAddress(fieldName)
+				value := event.GetValue(fieldName)
+				// If the plc-value was a raw-plcValue, we will try lazily decode the value
+				// In my installation all group addresses ending with "/10" are temperature values,
+				// So if I find a group address ending on that, decode it with a given type name,
+				// If not, simply output it as array of USINT values.
+				switch value.(type) {
+				case values.RawPlcValue:
+					rawValue := value.(values.RawPlcValue)
+					datatypeName := "USINT"
+					if strings.HasSuffix(address, "/10") {
+						datatypeName = "DPT_Value_Temp"
+					}
+					fmt.Printf("Got raw-value event for address %s: ", address)
+					for rawValue.RawHasMore() {
+						value = rawValue.RawDecodeValue(datatypeName)
+						fmt.Printf(" %s", value.GetString())
+					}
+					fmt.Printf("\n")
+				default:
+					fmt.Printf("Got event for address %s: %s\n", address, value.GetString())
+				}
+			}
+		}
 	})
 	subscriptionRequest, err := srb.Build()
 	if err != nil {
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go
index 5608c12..b3ee0ea 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go
@@ -1915,7 +1915,6 @@ func (m *KnxNetIpConnection) handleIncomingTunnelingRequest(tunnelingRequest *dr
 							payload = append(payload, groupValueWrite.Data...)
 
 							m.handleValueCacheUpdate(destinationAddress, payload)
-							// TODO: Continue
 						}
 					default:
 						// If this is an individual address and it is targeted at us, we need to ack that.
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go
index 5c9831e..900f1d1 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go
@@ -26,7 +26,6 @@ import (
 	values2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
 	apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
 	"github.com/apache/plc4x/plc4go/pkg/plc4go/values"
-	"strconv"
 	"time"
 )
 
@@ -124,35 +123,37 @@ func (m *KnxNetIpSubscriber) handleValueChange(destinationAddress []int8, payloa
 						}
 						elementType := *groupAddressField.GetFieldType()
 						numElements := groupAddressField.GetQuantity()
-						if elementType == driverModel.KnxDatapointType_DPT_UNKNOWN {
-							elementType = driverModel.KnxDatapointType_BYTE
-							numElements = uint16(rb.GetTotalBytes()) - rb.GetPos()
-						}
 
-						// Replace the potentially patten-field with a concrete version.
-						fields[fieldName] = m.getFieldFromGroupAddress(groupAddress, groupAddressField.GetFieldType())
+						fields[fieldName] = groupAddressField
 						types[fieldName] = subscriptionRequest.GetType(fieldName)
 						intervals[fieldName] = subscriptionRequest.GetInterval(fieldName)
 						addresses[fieldName] = destinationAddress
 
-						var values []values.PlcValue
+						var plcValueList []values.PlcValue
 						responseCode := apiModel.PlcResponseCode_OK
 						for i := uint16(0); i < numElements; i++ {
-							plcValue, err2 := driverModel.KnxDatapointParse(rb, elementType)
-							if err2 == nil {
-								values = append(values, plcValue)
+							// If we don't know the datatype, we'll create a RawPlcValue instead
+							// so the application can decode the content later on.
+							if elementType == driverModel.KnxDatapointType_DPT_UNKNOWN {
+								plcValue := values2.NewRawPlcValue(rb, NewKnxNetIpValueDecoder(rb))
+								plcValueList = append(plcValueList, plcValue)
 							} else {
-								// TODO: Do a little more here ...
-								responseCode = apiModel.PlcResponseCode_INTERNAL_ERROR
-								break
+								plcValue, err2 := driverModel.KnxDatapointParse(rb, elementType)
+								if err2 == nil {
+									plcValueList = append(plcValueList, plcValue)
+								} else {
+									// TODO: Do a little more here ...
+									responseCode = apiModel.PlcResponseCode_INTERNAL_ERROR
+									break
+								}
 							}
 						}
 						responseCodes[fieldName] = responseCode
 						if responseCode == apiModel.PlcResponseCode_OK {
-							if len(values) == 1 {
-								plcValues[fieldName] = values[0]
+							if len(plcValueList) == 1 {
+								plcValues[fieldName] = plcValueList[0]
 							} else {
-								plcValues[fieldName] = values2.NewPlcList(values)
+								plcValues[fieldName] = values2.NewPlcList(plcValueList)
 							}
 						}
 					}
@@ -172,30 +173,3 @@ func (m *KnxNetIpSubscriber) handleValueChange(destinationAddress []int8, payloa
 		}
 	}
 }
-
-func (m *KnxNetIpSubscriber) getFieldFromGroupAddress(groupAddress *driverModel.KnxGroupAddress, fieldType *driverModel.KnxDatapointType) apiModel.PlcField {
-	if groupAddress == nil {
-		return nil
-	}
-	switch groupAddress.Child.(type) {
-	case *driverModel.KnxGroupAddress3Level:
-		groupAddress3Level := groupAddress.Child.(*driverModel.KnxGroupAddress3Level)
-		return NewKnxNetIpGroupAddress3LevelPlcField(
-			strconv.Itoa(int(groupAddress3Level.MainGroup)),
-			strconv.Itoa(int(groupAddress3Level.MiddleGroup)),
-			strconv.Itoa(int(groupAddress3Level.SubGroup)),
-			fieldType)
-	case *driverModel.KnxGroupAddress2Level:
-		groupAddress2Level := groupAddress.Child.(*driverModel.KnxGroupAddress2Level)
-		return NewKnxNetIpGroupAddress2LevelPlcField(
-			strconv.Itoa(int(groupAddress2Level.MainGroup)),
-			strconv.Itoa(int(groupAddress2Level.SubGroup)),
-			fieldType)
-	case *driverModel.KnxGroupAddressFreeLevel:
-		groupAddress1Level := groupAddress.Child.(*driverModel.KnxGroupAddressFreeLevel)
-		return NewKnxNetIpGroupAddress1LevelPlcField(
-			strconv.Itoa(int(groupAddress1Level.SubGroup)),
-			fieldType)
-	}
-	return nil
-}
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpValueDecoder.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpValueDecoder.go
new file mode 100644
index 0000000..629c75d
--- /dev/null
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpValueDecoder.go
@@ -0,0 +1,44 @@
+//
+// 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 knxnetip
+
+import (
+	driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+	api "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+)
+
+type KnxNetIpValueDecoder struct {
+	rb *utils.ReadBuffer
+}
+
+func NewKnxNetIpValueDecoder(rb *utils.ReadBuffer) KnxNetIpValueDecoder {
+	return KnxNetIpValueDecoder{
+		rb: rb,
+	}
+}
+
+func (m KnxNetIpValueDecoder) Decode(typeName string) api.PlcValue {
+	datatype := driverModel.KnxDatapointTypeByName(typeName)
+	plcValue, err := driverModel.KnxDatapointParse(m.rb, datatype)
+	if err != nil {
+		return nil
+	}
+	return plcValue
+}
diff --git a/plc4go/internal/plc4go/spi/values/RawPlcValue.go b/plc4go/internal/plc4go/spi/values/RawPlcValue.go
new file mode 100644
index 0000000..b825736
--- /dev/null
+++ b/plc4go/internal/plc4go/spi/values/RawPlcValue.go
@@ -0,0 +1,87 @@
+//
+// 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 values
+
+import (
+	"encoding/xml"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+	api "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+)
+
+type PlcValueDecoder interface {
+	Decode(typeName string) api.PlcValue
+}
+
+type RawPlcValue struct {
+	readBuffer *utils.ReadBuffer
+	decoder    PlcValueDecoder
+	PlcValueAdapter
+}
+
+func NewRawPlcValue(readBuffer *utils.ReadBuffer, decoder PlcValueDecoder) RawPlcValue {
+	return RawPlcValue{
+		readBuffer: readBuffer,
+		decoder:    decoder,
+	}
+}
+
+func (m RawPlcValue) GetRaw() []byte {
+	return m.readBuffer.GetBytes()
+}
+
+func (m RawPlcValue) IsList() bool {
+	return true
+}
+
+func (m RawPlcValue) GetLength() uint32 {
+	return uint32(m.readBuffer.GetTotalBytes())
+}
+
+func (m RawPlcValue) GetIndex(i uint32) api.PlcValue {
+	return NewPlcUSINT(m.readBuffer.GetBytes()[i])
+}
+
+func (m RawPlcValue) GetList() []api.PlcValue {
+	var plcValues []api.PlcValue
+	for _, value := range m.readBuffer.GetBytes() {
+		plcValues = append(plcValues, NewPlcUSINT(value))
+	}
+	return plcValues
+}
+
+func (m RawPlcValue) RawDecodeValue(typeName string) api.PlcValue {
+	return m.decoder.Decode(typeName)
+}
+
+func (m RawPlcValue) RawHasMore() bool {
+	return m.readBuffer.HasMore(1)
+}
+
+func (m RawPlcValue) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcByteArray"}}); err != nil {
+		return err
+	}
+
+	// TODO: Implement this ...
+
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcByteArray"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/plc4go/pkg/plc4go/values/plc_value.go b/plc4go/pkg/plc4go/values/plc_value.go
index 3acce17..97f774d 100644
--- a/plc4go/pkg/plc4go/values/plc_value.go
+++ b/plc4go/pkg/plc4go/values/plc_value.go
@@ -86,3 +86,14 @@ type PlcValue interface {
 	GetValue(key string) PlcValue
 	GetStruct() map[string]PlcValue
 }
+
+/*
+   This type is used in cases where the driver doesn't have access to type information and therefore can't decode
+   the payload yet. This allows an application to take the raw plc-value and have the payload decoded later.
+*/
+type RawPlcValue interface {
+	RawDecodeValue(typeName string) PlcValue
+	RawHasMore() bool
+
+	PlcValue
+}