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
+}