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/02/05 16:52:41 UTC
[plc4x] branch develop updated: - Refactoring of the KNX LData
messages - Finished a first working KNX Group-Address & ComObject discovery
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 db126a9 - Refactoring of the KNX LData messages - Finished a first working KNX Group-Address & ComObject discovery
db126a9 is described below
commit db126a9a665c8d4ed56cfce18f4a29117f7253c2
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Fri Feb 5 17:52:32 2021 +0100
- Refactoring of the KNX LData messages
- Finished a first working KNX Group-Address & ComObject discovery
---
.../knxnetip/ParserSerializerTestsuite.xml | 56 +-
.../testing/protocols/modbus/DriverTestsuite.xml | 4 +-
.../protocols/modbus/ParserSerializerTestsuite.xml | 15 +-
.../protocols/s7/ParserSerializerTestsuite.xml | 22 +-
plc4go/cmd/main/drivers/knxnetip_test.go | 27 +-
.../discovery/hello_world_plc4go_knx_discovery.go | 99 +
plc4go/examples/read/hello_world_plc4go_read.go | 14 +-
plc4go/examples/write/hello_world_plc4go_write.go | 14 +-
plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go | 584 ++--
.../internal/plc4go/knxnetip/KnxNetIpConnection.go | 2946 ++++++++++----------
.../internal/plc4go/knxnetip/KnxNetIpDiscoverer.go | 2 +-
plc4go/internal/plc4go/knxnetip/KnxNetIpField.go | 196 +-
.../plc4go/knxnetip/KnxNetIpFieldHandler.go | 100 +-
plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go | 36 +-
.../internal/plc4go/knxnetip/KnxNetIpSubscriber.go | 79 +-
plc4go/internal/plc4go/knxnetip/Utils.go | 30 -
.../plc4go/knxnetip/readwrite/ParserHelper.go | 6 +-
.../plc4go/knxnetip/readwrite/model/Apdu.go | 36 +-
.../readwrite/model/ApduControlContainer.go | 7 +-
.../knxnetip/readwrite/model/ApduDataContainer.go | 7 +-
.../plc4go/knxnetip/readwrite/model/KnxProperty.go | 22 +-
.../plc4go/knxnetip/readwrite/model/LBusmonInd.go | 2 +-
.../{LDataFrameDataExt.go => LDataExtended.go} | 70 +-
.../plc4go/knxnetip/readwrite/model/LDataFrame.go | 106 +-
.../knxnetip/readwrite/model/LDataFrameACK.go | 144 +
.../knxnetip/readwrite/model/LDataFrameData.go | 301 --
.../{LDataFramePollingData.go => LPollData.go} | 54 +-
.../knxnetip/protocol/KnxNetIpProtocolLogic.java | 23 +-
.../resources/protocols/knxnetip/knxnetip.mspec | 61 +-
.../knxnetip/ParserSerializerTestsuite.xml | 14 +-
30 files changed, 2566 insertions(+), 2511 deletions(-)
diff --git a/plc4go/assets/testing/protocols/knxnetip/ParserSerializerTestsuite.xml b/plc4go/assets/testing/protocols/knxnetip/ParserSerializerTestsuite.xml
index 6ad045f..9e954a3 100644
--- a/plc4go/assets/testing/protocols/knxnetip/ParserSerializerTestsuite.xml
+++ b/plc4go/assets/testing/protocols/knxnetip/ParserSerializerTestsuite.xml
@@ -123,7 +123,7 @@
<hpaiIDiscoveryEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDiscoveryEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>wKgqyA==</addr>
+ <addr>C0A82AC8</addr>
</ipAddress>
<ipPort>54964</ipPort>
</hpaiIDiscoveryEndpoint>
@@ -140,7 +140,7 @@
<hpaiControlEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIControlEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>wKgqCw==</addr>
+ <addr>C0A82A0B</addr>
</ipAddress>
<ipPort>3671</ipPort>
</hpaiControlEndpoint>
@@ -159,14 +159,14 @@
<projectNumber>0</projectNumber>
<installationNumber>0</installationNumber>
</projectInstallationIdentifier>
- <knxNetIpDeviceSerialNumber>AAgtQJhS</knxNetIpDeviceSerialNumber>
+ <knxNetIpDeviceSerialNumber>00082D409852</knxNetIpDeviceSerialNumber>
<knxNetIpDeviceMulticastAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>4AAXDA==</addr>
+ <addr>E000170C</addr>
</knxNetIpDeviceMulticastAddress>
<knxNetIpDeviceMacAddress className="org.apache.plc4x.java.knxnetip.readwrite.MACAddress">
- <addr>AAqzJ1U2</addr>
+ <addr>000AB3275536</addr>
</knxNetIpDeviceMacAddress>
- <deviceFriendlyName>R2lyYSBLTlgvSVAtU2Nobml0dHN0ZWxsZQAAAAAA</deviceFriendlyName>
+ <deviceFriendlyName>47697261204B4E582F49502D5363686E6974747374656C6C650000000000</deviceFriendlyName>
</dibDeviceInfo>
<dibSuppSvcFamilies className="org.apache.plc4x.java.knxnetip.readwrite.DIBSuppSvcFamilies">
<descriptionType>2</descriptionType>
@@ -195,7 +195,7 @@
<hpaiControlEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIControlEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>AAAAAA==</addr>
+ <addr>00000000</addr>
</ipAddress>
<ipPort>0</ipPort>
</hpaiControlEndpoint>
@@ -224,14 +224,14 @@
<projectNumber>0</projectNumber>
<installationNumber>0</installationNumber>
</projectInstallationIdentifier>
- <knxNetIpDeviceSerialNumber>AAgtQJhS</knxNetIpDeviceSerialNumber>
+ <knxNetIpDeviceSerialNumber>00082D409852</knxNetIpDeviceSerialNumber>
<knxNetIpDeviceMulticastAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>4AAXDA==</addr>
+ <addr>E000170C</addr>
</knxNetIpDeviceMulticastAddress>
<knxNetIpDeviceMacAddress className="org.apache.plc4x.java.knxnetip.readwrite.MACAddress">
- <addr>AAqzJ1U2</addr>
+ <addr>000AB3275536</addr>
</knxNetIpDeviceMacAddress>
- <deviceFriendlyName>R2lyYSBLTlgvSVAtU2Nobml0dHN0ZWxsZQAAAAAA</deviceFriendlyName>
+ <deviceFriendlyName>47697261204B4E582F49502D5363686E6974747374656C6C650000000000</deviceFriendlyName>
</dibDeviceInfo>
<dibSuppSvcFamilies className="org.apache.plc4x.java.knxnetip.readwrite.DIBSuppSvcFamilies">
<descriptionType>2</descriptionType>
@@ -261,14 +261,14 @@
<hpaiDiscoveryEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDiscoveryEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>wKgqyA==</addr>
+ <addr>C0A82AC8</addr>
</ipAddress>
<ipPort>54964</ipPort>
</hpaiDiscoveryEndpoint>
<hpaiDataEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDataEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>wKgqyA==</addr>
+ <addr>C0A82AC8</addr>
</ipAddress>
<ipPort>54964</ipPort>
</hpaiDataEndpoint>
@@ -290,7 +290,7 @@
<hpaiDataEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDataEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>wKgqCw==</addr>
+ <addr>C0A82A0B</addr>
</ipAddress>
<ipPort>3671</ipPort>
</hpaiDataEndpoint>
@@ -315,7 +315,7 @@
<hpaiControlEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIControlEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>wKgqyA==</addr>
+ <addr>C0A82AC8</addr>
</ipAddress>
<ipPort>54964</ipPort>
</hpaiControlEndpoint>
@@ -381,7 +381,7 @@
<hpaiControlEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIControlEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>wKgqCw==</addr>
+ <addr>C0A82A0B</addr>
</ipAddress>
<ipPort>3671</ipPort>
</hpaiControlEndpoint>
@@ -414,9 +414,9 @@
<cemi className="org.apache.plc4x.java.knxnetip.readwrite.LDataInd">
<additionalInformationLength>0</additionalInformationLength>
<additionalInformation/>
- <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt">
- <repeated>true</repeated>
- <notAckFrame>true</notAckFrame>
+ <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataExtended">
+ <frameType>true</frameType>
+ <notRepeated>true</notRepeated>
<priority>LOW</priority>
<acknowledgeRequested>false</acknowledgeRequested>
<errorFlag>false</errorFlag>
@@ -428,9 +428,8 @@
<middleGroup>2</middleGroup>
<subGroup>10</subGroup>
</sourceAddress>
- <destinationAddress>Egw=</destinationAddress>
+ <destinationAddress>120C</destinationAddress>
<apdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduDataContainer">
- <dataLength>1</dataLength>
<numbered>false</numbered>
<counter>0</counter>
<dataApdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduDataGroupValueWrite">
@@ -475,9 +474,9 @@
</relativeTimestamp>
</additionalInformation>
</additionalInformation>
- <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt">
- <repeated>true</repeated>
- <notAckFrame>true</notAckFrame>
+ <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataExtended">
+ <frameType>true</frameType>
+ <notRepeated>true</notRepeated>
<priority>LOW</priority>
<acknowledgeRequested>false</acknowledgeRequested>
<errorFlag>false</errorFlag>
@@ -489,9 +488,8 @@
<middleGroup>10</middleGroup>
<subGroup>30</subGroup>
</sourceAddress>
- <destinationAddress>DOE=</destinationAddress>
+ <destinationAddress>0CE1</destinationAddress>
<apdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduControlContainer">
- <dataLength>0</dataLength>
<numbered>false</numbered>
<counter>0</counter>
<controlApdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduControlDisconnect"/>
@@ -527,14 +525,14 @@
<hpaiDiscoveryEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDiscoveryEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>wKgqRg==</addr>
+ <addr>C0A82A46</addr>
</ipAddress>
<ipPort>50185</ipPort>
</hpaiDiscoveryEndpoint>
<hpaiDataEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDataEndpoint">
<hostProtocolCode>IPV4_UDP</hostProtocolCode>
<ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
- <addr>wKgqRg==</addr>
+ <addr>C0A82A46</addr>
</ipAddress>
<ipPort>50186</ipPort>
</hpaiDataEndpoint>
@@ -543,4 +541,4 @@
</xml>
</testcase>
-</test:testsuite>
\ No newline at end of file
+</test:testsuite>
diff --git a/plc4go/assets/testing/protocols/modbus/DriverTestsuite.xml b/plc4go/assets/testing/protocols/modbus/DriverTestsuite.xml
index ed35a3f..bfe6617 100644
--- a/plc4go/assets/testing/protocols/modbus/DriverTestsuite.xml
+++ b/plc4go/assets/testing/protocols/modbus/DriverTestsuite.xml
@@ -275,7 +275,7 @@
<pdu className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUWriteMultipleHoldingRegistersRequest">
<startingAddress>0</startingAddress>
<quantity>2</quantity>
- <value>QEkP2w==</value>
+ <value>40490FDB</value>
</pdu>
</ModbusTcpADU>
</outgoing-plc-message>
@@ -339,7 +339,7 @@
<pdu className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUWriteMultipleHoldingRegistersRequest">
<startingAddress>0</startingAddress>
<quantity>4</quantity>
- <value>QEkP20BJD9s=</value>
+ <value>40490FDB40490FDB</value>
</pdu>
</ModbusTcpADU>
</outgoing-plc-message>
diff --git a/plc4go/assets/testing/protocols/modbus/ParserSerializerTestsuite.xml b/plc4go/assets/testing/protocols/modbus/ParserSerializerTestsuite.xml
index 3dd0c83..e33dd17 100644
--- a/plc4go/assets/testing/protocols/modbus/ParserSerializerTestsuite.xml
+++ b/plc4go/assets/testing/protocols/modbus/ParserSerializerTestsuite.xml
@@ -54,10 +54,7 @@
<transactionIdentifier>31998</transactionIdentifier>
<unitIdentifier>255</unitIdentifier>
<pdu className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUReadInputRegistersResponse">
- <value>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAABAAAAAAAAAAAAAAAAAAAAdsAAAHWAABKOAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRhaW0AbgAAAAAAAAAAAAAAAAAAMDEAMAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</value>
+ <value>00000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000004000000000000000000000000000001DB000001D600004A380000000000000000000000000000000000000000000000000000000000006461696D006E0000000000000000000000000000303100300000000000000000000000000000000000000000000000000000000000000000000000000000</value>
</pdu>
</ModbusTcpADU>
</xml>
@@ -109,11 +106,11 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</value>
<items>
<items className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUReadFileRecordResponseItem">
<referenceType>6</referenceType>
- <data>AAAAAA==</data>
+ <data>00000000</data>
</items>
<items className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUReadFileRecordResponseItem">
<referenceType>6</referenceType>
- <data>AAAAAAAAAAAAAAAAAAAAAA==</data>
+ <data>00000000000000000000000000000000</data>
</items>
</items>
</pdu>
@@ -139,7 +136,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</value>
<referenceType>6</referenceType>
<fileNumber>2</fileNumber>
<recordNumber>0</recordNumber>
- <recordData>AAg=</recordData>
+ <recordData>0008</recordData>
</items>
</items>
</pdu>
@@ -164,13 +161,13 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</value>
<referenceType>6</referenceType>
<fileNumber>1</fileNumber>
<recordNumber>9999</recordNumber>
- <recordData>AAA=</recordData>
+ <recordData>0000</recordData>
</items>
<items className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUWriteFileRecordRequestItem">
<referenceType>6</referenceType>
<fileNumber>2</fileNumber>
<recordNumber>0</recordNumber>
- <recordData>AAA=</recordData>
+ <recordData>0000</recordData>
</items>
</items>
</pdu>
diff --git a/plc4go/assets/testing/protocols/s7/ParserSerializerTestsuite.xml b/plc4go/assets/testing/protocols/s7/ParserSerializerTestsuite.xml
index 1c3b735..01e1381 100644
--- a/plc4go/assets/testing/protocols/s7/ParserSerializerTestsuite.xml
+++ b/plc4go/assets/testing/protocols/s7/ParserSerializerTestsuite.xml
@@ -209,21 +209,21 @@
<items>
<items className="org.apache.plc4x.java.s7.readwrite.SzlDataTreeItem">
<itemIndex>1</itemIndex>
- <mlfb>NkVTNyAyMTItMUJEMzAtMFhCMCA=</mlfb>
+ <mlfb>36455337203231322D31424433302D3058423020</mlfb>
<moduleTypeId>8224</moduleTypeId>
<ausbg>1</ausbg>
<ausbe>8224</ausbe>
</items>
<items className="org.apache.plc4x.java.s7.readwrite.SzlDataTreeItem">
<itemIndex>6</itemIndex>
- <mlfb>NkVTNyAyMTItMUJEMzAtMFhCMCA=</mlfb>
+ <mlfb>36455337203231322D31424433302D3058423020</mlfb>
<moduleTypeId>8224</moduleTypeId>
<ausbg>1</ausbg>
<ausbe>8224</ausbe>
</items>
<items className="org.apache.plc4x.java.s7.readwrite.SzlDataTreeItem">
<itemIndex>7</itemIndex>
- <mlfb>NkVTNyAyMTItMUJEMzAtMFhCMCA=</mlfb>
+ <mlfb>36455337203231322D31424433302D3058423020</mlfb>
<moduleTypeId>8224</moduleTypeId>
<ausbg>22018</ausbg>
<ausbe>2</ausbe>
@@ -322,22 +322,22 @@
<items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
<returnCode>OK</returnCode>
<transportSize>BIT</transportSize>
- <data>AQ==</data>
+ <data>01</data>
</items>
<items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
<returnCode>OK</returnCode>
<transportSize>BIT</transportSize>
- <data>AQ==</data>
+ <data>01</data>
</items>
<items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
<returnCode>OK</returnCode>
<transportSize>BIT</transportSize>
- <data>AQ==</data>
+ <data>01</data>
</items>
<items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
<returnCode>OK</returnCode>
<transportSize>BIT</transportSize>
- <data>AQ==</data>
+ <data>01</data>
</items>
</items>
</payload>
@@ -432,22 +432,22 @@
<items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
<returnCode>OK</returnCode>
<transportSize>BIT</transportSize>
- <data>AQ==</data>
+ <data>01</data>
</items>
<items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
<returnCode>OK</returnCode>
<transportSize>BIT</transportSize>
- <data>AQ==</data>
+ <data>01</data>
</items>
<items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
<returnCode>OK</returnCode>
<transportSize>BIT</transportSize>
- <data>AQ==</data>
+ <data>01</data>
</items>
<items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
<returnCode>OK</returnCode>
<transportSize>BIT</transportSize>
- <data>AQ==</data>
+ <data>01</data>
</items>
</items>
</payload>
diff --git a/plc4go/cmd/main/drivers/knxnetip_test.go b/plc4go/cmd/main/drivers/knxnetip_test.go
index 0b0c205..21a2a97 100644
--- a/plc4go/cmd/main/drivers/knxnetip_test.go
+++ b/plc4go/cmd/main/drivers/knxnetip_test.go
@@ -384,6 +384,10 @@ func TestKnxNetIpPlc4goMemoryRead(t *testing.T) {
connection := connectionResult.Connection
defer connection.Close()
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Group Address Table reading
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+
// First of all, request the starting address of the group address table
readRequestBuilder := connection.ReadRequestBuilder()
readRequestBuilder.AddItem("groupAddressTableAddress", "1.1.10/1/7")
@@ -426,6 +430,10 @@ func TestKnxNetIpPlc4goMemoryRead(t *testing.T) {
knxGroupAddresses = append(knxGroupAddresses, groupAddress)
}
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Group Address Association Table reading
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+
// Now we read the group address association table address
readRequestBuilder = connection.ReadRequestBuilder()
readRequestBuilder.AddItem("groupAddressAssociationTableAddress", "1.1.10/2/7")
@@ -472,7 +480,24 @@ func TestKnxNetIpPlc4goMemoryRead(t *testing.T) {
}
}
- fmt.Printf("%d", numberOfGroupAddressAssociationTableEntries)
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Com Object Table reading
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ // Now we read the group address association table address
+ readRequestBuilder = connection.ReadRequestBuilder()
+ readRequestBuilder.AddItem("comObjectTableAddress", "1.1.10/3/7")
+ readRequest, err = readRequestBuilder.Build()
+ if err != nil {
+ t.Errorf("error creating read request: %s", err.Error())
+ t.Fail()
+ return
+ }
+ rrr = readRequest.Execute()
+ readResult = <-rrr
+ comObjectTableAddress := readResult.Response.GetValue("comObjectTableAddress").GetUint16()
+
+ fmt.Printf("%d", comObjectTableAddress)
}
func PlcValueUint8ListToByteArray(value values.PlcValue) []byte {
diff --git a/plc4go/examples/discovery/hello_world_plc4go_knx_discovery.go b/plc4go/examples/discovery/hello_world_plc4go_knx_discovery.go
new file mode 100644
index 0000000..c9f743f
--- /dev/null
+++ b/plc4go/examples/discovery/hello_world_plc4go_knx_discovery.go
@@ -0,0 +1,99 @@
+//
+// 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 main
+
+import (
+ "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports/udp"
+ "github.com/apache/plc4x/plc4go/pkg/plc4go"
+ "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+ log "github.com/sirupsen/logrus"
+ "os"
+ "time"
+)
+
+func main() {
+ //log.SetFormatter(&log.JSONFormatter{})
+ log.SetOutput(os.Stdout)
+ log.SetLevel(log.DebugLevel)
+
+ driverManager := plc4go.NewPlcDriverManager()
+ driverManager.RegisterDriver(knxnetip.NewKnxNetIpDriver())
+ driverManager.RegisterTransport(udp.NewUdpTransport())
+
+ // Try to auto-find KNX gateways via broadcast-message discovery
+ driverManager.Discover(func(event model.PlcDiscoveryEvent) {
+ connStr := event.ProtocolCode + /*":" + event.TransportCode +*/ "://" + event.TransportUrl.Host
+ crc := driverManager.GetConnection(connStr)
+
+ // Wait for the driver to connect (or not)
+ connectionResult := <-crc
+ if connectionResult.Err != nil {
+ log.Errorf("error connecting to PLC: %s", connectionResult.Err.Error())
+ return
+ }
+ connection := connectionResult.Connection
+ defer connection.Close()
+
+ // Try to find all KNX devices on the current network
+ browseRequestBuilder := connection.BrowseRequestBuilder()
+ //browseRequestBuilder.AddItem("allDevices", "[1-15].[1-15].[0-255]")
+ browseRequestBuilder.AddItem("allDevices", "[1-3].[1-6].[0-60]")
+ browseRequest, err := browseRequestBuilder.Build()
+ if err != nil {
+ log.Errorf("error creating browse request: %s", err.Error())
+ return
+ }
+ brr := browseRequest.ExecuteWithInterceptor(func(result model.PlcBrowseEvent) bool {
+ knxAddress := result.Result.Address
+ log.Info("Inspecting detected Device at KNX Address: " + knxAddress)
+
+ // Try to get all the com-objects and the group addresses they are attached to.
+ browseRequestBuilder = connection.BrowseRequestBuilder()
+ browseRequestBuilder.AddItem("comObjects", knxAddress+"#com-obj")
+ browseRequest, err := browseRequestBuilder.Build()
+ if err != nil {
+ log.Errorf("error creating read request: %s", err.Error())
+ return false
+ }
+ brr := browseRequest.Execute()
+ browseResult := <-brr
+ if browseResult.Err != nil {
+ log.Errorf("error executing the browse request for com-objects: %s", browseResult.Err.Error())
+ return false
+ }
+ for _, result := range browseResult.Response.GetQueryResults("comObjects") {
+ log.Infof(" - %s", result.Address)
+ }
+
+ return true
+ })
+ if brr == nil {
+ log.Errorf("error executing browse request")
+ return
+ }
+ select {
+ case browseRequestResult := <- brr:
+ log.Info(browseRequestResult)
+ }
+ return
+ })
+
+ time.Sleep(time.Second * 1000000)
+}
diff --git a/plc4go/examples/read/hello_world_plc4go_read.go b/plc4go/examples/read/hello_world_plc4go_read.go
index ef45fdc..81954b7 100644
--- a/plc4go/examples/read/hello_world_plc4go_read.go
+++ b/plc4go/examples/read/hello_world_plc4go_read.go
@@ -16,7 +16,7 @@
// specific language governing permissions and limitations
// under the License.
//
-package read
+package main
import (
"encoding/json"
@@ -24,7 +24,7 @@ import (
"github.com/apache/plc4x/plc4go/pkg/plc4go"
)
-func main() int {
+func main() {
// Get a connection to a remote PLC
crc := plc4go.NewPlcDriverManager().GetConnection("modbus:tcp://192.168.23.30")
@@ -32,7 +32,7 @@ func main() int {
connectionResult := <-crc
if connectionResult.Err != nil {
fmt.Printf("error connecting to PLC: %s", connectionResult.Err.Error())
- return 1
+ return
}
connection := connectionResult.Connection
@@ -45,7 +45,7 @@ func main() int {
readRequest, err := rrb.Build()
if err != nil {
fmt.Printf("error preparing read-request: %s", connectionResult.Err.Error())
- return 2
+ return
}
// Execute a read-request
@@ -55,16 +55,14 @@ func main() int {
rrr := <-rrc
if rrr.Err != nil {
fmt.Printf("error executing read-request: %s", rrr.Err.Error())
- return 3
+ return
}
// Do something with the response
readResponseJson, err := json.Marshal(rrr.Response)
if err != nil {
fmt.Printf("error serializing read-response: %s", err.Error())
- return 4
+ return
}
fmt.Printf("Result: %s\n", string(readResponseJson))
-
- return 0
}
diff --git a/plc4go/examples/write/hello_world_plc4go_write.go b/plc4go/examples/write/hello_world_plc4go_write.go
index ba4b84c..c585d57 100644
--- a/plc4go/examples/write/hello_world_plc4go_write.go
+++ b/plc4go/examples/write/hello_world_plc4go_write.go
@@ -16,7 +16,7 @@
// specific language governing permissions and limitations
// under the License.
//
-package write
+package main
import (
"encoding/json"
@@ -24,7 +24,7 @@ import (
"github.com/apache/plc4x/plc4go/pkg/plc4go"
)
-func main() int {
+func main() {
// Get a connection to a remote PLC
crc := plc4go.NewPlcDriverManager().GetConnection("s7://192.168.23.30")
@@ -32,7 +32,7 @@ func main() int {
connectionResult := <-crc
if connectionResult.Err != nil {
fmt.Printf("error connecting to PLC: %s", connectionResult.Err.Error())
- return 1
+ return
}
connection := connectionResult.Connection
@@ -46,7 +46,7 @@ func main() int {
writeRequest, err := wrb.Build()
if err != nil {
fmt.Printf("error preparing read-request: %s", connectionResult.Err.Error())
- return 2
+ return
}
// Execute a read-request
@@ -56,16 +56,14 @@ func main() int {
wrr := <-wrc
if wrr.Err != nil {
fmt.Printf("error executing write-request: %s", wrr.Err.Error())
- return 3
+ return
}
// Do something with the response
writeResponseJson, err := json.Marshal(wrr.Response)
if err != nil {
fmt.Printf("error serializing write-response: %s", err.Error())
- return 4
+ return
}
fmt.Printf("Result: %s\n", string(writeResponseJson))
-
- return 0
}
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go
index 56269e6..ca51851 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go
@@ -19,210 +19,416 @@
package knxnetip
import (
- "fmt"
- driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
- "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
- "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
- apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
- "strconv"
- "strings"
- "time"
+ "errors"
+ "fmt"
+ driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+ apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+ "strconv"
+ "strings"
+ "time"
)
type KnxNetIpBrowser struct {
- connection *KnxNetIpConnection
- messageCodec spi.MessageCodec
- sequenceCounter uint8
- spi.PlcBrowser
+ connection *KnxNetIpConnection
+ messageCodec spi.MessageCodec
+ sequenceCounter uint8
+ spi.PlcBrowser
}
func NewKnxNetIpBrowser(connection *KnxNetIpConnection, messageCodec spi.MessageCodec) *KnxNetIpBrowser {
- return &KnxNetIpBrowser{
- connection: connection,
- messageCodec: messageCodec,
- sequenceCounter: 0,
- }
+ return &KnxNetIpBrowser{
+ connection: connection,
+ messageCodec: messageCodec,
+ sequenceCounter: 0,
+ }
}
func (b KnxNetIpBrowser) Browse(browseRequest apiModel.PlcBrowseRequest) <-chan apiModel.PlcBrowseRequestResult {
- return b.BrowseWithInterceptor(browseRequest, func(result apiModel.PlcBrowseEvent) bool {
- return true
- })
+ return b.BrowseWithInterceptor(browseRequest, func(result apiModel.PlcBrowseEvent) bool {
+ return true
+ })
}
func (b KnxNetIpBrowser) BrowseWithInterceptor(browseRequest apiModel.PlcBrowseRequest, interceptor func(result apiModel.PlcBrowseEvent) bool) <-chan apiModel.PlcBrowseRequestResult {
- result := make(chan apiModel.PlcBrowseRequestResult)
- sendResult := func(browseResponse apiModel.PlcBrowseResponse, err error) {
- result <- apiModel.PlcBrowseRequestResult{
- Request: browseRequest,
- Response: browseResponse,
- Err: err,
- }
- }
-
- go func() {
- results := map[string][]apiModel.PlcBrowseQueryResult{}
- for _, queryName := range browseRequest.GetQueryNames() {
- queryString := browseRequest.GetQueryString(queryName)
- field, err := b.connection.fieldHandler.ParseQuery(queryString)
- if err != nil {
- sendResult(nil, err)
- }
-
- // Create a list of address strings, which doesn't contain any ranges, lists or wildcards
- options, err := b.calculateAddresses(field)
- if err != nil {
- sendResult(nil, err)
- }
-
- var queryResults []apiModel.PlcBrowseQueryResult
- // Parse each of these expanded addresses and handle them accordingly.
- for _, option := range options {
- field, err = b.connection.fieldHandler.ParseQuery(option)
- if err != nil {
- sendResult(nil, err)
- }
-
- // The following browse actions could be required:
- switch field.(type) {
- // - A Device Address
- // - A Device has to be detected (This is done in every case)
- // TODO: Send a Connect to the physical knx address
- // - If an object-id is provided, check if this object id exists
- // - If a property-id is provided, check if this property exists and try to get more information about it
- case KnxNetIpDevicePropertyAddressPlcField:
- targetAddress := FieldToKnxAddress(field.(KnxNetIpDevicePropertyAddressPlcField))
-
- // Send a connection request to the device
- deviceConnections := b.connection.ConnectToDevice(*targetAddress)
- select {
- case deviceConnection := <-deviceConnections:
- if deviceConnection != nil {
- queryResult := apiModel.PlcBrowseQueryResult{
- Address: fmt.Sprintf("%d.%d.%d",
- targetAddress.MainGroup,
- targetAddress.MiddleGroup,
- targetAddress.SubGroup),
- PossibleDataTypes: nil,
- }
-
- // Pass it to the callback
- add := interceptor(apiModel.PlcBrowseEvent{
- Request: browseRequest,
- QueryName: queryName,
- Result: &queryResult,
- Err: nil,
- })
-
- // If the interceptor opted for adding it to the result, do so
- if add {
- queryResults = append(queryResults, queryResult)
- }
-
- deviceDisconnections := b.connection.DisconnectFromDevice(*targetAddress)
- select {
- case _ = <-deviceDisconnections:
- case <-time.After(b.connection.defaultTtl * 10):
- fmt.Printf("Timedout")
- }
- }
- case <-time.After(b.connection.defaultTtl):
- // In this case the remote was just not responding.
- }
- // Just to slow things down a bit (This way we can't exceed the max number of requests per minute)
- //time.Sleep(time.Millisecond * 20)
- // - A Group Address
- // - Check the cache of known group addresses. If there is data available from that group-id, it's returned
- case KnxNetIpGroupAddress3LevelPlcField:
- // - A Group Address
- // - Check the cache of known group addresses. If there is data available from that group-id, it's returned
- case KnxNetIpGroupAddress2LevelPlcField:
- // - A Group Address
- // - Check the cache of known group addresses. If there is data available from that group-id, it's returned
- case KnxNetIpGroupAddress1LevelPlcField:
- }
- }
- results[queryName] = queryResults
- }
- sendResult(model.NewDefaultPlcBrowseResponse(browseRequest, results), nil)
- }()
- return result
+ result := make(chan apiModel.PlcBrowseRequestResult)
+ sendResult := func(browseResponse apiModel.PlcBrowseResponse, err error) {
+ result <- apiModel.PlcBrowseRequestResult{
+ Request: browseRequest,
+ Response: browseResponse,
+ Err: err,
+ }
+ }
+
+ go func() {
+ results := map[string][]apiModel.PlcBrowseQueryResult{}
+ for _, queryName := range browseRequest.GetQueryNames() {
+ queryString := browseRequest.GetQueryString(queryName)
+ field, err := b.connection.fieldHandler.ParseQuery(queryString)
+ if err != nil {
+ sendResult(nil, err)
+ return
+ }
+
+ switch field.(type) {
+ case KnxNetIpDeviceQueryField:
+ queryResults, err := b.executeDeviceQuery(field.(KnxNetIpDeviceQueryField), browseRequest, queryName, interceptor)
+ if err != nil {
+ // TODO: Return some sort of return code like with the read and write APIs
+ results[queryName] = nil
+ } else {
+ results[queryName] = queryResults
+ }
+ case KnxNetIpCommunicationObjectQueryField:
+ queryResults, err := b.executeCommunicationObjectQuery(field.(KnxNetIpCommunicationObjectQueryField))
+ if err != nil {
+ // TODO: Return some sort of return code like with the read and write APIs
+ results[queryName] = nil
+ } else {
+ results[queryName] = queryResults
+ }
+ default:
+ // TODO: Return some sort of return code like with the read and write APIs
+ results[queryName] = nil
+ }
+ }
+ sendResult(model.NewDefaultPlcBrowseResponse(browseRequest, results), nil)
+ }()
+ return result
+}
+
+func (b KnxNetIpBrowser) executeDeviceQuery(field KnxNetIpDeviceQueryField, browseRequest apiModel.PlcBrowseRequest, queryName string, interceptor func(result apiModel.PlcBrowseEvent) bool) ([]apiModel.PlcBrowseQueryResult, error) {
+ // Create a list of address strings, which doesn't contain any ranges, lists or wildcards
+ knxAddresses, err := b.calculateAddresses(field)
+ if err != nil {
+ return nil, err
+ }
+ if len(knxAddresses) == 0 {
+ return nil, errors.New("query resulted in not a single valid address")
+ }
+
+ var queryResults []apiModel.PlcBrowseQueryResult
+ // Parse each of these expanded addresses and handle them accordingly.
+ for _, knxAddress := range knxAddresses {
+ // Send a connection request to the device
+ deviceConnections := b.connection.ConnectToDevice(knxAddress)
+ select {
+ case deviceConnection := <-deviceConnections:
+ if deviceConnection != nil {
+ queryResult := apiModel.PlcBrowseQueryResult{
+ Address: fmt.Sprintf("%d.%d.%d",
+ knxAddress.MainGroup,
+ knxAddress.MiddleGroup,
+ knxAddress.SubGroup),
+ PossibleDataTypes: nil,
+ }
+
+ // Pass it to the callback
+ add := true
+ if interceptor != nil {
+ add = interceptor(apiModel.PlcBrowseEvent{
+ Request: browseRequest,
+ QueryName: queryName,
+ Result: &queryResult,
+ Err: nil,
+ })
+ }
+
+ // If the interceptor opted for adding it to the result, do so
+ if add {
+ queryResults = append(queryResults, queryResult)
+ }
+
+ deviceDisconnections := b.connection.DisconnectFromDevice(knxAddress)
+ select {
+ case _ = <-deviceDisconnections:
+ case <-time.After(b.connection.defaultTtl * 10):
+ fmt.Printf("Timedout")
+ }
+ }
+ case <-time.After(b.connection.defaultTtl):
+ // In this case the remote was just not responding.
+ }
+ // Just to slow things down a bit (This way we can't exceed the max number of requests per minute)
+ //time.Sleep(time.Millisecond * 20)
+ }
+ return queryResults, nil
+}
+
+func (b KnxNetIpBrowser) executeCommunicationObjectQuery(field KnxNetIpCommunicationObjectQueryField) ([]apiModel.PlcBrowseQueryResult, error) {
+ var results []apiModel.PlcBrowseQueryResult
+
+ knxAddress := field.toKnxAddress()
+ knxAddressString := KnxAddressToString(knxAddress)
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Group Address Table reading
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ // First of all, request the starting address of the group address table
+ readRequestBuilder := b.connection.ReadRequestBuilder()
+ readRequestBuilder.AddItem("groupAddressTableAddress", knxAddressString+"#1/7")
+ readRequest, err := readRequestBuilder.Build()
+ if err != nil {
+ return nil, errors.New("error creating read request: " + err.Error())
+ }
+ rrr := readRequest.Execute()
+ readResult := <-rrr
+ if readResult.Err != nil {
+ return nil, errors.New("error reading the group address table starting address: " + readResult.Err.Error())
+ }
+ if readResult.Response.GetResponseCode("groupAddressTableAddress") != apiModel.PlcResponseCode_OK {
+ return nil, errors.New("error reading group address table starting address: " +
+ readResult.Response.GetResponseCode("groupAddressTableAddress").GetName())
+ }
+ groupAddressTableStartAddress := readResult.Response.GetValue("groupAddressTableAddress").GetUint32()
+
+ // Then read one byte at the given location.
+ // This will return the number of entries in the group address table (each 2 bytes)
+ readRequestBuilder = b.connection.ReadRequestBuilder()
+ // Depending on the type of device, query an USINT (1 byte) or UINT (2 bytes)
+ // TODO: Do this correctly depending on the device connection device-descriptor
+ if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+ readRequestBuilder.AddItem("numberOfAddressTableEntries",
+ fmt.Sprintf("%s#%X:UINT", knxAddressString, groupAddressTableStartAddress))
+ } else {
+ readRequestBuilder.AddItem("numberOfAddressTableEntries",
+ fmt.Sprintf("%s#%X:USINT", knxAddressString, groupAddressTableStartAddress))
+ }
+ readRequest, err = readRequestBuilder.Build()
+ if err != nil {
+ return nil, errors.New("error creating read request: " + err.Error())
+ }
+ rrr = readRequest.Execute()
+ readResult = <-rrr
+ if readResult.Err != nil {
+ return nil, errors.New("error reading the number of group address table entries: " + readResult.Err.Error())
+ }
+ if readResult.Response.GetResponseCode("numberOfAddressTableEntries") != apiModel.PlcResponseCode_OK {
+ return nil, errors.New("error reading the number of group address table entries: " +
+ readResult.Response.GetResponseCode("numberOfAddressTableEntries").GetName())
+ }
+ numGroupAddresses := readResult.Response.GetValue("numberOfAddressTableEntries").GetUint16()
+
+ // Read the data in the group address table
+ readRequestBuilder = b.connection.ReadRequestBuilder()
+ // TODO: This request needs to be automatically split up into multiple requests.
+ // Reasons for splitting up:
+ // - Max APDU Size exceeded
+ // - Max 63 bytes readable in one request, due to max of count field
+
+ if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+ readRequestBuilder.AddItem("groupAddressTable",
+ fmt.Sprintf("%s#%X:UINT[%d]", knxAddressString, groupAddressTableStartAddress+2, numGroupAddresses))
+ } else {
+ readRequestBuilder.AddItem("groupAddressTable",
+ fmt.Sprintf("%s#%X:UINT[%d]", knxAddressString, groupAddressTableStartAddress+3, numGroupAddresses-1))
+ }
+ readRequest, err = readRequestBuilder.Build()
+ if err != nil {
+ return nil, errors.New("error creating read request: " + err.Error())
+ }
+ rrr = readRequest.Execute()
+ readResult = <-rrr
+ if readResult.Err != nil {
+ return nil, errors.New("error reading the group address table content: " + readResult.Err.Error())
+ }
+ if (readResult.Response == nil) ||
+ (readResult.Response.GetResponseCode("groupAddressTable") != apiModel.PlcResponseCode_OK) {
+ return nil, errors.New("error reading the group address table content: " +
+ readResult.Response.GetResponseCode("groupAddressTable").GetName())
+ }
+ var knxGroupAddresses []*driverModel.KnxGroupAddress
+ for _, groupAddress := range readResult.Response.GetValue("groupAddressTable").GetList() {
+ groupAddress := Uint16ToKnxGroupAddress(groupAddress.GetUint16(), 3)
+ knxGroupAddresses = append(knxGroupAddresses, groupAddress)
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Group Address Association Table reading
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ // Now we read the group address association table address
+ readRequestBuilder = b.connection.ReadRequestBuilder()
+ readRequestBuilder.AddItem("groupAddressAssociationTableAddress",
+ fmt.Sprintf("%s#2/7", knxAddressString))
+ readRequest, err = readRequestBuilder.Build()
+ if err != nil {
+ return nil, errors.New("error creating read request: " + err.Error())
+ }
+ rrr = readRequest.Execute()
+ readResult = <-rrr
+ if readResult.Err != nil {
+ return nil, errors.New("error reading the group address association table content: " + readResult.Err.Error())
+ }
+ if (readResult.Response != nil) &&
+ (readResult.Response.GetResponseCode("groupAddressAssociationTableAddress") != apiModel.PlcResponseCode_OK) {
+ return nil, errors.New("error reading the group address association table content: " +
+ readResult.Response.GetResponseCode("groupAddressAssociationTableAddress").GetName())
+ }
+ groupAddressAssociationTableAddress := readResult.Response.GetValue("groupAddressAssociationTableAddress").GetUint16()
+
+ // Then read one uint16 at the given location.
+ // This will return the number of entries in the group address table (each 2 bytes)
+ readRequestBuilder = b.connection.ReadRequestBuilder()
+ if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+ readRequestBuilder.AddItem("numberOfGroupAddressAssociationTableEntries",
+ fmt.Sprintf("%s#%X:UINT", knxAddressString, groupAddressAssociationTableAddress))
+ } else {
+ readRequestBuilder.AddItem("numberOfGroupAddressAssociationTableEntries",
+ fmt.Sprintf("%s#%X:USINT", knxAddressString, groupAddressAssociationTableAddress))
+ }
+ readRequest, err = readRequestBuilder.Build()
+ if err != nil {
+ return nil, errors.New("error creating read request: " + err.Error())
+ }
+ rrr = readRequest.Execute()
+ readResult = <-rrr
+ if readResult.Err != nil {
+ return nil, errors.New("error reading the number of group address association table entries: " + readResult.Err.Error())
+ }
+ if (readResult.Response != nil) &&
+ (readResult.Response.GetResponseCode("numberOfGroupAddressAssociationTableEntries") != apiModel.PlcResponseCode_OK) {
+ return nil, errors.New("error reading the number of group address association table entries: " +
+ readResult.Response.GetResponseCode("numberOfGroupAddressAssociationTableEntries").GetName())
+ }
+ numberOfGroupAddressAssociationTableEntries := readResult.Response.GetValue("numberOfGroupAddressAssociationTableEntries").GetUint16()
+
+ // Read the data in the group address table
+ readRequestBuilder = b.connection.ReadRequestBuilder()
+ // TODO: This request needs to be automatically split up into multiple requests.
+ // Reasons for splitting up:
+ // - Max APDU Size exceeded
+ // - Max 63 bytes readable in one request, due to max of count field
+ if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+ readRequestBuilder.AddItem("groupAddressAssociationTable",
+ fmt.Sprintf("%s#%X:UDINT[%d]", knxAddressString, groupAddressAssociationTableAddress+2, numberOfGroupAddressAssociationTableEntries))
+ } else {
+ readRequestBuilder.AddItem("groupAddressAssociationTable",
+ fmt.Sprintf("%s#%X:UINT[%d]", knxAddressString, groupAddressAssociationTableAddress+1, numberOfGroupAddressAssociationTableEntries))
+ }
+ readRequest, err = readRequestBuilder.Build()
+ if err != nil {
+ return nil, errors.New("error creating read request: " + err.Error())
+ }
+ rrr = readRequest.Execute()
+ readResult = <-rrr
+ if readResult.Err != nil {
+ return nil, errors.New("error reading the group address association table content: " + readResult.Err.Error())
+ }
+ if (readResult.Response != nil) &&
+ (readResult.Response.GetResponseCode("groupAddressAssociationTable") != apiModel.PlcResponseCode_OK) {
+ return nil, errors.New("error reading the group address association table content: " +
+ readResult.Response.GetResponseCode("groupAddressAssociationTable").GetName())
+ }
+ // Output the group addresses
+ for _, groupAddressAssociation := range readResult.Response.GetValue("groupAddressAssociationTable").GetList() {
+ var addressIndex uint16
+ var comObjectNumber uint16
+ if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+ addressIndex = uint16((groupAddressAssociation.GetUint32()>>16)&0xFFFF) - 1
+ comObjectNumber = uint16(groupAddressAssociation.GetUint32() & 0xFFFF)
+ } else {
+ addressIndex = ((groupAddressAssociation.GetUint16() >> 8) & 0xFF) - 1
+ comObjectNumber = groupAddressAssociation.GetUint16() & 0xFF
+ }
+ if addressIndex < uint16(len(knxGroupAddresses)) {
+ groupAddress := knxGroupAddresses[addressIndex]
+ result := apiModel.PlcBrowseQueryResult{
+ Address: fmt.Sprintf(
+ "%s#%s %d", knxAddressString, GroupAddressToString(groupAddress), comObjectNumber),
+ PossibleDataTypes: nil,
+ }
+ results = append(results, result)
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Com Object Table reading (Not supported on all devices)
+ // (This part is optional and experimental ...)
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ // Now we read the group address association table address
+ /*readRequestBuilder = b.connection.ReadRequestBuilder()
+ readRequestBuilder.AddItem("comObjectTableAddress", fmt.Sprintf("%s#3/7", knxAddressString))
+ readRequest, err = readRequestBuilder.Build()
+ if err != nil {
+ return nil, errors.New("error creating read request: " + err.Error())
+ }
+ rrr = readRequest.Execute()
+ readResult = <-rrr
+ if readResult.Response.GetResponseCode("comObjectTableAddress") == apiModel.PlcResponseCode_OK {
+ comObjectTableAddress := readResult.Response.GetValue("comObjectTableAddress").GetUint16()
+ log.Info("Com Object Table Address: %d", comObjectTableAddress)
+ }*/
+
+ return results, nil
}
-func (b KnxNetIpBrowser) calculateAddresses(field apiModel.PlcField) ([]string, error) {
- var addresses []string
- switch field.(type) {
- case KnxNetIpDevicePropertyAddressPlcField:
- propertyAddressField := field.(KnxNetIpDevicePropertyAddressPlcField)
- mainGroupOptions, err := b.explodeSegment(propertyAddressField.MainGroup, 1, 15)
- if err != nil {
- return nil, err
- }
- middleGroupOptions, err := b.explodeSegment(propertyAddressField.MiddleGroup, 1, 15)
- if err != nil {
- return nil, err
- }
- subGroupOptions, err := b.explodeSegment(propertyAddressField.SubGroup, 0, 255)
- if err != nil {
- return nil, err
- }
- for _, mainOption := range mainGroupOptions {
- for _, middleOption := range middleGroupOptions {
- for _, subOption := range subGroupOptions {
- // Don't try connecting to ourselves.
- if b.connection.ClientKnxAddress != nil {
- currentAddress := driverModel.KnxAddress{
- MainGroup: mainOption,
- MiddleGroup: middleOption,
- SubGroup: subOption,
- }
- if currentAddress != *b.connection.ClientKnxAddress {
- address := fmt.Sprintf("%d.%d.%d", mainOption, middleOption, subOption)
- addresses = append(addresses, address)
- }
- }
- }
- }
- }
- case KnxNetIpGroupAddress3LevelPlcField:
- case KnxNetIpGroupAddress2LevelPlcField:
- case KnxNetIpGroupAddress1LevelPlcField:
-
- }
- return addresses, nil
+func (b KnxNetIpBrowser) calculateAddresses(field KnxNetIpDeviceQueryField) ([]driverModel.KnxAddress, error) {
+ var explodedAddresses []driverModel.KnxAddress
+ mainGroupOptions, err := b.explodeSegment(field.MainGroup, 1, 15)
+ if err != nil {
+ return nil, err
+ }
+ middleGroupOptions, err := b.explodeSegment(field.MiddleGroup, 1, 15)
+ if err != nil {
+ return nil, err
+ }
+ subGroupOptions, err := b.explodeSegment(field.SubGroup, 0, 255)
+ if err != nil {
+ return nil, err
+ }
+ for _, mainOption := range mainGroupOptions {
+ for _, middleOption := range middleGroupOptions {
+ for _, subOption := range subGroupOptions {
+ // Don't try connecting to ourselves.
+ if b.connection.ClientKnxAddress != nil {
+ currentAddress := driverModel.KnxAddress{
+ MainGroup: mainOption,
+ MiddleGroup: middleOption,
+ SubGroup: subOption,
+ }
+ explodedAddresses = append(explodedAddresses, currentAddress)
+ }
+ }
+ }
+ }
+ return explodedAddresses, nil
}
func (b KnxNetIpBrowser) explodeSegment(segment string, min uint8, max uint8) ([]uint8, error) {
- var options []uint8
- if strings.Contains(segment, "*") {
- for i := min; i <= max; i++ {
- options = append(options, i)
- }
- } else if strings.HasPrefix(segment, "[") && strings.HasSuffix(segment, "]") {
- segment = strings.TrimPrefix(segment, "[")
- segment = strings.TrimSuffix(segment, "]")
- for _, segment := range strings.Split(segment, ",") {
- if strings.Contains(segment, "-") {
- split := strings.Split(segment, "-")
- localMin, err := strconv.Atoi(split[0])
- if err != nil {
- return nil, err
- }
- localMax, err := strconv.Atoi(split[1])
- if err != nil {
- return nil, err
- }
- for i := localMin; i <= localMax; i++ {
- options = append(options, uint8(i))
- }
- } else {
- option, err := strconv.Atoi(segment)
- if err != nil {
- return nil, err
- }
- options = append(options, uint8(option))
- }
- }
- }
- return options, nil
+ var options []uint8
+ if strings.Contains(segment, "*") {
+ for i := min; i <= max; i++ {
+ options = append(options, i)
+ }
+ } else if strings.HasPrefix(segment, "[") && strings.HasSuffix(segment, "]") {
+ segment = strings.TrimPrefix(segment, "[")
+ segment = strings.TrimSuffix(segment, "]")
+ for _, segment := range strings.Split(segment, ",") {
+ if strings.Contains(segment, "-") {
+ split := strings.Split(segment, "-")
+ localMin, err := strconv.Atoi(split[0])
+ if err != nil {
+ return nil, err
+ }
+ localMax, err := strconv.Atoi(split[1])
+ if err != nil {
+ return nil, err
+ }
+ for i := localMin; i <= localMax; i++ {
+ options = append(options, uint8(i))
+ }
+ } else {
+ option, err := strconv.Atoi(segment)
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, uint8(option))
+ }
+ }
+ }
+ return options, nil
}
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go
index 8fe8612..f96d2e3 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go
@@ -19,1679 +19,1581 @@
package knxnetip
import (
- "bytes"
- "errors"
- "fmt"
- driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
- "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
- "github.com/apache/plc4x/plc4go/internal/plc4go/spi/interceptors"
- internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
- "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports"
- "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports/udp"
- "github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
- values2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
- "github.com/apache/plc4x/plc4go/pkg/plc4go"
- apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
- "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
- log "github.com/sirupsen/logrus"
- "math"
- "net"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "time"
+ "bytes"
+ "errors"
+ "fmt"
+ driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi/interceptors"
+ internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports/udp"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+ values2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
+ "github.com/apache/plc4x/plc4go/pkg/plc4go"
+ apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+ "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+ log "github.com/sirupsen/logrus"
+ "math"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
)
type ConnectionMetadata struct {
- KnxMedium driverModel.KnxMedium
- GatewayName string
- GatewayKnxAddress string
- ClientKnxAddress string
-
- ProjectNumber uint8
- InstallationNumber uint8
- DeviceSerialNumber []int8
- DeviceMulticastAddress []int8
- DeviceMacAddress []int8
- SupportedServices []string
-
- apiModel.PlcConnectionMetadata
+ KnxMedium driverModel.KnxMedium
+ GatewayName string
+ GatewayKnxAddress string
+ ClientKnxAddress string
+
+ ProjectNumber uint8
+ InstallationNumber uint8
+ DeviceSerialNumber []int8
+ DeviceMulticastAddress []int8
+ DeviceMacAddress []int8
+ SupportedServices []string
+
+ apiModel.PlcConnectionMetadata
}
func (m ConnectionMetadata) GetConnectionAttributes() map[string]string {
- return map[string]string{
- "KnxMedium": m.KnxMedium.String(),
- "GatewayName": m.GatewayName,
- "GatewayKnxAddress": m.GatewayKnxAddress,
- "ClientKnxAddress": m.ClientKnxAddress,
-
- "ProjectNumber": strconv.Itoa(int(m.ProjectNumber)),
- "InstallationNumber": strconv.Itoa(int(m.InstallationNumber)),
- "DeviceSerialNumber": utils.Int8ArrayToString(m.DeviceSerialNumber, " "),
- "DeviceMulticastAddress": utils.Int8ArrayToString(m.DeviceSerialNumber, "."),
- "DeviceMacAddress": utils.Int8ArrayToString(m.DeviceSerialNumber, ":"),
- "SupportedServices": strings.Join(m.SupportedServices, ", "),
- }
+ return map[string]string{
+ "KnxMedium": m.KnxMedium.String(),
+ "GatewayName": m.GatewayName,
+ "GatewayKnxAddress": m.GatewayKnxAddress,
+ "ClientKnxAddress": m.ClientKnxAddress,
+
+ "ProjectNumber": strconv.Itoa(int(m.ProjectNumber)),
+ "InstallationNumber": strconv.Itoa(int(m.InstallationNumber)),
+ "DeviceSerialNumber": utils.Int8ArrayToString(m.DeviceSerialNumber, " "),
+ "DeviceMulticastAddress": utils.Int8ArrayToString(m.DeviceSerialNumber, "."),
+ "DeviceMacAddress": utils.Int8ArrayToString(m.DeviceSerialNumber, ":"),
+ "SupportedServices": strings.Join(m.SupportedServices, ", "),
+ }
}
func (m ConnectionMetadata) CanRead() bool {
- return true
+ return true
}
func (m ConnectionMetadata) CanWrite() bool {
- return true
+ return true
}
func (m ConnectionMetadata) CanSubscribe() bool {
- return true
+ return true
}
func (m ConnectionMetadata) CanBrowse() bool {
- return true
+ return true
}
type KnxDeviceConnection struct {
- counter uint8
- deviceDescriptor uint16
- maxApdu uint16
+ counter uint8
+ deviceDescriptor uint16
+ maxApdu uint16
}
type KnxMemoryReadFragment struct {
- numElements uint8
- startingAddress uint16
+ numElements uint8
+ startingAddress uint16
}
type KnxNetIpConnection struct {
- messageCodec spi.MessageCodec
- options map[string][]string
- fieldHandler spi.PlcFieldHandler
- valueHandler spi.PlcValueHandler
- connectionStateTimer *time.Ticker
- quitConnectionStateTimer chan struct{}
- subscribers []*KnxNetIpSubscriber
- leve3AddressCache map[uint16]*driverModel.KnxGroupAddress3Level
- leve2AddressCache map[uint16]*driverModel.KnxGroupAddress2Level
- leve1AddressCache map[uint16]*driverModel.KnxGroupAddressFreeLevel
-
- valueCache map[uint16][]int8
- valueCacheMutex sync.RWMutex
- metadata *ConnectionMetadata
- defaultTtl time.Duration
-
- GatewayKnxAddress *driverModel.KnxAddress
- ClientKnxAddress *driverModel.KnxAddress
- CommunicationChannelId uint8
- SequenceCounter int32
- TunnelingRequestExpectationId int32
- DeviceConnections map[driverModel.KnxAddress]*KnxDeviceConnection
-
- requestInterceptor internalModel.RequestInterceptor
- plc4go.PlcConnection
+ messageCodec spi.MessageCodec
+ options map[string][]string
+ fieldHandler spi.PlcFieldHandler
+ valueHandler spi.PlcValueHandler
+ connectionStateTimer *time.Ticker
+ quitConnectionStateTimer chan struct{}
+ subscribers []*KnxNetIpSubscriber
+ leve3AddressCache map[uint16]*driverModel.KnxGroupAddress3Level
+ leve2AddressCache map[uint16]*driverModel.KnxGroupAddress2Level
+ leve1AddressCache map[uint16]*driverModel.KnxGroupAddressFreeLevel
+
+ valueCache map[uint16][]int8
+ valueCacheMutex sync.RWMutex
+ metadata *ConnectionMetadata
+ defaultTtl time.Duration
+
+ GatewayKnxAddress *driverModel.KnxAddress
+ ClientKnxAddress *driverModel.KnxAddress
+ CommunicationChannelId uint8
+ SequenceCounter int32
+ TunnelingRequestExpectationId int32
+ DeviceConnections map[driverModel.KnxAddress]*KnxDeviceConnection
+
+ requestInterceptor internalModel.RequestInterceptor
+ plc4go.PlcConnection
}
type KnxReadResult struct {
- returnCode apiModel.PlcResponseCode
- value *values.PlcValue
+ returnCode apiModel.PlcResponseCode
+ value *values.PlcValue
}
type InternalResult struct {
- responseMessage interface{}
- err error
+ responseMessage interface{}
+ err error
}
func NewKnxNetIpConnection(transportInstance transports.TransportInstance, options map[string][]string, fieldHandler spi.PlcFieldHandler) *KnxNetIpConnection {
- connection := &KnxNetIpConnection{
- options: options,
- fieldHandler: fieldHandler,
- valueHandler: NewValueHandler(),
- requestInterceptor: interceptors.NewSingleItemRequestInterceptor(),
- subscribers: []*KnxNetIpSubscriber{},
- leve3AddressCache: map[uint16]*driverModel.KnxGroupAddress3Level{},
- leve2AddressCache: map[uint16]*driverModel.KnxGroupAddress2Level{},
- leve1AddressCache: map[uint16]*driverModel.KnxGroupAddressFreeLevel{},
- valueCache: map[uint16][]int8{},
- valueCacheMutex: sync.RWMutex{},
- metadata: &ConnectionMetadata{},
- defaultTtl: time.Millisecond * 10000,
- DeviceConnections: map[driverModel.KnxAddress]*KnxDeviceConnection{},
- }
- connection.messageCodec = NewKnxNetIpMessageCodec(transportInstance, connection.interceptIncomingMessage)
- return connection
+ connection := &KnxNetIpConnection{
+ options: options,
+ fieldHandler: fieldHandler,
+ valueHandler: NewValueHandler(),
+ requestInterceptor: interceptors.NewSingleItemRequestInterceptor(),
+ subscribers: []*KnxNetIpSubscriber{},
+ leve3AddressCache: map[uint16]*driverModel.KnxGroupAddress3Level{},
+ leve2AddressCache: map[uint16]*driverModel.KnxGroupAddress2Level{},
+ leve1AddressCache: map[uint16]*driverModel.KnxGroupAddressFreeLevel{},
+ valueCache: map[uint16][]int8{},
+ valueCacheMutex: sync.RWMutex{},
+ metadata: &ConnectionMetadata{},
+ defaultTtl: time.Second * 60,
+ DeviceConnections: map[driverModel.KnxAddress]*KnxDeviceConnection{},
+ }
+ connection.messageCodec = NewKnxNetIpMessageCodec(transportInstance, connection.interceptIncomingMessage)
+ return connection
}
func (m *KnxNetIpConnection) Connect() <-chan plc4go.PlcConnectionConnectResult {
- result := make(chan plc4go.PlcConnectionConnectResult)
- sendResult := func(connection plc4go.PlcConnection, err error) {
- result <- plc4go.NewPlcConnectionConnectResult(connection, err)
- }
-
- go func() {
- err := m.messageCodec.Connect()
- if err != nil {
- sendResult(nil, errors.New("error opening connection"))
- return
- }
-
- searchResponseChannel := m.sendGatewaySearchRequest()
- select {
- case searchResponse := <-searchResponseChannel:
- if searchResponse.err != nil {
- sendResult(nil, errors.New("error discovering device capabilities"))
- return
- }
-
- searchResponseMessage := driverModel.CastSearchResponse(searchResponse.responseMessage)
- // Save some important information
- m.metadata.KnxMedium = searchResponseMessage.DibDeviceInfo.KnxMedium
- m.metadata.GatewayName = string(bytes.Trim(utils.Int8ArrayToByteArray(
- searchResponseMessage.DibDeviceInfo.DeviceFriendlyName), "\x00"))
- m.GatewayKnxAddress = searchResponseMessage.DibDeviceInfo.KnxAddress
- m.metadata.GatewayKnxAddress = KnxAddressToString(m.GatewayKnxAddress)
- m.metadata.ProjectNumber = searchResponseMessage.DibDeviceInfo.ProjectInstallationIdentifier.ProjectNumber
- m.metadata.InstallationNumber = searchResponseMessage.DibDeviceInfo.ProjectInstallationIdentifier.InstallationNumber
- m.metadata.DeviceSerialNumber = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceSerialNumber
- m.metadata.DeviceMulticastAddress = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceMulticastAddress.Addr
- m.metadata.DeviceMacAddress = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceMacAddress.Addr
- m.metadata.SupportedServices = []string{}
- supportsTunneling := false
- for _, serviceId := range searchResponseMessage.DibSuppSvcFamilies.ServiceIds {
- m.metadata.SupportedServices = append(m.metadata.SupportedServices, serviceId.Child.GetTypeName())
- // If this is an instance of the "tunneling", service, this connection supports tunneling
- _, ok := serviceId.Child.(*driverModel.KnxNetIpTunneling)
- if ok {
- supportsTunneling = true
- break
- }
- }
-
- // If the current device supports tunneling, create a tunneling connection.
- // Via this connection we then get access to the entire KNX network this Gateway is connected to.
- if supportsTunneling {
- // As soon as we got a successful search-response back, send a connection request.
- connectionResponseChannel := m.sendGatewayConnectionRequest()
- select {
- case connectionResponse := <-connectionResponseChannel:
- if connectionResponse.err != nil {
- sendResult(nil, errors.New("error connecting to device"))
- return
- }
-
- connectionResponseMessage := driverModel.CastConnectionResponse(connectionResponse.responseMessage)
- // Save the communication channel id
- m.CommunicationChannelId = connectionResponseMessage.CommunicationChannelId
-
- // Reset the sequence counter
- m.SequenceCounter = -1
-
- // If the connection was successful, the gateway will now forward any packets
- // on the KNX bus that are broadcast packets to us, so we have to setup things
- // to handle these incoming messages.
- switch connectionResponseMessage.Status {
- case driverModel.Status_NO_ERROR:
- // Save the KNX Address the Gateway assigned to us for this connection.
- tunnelConnectionDataBlock :=
- driverModel.CastConnectionResponseDataBlockTunnelConnection(
- connectionResponseMessage.ConnectionResponseDataBlock)
- m.ClientKnxAddress = tunnelConnectionDataBlock.KnxAddress
-
- // Start a timer that sends connection-state requests every 60 seconds
- m.connectionStateTimer = time.NewTicker(60 * time.Second)
- m.quitConnectionStateTimer = make(chan struct{})
- go func() {
- for {
- select {
- case <-m.connectionStateTimer.C:
- // We're using the connection-state-request as ping operation ...
- ping := m.Ping()
- select {
- case pingResult := <-ping:
- if pingResult.Err != nil {
- // TODO: Do some error handling here ...
- m.connectionStateTimer.Stop()
- }
- case <-time.After(m.defaultTtl * 2):
- // Close the connection
- m.Close()
- }
-
- // If externally a request to stop the timer was issued, stop the timer.
- case <-m.quitConnectionStateTimer:
- // TODO: Do some error handling here ...
- m.connectionStateTimer.Stop()
- return
- }
- }
- }()
-
- // Create a go routine to handle incoming tunneling-requests which haven't been
- // handled by any other handler. This is where usually the GroupValueWrite messages
- // are being handled.
- go func() {
- defaultIncomingMessageChannel := m.messageCodec.GetDefaultIncomingMessageChannel()
- for {
- incomingMessage := <-defaultIncomingMessageChannel
- tunnelingRequest := driverModel.CastTunnelingRequest(incomingMessage)
- if tunnelingRequest == nil {
- tunnelingResponse := driverModel.CastTunnelingResponse(incomingMessage)
- if tunnelingResponse != nil {
- log.Warnf("Got an unhandled TunnelingResponse message %v\n", tunnelingResponse)
- } else {
- log.Warnf("Not a TunnelingRequest or TunnelingResponse message %v\n", incomingMessage)
- }
- } else {
- if tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
- log.Warnf("Not for this connection %v\n", tunnelingRequest)
- continue
- }
-
- lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
- if lDataInd != nil {
- // Get APDU, source and target address
- var apdu *driverModel.Apdu
- var sourceAddress *driverModel.KnxAddress
- lDataFrameData := driverModel.CastLDataFrameData(lDataInd.DataFrame)
- if lDataFrameData != nil {
- apdu = lDataFrameData.Apdu
- sourceAddress = lDataFrameData.SourceAddress
- } else {
- lDataFrameDataExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
- if lDataFrameDataExt != nil {
- apdu = lDataFrameDataExt.Apdu
- sourceAddress = lDataFrameDataExt.SourceAddress
- }
- }
-
- // If this is not an APDU, there is no need to further handle it.
- if apdu == nil {
- continue
- }
-
- // If this is an incoming disconnect request, remove the device
- // from the device connections, otherwise handle it as normal
- // incoming message.
- apduControlContainer := driverModel.CastApduControlContainer(apdu)
- if apduControlContainer != nil {
- disconnectApdu := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
- if disconnectApdu != nil {
- if m.DeviceConnections[*sourceAddress] != nil /* && m.ClientKnxAddress == Int8ArrayToKnxAddress(targetAddress)*/ {
- // Remove the connection
- delete(m.DeviceConnections, *sourceAddress)
- }
- }
- } else {
- m.handleIncomingTunnelingRequest(tunnelingRequest)
- }
- }
- }
- }
- }()
-
- // Fire the "connected" event
- sendResult(m, nil)
- case driverModel.Status_NO_MORE_CONNECTIONS:
- sendResult(nil, errors.New("no more connections"))
- }
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout receiving connection response from gateway.")
- }
- }
- case <-time.After(m.defaultTtl * 3):
- log.Errorf("Timeout receiving search request from gateway.")
- }
- }()
- return result
+ result := make(chan plc4go.PlcConnectionConnectResult)
+ sendResult := func(connection plc4go.PlcConnection, err error) {
+ result <- plc4go.NewPlcConnectionConnectResult(connection, err)
+ }
+
+ go func() {
+ err := m.messageCodec.Connect()
+ if err != nil {
+ sendResult(nil, errors.New("error opening connection"))
+ return
+ }
+
+ searchResponseChannel := m.sendGatewaySearchRequest()
+ select {
+ case searchResponse := <-searchResponseChannel:
+ if searchResponse.err != nil {
+ sendResult(nil, errors.New("error discovering device capabilities"))
+ return
+ }
+
+ searchResponseMessage := driverModel.CastSearchResponse(searchResponse.responseMessage)
+ // Save some important information
+ m.metadata.KnxMedium = searchResponseMessage.DibDeviceInfo.KnxMedium
+ m.metadata.GatewayName = string(bytes.Trim(utils.Int8ArrayToByteArray(
+ searchResponseMessage.DibDeviceInfo.DeviceFriendlyName), "\x00"))
+ m.GatewayKnxAddress = searchResponseMessage.DibDeviceInfo.KnxAddress
+ m.metadata.GatewayKnxAddress = KnxAddressToString(m.GatewayKnxAddress)
+ m.metadata.ProjectNumber = searchResponseMessage.DibDeviceInfo.ProjectInstallationIdentifier.ProjectNumber
+ m.metadata.InstallationNumber = searchResponseMessage.DibDeviceInfo.ProjectInstallationIdentifier.InstallationNumber
+ m.metadata.DeviceSerialNumber = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceSerialNumber
+ m.metadata.DeviceMulticastAddress = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceMulticastAddress.Addr
+ m.metadata.DeviceMacAddress = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceMacAddress.Addr
+ m.metadata.SupportedServices = []string{}
+ supportsTunneling := false
+ for _, serviceId := range searchResponseMessage.DibSuppSvcFamilies.ServiceIds {
+ m.metadata.SupportedServices = append(m.metadata.SupportedServices, serviceId.Child.GetTypeName())
+ // If this is an instance of the "tunneling", service, this connection supports tunneling
+ _, ok := serviceId.Child.(*driverModel.KnxNetIpTunneling)
+ if ok {
+ supportsTunneling = true
+ break
+ }
+ }
+
+ // If the current device supports tunneling, create a tunneling connection.
+ // Via this connection we then get access to the entire KNX network this Gateway is connected to.
+ if supportsTunneling {
+ // As soon as we got a successful search-response back, send a connection request.
+ connectionResponseChannel := m.sendGatewayConnectionRequest()
+ select {
+ case connectionResponse := <-connectionResponseChannel:
+ if connectionResponse.err != nil {
+ sendResult(nil, errors.New("error connecting to device"))
+ return
+ }
+
+ connectionResponseMessage := driverModel.CastConnectionResponse(connectionResponse.responseMessage)
+ // Save the communication channel id
+ m.CommunicationChannelId = connectionResponseMessage.CommunicationChannelId
+
+ // Reset the sequence counter
+ m.SequenceCounter = -1
+
+ // If the connection was successful, the gateway will now forward any packets
+ // on the KNX bus that are broadcast packets to us, so we have to setup things
+ // to handle these incoming messages.
+ switch connectionResponseMessage.Status {
+ case driverModel.Status_NO_ERROR:
+ // Save the KNX Address the Gateway assigned to us for this connection.
+ tunnelConnectionDataBlock :=
+ driverModel.CastConnectionResponseDataBlockTunnelConnection(
+ connectionResponseMessage.ConnectionResponseDataBlock)
+ m.ClientKnxAddress = tunnelConnectionDataBlock.KnxAddress
+
+ // Start a timer that sends connection-state requests every 60 seconds
+ m.connectionStateTimer = time.NewTicker(60 * time.Second)
+ m.quitConnectionStateTimer = make(chan struct{})
+ go func() {
+ for {
+ select {
+ case <-m.connectionStateTimer.C:
+ // We're using the connection-state-request as ping operation ...
+ ping := m.Ping()
+ select {
+ case pingResult := <-ping:
+ if pingResult.Err != nil {
+ // TODO: Do some error handling here ...
+ m.connectionStateTimer.Stop()
+ }
+ case <-time.After(m.defaultTtl * 2):
+ // Close the connection
+ m.Close()
+ }
+
+ // If externally a request to stop the timer was issued, stop the timer.
+ case <-m.quitConnectionStateTimer:
+ // TODO: Do some error handling here ...
+ m.connectionStateTimer.Stop()
+ return
+ }
+ }
+ }()
+
+ // Create a go routine to handle incoming tunneling-requests which haven't been
+ // handled by any other handler. This is where usually the GroupValueWrite messages
+ // are being handled.
+ go func() {
+ defaultIncomingMessageChannel := m.messageCodec.GetDefaultIncomingMessageChannel()
+ for {
+ incomingMessage := <-defaultIncomingMessageChannel
+ tunnelingRequest := driverModel.CastTunnelingRequest(incomingMessage)
+ if tunnelingRequest == nil {
+ tunnelingResponse := driverModel.CastTunnelingResponse(incomingMessage)
+ if tunnelingResponse != nil {
+ log.Warnf("Got an unhandled TunnelingResponse message %v\n", tunnelingResponse)
+ } else {
+ log.Warnf("Not a TunnelingRequest or TunnelingResponse message %v\n", incomingMessage)
+ }
+ } else {
+ if tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+ log.Warnf("Not for this connection %v\n", tunnelingRequest)
+ continue
+ }
+
+ lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+ if lDataInd != nil {
+ // Get APDU, source and target address
+ lDataFrameData := driverModel.CastLDataExtended(lDataInd.DataFrame)
+ sourceAddress := lDataFrameData.SourceAddress
+
+ // If this is not an APDU, there is no need to further handle it.
+ if lDataFrameData.Apdu == nil {
+ continue
+ }
+
+ // If this is an incoming disconnect request, remove the device
+ // from the device connections, otherwise handle it as normal
+ // incoming message.
+ apduControlContainer := driverModel.CastApduControlContainer(lDataFrameData.Apdu)
+ if apduControlContainer != nil {
+ disconnectApdu := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
+ if disconnectApdu != nil {
+ if m.DeviceConnections[*sourceAddress] != nil /* && m.ClientKnxAddress == Int8ArrayToKnxAddress(targetAddress)*/ {
+ // Remove the connection
+ delete(m.DeviceConnections, *sourceAddress)
+ }
+ }
+ } else {
+ m.handleIncomingTunnelingRequest(tunnelingRequest)
+ }
+ }
+ }
+ }
+ }()
+
+ // Fire the "connected" event
+ sendResult(m, nil)
+ case driverModel.Status_NO_MORE_CONNECTIONS:
+ sendResult(nil, errors.New("no more connections"))
+ }
+ case <-time.After(m.defaultTtl * 2):
+ log.Debug("Timeout receiving connection response from gateway.")
+ }
+ }
+ case <-time.After(m.defaultTtl * 3):
+ log.Debug("Timeout receiving search request from gateway.")
+ }
+ }()
+ return result
}
func (m *KnxNetIpConnection) Close() <-chan plc4go.PlcConnectionCloseResult {
- result := make(chan plc4go.PlcConnectionCloseResult)
-
- go func() {
- // Stop the connection-state checker.
- m.connectionStateTimer.Stop()
-
- // Disconnect from all knx devices we are still connected to.
- for targetAddress := range m.DeviceConnections {
- disconnects := m.DisconnectFromDevice(targetAddress)
- select {
- case _ = <-disconnects:
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout disconnecting from device %s.", KnxAddressToString(&targetAddress))
- }
- }
-
- // Send a disconnect request from the gateway.
- disconnectionResponseChannel := m.sendGatewayDisconnectionRequest()
- select {
- case disconnectResponse := <-disconnectionResponseChannel:
- if disconnectResponse.err != nil {
- result <- plc4go.NewPlcConnectionCloseResult(m, errors.New(
- fmt.Sprintf("got an error when disconnecting: %s", disconnectResponse.err.Error())))
- } else {
- result <- plc4go.NewPlcConnectionCloseResult(m, nil)
- }
-
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout disconnecting from gateway.")
- result <- plc4go.NewPlcConnectionCloseResult(m, nil)
- }
- }()
-
- return result
+ result := make(chan plc4go.PlcConnectionCloseResult)
+
+ go func() {
+ // Stop the connection-state checker.
+ m.connectionStateTimer.Stop()
+
+ // Disconnect from all knx devices we are still connected to.
+ for targetAddress := range m.DeviceConnections {
+ disconnects := m.DisconnectFromDevice(targetAddress)
+ select {
+ case _ = <-disconnects:
+ case <-time.After(m.defaultTtl * 2):
+ log.Debugf("Timeout disconnecting from device %s.", KnxAddressToString(&targetAddress))
+ }
+ }
+
+ // Send a disconnect request from the gateway.
+ disconnectionResponseChannel := m.sendGatewayDisconnectionRequest()
+ select {
+ case disconnectResponse := <-disconnectionResponseChannel:
+ if disconnectResponse.err != nil {
+ result <- plc4go.NewPlcConnectionCloseResult(m, errors.New(
+ fmt.Sprintf("got an error when disconnecting: %s", disconnectResponse.err.Error())))
+ } else {
+ result <- plc4go.NewPlcConnectionCloseResult(m, nil)
+ }
+
+ case <-time.After(m.defaultTtl * 2):
+ log.Debug("Timeout disconnecting from gateway.")
+ result <- plc4go.NewPlcConnectionCloseResult(m, nil)
+ }
+ }()
+
+ return result
}
func (m *KnxNetIpConnection) IsConnected() bool {
- if m.messageCodec != nil {
- pingChannel := m.Ping()
- select {
- case pingResponse := <-pingChannel:
- return pingResponse.Err == nil
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout checking if the connection is alive.")
- return false
- }
- }
- return false
+ if m.messageCodec != nil {
+ pingChannel := m.Ping()
+ select {
+ case pingResponse := <-pingChannel:
+ return pingResponse.Err == nil
+ case <-time.After(m.defaultTtl * 2):
+ log.Debug("Timeout checking if the connection is alive.")
+ return false
+ }
+ }
+ return false
}
func (m *KnxNetIpConnection) Ping() <-chan plc4go.PlcConnectionPingResult {
- result := make(chan plc4go.PlcConnectionPingResult)
-
- go func() {
- // Send the connection state request
- connectionStateResponseChannel := m.sendConnectionStateRequest()
- select {
- case connectionStateResponse := <-connectionStateResponseChannel:
- if connectionStateResponse.err != nil {
- result <- plc4go.NewPlcConnectionPingResult(errors.New(
- fmt.Sprintf("got an error: %s", connectionStateResponse.err.Error())))
- } else {
- result <- plc4go.NewPlcConnectionPingResult(nil)
- }
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout executing connection-state-request.")
- }
- return
- }()
-
- return result
+ result := make(chan plc4go.PlcConnectionPingResult)
+
+ go func() {
+ // Send the connection state request
+ connectionStateResponseChannel := m.sendConnectionStateRequest()
+ select {
+ case connectionStateResponse := <-connectionStateResponseChannel:
+ if connectionStateResponse.err != nil {
+ result <- plc4go.NewPlcConnectionPingResult(errors.New(
+ fmt.Sprintf("got an error: %s", connectionStateResponse.err.Error())))
+ } else {
+ result <- plc4go.NewPlcConnectionPingResult(nil)
+ }
+ case <-time.After(m.defaultTtl * 2):
+ log.Debug("Timeout executing connection-state-request.")
+ }
+ return
+ }()
+
+ return result
}
func (m *KnxNetIpConnection) ConnectToDevice(targetAddress driverModel.KnxAddress) <-chan *KnxDeviceConnection {
- result := make(chan *KnxDeviceConnection)
-
- // If we're already connected, use that connection instead.
- if connection, ok := m.DeviceConnections[targetAddress]; ok {
- result <- connection
- return result
- }
-
- // First send a connection request
- go func() {
- controlConnectResponseChannel := m.sendDeviceConnectionRequest(targetAddress)
- select {
- case conResult := <-controlConnectResponseChannel:
- if conResult.err != nil {
- result <- nil
- return
- }
-
- // If the connection request was successful, try to read the device-descriptor
- deviceDescriptorResponses := m.sendDeviceDeviceDescriptorReadRequest(targetAddress)
- go func() {
- select {
- case _ = <-deviceDescriptorResponses:
- // Last, not least, read the max APDU size
- propertyValueResponses := m.sendDevicePropertyReadRequest(targetAddress, 0, 56)
- go func() {
- select {
- case propertyValueResponse := <-propertyValueResponses:
- // If we were able to read the max APDU size, then use the minimum of
- // the connection APDU size and the device APDU size, otherwise use the
- // default APDU Size of 15
- // Defined in: 03_05_01 Resources v01.09.03 AS Page 40
- deviceApduSize := uint16(15)
- if propertyValueResponse.returnCode == apiModel.PlcResponseCode_OK {
- deviceApduSize = (*propertyValueResponse.value).GetUint16()
- }
- connection := m.DeviceConnections[targetAddress]
- connection.maxApdu = uint16(math.Min(float64(deviceApduSize), 240))
- result <- connection
- case <-time.After(m.defaultTtl * 2):
- result <- nil
- log.Errorf("Timeout receiving property read response from device: %s", KnxAddressToString(&targetAddress))
- }
- }()
- }
- }()
- case <-time.After(m.defaultTtl * 3):
- result <- nil
- log.Errorf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
- }
- }()
-
- return result
+ result := make(chan *KnxDeviceConnection)
+
+ // If we're already connected, use that connection instead.
+ if connection, ok := m.DeviceConnections[targetAddress]; ok {
+ result <- connection
+ return result
+ }
+
+ // First send a connection request
+ go func() {
+ controlConnectResponseChannel := m.sendDeviceConnectionRequest(targetAddress)
+ select {
+ case conResult := <-controlConnectResponseChannel:
+ if conResult.err != nil {
+ result <- nil
+ return
+ }
+
+ // If the connection request was successful, try to read the device-descriptor
+ deviceDescriptorResponses := m.sendDeviceDeviceDescriptorReadRequest(targetAddress)
+ go func() {
+ select {
+ case _ = <-deviceDescriptorResponses:
+ // Last, not least, read the max APDU size
+ propertyValueResponses := m.sendDevicePropertyReadRequest(targetAddress, 0, 56)
+ go func() {
+ select {
+ case propertyValueResponse := <-propertyValueResponses:
+ // If we were able to read the max APDU size, then use the minimum of
+ // the connection APDU size and the device APDU size, otherwise use the
+ // default APDU Size of 15
+ // Defined in: 03_05_01 Resources v01.09.03 AS Page 40
+ deviceApduSize := uint16(15)
+ if propertyValueResponse.returnCode == apiModel.PlcResponseCode_OK {
+ deviceApduSize = (*propertyValueResponse.value).GetUint16()
+ }
+ connection := m.DeviceConnections[targetAddress]
+ connection.maxApdu = uint16(math.Min(float64(deviceApduSize), 240))
+ result <- connection
+ case <-time.After(m.defaultTtl * 2):
+ result <- nil
+ log.Debugf("Timeout receiving property read response from device: %s", KnxAddressToString(&targetAddress))
+ }
+ }()
+ }
+ }()
+ case <-time.After(m.defaultTtl * 3):
+ result <- nil
+ log.Debugf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
+ }
+ }()
+
+ return result
}
func (m *KnxNetIpConnection) DisconnectFromDevice(targetAddress driverModel.KnxAddress) <-chan *KnxDeviceConnection {
- result := make(chan *KnxDeviceConnection)
-
- if connection, ok := m.DeviceConnections[targetAddress]; ok {
- disconnects := m.sendDeviceDisconnectionRequest(targetAddress)
- go func() {
- select {
- case _ = <-disconnects:
- // Remove the connection from the list.
- delete(m.DeviceConnections, targetAddress)
- result <- connection
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout disconnecting from device: %s", KnxAddressToString(&targetAddress))
- }
- }()
- } else {
- result <- nil
- }
-
- return result
+ result := make(chan *KnxDeviceConnection)
+
+ if connection, ok := m.DeviceConnections[targetAddress]; ok {
+ disconnects := m.sendDeviceDisconnectionRequest(targetAddress)
+ go func() {
+ select {
+ case _ = <-disconnects:
+ // Remove the connection from the list.
+ delete(m.DeviceConnections, targetAddress)
+ result <- connection
+ case <-time.After(m.defaultTtl * 2):
+ log.Debugf("Timeout disconnecting from device: %s", KnxAddressToString(&targetAddress))
+ }
+ }()
+ } else {
+ result <- nil
+ }
+
+ return result
}
func (m *KnxNetIpConnection) ReadDeviceProperty(targetAddress driverModel.KnxAddress, objectId uint8, propertyId uint8) <-chan KnxReadResult {
- result := make(chan KnxReadResult)
-
- go func() {
- // Check if there is already a connection available,
- // if not, create a new one.
- connection, ok := m.DeviceConnections[targetAddress]
- if !ok {
- connections := m.ConnectToDevice(targetAddress)
- select {
- case connection = <-connections:
- // If we didn't get a connect, abort
- if connection == nil {
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_INVALID_ADDRESS,
- value: nil,
- }
- }
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
- }
- }
-
- // If we successfully got a connection, read the property
- if connection != nil {
- propertyValueResponses := m.sendDevicePropertyReadRequest(targetAddress, objectId, propertyId)
- select {
- case propertyValueResponse := <-propertyValueResponses:
- result <- propertyValueResponse
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout reading device property from device: %s: ObjectId %d, PropertyId %d",
- KnxAddressToString(&targetAddress), objectId, propertyId)
- }
- }
- }()
-
- return result
+ result := make(chan KnxReadResult)
+
+ go func() {
+ // Check if there is already a connection available,
+ // if not, create a new one.
+ connection, ok := m.DeviceConnections[targetAddress]
+ if !ok {
+ connections := m.ConnectToDevice(targetAddress)
+ select {
+ case connection = <-connections:
+ // If we didn't get a connect, abort
+ if connection == nil {
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_INVALID_ADDRESS,
+ value: nil,
+ }
+ }
+ case <-time.After(m.defaultTtl * 2):
+ log.Debugf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
+ }
+ }
+
+ // If we successfully got a connection, read the property
+ if connection != nil {
+ propertyValueResponses := m.sendDevicePropertyReadRequest(targetAddress, objectId, propertyId)
+ select {
+ case propertyValueResponse := <-propertyValueResponses:
+ result <- propertyValueResponse
+ case <-time.After(m.defaultTtl * 2):
+ log.Debugf("Timeout reading device property from device: %s: ObjectId %d, PropertyId %d",
+ KnxAddressToString(&targetAddress), objectId, propertyId)
+ }
+ }
+ }()
+
+ return result
}
func (m *KnxNetIpConnection) ReadDeviceMemory(targetAddress driverModel.KnxAddress, address uint16, numElements uint8, datapointType *driverModel.KnxDatapointType) <-chan KnxReadResult {
- result := make(chan KnxReadResult)
-
- if datapointType == nil {
- // This is a 0-255 valued 1-byte value.
- dpt := driverModel.KnxDatapointType_DPT_DecimalFactor
- datapointType = &dpt
- }
-
- go func() {
- // Check if there is already a connection available,
- // if not, create a new one.
- connection, ok := m.DeviceConnections[targetAddress]
- if !ok {
- connections := m.ConnectToDevice(targetAddress)
- select {
- case connection = <-connections:
- // If we didn't get a connect, abort
- if connection == nil {
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_INVALID_ADDRESS,
- value: nil,
- }
- }
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
- }
- }
-
- // If we successfully got a connection, read the property
- if connection != nil {
- // Depending on the gateway Max APDU and the device Max APDU, split this up into multiple requests.
- // An APDU starts with the last 6 bits of the first data byte containing the count
- // followed by the 16-bit address, so these are already used.
- maxNumBytes := uint8(math.Min(float64(connection.maxApdu-3), float64(63)))
- elementSize := datapointType.DatapointMainType().SizeInBits() / 8
- maxNumElementsPerRequest := uint8(math.Floor(float64(maxNumBytes / elementSize)))
- maxNumBytesPerRequest := maxNumElementsPerRequest * elementSize
- remainingRequestElements := numElements
- curStartingAddress := address
- var fragments []KnxMemoryReadFragment
- for remainingRequestElements > maxNumElementsPerRequest {
- fragment := KnxMemoryReadFragment{
- numElements: maxNumElementsPerRequest,
- startingAddress: curStartingAddress,
- }
- fragments = append(fragments, fragment)
- remainingRequestElements = remainingRequestElements - maxNumElementsPerRequest
- curStartingAddress = curStartingAddress + uint16(maxNumBytesPerRequest)
- }
- fragment := KnxMemoryReadFragment{
- numElements: remainingRequestElements,
- startingAddress: curStartingAddress,
- }
- fragments = append(fragments, fragment)
-
- var results []KnxReadResult
- for _, fragment := range fragments {
- propertyValueResponses := m.sendDeviceMemoryReadRequest(targetAddress, fragment.startingAddress, fragment.numElements, *datapointType)
- select {
- case propertyValueResponse := <-propertyValueResponses:
- results = append(results, propertyValueResponse)
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout reading device memory from device: %s: address %d, number of elements %d of type %s",
- KnxAddressToString(&targetAddress), address, numElements, datapointType.Name())
- }
- }
- if len(results) > 1 {
- returnCode := apiModel.PlcResponseCode_OK
- // Merge the results
- var plcValues []values.PlcValue
- for _, curResult := range results {
- if curResult.returnCode > returnCode {
- returnCode = curResult.returnCode
- }
- plcValues = append(plcValues, (*(curResult.value)).GetList()...)
- }
- var plcList values.PlcValue
- plcList = values2.NewPlcList(plcValues)
- result <- KnxReadResult{
- returnCode: returnCode,
- value: &plcList,
- }
- } else if len(results) == 1 {
- result <- results[0]
- }
- }
- }()
-
- return result
+ result := make(chan KnxReadResult)
+
+ if datapointType == nil {
+ // This is a 0-255 valued 1-byte value.
+ dpt := driverModel.KnxDatapointType_DPT_DecimalFactor
+ datapointType = &dpt
+ }
+
+ go func() {
+ // Check if there is already a connection available,
+ // if not, create a new one.
+ connection, ok := m.DeviceConnections[targetAddress]
+ if !ok {
+ connections := m.ConnectToDevice(targetAddress)
+ select {
+ case connection = <-connections:
+ // If we didn't get a connect, abort
+ if connection == nil {
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_INVALID_ADDRESS,
+ value: nil,
+ }
+ }
+ case <-time.After(m.defaultTtl * 2):
+ log.Debugf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
+ }
+ }
+
+ // If we successfully got a connection, read the property
+ if connection != nil {
+ // Depending on the gateway Max APDU and the device Max APDU, split this up into multiple requests.
+ // An APDU starts with the last 6 bits of the first data byte containing the count
+ // followed by the 16-bit address, so these are already used.
+ maxNumBytes := uint8(math.Min(float64(connection.maxApdu-3), float64(63)))
+ elementSize := datapointType.DatapointMainType().SizeInBits() / 8
+ maxNumElementsPerRequest := uint8(math.Floor(float64(maxNumBytes / elementSize)))
+ maxNumBytesPerRequest := maxNumElementsPerRequest * elementSize
+ remainingRequestElements := numElements
+ curStartingAddress := address
+ var fragments []KnxMemoryReadFragment
+ for remainingRequestElements > maxNumElementsPerRequest {
+ fragment := KnxMemoryReadFragment{
+ numElements: maxNumElementsPerRequest,
+ startingAddress: curStartingAddress,
+ }
+ fragments = append(fragments, fragment)
+ remainingRequestElements = remainingRequestElements - maxNumElementsPerRequest
+ curStartingAddress = curStartingAddress + uint16(maxNumBytesPerRequest)
+ }
+ fragment := KnxMemoryReadFragment{
+ numElements: remainingRequestElements,
+ startingAddress: curStartingAddress,
+ }
+ fragments = append(fragments, fragment)
+
+ var results []KnxReadResult
+ for _, fragment := range fragments {
+ propertyValueResponses := m.sendDeviceMemoryReadRequest(targetAddress, fragment.startingAddress, fragment.numElements, *datapointType)
+ select {
+ case propertyValueResponse := <-propertyValueResponses:
+ results = append(results, propertyValueResponse)
+ case <-time.After(m.defaultTtl * 4):
+ log.Debugf("Timeout reading device memory from device: %s: address %d, number of elements %d of type %s",
+ KnxAddressToString(&targetAddress), address, numElements, datapointType.Name())
+ }
+ }
+ if len(results) > 1 {
+ returnCode := apiModel.PlcResponseCode_OK
+ // Merge the results
+ var plcValues []values.PlcValue
+ for _, curResult := range results {
+ if curResult.returnCode > returnCode {
+ returnCode = curResult.returnCode
+ }
+ plcValues = append(plcValues, (*(curResult.value)).GetList()...)
+ }
+ var plcList values.PlcValue
+ plcList = values2.NewPlcList(plcValues)
+ result <- KnxReadResult{
+ returnCode: returnCode,
+ value: &plcList,
+ }
+ } else if len(results) == 1 {
+ result <- results[0]
+ }
+ }
+ }()
+
+ return result
}
func (m *KnxNetIpConnection) GetMetadata() apiModel.PlcConnectionMetadata {
- return m.metadata
+ return m.metadata
}
func (m *KnxNetIpConnection) ReadRequestBuilder() apiModel.PlcReadRequestBuilder {
- return internalModel.NewDefaultPlcReadRequestBuilder(
- m.fieldHandler, NewKnxNetIpReader(m))
+ return internalModel.NewDefaultPlcReadRequestBuilder(
+ m.fieldHandler, NewKnxNetIpReader(m))
}
func (m *KnxNetIpConnection) WriteRequestBuilder() apiModel.PlcWriteRequestBuilder {
- return internalModel.NewDefaultPlcWriteRequestBuilder(
- m.fieldHandler, m.valueHandler, NewKnxNetIpWriter(m.messageCodec))
+ return internalModel.NewDefaultPlcWriteRequestBuilder(
+ m.fieldHandler, m.valueHandler, NewKnxNetIpWriter(m.messageCodec))
}
func (m *KnxNetIpConnection) SubscriptionRequestBuilder() apiModel.PlcSubscriptionRequestBuilder {
- return internalModel.NewDefaultPlcSubscriptionRequestBuilder(
- m.fieldHandler, m.valueHandler, NewKnxNetIpSubscriber(m))
+ return internalModel.NewDefaultPlcSubscriptionRequestBuilder(
+ m.fieldHandler, m.valueHandler, NewKnxNetIpSubscriber(m))
}
func (m *KnxNetIpConnection) BrowseRequestBuilder() apiModel.PlcBrowseRequestBuilder {
- return internalModel.NewDefaultPlcBrowseRequestBuilder(NewKnxNetIpBrowser(m, m.messageCodec))
+ return internalModel.NewDefaultPlcBrowseRequestBuilder(NewKnxNetIpBrowser(m, m.messageCodec))
}
func (m *KnxNetIpConnection) UnsubscriptionRequestBuilder() apiModel.PlcUnsubscriptionRequestBuilder {
- return nil /*internalModel.NewDefaultPlcUnsubscriptionRequestBuilder(
- m.fieldHandler, m.valueHandler, NewKnxNetIpSubscriber(m.messageCodec))*/
+ return nil /*internalModel.NewDefaultPlcUnsubscriptionRequestBuilder(
+ m.fieldHandler, m.valueHandler, NewKnxNetIpSubscriber(m.messageCodec))*/
}
func (m *KnxNetIpConnection) GetTransportInstance() transports.TransportInstance {
- if mc, ok := m.messageCodec.(spi.TransportInstanceExposer); ok {
- return mc.GetTransportInstance()
- }
- return nil
+ if mc, ok := m.messageCodec.(spi.TransportInstanceExposer); ok {
+ return mc.GetTransportInstance()
+ }
+ return nil
}
func (m *KnxNetIpConnection) GetPlcFieldHandler() spi.PlcFieldHandler {
- return m.fieldHandler
+ return m.fieldHandler
}
func (m *KnxNetIpConnection) GetPlcValueHandler() spi.PlcValueHandler {
- return m.valueHandler
+ return m.valueHandler
}
func (m *KnxNetIpConnection) send(request *driverModel.KnxNetIpMessage) error {
- // If this is a tunneling request, we need to update the communicationChannelId and assign a sequenceCounter
- tunnelingRequest := driverModel.CastTunnelingRequest(request)
- if tunnelingRequest != nil {
- tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId = m.CommunicationChannelId
- tunnelingRequest.TunnelingRequestDataBlock.SequenceCounter = m.getNewSequenceCounter()
- }
- return m.messageCodec.Send(request)
+ // If this is a tunneling request, we need to update the communicationChannelId and assign a sequenceCounter
+ tunnelingRequest := driverModel.CastTunnelingRequest(request)
+ if tunnelingRequest != nil {
+ tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId = m.CommunicationChannelId
+ tunnelingRequest.TunnelingRequestDataBlock.SequenceCounter = m.getNewSequenceCounter()
+ }
+ return m.messageCodec.Send(request)
}
func (m *KnxNetIpConnection) sendGatewaySearchRequest() chan InternalResult {
- result := make(chan InternalResult)
- localAddress, err := m.getLocalAddress()
- if err != nil {
- // close(result)
- return result
- }
- localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP))
- discoveryEndpoint := driverModel.NewHPAIDiscoveryEndpoint(
- driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port))
- searchRequest := driverModel.NewSearchRequest(discoveryEndpoint)
- err = m.messageCodec.SendRequest(searchRequest,
- func(message interface{}) bool {
- searchResponse := driverModel.CastSearchResponse(message)
- return searchResponse != nil
- },
- func(message interface{}) error {
- searchResponse := driverModel.CastSearchResponse(message)
- result <- InternalResult{
- responseMessage: *searchResponse,
- }
- // close(result)
- return nil
- },
- func(err error) error {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
- }
- return nil
- },
- m.defaultTtl)
- if err != nil {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
- }
- }
- return result
+ result := make(chan InternalResult)
+ localAddress, err := m.getLocalAddress()
+ if err != nil {
+ // close(result)
+ return result
+ }
+ localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP))
+ discoveryEndpoint := driverModel.NewHPAIDiscoveryEndpoint(
+ driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port))
+ searchRequest := driverModel.NewSearchRequest(discoveryEndpoint)
+ err = m.messageCodec.SendRequest(searchRequest,
+ func(message interface{}) bool {
+ searchResponse := driverModel.CastSearchResponse(message)
+ return searchResponse != nil
+ },
+ func(message interface{}) error {
+ searchResponse := driverModel.CastSearchResponse(message)
+ result <- InternalResult{
+ responseMessage: *searchResponse,
+ }
+ // close(result)
+ return nil
+ },
+ func(err error) error {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+ }
+ return nil
+ },
+ m.defaultTtl)
+ if err != nil {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+ }
+ }
+ return result
}
func (m *KnxNetIpConnection) sendGatewayConnectionRequest() chan InternalResult {
- result := make(chan InternalResult)
- localAddress, err := m.getLocalAddress()
- if err != nil {
- // close(result)
- result <- InternalResult{
- err: err,
- }
- return result
- }
- localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
- connectionRequest := driverModel.NewConnectionRequest(
- driverModel.NewHPAIDiscoveryEndpoint(driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port)),
- driverModel.NewHPAIDataEndpoint(driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port)),
- driverModel.NewConnectionRequestInformationTunnelConnection(driverModel.KnxLayer_TUNNEL_LINK_LAYER),
- )
- err = m.messageCodec.SendRequest(connectionRequest,
- func(message interface{}) bool {
- connectionResponse := driverModel.CastConnectionResponse(message)
- return connectionResponse != nil
- },
- func(message interface{}) error {
- connectionResponse := driverModel.CastConnectionResponse(message)
- result <- InternalResult{
- responseMessage: *connectionResponse,
- }
- // close(result)
- return nil
- },
- func(err error) error {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
- }
- return nil
- },
- m.defaultTtl)
- if err != nil {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
- }
- }
- return result
+ result := make(chan InternalResult)
+ localAddress, err := m.getLocalAddress()
+ if err != nil {
+ // close(result)
+ result <- InternalResult{
+ err: err,
+ }
+ return result
+ }
+ localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
+ connectionRequest := driverModel.NewConnectionRequest(
+ driverModel.NewHPAIDiscoveryEndpoint(driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port)),
+ driverModel.NewHPAIDataEndpoint(driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port)),
+ driverModel.NewConnectionRequestInformationTunnelConnection(driverModel.KnxLayer_TUNNEL_LINK_LAYER),
+ )
+ err = m.messageCodec.SendRequest(connectionRequest,
+ func(message interface{}) bool {
+ connectionResponse := driverModel.CastConnectionResponse(message)
+ return connectionResponse != nil
+ },
+ func(message interface{}) error {
+ connectionResponse := driverModel.CastConnectionResponse(message)
+ result <- InternalResult{
+ responseMessage: *connectionResponse,
+ }
+ // close(result)
+ return nil
+ },
+ func(err error) error {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+ }
+ return nil
+ },
+ m.defaultTtl)
+ if err != nil {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+ }
+ }
+ return result
}
func (m *KnxNetIpConnection) sendGatewayDisconnectionRequest() chan InternalResult {
- result := make(chan InternalResult)
- localAddress, err := m.getLocalAddress()
- if err != nil {
- // close(result)
- return result
- }
- localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
- disconnectRequest := driverModel.NewDisconnectRequest(
- m.CommunicationChannelId,
- driverModel.NewHPAIControlEndpoint(
- driverModel.HostProtocolCode_IPV4_UDP,
- localAddr,
- uint16(localAddress.Port)))
- err = m.messageCodec.SendRequest(disconnectRequest,
- func(message interface{}) bool {
- disconnectResponse := driverModel.CastDisconnectResponse(message)
- return disconnectResponse != nil
- },
- func(message interface{}) error {
- disconnectResponse := driverModel.CastDisconnectResponse(message)
- result <- InternalResult{
- responseMessage: *disconnectResponse,
- }
- // close(result)
- return nil
- },
- func(err error) error {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
- }
- return nil
- },
- m.defaultTtl)
- if err != nil {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
- }
- }
- return result
+ result := make(chan InternalResult)
+ localAddress, err := m.getLocalAddress()
+ if err != nil {
+ // close(result)
+ return result
+ }
+ localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
+ disconnectRequest := driverModel.NewDisconnectRequest(
+ m.CommunicationChannelId,
+ driverModel.NewHPAIControlEndpoint(
+ driverModel.HostProtocolCode_IPV4_UDP,
+ localAddr,
+ uint16(localAddress.Port)))
+ err = m.messageCodec.SendRequest(disconnectRequest,
+ func(message interface{}) bool {
+ disconnectResponse := driverModel.CastDisconnectResponse(message)
+ return disconnectResponse != nil
+ },
+ func(message interface{}) error {
+ disconnectResponse := driverModel.CastDisconnectResponse(message)
+ result <- InternalResult{
+ responseMessage: *disconnectResponse,
+ }
+ // close(result)
+ return nil
+ },
+ func(err error) error {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+ }
+ return nil
+ },
+ m.defaultTtl)
+ if err != nil {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+ }
+ }
+ return result
}
func (m *KnxNetIpConnection) sendConnectionStateRequest() chan InternalResult {
- result := make(chan InternalResult)
- localAddress, err := m.getLocalAddress()
- if err != nil {
- // close(result)
- return result
- }
- localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
- connectionStateRequest := driverModel.NewConnectionStateRequest(
- m.CommunicationChannelId,
- driverModel.NewHPAIControlEndpoint(
- driverModel.HostProtocolCode_IPV4_UDP,
- localAddr, uint16(localAddress.Port)))
- err = m.messageCodec.SendRequest(connectionStateRequest,
- func(message interface{}) bool {
- connectionStateResponse := driverModel.CastConnectionStateResponse(message)
- return connectionStateResponse != nil
- },
- func(message interface{}) error {
- connectionStateResponse := driverModel.CastConnectionStateResponse(message)
- result <- InternalResult{
- responseMessage: *connectionStateResponse,
- }
- // close(result)
- return nil
- },
- func(err error) error {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
- }
- return nil
- },
- m.defaultTtl)
- if err != nil {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
- }
- }
- return result
+ result := make(chan InternalResult)
+ localAddress, err := m.getLocalAddress()
+ if err != nil {
+ // close(result)
+ return result
+ }
+ localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
+ connectionStateRequest := driverModel.NewConnectionStateRequest(
+ m.CommunicationChannelId,
+ driverModel.NewHPAIControlEndpoint(
+ driverModel.HostProtocolCode_IPV4_UDP,
+ localAddr, uint16(localAddress.Port)))
+ err = m.messageCodec.SendRequest(connectionStateRequest,
+ func(message interface{}) bool {
+ connectionStateResponse := driverModel.CastConnectionStateResponse(message)
+ return connectionStateResponse != nil
+ },
+ func(message interface{}) error {
+ connectionStateResponse := driverModel.CastConnectionStateResponse(message)
+ result <- InternalResult{
+ responseMessage: *connectionStateResponse,
+ }
+ // close(result)
+ return nil
+ },
+ func(err error) error {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+ }
+ return nil
+ },
+ m.defaultTtl)
+ if err != nil {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+ }
+ }
+ return result
}
func (m *KnxNetIpConnection) sendDeviceConnectionRequest(targetAddress driverModel.KnxAddress) chan InternalResult {
- result := make(chan InternalResult)
-
- go func() {
- // Send a connection request to the individual KNX device
- deviceConnectionRequest := driverModel.NewTunnelingRequest(
- driverModel.NewTunnelingRequestDataBlock(0, 0),
- driverModel.NewLDataReq(0, nil,
- driverModel.NewLDataFrameDataExt(false, 6, uint8(0),
- driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
- driverModel.NewApduControlContainer(driverModel.NewApduControlConnect(), 0, false, 0),
- true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
- err := m.sendRequest(
- deviceConnectionRequest,
- // The Gateway is now supposed to send an Ack to this request.
- func(message interface{}) bool {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- if tunnelingRequest == nil ||
- tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
- return false
- }
- lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
- if lDataCon == nil {
- return false
- }
- lDataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
- if lDataFrameExt == nil {
- return false
- }
- // Check if the address matches
- if *Int8ArrayToKnxAddress(lDataFrameExt.DestinationAddress) != targetAddress {
- return false
- }
- apduControlContainer := driverModel.CastApduControlContainer(lDataFrameExt.Apdu)
- if apduControlContainer == nil {
- return false
- }
- apduControlConnect := driverModel.CastApduControlConnect(apduControlContainer.ControlApdu)
- return apduControlConnect != nil
- },
- func(message interface{}) error {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
- // If the error flag is set, there was an error connecting
- if lDataCon.DataFrame.ErrorFlag {
- result <- InternalResult{
- err: errors.New("error flag set"),
- }
- return nil
- }
- lDataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
- apduControlContainer := driverModel.CastApduControlContainer(lDataFrameExt.Apdu)
- apduControlConnect := driverModel.CastApduControlConnect(apduControlContainer.ControlApdu)
-
- // Create a new connection object and save it in the map
- deviceConnection := &KnxDeviceConnection{
- counter: 0,
- // I was told this value on the knx-forum.
- // Seems the max payload is 3 bytes less ...
- maxApdu: 0, // This is the default max APDU Size
- }
- m.DeviceConnections[targetAddress] = deviceConnection
-
- result <- InternalResult{
- responseMessage: apduControlConnect,
- }
- return nil
- },
- func(err error) error {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
- }
- return nil
- },
- m.defaultTtl)
- if err != nil {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
- }
- }
- }()
-
- return result
+ result := make(chan InternalResult)
+
+ go func() {
+ // Send a connection request to the individual KNX device
+ deviceConnectionRequest := driverModel.NewTunnelingRequest(
+ driverModel.NewTunnelingRequestDataBlock(0, 0),
+ driverModel.NewLDataReq(0, nil,
+ driverModel.NewLDataExtended(false, 6, uint8(0),
+ driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
+ driverModel.NewApduControlContainer(driverModel.NewApduControlConnect(), false, 0),
+ true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
+ err := m.sendRequest(
+ deviceConnectionRequest,
+ // The Gateway is now supposed to send an Ack to this request.
+ func(message interface{}) bool {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ if tunnelingRequest == nil ||
+ tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+ return false
+ }
+ lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+ if lDataCon == nil {
+ return false
+ }
+ lDataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+ if lDataFrameExt == nil {
+ return false
+ }
+ // Check if the address matches
+ if *Int8ArrayToKnxAddress(lDataFrameExt.DestinationAddress) != targetAddress {
+ return false
+ }
+ apduControlContainer := driverModel.CastApduControlContainer(lDataFrameExt.Apdu)
+ if apduControlContainer == nil {
+ return false
+ }
+ apduControlConnect := driverModel.CastApduControlConnect(apduControlContainer.ControlApdu)
+ return apduControlConnect != nil
+ },
+ func(message interface{}) error {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+ // If the error flag is set, there was an error connecting
+ if lDataCon.DataFrame.ErrorFlag {
+ result <- InternalResult{
+ err: errors.New("error flag set"),
+ }
+ return nil
+ }
+ lDataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+ apduControlContainer := driverModel.CastApduControlContainer(lDataFrameExt.Apdu)
+ apduControlConnect := driverModel.CastApduControlConnect(apduControlContainer.ControlApdu)
+
+ // Create a new connection object and save it in the map
+ deviceConnection := &KnxDeviceConnection{
+ counter: 0,
+ // I was told this value on the knx-forum.
+ // Seems the max payload is 3 bytes less ...
+ maxApdu: 0, // This is the default max APDU Size
+ }
+ m.DeviceConnections[targetAddress] = deviceConnection
+
+ result <- InternalResult{
+ responseMessage: apduControlConnect,
+ }
+ return nil
+ },
+ func(err error) error {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+ }
+ return nil
+ },
+ m.defaultTtl)
+ if err != nil {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+ }
+ }
+ }()
+
+ return result
}
func (m *KnxNetIpConnection) sendDeviceDisconnectionRequest(targetAddress driverModel.KnxAddress) chan InternalResult {
- result := make(chan InternalResult)
-
- go func() {
- // Send a connection request to the individual KNX device
- deviceDisconnectionRequest := driverModel.NewTunnelingRequest(
- driverModel.NewTunnelingRequestDataBlock(0, 0),
- driverModel.NewLDataReq(0, nil,
- driverModel.NewLDataFrameDataExt(false, 6, uint8(0),
- driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
- driverModel.NewApduControlContainer(driverModel.NewApduControlDisconnect(), 0, false, 0),
- true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
- err := m.sendRequest(
- deviceDisconnectionRequest,
- // The Gateway is now supposed to send an Ack to this request.
- func(message interface{}) bool {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- if tunnelingRequest == nil ||
- tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
- return false
- }
- lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
- if lDataCon == nil {
- return false
- }
- var apdu *driverModel.Apdu
- var curTargetAddress *driverModel.KnxAddress
- dataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
- if dataFrameExt != nil {
- apdu = dataFrameExt.Apdu
- curTargetAddress = Int8ArrayToKnxAddress(dataFrameExt.DestinationAddress)
- } else {
- dataFrame := driverModel.CastLDataFrameData(lDataCon.DataFrame)
- if dataFrame != nil {
- apdu = dataFrame.Apdu
- curTargetAddress = Int8ArrayToKnxAddress(dataFrame.DestinationAddress)
- } else {
- return false
- }
- }
- // Check if the address matches
- if *curTargetAddress != targetAddress {
- return false
- }
- apduControlContainer := driverModel.CastApduControlContainer(apdu)
- if apduControlContainer == nil {
- return false
- }
- apduControlDisconnect := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
- return apduControlDisconnect != nil
- },
- func(message interface{}) error {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
- // If the error flag is set, there was an error connecting
- if lDataCon.DataFrame.ErrorFlag {
- // close(result)
- }
- var apdu *driverModel.Apdu
- dataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
- if dataFrameExt != nil {
- apdu = dataFrameExt.Apdu
- } else {
- dataFrame := driverModel.CastLDataFrameData(lDataCon.DataFrame)
- if dataFrame != nil {
- apdu = dataFrame.Apdu
- }
- }
- apduControlContainer := driverModel.CastApduControlContainer(apdu)
- apduControlDisconnect := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
- result <- InternalResult{
- responseMessage: *apduControlDisconnect,
- }
- return nil
- },
- func(err error) error {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
- }
- return nil
- },
- m.defaultTtl)
- if err != nil {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
- }
- }
- }()
-
- return result
+ result := make(chan InternalResult)
+
+ go func() {
+ // Send a connection request to the individual KNX device
+ deviceDisconnectionRequest := driverModel.NewTunnelingRequest(
+ driverModel.NewTunnelingRequestDataBlock(0, 0),
+ driverModel.NewLDataReq(0, nil,
+ driverModel.NewLDataExtended(false, 6, uint8(0),
+ driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
+ driverModel.NewApduControlContainer(driverModel.NewApduControlDisconnect(), false, 0),
+ true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
+ err := m.sendRequest(
+ deviceDisconnectionRequest,
+ // The Gateway is now supposed to send an Ack to this request.
+ func(message interface{}) bool {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ if tunnelingRequest == nil ||
+ tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+ return false
+ }
+ lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+ if lDataCon == nil {
+ return false
+ }
+ dataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+ if dataFrameExt == nil {
+ return false
+ }
+ curTargetAddress := Int8ArrayToKnxAddress(dataFrameExt.DestinationAddress)
+ // Check if the address matches
+ if *curTargetAddress != targetAddress {
+ return false
+ }
+ apduControlContainer := driverModel.CastApduControlContainer(dataFrameExt.Apdu)
+ if apduControlContainer == nil {
+ return false
+ }
+ apduControlDisconnect := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
+ return apduControlDisconnect != nil
+ },
+ func(message interface{}) error {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+ // If the error flag is set, there was an error connecting
+ if lDataCon.DataFrame.ErrorFlag {
+ // close(result)
+ }
+ dataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+ apduControlContainer := driverModel.CastApduControlContainer(dataFrameExt.Apdu)
+ apduControlDisconnect := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
+ result <- InternalResult{
+ responseMessage: *apduControlDisconnect,
+ }
+ return nil
+ },
+ func(err error) error {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+ }
+ return nil
+ },
+ m.defaultTtl)
+ if err != nil {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+ }
+ }
+ }()
+
+ return result
}
func (m *KnxNetIpConnection) sendDeviceDeviceDescriptorReadRequest(targetAddress driverModel.KnxAddress) chan InternalResult {
- result := make(chan InternalResult)
-
- connection, ok := m.DeviceConnections[targetAddress]
- if !ok {
- result <- InternalResult{
- err: errors.New("not connected"),
- }
- return result
- }
-
- // Next, read the device descriptor so we know how we have to communicate with the device.
- counter := connection.counter
- connection.counter++
- deviceDescriptorReadRequest := driverModel.NewTunnelingRequest(
- driverModel.NewTunnelingRequestDataBlock(
- // This is actually set in the KnxNetIpConnection.SendMessage method
- 0,
- // This is actually set in the KnxNetIpConnection.SendMessage method
- 0),
- driverModel.NewLDataReq(0, nil,
- driverModel.NewLDataFrameDataExt(false, 6, uint8(0),
- driverModel.NewKnxAddress(0, 0, 0),
- KnxAddressToInt8Array(targetAddress),
- driverModel.NewApduDataContainer(
- driverModel.NewApduDataDeviceDescriptorRead(0), 1, true, counter),
- true, true, driverModel.CEMIPriority_LOW, false, false)))
- err := m.sendRequest(
- deviceDescriptorReadRequest,
- func(message interface{}) bool {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- if tunnelingRequest == nil ||
- tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
- return false
- }
- lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
- if lDataInd == nil {
- return false
- }
- var apdu *driverModel.Apdu
- var sourceAddress *driverModel.KnxAddress
- dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
- if dataFrameExt != nil {
- apdu = dataFrameExt.Apdu
- sourceAddress = dataFrameExt.SourceAddress
- } else {
- dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
- if dataFrame != nil {
- apdu = dataFrame.Apdu
- sourceAddress = dataFrame.SourceAddress
- } else {
- return false
- }
- }
- // Check if the address matches
- if *sourceAddress != targetAddress {
- return false
- }
- // Check if the counter matches
- if apdu.Counter != counter {
- return false
- }
- dataContainer := driverModel.CastApduDataContainer(apdu)
- if dataContainer == nil {
- return false
- }
- deviceDescriptorResponse := driverModel.CastApduDataDeviceDescriptorResponse(dataContainer.DataApdu)
- if deviceDescriptorResponse == nil {
- return false
- }
- return true
- },
- func(message interface{}) error {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
- dataFrame := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
- dataContainer := driverModel.CastApduDataContainer(dataFrame.Apdu)
- deviceDescriptorResponse := driverModel.CastApduDataDeviceDescriptorResponse(dataContainer.DataApdu)
-
- // Send an ACK and wait for it to be delivered
- go func() {
- ackResults := m.sendDeviceAck(targetAddress, dataFrame.Apdu.Counter)
- select {
- case ackResult := <-ackResults:
- if ackResult.err != nil {
- fmt.Printf("Error sending Ack: %s", ackResult.err)
- }
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
- }
-
- // Save the device-descriptor value
- deviceDescriptor := uint16(deviceDescriptorResponse.Data[0])<<8 | uint16(deviceDescriptorResponse.Data[1])
- connection.deviceDescriptor = deviceDescriptor
-
- result <- InternalResult{
- responseMessage: *deviceDescriptorResponse,
- }
- }()
-
- return nil
- },
- func(err error) error {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
- }
- return nil
- },
- m.defaultTtl)
- if err != nil {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
- }
- }
- return result
+ result := make(chan InternalResult)
+
+ connection, ok := m.DeviceConnections[targetAddress]
+ if !ok {
+ result <- InternalResult{
+ err: errors.New("not connected"),
+ }
+ return result
+ }
+
+ // Next, read the device descriptor so we know how we have to communicate with the device.
+ counter := connection.counter
+ connection.counter++
+ deviceDescriptorReadRequest := driverModel.NewTunnelingRequest(
+ driverModel.NewTunnelingRequestDataBlock(
+ // This is actually set in the KnxNetIpConnection.SendMessage method
+ 0,
+ // This is actually set in the KnxNetIpConnection.SendMessage method
+ 0),
+ driverModel.NewLDataReq(0, nil,
+ driverModel.NewLDataExtended(false, 6, uint8(0),
+ driverModel.NewKnxAddress(0, 0, 0),
+ KnxAddressToInt8Array(targetAddress),
+ driverModel.NewApduDataContainer(
+ driverModel.NewApduDataDeviceDescriptorRead(0), true, counter),
+ true, true, driverModel.CEMIPriority_LOW, false, false)))
+ err := m.sendRequest(
+ deviceDescriptorReadRequest,
+ func(message interface{}) bool {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ if tunnelingRequest == nil ||
+ tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+ return false
+ }
+ lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+ if lDataInd == nil {
+ return false
+ }
+ dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+ if dataFrameExt == nil {
+ return false
+ }
+ // Check if the address matches
+ if *dataFrameExt.SourceAddress != targetAddress {
+ return false
+ }
+ // Check if the counter matches
+ if dataFrameExt.Apdu.Counter != counter {
+ return false
+ }
+ dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+ if dataContainer == nil {
+ return false
+ }
+ deviceDescriptorResponse := driverModel.CastApduDataDeviceDescriptorResponse(dataContainer.DataApdu)
+ if deviceDescriptorResponse == nil {
+ return false
+ }
+ return true
+ },
+ func(message interface{}) error {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+ dataFrame := driverModel.CastLDataExtended(lDataInd.DataFrame)
+ dataContainer := driverModel.CastApduDataContainer(dataFrame.Apdu)
+ deviceDescriptorResponse := driverModel.CastApduDataDeviceDescriptorResponse(dataContainer.DataApdu)
+
+ // Send an ACK and wait for it to be delivered
+ go func() {
+ ackResults := m.sendDeviceAck(targetAddress, dataFrame.Apdu.Counter)
+ select {
+ case ackResult := <-ackResults:
+ if ackResult.err != nil {
+ fmt.Printf("Error sending Ack: %s", ackResult.err)
+ }
+ case <-time.After(m.defaultTtl * 2):
+ log.Debugf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
+ }
+
+ // Save the device-descriptor value
+ deviceDescriptor := uint16(deviceDescriptorResponse.Data[0])<<8 | (uint16(deviceDescriptorResponse.Data[1]) & 0xFF)
+ connection.deviceDescriptor = deviceDescriptor
+
+ result <- InternalResult{
+ responseMessage: *deviceDescriptorResponse,
+ }
+ }()
+
+ return nil
+ },
+ func(err error) error {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+ }
+ return nil
+ },
+ m.defaultTtl)
+ if err != nil {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+ }
+ }
+ return result
}
func (m *KnxNetIpConnection) sendDevicePropertyReadRequest(targetAddress driverModel.KnxAddress, objectId uint8, propertyId uint8) chan KnxReadResult {
- result := make(chan KnxReadResult)
-
- connection, ok := m.DeviceConnections[targetAddress]
- if !ok {
- // close(result)
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
- }
- return result
- }
-
- // Next, read the device descriptor so we know how we have to communicate with the device.
- counter := connection.counter
- connection.counter++
-
- // Send the property read request and wait for a confirmation that this property is readable.
- propertyReadRequest := driverModel.NewTunnelingRequest(
- driverModel.NewTunnelingRequestDataBlock(0, 0),
- driverModel.NewLDataReq(0, nil,
- driverModel.NewLDataFrameDataExt(false, 6, 0,
- driverModel.NewKnxAddress(0, 0, 0),
- KnxAddressToInt8Array(targetAddress),
- driverModel.NewApduDataContainer(
- driverModel.NewApduDataOther(
- driverModel.NewApduDataExtPropertyValueRead(objectId, propertyId, 1, 1)),
- 5, true, counter),
- true, true, driverModel.CEMIPriority_LOW, false, false)))
- err := m.sendRequest(
- propertyReadRequest,
- func(message interface{}) bool {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- if tunnelingRequest == nil ||
- tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
- return false
- }
- lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
- if lDataInd == nil {
- return false
- }
- var apdu *driverModel.Apdu
- var sourceAddress *driverModel.KnxAddress
- dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
- if dataFrameExt != nil {
- apdu = dataFrameExt.Apdu
- sourceAddress = dataFrameExt.SourceAddress
- } else {
- dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
- if dataFrame != nil {
- apdu = dataFrame.Apdu
- sourceAddress = dataFrame.SourceAddress
- } else {
- return false
- }
- }
- // Check if the address matches
- if *sourceAddress != targetAddress {
- return false
- }
- // Check if the counter matches
- if apdu.Counter != counter {
- return false
- }
- dataContainer := driverModel.CastApduDataContainer(apdu)
- if dataContainer == nil {
- return false
- }
- dataApduOther := driverModel.CastApduDataOther(dataContainer.DataApdu)
- if dataApduOther == nil {
- return false
- }
- propertyValueResponse := driverModel.CastApduDataExtPropertyValueResponse(dataApduOther.ExtendedApdu)
- if propertyValueResponse == nil {
- return false
- }
- return propertyValueResponse.ObjectIndex == objectId && propertyValueResponse.PropertyId == propertyId
- },
- func(message interface{}) error {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
- var apdu *driverModel.Apdu
- dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
- if dataFrameExt != nil {
- apdu = dataFrameExt.Apdu
- } else {
- dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
- if dataFrame != nil {
- apdu = dataFrame.Apdu
- }
- }
- dataContainer := driverModel.CastApduDataContainer(apdu)
- dataApduOther := driverModel.CastApduDataOther(dataContainer.DataApdu)
- propertyValueResponse := driverModel.CastApduDataExtPropertyValueResponse(dataApduOther.ExtendedApdu)
-
- // Send an ACK and wait for it to be delivered
- go func() {
- ackResults := m.sendDeviceAck(targetAddress, apdu.Counter)
- select {
- case ackResult := <-ackResults:
- if ackResult.err != nil {
- log.Errorf("Error sending Ack: %s", ackResult.err)
- }
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
- }
-
- // If the count is 0, then this property doesn't exist or the user has no permission to read it.
- if propertyValueResponse.Count == 0 {
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_NOT_FOUND,
- }
- } else {
- // Find out the type of the property
- var objectType *driverModel.KnxInterfaceObjectType
- for curObjectType := driverModel.KnxInterfaceObjectType_OT_UNKNOWN; curObjectType <= driverModel.KnxInterfaceObjectType_OT_SUNBLIND_SENSOR_BASIC; curObjectType++ {
- if curObjectType.Code() == strconv.Itoa(int(objectId)) {
- objectType = &curObjectType
- break
- }
- }
- property := driverModel.KnxInterfaceObjectProperty_PID_UNKNOWN
- if objectType != nil {
- for curProperty := driverModel.KnxInterfaceObjectProperty_PID_UNKNOWN; curProperty <= driverModel.KnxInterfaceObjectProperty_PID_SUNBLIND_SENSOR_BASIC_ENABLE_TOGGLE_MODE; curProperty++ {
- if curProperty.PropertyId() == propertyId {
- if curProperty.ObjectType() == driverModel.KnxInterfaceObjectType_OT_GENERAL || curProperty.ObjectType() == *objectType {
- property = curProperty
- break
- }
- }
- }
- }
-
- dataLength := uint8(len(propertyValueResponse.Data))
- data := propertyValueResponse.Data
- rb := utils.NewReadBuffer(data)
- plcValue, err := driverModel.KnxPropertyParse(rb, property.PropertyDataType(), dataLength)
-
- // Return the result
- if err != nil {
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
- value: nil,
- }
- } else {
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_OK,
- value: &plcValue,
- }
- }
- }
-
- }()
- return nil
- },
- func(err error) error {
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_RESPONSE_PENDING,
- }
- return nil
- },
- m.defaultTtl)
-
- if err != nil {
- // close(result)
- }
- return result
+ result := make(chan KnxReadResult)
+
+ connection, ok := m.DeviceConnections[targetAddress]
+ if !ok {
+ // close(result)
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
+ }
+ return result
+ }
+
+ // Next, read the device descriptor so we know how we have to communicate with the device.
+ counter := connection.counter
+ connection.counter++
+
+ // Send the property read request and wait for a confirmation that this property is readable.
+ propertyReadRequest := driverModel.NewTunnelingRequest(
+ driverModel.NewTunnelingRequestDataBlock(0, 0),
+ driverModel.NewLDataReq(0, nil,
+ driverModel.NewLDataExtended(false, 6, 0,
+ driverModel.NewKnxAddress(0, 0, 0),
+ KnxAddressToInt8Array(targetAddress),
+ driverModel.NewApduDataContainer(
+ driverModel.NewApduDataOther(
+ driverModel.NewApduDataExtPropertyValueRead(objectId, propertyId, 1, 1)),
+ true, counter),
+ true, true, driverModel.CEMIPriority_LOW, false, false)))
+ err := m.sendRequest(
+ propertyReadRequest,
+ func(message interface{}) bool {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ if tunnelingRequest == nil ||
+ tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+ return false
+ }
+ lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+ if lDataInd == nil {
+ return false
+ }
+ dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+ if dataFrameExt == nil {
+ return false
+ }
+ // Check if the address matches
+ if *dataFrameExt.SourceAddress != targetAddress {
+ return false
+ }
+ // Check if the counter matches
+ if dataFrameExt.Apdu.Counter != counter {
+ return false
+ }
+ dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+ if dataContainer == nil {
+ return false
+ }
+ dataApduOther := driverModel.CastApduDataOther(dataContainer.DataApdu)
+ if dataApduOther == nil {
+ return false
+ }
+ propertyValueResponse := driverModel.CastApduDataExtPropertyValueResponse(dataApduOther.ExtendedApdu)
+ if propertyValueResponse == nil {
+ return false
+ }
+ return propertyValueResponse.ObjectIndex == objectId && propertyValueResponse.PropertyId == propertyId
+ },
+ func(message interface{}) error {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+ dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+ dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+ dataApduOther := driverModel.CastApduDataOther(dataContainer.DataApdu)
+ propertyValueResponse := driverModel.CastApduDataExtPropertyValueResponse(dataApduOther.ExtendedApdu)
+
+ // Send an ACK and wait for it to be delivered
+ go func() {
+ ackResults := m.sendDeviceAck(targetAddress, dataFrameExt.Apdu.Counter)
+ select {
+ case ackResult := <-ackResults:
+ if ackResult.err != nil {
+ log.Errorf("Error sending Ack: %s", ackResult.err)
+ }
+ case <-time.After(m.defaultTtl * 2):
+ log.Debugf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
+ }
+
+ // If the count is 0, then this property doesn't exist or the user has no permission to read it.
+ if propertyValueResponse.Count == 0 {
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_NOT_FOUND,
+ }
+ } else {
+ // Find out the type of the property
+ var objectType *driverModel.KnxInterfaceObjectType
+ for curObjectType := driverModel.KnxInterfaceObjectType_OT_UNKNOWN; curObjectType <= driverModel.KnxInterfaceObjectType_OT_SUNBLIND_SENSOR_BASIC; curObjectType++ {
+ if curObjectType.Code() == strconv.Itoa(int(objectId)) {
+ objectType = &curObjectType
+ break
+ }
+ }
+ property := driverModel.KnxInterfaceObjectProperty_PID_UNKNOWN
+ if objectType != nil {
+ for curProperty := driverModel.KnxInterfaceObjectProperty_PID_UNKNOWN; curProperty <= driverModel.KnxInterfaceObjectProperty_PID_SUNBLIND_SENSOR_BASIC_ENABLE_TOGGLE_MODE; curProperty++ {
+ if curProperty.PropertyId() == propertyId {
+ if curProperty.ObjectType() == driverModel.KnxInterfaceObjectType_OT_GENERAL || curProperty.ObjectType() == *objectType {
+ property = curProperty
+ break
+ }
+ }
+ }
+ }
+
+ dataLength := uint8(len(propertyValueResponse.Data))
+ data := propertyValueResponse.Data
+ rb := utils.NewReadBuffer(data)
+ plcValue, err := driverModel.KnxPropertyParse(rb, property.PropertyDataType(), dataLength)
+
+ // Return the result
+ if err != nil {
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
+ value: nil,
+ }
+ } else {
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_OK,
+ value: &plcValue,
+ }
+ }
+ }
+
+ }()
+ return nil
+ },
+ func(err error) error {
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_RESPONSE_PENDING,
+ }
+ return nil
+ },
+ m.defaultTtl)
+
+ if err != nil {
+ // close(result)
+ }
+ return result
}
func (m *KnxNetIpConnection) sendDeviceMemoryReadRequest(targetAddress driverModel.KnxAddress, address uint16, numElements uint8, datapointType driverModel.KnxDatapointType) chan KnxReadResult {
- result := make(chan KnxReadResult)
-
- connection, ok := m.DeviceConnections[targetAddress]
- if !ok {
- // close(result)
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
- }
- return result
- }
-
- // Next, read the device descriptor so we know how we have to communicate with the device.
- counter := connection.counter
- connection.counter++
- numBytes := numElements * uint8(math.Max(float64(1), float64(datapointType.DatapointMainType().SizeInBits()/8)))
-
- // Send the property read request and wait for a confirmation that this property is readable.
- propertyReadRequest := driverModel.NewTunnelingRequest(
- driverModel.NewTunnelingRequestDataBlock(0, 0),
- driverModel.NewLDataReq(0, nil,
- driverModel.NewLDataFrameDataExt(false, 6, 0,
- driverModel.NewKnxAddress(0, 0, 0),
- KnxAddressToInt8Array(targetAddress),
- driverModel.NewApduDataContainer(
- driverModel.NewApduDataMemoryRead(numBytes, address),
- 3, true, counter),
- true, true, driverModel.CEMIPriority_LOW, false, false)))
- err := m.sendRequest(
- propertyReadRequest,
- func(message interface{}) bool {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- if tunnelingRequest == nil ||
- tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
- return false
- }
- lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
- if lDataInd == nil {
- return false
- }
- var apdu *driverModel.Apdu
- var sourceAddress *driverModel.KnxAddress
- dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
- if dataFrameExt != nil {
- apdu = dataFrameExt.Apdu
- sourceAddress = dataFrameExt.SourceAddress
- } else {
- dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
- if dataFrame != nil {
- apdu = dataFrame.Apdu
- sourceAddress = dataFrame.SourceAddress
- } else {
- return false
- }
- }
- dataContainer := driverModel.CastApduDataContainer(apdu)
- if dataContainer == nil {
- return false
- }
- dataApduMemoryResponse := driverModel.CastApduDataMemoryResponse(dataContainer.DataApdu)
- if dataApduMemoryResponse == nil {
- return false
- }
-
- // Check if the address matches
- if *sourceAddress != targetAddress {
- return false
- }
- // Check if the counter matches
- if apdu.Counter != counter {
- return false
- }
- return dataApduMemoryResponse.Address == address && uint8(len(dataApduMemoryResponse.Data)) == numBytes
- },
- func(message interface{}) error {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
- var apdu *driverModel.Apdu
- dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
- if dataFrameExt != nil {
- apdu = dataFrameExt.Apdu
- } else {
- dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
- if dataFrame != nil {
- apdu = dataFrame.Apdu
- }
- }
- dataContainer := driverModel.CastApduDataContainer(apdu)
- dataApduMemoryResponse := driverModel.CastApduDataMemoryResponse(dataContainer.DataApdu)
-
- // Send an ACK and wait for it to be delivered
- go func() {
- ackResults := m.sendDeviceAck(targetAddress, apdu.Counter)
- select {
- case ackResult := <-ackResults:
- if ackResult.err != nil {
- log.Errorf("Error sending Ack: %s", ackResult.err)
- }
- case <-time.After(m.defaultTtl * 2):
- log.Errorf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
- }
-
- // Parse the data according to the property type information
- rb := utils.NewReadBuffer(dataApduMemoryResponse.Data)
- var plcValues []values.PlcValue
- for i := uint8(0); i < numElements; i++ {
- plcValue, err := driverModel.KnxDatapointParse(rb, datapointType)
- // Return the result
- if err != nil {
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
- value: nil,
- }
- return
- } else {
- plcValues = append(plcValues, plcValue)
- }
- }
-
- // If this is a single value, just return that directly.
- // If it's not, wrap it in a PlcList structure.
- if len(plcValues) == 1 {
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_OK,
- value: &plcValues[0],
- }
- } else {
- var plcList values.PlcValue
- plcList = values2.NewPlcList(plcValues)
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_OK,
- // TODO: Check if this is correct ...
- value: &plcList,
- }
- }
- }()
- return nil
- },
- func(err error) error {
- result <- KnxReadResult{
- returnCode: apiModel.PlcResponseCode_RESPONSE_PENDING,
- }
- return nil
- },
- m.defaultTtl)
-
- if err != nil {
- // close(result)
- }
- return result
+ result := make(chan KnxReadResult)
+
+ connection, ok := m.DeviceConnections[targetAddress]
+ if !ok {
+ // close(result)
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
+ }
+ return result
+ }
+
+ // Next, read the device descriptor so we know how we have to communicate with the device.
+ counter := connection.counter
+ connection.counter++
+ numBytes := numElements * uint8(math.Max(float64(1), float64(datapointType.DatapointMainType().SizeInBits()/8)))
+
+ // Send the property read request and wait for a confirmation that this property is readable.
+ propertyReadRequest := driverModel.NewTunnelingRequest(
+ driverModel.NewTunnelingRequestDataBlock(0, 0),
+ driverModel.NewLDataReq(0, nil,
+ driverModel.NewLDataExtended(false, 6, 0,
+ driverModel.NewKnxAddress(0, 0, 0),
+ KnxAddressToInt8Array(targetAddress),
+ driverModel.NewApduDataContainer(
+ driverModel.NewApduDataMemoryRead(numBytes, address),
+ true, counter),
+ true, true, driverModel.CEMIPriority_LOW, false, false)))
+ err := m.sendRequest(
+ propertyReadRequest,
+ func(message interface{}) bool {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ if tunnelingRequest == nil ||
+ tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+ return false
+ }
+ lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+ if lDataInd == nil {
+ return false
+ }
+ dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+ if dataFrameExt == nil {
+ return false
+ }
+ dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+ if dataContainer == nil {
+ return false
+ }
+ dataApduMemoryResponse := driverModel.CastApduDataMemoryResponse(dataContainer.DataApdu)
+ if dataApduMemoryResponse == nil {
+ return false
+ }
+
+ // Check if the address matches
+ if *dataFrameExt.SourceAddress != targetAddress {
+ return false
+ }
+ // Check if the counter matches
+ if dataFrameExt.Apdu.Counter != counter {
+ return false
+ }
+ return dataApduMemoryResponse.Address == address && uint8(len(dataApduMemoryResponse.Data)) == numBytes
+ },
+ func(message interface{}) error {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+ dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+ dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+ dataApduMemoryResponse := driverModel.CastApduDataMemoryResponse(dataContainer.DataApdu)
+
+ // Send an ACK and wait for it to be delivered
+ go func() {
+ ackResults := m.sendDeviceAck(targetAddress, dataFrameExt.Apdu.Counter)
+ select {
+ case ackResult := <-ackResults:
+ if ackResult.err != nil {
+ log.Errorf("Error sending Ack: %s", ackResult.err)
+ }
+ case <-time.After(m.defaultTtl * 2):
+ log.Debugf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
+ }
+
+ // Parse the data according to the property type information
+ rb := utils.NewReadBuffer(dataApduMemoryResponse.Data)
+ var plcValues []values.PlcValue
+ for i := uint8(0); i < numElements; i++ {
+ plcValue, err := driverModel.KnxDatapointParse(rb, datapointType)
+ // Return the result
+ if err != nil {
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
+ value: nil,
+ }
+ return
+ } else {
+ plcValues = append(plcValues, plcValue)
+ }
+ }
+
+ // If this is a single value, just return that directly.
+ // If it's not, wrap it in a PlcList structure.
+ if len(plcValues) == 1 {
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_OK,
+ value: &plcValues[0],
+ }
+ } else {
+ var plcList values.PlcValue
+ plcList = values2.NewPlcList(plcValues)
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_OK,
+ // TODO: Check if this is correct ...
+ value: &plcList,
+ }
+ }
+ }()
+ return nil
+ },
+ func(err error) error {
+ result <- KnxReadResult{
+ returnCode: apiModel.PlcResponseCode_RESPONSE_PENDING,
+ }
+ return nil
+ },
+ m.defaultTtl)
+
+ if err != nil {
+ // close(result)
+ }
+ return result
}
func (m *KnxNetIpConnection) sendDeviceAck(targetAddress driverModel.KnxAddress, counter uint8) chan InternalResult {
- result := make(chan InternalResult)
-
- ack := driverModel.NewTunnelingRequest(
- driverModel.NewTunnelingRequestDataBlock(0, 0),
- driverModel.NewLDataReq(0, nil,
- driverModel.NewLDataFrameDataExt(false, 6, uint8(0),
- driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
- driverModel.NewApduControlContainer(driverModel.NewApduControlAck(), 0, true, counter),
- true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
- err := m.sendRequest(
- ack,
- func(message interface{}) bool {
- tunnelingRequest := driverModel.CastTunnelingRequest(message)
- if tunnelingRequest == nil ||
- tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
- return false
- }
- lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
- if lDataCon == nil {
- return false
- }
- var apdu *driverModel.Apdu
- var curSourceAddress *driverModel.KnxAddress
- var curTargetAddress *driverModel.KnxAddress
- dataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
- if dataFrameExt != nil {
- apdu = dataFrameExt.Apdu
- curSourceAddress = dataFrameExt.SourceAddress
- curTargetAddress = Int8ArrayToKnxAddress(dataFrameExt.DestinationAddress)
- } else {
- dataFrame := driverModel.CastLDataFrameData(lDataCon.DataFrame)
- if dataFrame != nil {
- apdu = dataFrame.Apdu
- curSourceAddress = dataFrame.SourceAddress
- curTargetAddress = Int8ArrayToKnxAddress(dataFrame.DestinationAddress)
- } else {
- return false
- }
- }
- // Check if the addresses match
- if *curSourceAddress != *m.ClientKnxAddress {
- return false
- }
- if *curTargetAddress != targetAddress {
- return false
- }
- // Check if the counter matches
- if apdu.Counter != counter {
- return false
- }
- controlContainer := driverModel.CastApduControlContainer(apdu)
- if controlContainer == nil {
- return false
- }
- dataApduAck := driverModel.CastApduControlAck(controlContainer.ControlApdu)
- if dataApduAck == nil {
- return false
- }
- return true
- },
- func(message interface{}) error {
- result <- InternalResult{}
- return nil
- },
- func(err error) error {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
- }
- return nil
- },
- m.defaultTtl)
-
- if err != nil {
- result <- InternalResult{
- err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
- }
- }
-
- return result
+ result := make(chan InternalResult)
+
+ ack := driverModel.NewTunnelingRequest(
+ driverModel.NewTunnelingRequestDataBlock(0, 0),
+ driverModel.NewLDataReq(0, nil,
+ driverModel.NewLDataExtended(false, 6, uint8(0),
+ driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
+ driverModel.NewApduControlContainer(driverModel.NewApduControlAck(), true, counter),
+ true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
+ err := m.sendRequest(
+ ack,
+ func(message interface{}) bool {
+ tunnelingRequest := driverModel.CastTunnelingRequest(message)
+ if tunnelingRequest == nil ||
+ tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+ return false
+ }
+ lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+ if lDataCon == nil {
+ return false
+ }
+ dataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+ if dataFrameExt == nil {
+ return false
+ }
+ // Check if the addresses match
+ if *dataFrameExt.SourceAddress != *m.ClientKnxAddress {
+ return false
+ }
+ curTargetAddress := Int8ArrayToKnxAddress(dataFrameExt.DestinationAddress)
+ if *curTargetAddress != targetAddress {
+ return false
+ }
+ // Check if the counter matches
+ if dataFrameExt.Apdu.Counter != counter {
+ return false
+ }
+ controlContainer := driverModel.CastApduControlContainer(dataFrameExt.Apdu)
+ if controlContainer == nil {
+ return false
+ }
+ dataApduAck := driverModel.CastApduControlAck(controlContainer.ControlApdu)
+ if dataApduAck == nil {
+ return false
+ }
+ return true
+ },
+ func(message interface{}) error {
+ result <- InternalResult{}
+ return nil
+ },
+ func(err error) error {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+ }
+ return nil
+ },
+ m.defaultTtl)
+
+ if err != nil {
+ result <- InternalResult{
+ err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+ }
+ }
+
+ return result
}
func (m *KnxNetIpConnection) sendRequest(request *driverModel.KnxNetIpMessage, acceptsMessage spi.AcceptsMessage, handleMessage spi.HandleMessage, handleError spi.HandleError, ttl time.Duration) error {
- // If this is a tunneling request, we need to update the communicationChannelId and assign a sequenceCounter
- tunnelingRequest := driverModel.CastTunnelingRequest(request)
- if tunnelingRequest != nil {
- tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId = m.CommunicationChannelId
- tunnelingRequest.TunnelingRequestDataBlock.SequenceCounter = m.getNewSequenceCounter()
- }
- return m.messageCodec.SendRequest(request, acceptsMessage, handleMessage, handleError, ttl)
+ // If this is a tunneling request, we need to update the communicationChannelId and assign a sequenceCounter
+ tunnelingRequest := driverModel.CastTunnelingRequest(request)
+ if tunnelingRequest != nil {
+ tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId = m.CommunicationChannelId
+ tunnelingRequest.TunnelingRequestDataBlock.SequenceCounter = m.getNewSequenceCounter()
+ }
+ return m.messageCodec.SendRequest(request, acceptsMessage, handleMessage, handleError, ttl)
}
func (m *KnxNetIpConnection) getLocalAddress() (*net.UDPAddr, error) {
- transportInstanceExposer, ok := m.messageCodec.(spi.TransportInstanceExposer)
- if !ok {
- return nil, errors.New("used transport, is not a TransportInstanceExposer")
- }
-
- // Prepare a SearchReq
- udpTransportInstance, ok := transportInstanceExposer.GetTransportInstance().(*udp.UdpTransportInstance)
- if !ok {
- return nil, errors.New("used transport, is not a UdpTransportInstance")
- }
-
- return udpTransportInstance.LocalAddress, nil
+ transportInstanceExposer, ok := m.messageCodec.(spi.TransportInstanceExposer)
+ if !ok {
+ return nil, errors.New("used transport, is not a TransportInstanceExposer")
+ }
+
+ // Prepare a SearchReq
+ udpTransportInstance, ok := transportInstanceExposer.GetTransportInstance().(*udp.UdpTransportInstance)
+ if !ok {
+ return nil, errors.New("used transport, is not a UdpTransportInstance")
+ }
+
+ return udpTransportInstance.LocalAddress, nil
}
func (m *KnxNetIpConnection) interceptIncomingMessage(interface{}) {
- if m.connectionStateTimer != nil {
- // Reset the timer for sending the ConnectionStateRequest
- m.connectionStateTimer.Reset(60 * time.Second)
- }
+ if m.connectionStateTimer != nil {
+ // Reset the timer for sending the ConnectionStateRequest
+ m.connectionStateTimer.Reset(60 * time.Second)
+ }
}
func (m *KnxNetIpConnection) getNewSequenceCounter() uint8 {
- sequenceCounter := atomic.AddInt32(&m.SequenceCounter, 1)
- if sequenceCounter >= math.MaxUint8 {
- atomic.StoreInt32(&m.SequenceCounter, -1)
- sequenceCounter = -1
- }
- return uint8(sequenceCounter)
+ sequenceCounter := atomic.AddInt32(&m.SequenceCounter, 1)
+ if sequenceCounter >= math.MaxUint8 {
+ atomic.StoreInt32(&m.SequenceCounter, -1)
+ sequenceCounter = -1
+ }
+ return uint8(sequenceCounter)
}
func (m *KnxNetIpConnection) castIpToKnxAddress(ip net.IP) *driverModel.IPAddress {
- return driverModel.NewIPAddress(utils.ByteArrayToInt8Array(ip)[len(ip)-4:])
+ return driverModel.NewIPAddress(utils.ByteArrayToInt8Array(ip)[len(ip)-4:])
}
func (m *KnxNetIpConnection) handleIncomingTunnelingRequest(tunnelingRequest *driverModel.TunnelingRequest) {
- go func() {
- lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi.Child)
- if lDataInd != nil {
- var destinationAddress []int8
- var apdu *driverModel.Apdu
- switch lDataInd.DataFrame.Child.(type) {
- case *driverModel.LDataFrameData:
- dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
- destinationAddress = dataFrame.DestinationAddress
- apdu = dataFrame.Apdu
- case *driverModel.LDataFrameDataExt:
- dataFrame := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
- destinationAddress = dataFrame.DestinationAddress
- apdu = dataFrame.Apdu
- }
- container := driverModel.CastApduDataContainer(apdu)
- if container == nil {
- return
- }
- groupValueWrite := driverModel.CastApduDataGroupValueWrite(container.DataApdu)
- if groupValueWrite == nil {
- return
- }
- if destinationAddress != nil {
- addressData := uint16(destinationAddress[0])<<8 | (uint16(destinationAddress[1]) & 0xFF)
- m.valueCacheMutex.RLock()
- val, ok := m.valueCache[addressData]
- m.valueCacheMutex.RUnlock()
- changed := false
-
- var payload []int8
- payload = append(payload, groupValueWrite.DataFirstByte)
- payload = append(payload, groupValueWrite.Data...)
- if !ok || !m.sliceEqual(val, payload) {
- m.valueCacheMutex.Lock()
- m.valueCache[addressData] = payload
- m.valueCacheMutex.Unlock()
- // If this is a new value, we have to also provide the 3 different types of addresses.
- if !ok {
- arb := utils.NewReadBuffer(utils.Int8ArrayToUint8Array(destinationAddress))
- if address, err2 := driverModel.KnxGroupAddressParse(arb, 3); err2 == nil {
- m.leve3AddressCache[addressData] = driverModel.CastKnxGroupAddress3Level(address)
- } else {
- fmt.Printf("Error parsing Group Address %s", err2.Error())
- }
- arb.Reset()
- if address, err2 := driverModel.KnxGroupAddressParse(arb, 2); err2 == nil {
- m.leve2AddressCache[addressData] = driverModel.CastKnxGroupAddress2Level(address)
- }
- arb.Reset()
- if address, err2 := driverModel.KnxGroupAddressParse(arb, 1); err2 == nil {
- m.leve1AddressCache[addressData] = driverModel.CastKnxGroupAddressFreeLevel(address)
- }
- }
- changed = true
- }
- if m.subscribers != nil {
- for _, subscriber := range m.subscribers {
- subscriber.handleValueChange(lDataInd.DataFrame, changed)
- }
- }
- }
- }
- }()
+ go func() {
+ lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi.Child)
+ if lDataInd != nil {
+ var destinationAddress []int8
+ var apdu *driverModel.Apdu
+ switch lDataInd.DataFrame.Child.(type) {
+ case *driverModel.LDataExtended:
+ dataFrame := driverModel.CastLDataExtended(lDataInd.DataFrame)
+ destinationAddress = dataFrame.DestinationAddress
+ apdu = dataFrame.Apdu
+ }
+ container := driverModel.CastApduDataContainer(apdu)
+ if container == nil {
+ return
+ }
+ groupValueWrite := driverModel.CastApduDataGroupValueWrite(container.DataApdu)
+ if groupValueWrite == nil {
+ return
+ }
+ if destinationAddress != nil {
+ addressData := uint16(destinationAddress[0])<<8 | (uint16(destinationAddress[1]) & 0xFF)
+ m.valueCacheMutex.RLock()
+ val, ok := m.valueCache[addressData]
+ m.valueCacheMutex.RUnlock()
+ changed := false
+
+ var payload []int8
+ payload = append(payload, groupValueWrite.DataFirstByte)
+ payload = append(payload, groupValueWrite.Data...)
+ if !ok || !m.sliceEqual(val, payload) {
+ m.valueCacheMutex.Lock()
+ m.valueCache[addressData] = payload
+ m.valueCacheMutex.Unlock()
+ // If this is a new value, we have to also provide the 3 different types of addresses.
+ if !ok {
+ arb := utils.NewReadBuffer(utils.Int8ArrayToUint8Array(destinationAddress))
+ if address, err2 := driverModel.KnxGroupAddressParse(arb, 3); err2 == nil {
+ m.leve3AddressCache[addressData] = driverModel.CastKnxGroupAddress3Level(address)
+ } else {
+ fmt.Printf("Error parsing Group Address %s", err2.Error())
+ }
+ arb.Reset()
+ if address, err2 := driverModel.KnxGroupAddressParse(arb, 2); err2 == nil {
+ m.leve2AddressCache[addressData] = driverModel.CastKnxGroupAddress2Level(address)
+ }
+ arb.Reset()
+ if address, err2 := driverModel.KnxGroupAddressParse(arb, 1); err2 == nil {
+ m.leve1AddressCache[addressData] = driverModel.CastKnxGroupAddressFreeLevel(address)
+ }
+ }
+ changed = true
+ }
+ if m.subscribers != nil {
+ for _, subscriber := range m.subscribers {
+ subscriber.handleValueChange(lDataInd.DataFrame, changed)
+ }
+ }
+ }
+ }
+ }()
}
func (m *KnxNetIpConnection) getGroupAddressNumLevels() uint8 {
- if val, ok := m.options["group-address-num-levels"]; ok {
- groupAddressNumLevels, err := strconv.Atoi(val[0])
- if err == nil {
- return uint8(groupAddressNumLevels)
- }
- }
- return 3
+ if val, ok := m.options["group-address-num-levels"]; ok {
+ groupAddressNumLevels, err := strconv.Atoi(val[0])
+ if err == nil {
+ return uint8(groupAddressNumLevels)
+ }
+ }
+ return 3
}
func (m *KnxNetIpConnection) addSubscriber(subscriber *KnxNetIpSubscriber) {
- for _, sub := range m.subscribers {
- if sub == subscriber {
- return
- }
- }
- m.subscribers = append(m.subscribers, subscriber)
+ for _, sub := range m.subscribers {
+ if sub == subscriber {
+ return
+ }
+ }
+ m.subscribers = append(m.subscribers, subscriber)
}
func (m *KnxNetIpConnection) removeSubscriber(subscriber *KnxNetIpSubscriber) {
- for i, sub := range m.subscribers {
- if sub == subscriber {
- m.subscribers = append(m.subscribers[:i], m.subscribers[i+1:]...)
- }
- }
+ for i, sub := range m.subscribers {
+ if sub == subscriber {
+ m.subscribers = append(m.subscribers[:i], m.subscribers[i+1:]...)
+ }
+ }
}
func (m *KnxNetIpConnection) sliceEqual(a, b []int8) bool {
- if len(a) != len(b) {
- return false
- }
- for i, v := range a {
- if v != b[i] {
- return false
- }
- }
- return true
+ if len(a) != len(b) {
+ return false
+ }
+ for i, v := range a {
+ if v != b[i] {
+ return false
+ }
+ }
+ return true
}
func KnxAddressToString(knxAddress *driverModel.KnxAddress) string {
- return strconv.Itoa(int(knxAddress.MainGroup)) + "." + strconv.Itoa(int(knxAddress.MiddleGroup)) + "." + strconv.Itoa(int(knxAddress.SubGroup))
+ return strconv.Itoa(int(knxAddress.MainGroup)) + "." + strconv.Itoa(int(knxAddress.MiddleGroup)) + "." + strconv.Itoa(int(knxAddress.SubGroup))
}
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpDiscoverer.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpDiscoverer.go
index 1963c73..d1461ee 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpDiscoverer.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpDiscoverer.go
@@ -142,7 +142,7 @@ func (d *KnxNetIpDiscoverer) Discover(callback func(event model.PlcDiscoveryEven
return nil
},
func(err error) error {
- log.Error(fmt.Sprintf("got timeout waiting for search-response"))
+ log.Debugf(fmt.Sprintf("got timeout waiting for search-response"))
return nil
},
time.Second*1)
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpField.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpField.go
index db8de06..b684879 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpField.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpField.go
@@ -27,30 +27,35 @@ import (
)
type KnxNetIpField interface {
- IsPatternField() bool
+ apiModel.PlcField
+}
+
+type KnxNetIpGroupAddressField interface {
GetFieldType() *driverModel.KnxDatapointType
+ IsPatternField() bool
matches(knxGroupAddress *driverModel.KnxGroupAddress) bool
toGroupAddress() *driverModel.KnxGroupAddress
- apiModel.PlcField
+ KnxNetIpField
}
-type KnxNetIpGroupAddress3LevelPlcField struct {
- FieldType *driverModel.KnxDatapointType
- // 5 Bits: Values 0-31
- MainGroup string
- // 3 Bits: values 0-7
- MiddleGroup string
- // 8 Bits
- SubGroup string
+type KnxNetIpDeviceField interface {
+ toKnxAddress() *driverModel.KnxAddress
KnxNetIpField
}
-func NewKnxNetIpGroupAddress3LevelPlcField(fieldType *driverModel.KnxDatapointType, mainGroup string, middleGroup string, subGroup string) KnxNetIpGroupAddress3LevelPlcField {
+type KnxNetIpGroupAddress3LevelPlcField struct {
+ MainGroup string // 5 Bits: Values 0-31
+ MiddleGroup string // 3 Bits: values 0-7
+ SubGroup string // 8 Bits
+ FieldType *driverModel.KnxDatapointType
+}
+
+func NewKnxNetIpGroupAddress3LevelPlcField(mainGroup string, middleGroup string, subGroup string, fieldType *driverModel.KnxDatapointType) KnxNetIpGroupAddress3LevelPlcField {
return KnxNetIpGroupAddress3LevelPlcField{
- FieldType: fieldType,
MainGroup: mainGroup,
MiddleGroup: middleGroup,
SubGroup: subGroup,
+ FieldType: fieldType,
}
}
@@ -115,19 +120,17 @@ func (k KnxNetIpGroupAddress3LevelPlcField) toGroupAddress() *driverModel.KnxGro
}
type KnxNetIpGroupAddress2LevelPlcField struct {
+ MainGroup string // 5 Bits: Values 0-31
+ SubGroup string // 11 Bits
FieldType *driverModel.KnxDatapointType
- // 5 Bits: Values 0-31
- MainGroup string
- // 11 Bits
- SubGroup string
KnxNetIpField
}
-func NewKnxNetIpGroupAddress2LevelPlcField(fieldType *driverModel.KnxDatapointType, mainGroup string, subGroup string) KnxNetIpGroupAddress2LevelPlcField {
+func NewKnxNetIpGroupAddress2LevelPlcField(mainGroup string, subGroup string, fieldType *driverModel.KnxDatapointType) KnxNetIpGroupAddress2LevelPlcField {
return KnxNetIpGroupAddress2LevelPlcField{
- FieldType: fieldType,
MainGroup: mainGroup,
SubGroup: subGroup,
+ FieldType: fieldType,
}
}
@@ -183,16 +186,15 @@ func (k KnxNetIpGroupAddress2LevelPlcField) toGroupAddress() *driverModel.KnxGro
}
type KnxNetIpGroupAddress1LevelPlcField struct {
+ MainGroup string // 16 Bits
FieldType *driverModel.KnxDatapointType
- // 16 Bits
- MainGroup string
KnxNetIpField
}
-func NewKnxNetIpGroupAddress1LevelPlcField(fieldType *driverModel.KnxDatapointType, mainGroup string) KnxNetIpGroupAddress1LevelPlcField {
+func NewKnxNetIpGroupAddress1LevelPlcField(mainGroup string, fieldType *driverModel.KnxDatapointType) KnxNetIpGroupAddress1LevelPlcField {
return KnxNetIpGroupAddress1LevelPlcField{
- FieldType: fieldType,
MainGroup: mainGroup,
+ FieldType: fieldType,
}
}
@@ -238,111 +240,95 @@ func (k KnxNetIpGroupAddress1LevelPlcField) toGroupAddress() *driverModel.KnxGro
return ga
}
-type KnxNetIpDevicePropertyAddressPlcField struct {
- // 5 Bits: Values 0-31
- MainGroup string
- // 3 Bits: values 0-7
- MiddleGroup string
- // 8 Bits
- SubGroup string
- ObjectId string
- PropertyId string
- KnxNetIpField
+type KnxNetIpDeviceQueryField struct {
+ MainGroup string // 5 Bits: Values 0-31
+ MiddleGroup string // 3 Bits: values 0-7
+ SubGroup string // 8 Bits
+ KnxNetIpDeviceField
}
-func NewKnxNetIpDevicePropertyAddressPlcField(mainGroup string, middleGroup string, subGroup string, objectId string, propertyId string) KnxNetIpDevicePropertyAddressPlcField {
- return KnxNetIpDevicePropertyAddressPlcField{
+func NewKnxNetIpDeviceQueryField(mainGroup string, middleGroup string, subGroup string) KnxNetIpDeviceQueryField {
+ return KnxNetIpDeviceQueryField{
MainGroup: mainGroup,
MiddleGroup: middleGroup,
SubGroup: subGroup,
- ObjectId: objectId,
- PropertyId: propertyId,
}
}
-func (k KnxNetIpDevicePropertyAddressPlcField) GetTypeName() string {
- return ""
+func (k KnxNetIpDeviceQueryField) GetTypeName() string {
+ return ""
}
-func (k KnxNetIpDevicePropertyAddressPlcField) GetFieldType() *driverModel.KnxDatapointType {
+func (k KnxNetIpDeviceQueryField) GetQuantity() uint16 {
+ return 1
+}
+
+func (k KnxNetIpDeviceQueryField) toKnxAddress() *driverModel.KnxAddress {
return nil
}
-func (k KnxNetIpDevicePropertyAddressPlcField) GetQuantity() uint16 {
- return 1
+type KnxNetIpDevicePropertyAddressPlcField struct {
+ MainGroup uint8 // 5 Bits: Values 0-31
+ MiddleGroup uint8 // 3 Bits: values 0-7
+ SubGroup uint8 // 8 Bits
+ ObjectId uint8
+ PropertyId uint8
+ KnxNetIpDeviceField
}
-func (k KnxNetIpDevicePropertyAddressPlcField) IsPatternField() bool {
- _, err := strconv.Atoi(k.MainGroup)
- if err == nil {
- _, err = strconv.Atoi(k.MiddleGroup)
- if err == nil {
- _, err = strconv.Atoi(k.SubGroup)
- if err == nil {
- return false
- }
- }
+func NewKnxNetIpDevicePropertyAddressPlcField(mainGroup uint8, middleGroup uint8, subGroup uint8, objectId uint8, propertyId uint8) KnxNetIpDevicePropertyAddressPlcField {
+ return KnxNetIpDevicePropertyAddressPlcField{
+ MainGroup: mainGroup,
+ MiddleGroup: middleGroup,
+ SubGroup: subGroup,
+ ObjectId: objectId,
+ PropertyId: propertyId,
}
- return true
}
-func (k KnxNetIpDevicePropertyAddressPlcField) matches(knxGroupAddress *driverModel.KnxGroupAddress) bool {
- level3KnxGroupAddress := driverModel.CastKnxGroupAddress3Level(knxGroupAddress)
- if level3KnxGroupAddress == nil {
- return false
- }
- return matches(k.MainGroup, strconv.Itoa(int(level3KnxGroupAddress.MainGroup))) &&
- matches(k.MiddleGroup, strconv.Itoa(int(level3KnxGroupAddress.MiddleGroup))) &&
- matches(k.SubGroup, strconv.Itoa(int(level3KnxGroupAddress.SubGroup)))
+func (k KnxNetIpDevicePropertyAddressPlcField) GetTypeName() string {
+ return ""
+}
+
+func (k KnxNetIpDevicePropertyAddressPlcField) GetQuantity() uint16 {
+ return 1
}
func (k KnxNetIpDevicePropertyAddressPlcField) toKnxAddress() *driverModel.KnxAddress {
- mainGroup, err := strconv.Atoi(k.MainGroup)
- if err != nil {
- return nil
- }
- middleGroup, err := strconv.Atoi(k.MiddleGroup)
- if err != nil {
- return nil
- }
- subGroup, err := strconv.Atoi(k.SubGroup)
- if err != nil {
- return nil
- }
ga := &driverModel.KnxAddress{
- MainGroup: uint8(mainGroup),
- MiddleGroup: uint8(middleGroup),
- SubGroup: uint8(subGroup),
+ MainGroup: k.MainGroup,
+ MiddleGroup: k.MiddleGroup,
+ SubGroup: k.SubGroup,
}
return ga
}
type KnxNetIpDeviceMemoryAddressPlcField struct {
- FieldType *driverModel.KnxDatapointType
- // 5 Bits: Values 0-31
- MainGroup uint8
- // 3 Bits: values 0-7
- MiddleGroup uint8
- // 8 Bits
- SubGroup uint8
+ MainGroup uint8 // 5 Bits: Values 0-31
+ MiddleGroup uint8 // 3 Bits: values 0-7
+ SubGroup uint8 // 8 Bits
Address uint16
NumberOfElements uint8
- KnxNetIpField
+ FieldType *driverModel.KnxDatapointType
+ KnxNetIpDeviceField
}
-func NewKnxNetIpDeviceMemoryAddressPlcField(fieldType *driverModel.KnxDatapointType, mainGroup uint8, middleGroup uint8, subGroup uint8, address uint16, numberOfElements uint8) KnxNetIpDeviceMemoryAddressPlcField {
+func NewKnxNetIpDeviceMemoryAddressPlcField(mainGroup uint8, middleGroup uint8, subGroup uint8, address uint16, numberOfElements uint8, fieldType *driverModel.KnxDatapointType) KnxNetIpDeviceMemoryAddressPlcField {
return KnxNetIpDeviceMemoryAddressPlcField{
- FieldType: fieldType,
MainGroup: mainGroup,
MiddleGroup: middleGroup,
SubGroup: subGroup,
Address: address,
NumberOfElements: numberOfElements,
+ FieldType: fieldType,
}
}
func (k KnxNetIpDeviceMemoryAddressPlcField) GetTypeName() string {
- return k.FieldType.Name()
+ if k.FieldType != nil {
+ return k.FieldType.Name()
+ }
+ return ""
}
func (k KnxNetIpDeviceMemoryAddressPlcField) GetFieldType() *driverModel.KnxDatapointType {
@@ -353,21 +339,45 @@ func (k KnxNetIpDeviceMemoryAddressPlcField) GetQuantity() uint16 {
return uint16(k.NumberOfElements)
}
-func (k KnxNetIpDeviceMemoryAddressPlcField) IsPatternField() bool {
- return false
+func (k KnxNetIpDeviceMemoryAddressPlcField) toKnxAddress() *driverModel.KnxAddress {
+ individualAddress := &driverModel.KnxAddress{
+ MainGroup: k.MainGroup,
+ MiddleGroup: k.MiddleGroup,
+ SubGroup: k.SubGroup,
+ }
+ return individualAddress
}
-func (k KnxNetIpDeviceMemoryAddressPlcField) matches(knxGroupAddress *driverModel.KnxGroupAddress) bool {
- return false
+type KnxNetIpCommunicationObjectQueryField struct {
+ MainGroup uint8 // 5 Bits: Values 0-31
+ MiddleGroup uint8 // 3 Bits: values 0-7
+ SubGroup uint8 // 8 Bits
+ KnxNetIpDeviceField
}
-func (k KnxNetIpDeviceMemoryAddressPlcField) toKnxAddress() *driverModel.KnxAddress {
- ga := &driverModel.KnxAddress{
+func NewKnxNetIpCommunicationObjectQueryField(mainGroup uint8, middleGroup uint8, subGroup uint8) KnxNetIpCommunicationObjectQueryField {
+ return KnxNetIpCommunicationObjectQueryField{
+ MainGroup: mainGroup,
+ MiddleGroup: middleGroup,
+ SubGroup: subGroup,
+ }
+}
+
+func (k KnxNetIpCommunicationObjectQueryField) GetTypeName() string {
+ return ""
+}
+
+func (k KnxNetIpCommunicationObjectQueryField) GetQuantity() uint16 {
+ return 1
+}
+
+func (k KnxNetIpCommunicationObjectQueryField) toKnxAddress() *driverModel.KnxAddress {
+ individualAddress := &driverModel.KnxAddress{
MainGroup: k.MainGroup,
MiddleGroup: k.MiddleGroup,
SubGroup: k.SubGroup,
}
- return ga
+ return individualAddress
}
func CastToKnxNetIpFieldFromPlcField(plcField apiModel.PlcField) (KnxNetIpField, error) {
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpFieldHandler.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpFieldHandler.go
index e6a0c49..9aea58f 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpFieldHandler.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpFieldHandler.go
@@ -44,35 +44,62 @@ func (m KnxNetIpFieldType) GetName() string {
}
type FieldHandler struct {
- knxNetIpGroupAddress3Level *regexp.Regexp
- knxNetIpGroupAddress2Level *regexp.Regexp
- knxNetIpGroupAddress1Level *regexp.Regexp
- knxNetIpDevicePropertyAddress *regexp.Regexp
- knxNetIpDeviceMemoryAddress *regexp.Regexp
- knxNetIpDeviceQuery *regexp.Regexp
+ knxNetIpGroupAddress3Level *regexp.Regexp
+ knxNetIpGroupAddress2Level *regexp.Regexp
+ knxNetIpGroupAddress1Level *regexp.Regexp
+ knxNetIpDeviceQuery *regexp.Regexp
+ knxNetIpDevicePropertyAddress *regexp.Regexp
+ knxNetIpDeviceMemoryAddress *regexp.Regexp
+ knxNetIpDeviceCommunicationObjectQuery *regexp.Regexp
spi.PlcFieldHandler
}
func NewFieldHandler() FieldHandler {
return FieldHandler{
- knxNetIpGroupAddress3Level: regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\/(?P<middleGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\/(?P<subGroup>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
- knxNetIpGroupAddress2Level: regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))/(?P<subGroup>(\d{1,4}|\*|\[(\d{1,4}|\d{1,4}\-\d{1,4})(,(\d{1,4}|\d{1,4}\-\d{1,4}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
- knxNetIpGroupAddress1Level: regexp.MustCompile(`^(?P<mainGroup>(\d{1,5}|\*|\[(\d{1,5}|\d{1,5}\-\d{1,5})(,(\d{1,5}|\d{1,5}\-\d{1,5}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
- knxNetIpDevicePropertyAddress: regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})\/(?P<objectId>\d{1,3})\/(?P<propertyId>\d{1,3})$`),
- knxNetIpDeviceMemoryAddress: regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})#(?P<address>\d{1,5})(:(?P<datatype>[a-zA-Z_]+)(\[(?P<numElements>\d+)])?)?$`),
- knxNetIpDeviceQuery: regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\.(?P<middleGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\.(?P<subGroup>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))(\/(?P<objectId>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))(\/(?P<propertyId>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\ [...]
+ knxNetIpGroupAddress3Level: regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\/(?P<middleGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\/(?P<subGroup>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
+ knxNetIpGroupAddress2Level: regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))/(?P<subGroup>(\d{1,4}|\*|\[(\d{1,4}|\d{1,4}\-\d{1,4})(,(\d{1,4}|\d{1,4}\-\d{1,4}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
+ knxNetIpGroupAddress1Level: regexp.MustCompile(`^(?P<mainGroup>(\d{1,5}|\*|\[(\d{1,5}|\d{1,5}\-\d{1,5})(,(\d{1,5}|\d{1,5}\-\d{1,5}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
+
+ knxNetIpDeviceQuery: regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\.(?P<middleGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\.(?P<subGroup>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))$`),
+ knxNetIpDevicePropertyAddress: regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})#(?P<objectId>\d{1,3})\/(?P<propertyId>\d{1,3})$`),
+ knxNetIpDeviceMemoryAddress: regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})#(?P<address>[0-9a-fA-F]{1,8})(:(?P<datatype>[a-zA-Z_]+)(\[(?P<numElements>\d+)])?)?$`),
+ knxNetIpDeviceCommunicationObjectQuery: regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})#com-obj$`),
}
}
func (m FieldHandler) ParseQuery(query string) (apiModel.PlcField, error) {
- if match := utils.GetSubgroupMatches(m.knxNetIpDeviceQuery, query); match != nil {
- return NewKnxNetIpDevicePropertyAddressPlcField(
- match["mainGroup"], match["middleGroup"], match["subGroup"],
- match["objectId"], match["propertyId"]), nil
+ if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress1Level, query); match != nil {
+ fieldTypeName, ok := match["datatype"]
+ var fieldType driverModel.KnxDatapointType
+ if ok {
+ fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
+ }
+ return NewKnxNetIpGroupAddress1LevelPlcField(match["mainGroup"], &fieldType), nil
+ } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress2Level, query); match != nil {
+ fieldTypeName, ok := match["datatype"]
+ var fieldType driverModel.KnxDatapointType
+ if ok {
+ fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
+ }
+ return NewKnxNetIpGroupAddress2LevelPlcField(match["mainGroup"], match["subGroup"], &fieldType), nil
+ } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress3Level, query); match != nil {
+ fieldTypeName, ok := match["datatype"]
+ var fieldType driverModel.KnxDatapointType
+ if ok {
+ fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
+ }
+ return NewKnxNetIpGroupAddress3LevelPlcField(match["mainGroup"], match["middleGroup"], match["subGroup"], &fieldType), nil
+ } else if match := utils.GetSubgroupMatches(m.knxNetIpDeviceQuery, query); match != nil {
+ return NewKnxNetIpDeviceQueryField(
+ match["mainGroup"], match["middleGroup"], match["subGroup"]), nil
} else if match := utils.GetSubgroupMatches(m.knxNetIpDevicePropertyAddress, query); match != nil {
- return NewKnxNetIpDevicePropertyAddressPlcField(
- match["mainGroup"], match["middleGroup"], match["subGroup"],
- match["objectId"], match["propertyId"]), nil
+ mainGroup, _ := strconv.Atoi(match["mainGroup"])
+ middleGroup, _ := strconv.Atoi(match["middleGroup"])
+ subGroup, _ := strconv.Atoi(match["subGroup"])
+ objectId, _ := strconv.Atoi(match["objectId"])
+ propertyId, _ := strconv.Atoi(match["propertyId"])
+ return NewKnxNetIpDevicePropertyAddressPlcField(
+ uint8(mainGroup), uint8(middleGroup), uint8(subGroup), uint8(objectId), uint8(propertyId)), nil
} else if match := utils.GetSubgroupMatches(m.knxNetIpDeviceMemoryAddress, query); match != nil {
fieldTypeName, ok := match["datatype"]
// This is a 0-255 valued 1-byte value.
@@ -86,37 +113,24 @@ func (m FieldHandler) ParseQuery(query string) (apiModel.PlcField, error) {
addressData, _ := hex.DecodeString(match["address"])
var address uint16
if len(addressData) == 2 {
- address = uint16(addressData[0]) << 8 | uint16(addressData[1])
- } else {
+ address = uint16(addressData[0])<<8 | uint16(addressData[1])
+ } else if len(addressData) == 1 {
address = uint16(addressData[0])
+ } else {
+ return nil, errors.New("invalid address: " + match["address"])
}
numberOfElements := 1
numElements, ok := match["numElements"]
if ok && len(numElements) > 0 {
numberOfElements, _ = strconv.Atoi(numElements)
}
- return NewKnxNetIpDeviceMemoryAddressPlcField(&fieldType, uint8(mainGroup), uint8(middleGroup), uint8(subGroup), address, uint8(numberOfElements)), nil
- } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress3Level, query); match != nil {
- fieldTypeName, ok := match["datatype"]
- var fieldType driverModel.KnxDatapointType
- if ok {
- fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
- }
- return NewKnxNetIpGroupAddress3LevelPlcField(&fieldType, match["mainGroup"], match["middleGroup"], match["subGroup"]), nil
- } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress2Level, query); match != nil {
- fieldTypeName, ok := match["datatype"]
- var fieldType driverModel.KnxDatapointType
- if ok {
- fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
- }
- return NewKnxNetIpGroupAddress2LevelPlcField(&fieldType, match["mainGroup"], match["subGroup"]), nil
- } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress1Level, query); match != nil {
- fieldTypeName, ok := match["datatype"]
- var fieldType driverModel.KnxDatapointType
- if ok {
- fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
- }
- return NewKnxNetIpGroupAddress1LevelPlcField(&fieldType, match["mainGroup"]), nil
+ return NewKnxNetIpDeviceMemoryAddressPlcField(uint8(mainGroup), uint8(middleGroup), uint8(subGroup), address, uint8(numberOfElements), &fieldType), nil
+ } else if match := utils.GetSubgroupMatches(m.knxNetIpDeviceCommunicationObjectQuery, query); match != nil {
+ mainGroup, _ := strconv.Atoi(match["mainGroup"])
+ middleGroup, _ := strconv.Atoi(match["middleGroup"])
+ subGroup, _ := strconv.Atoi(match["subGroup"])
+ return NewKnxNetIpCommunicationObjectQueryField(
+ uint8(mainGroup), uint8(middleGroup), uint8(subGroup)), nil
}
return nil, errors.New("Invalid address format for address '" + query + "'")
}
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
index 3d215b1..221d60a 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
@@ -26,7 +26,6 @@ import (
internalValues "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
apiValues "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
- "strconv"
"time"
)
@@ -49,8 +48,8 @@ func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiMode
// Sort the fields in direct properties and memory addresses, which will have to be actively
// read from the devices and group-addresses which will be locally processed from the local cache.
- deviceAddresses := map[driverModel.KnxAddress]map[string]KnxNetIpField{}
- groupAddresses := map[string]KnxNetIpField{}
+ deviceAddresses := map[driverModel.KnxAddress]map[string]KnxNetIpDeviceField{}
+ groupAddresses := map[string]KnxNetIpGroupAddressField{}
for _, fieldName := range readRequest.GetFieldNames() {
// Get the knx field
field, err := CastToKnxNetIpFieldFromPlcField(readRequest.GetField(fieldName))
@@ -63,26 +62,34 @@ func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiMode
switch field.(type) {
case KnxNetIpDevicePropertyAddressPlcField:
propertyField := field.(KnxNetIpDevicePropertyAddressPlcField)
- knxAddress := FieldToKnxAddress(propertyField)
+ knxAddress := propertyField.toKnxAddress()
if knxAddress == nil {
continue
}
if _, ok := deviceAddresses[*knxAddress]; !ok {
- deviceAddresses[*knxAddress] = map[string]KnxNetIpField{}
+ deviceAddresses[*knxAddress] = map[string]KnxNetIpDeviceField{}
}
deviceAddresses[*knxAddress][fieldName] = propertyField
case KnxNetIpDeviceMemoryAddressPlcField:
memoryField := field.(KnxNetIpDeviceMemoryAddressPlcField)
- knxAddress := FieldToKnxAddress(memoryField)
+ knxAddress := memoryField.toKnxAddress()
if knxAddress == nil {
continue
}
if _, ok := deviceAddresses[*knxAddress]; !ok {
- deviceAddresses[*knxAddress] = map[string]KnxNetIpField{}
+ deviceAddresses[*knxAddress] = map[string]KnxNetIpDeviceField{}
}
deviceAddresses[*knxAddress][fieldName] = memoryField
+ case KnxNetIpCommunicationObjectQueryField:
+ responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+ plcValues[fieldName] = nil
+ continue
+ case KnxNetIpGroupAddressField:
+ groupAddressField := field.(KnxNetIpGroupAddressField)
+ groupAddresses[fieldName] = groupAddressField
default:
- groupAddresses[fieldName] = field
+ responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+ plcValues[fieldName] = nil
}
}
@@ -91,20 +98,11 @@ func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiMode
for deviceAddress, fields := range deviceAddresses {
// Collect all the properties on this device
for fieldName, field := range fields {
- // TODO: We'll add this as time progresses, for now we only support fully qualified addresses
- if field.IsPatternField() {
- responseCodes[fieldName] = apiModel.PlcResponseCode_UNSUPPORTED
- plcValues[fieldName] = nil
- continue
- }
-
switch field.(type) {
case KnxNetIpDevicePropertyAddressPlcField:
propertyField := field.(KnxNetIpDevicePropertyAddressPlcField)
- objectId, _ := strconv.Atoi(propertyField.ObjectId)
- propertyId, _ := strconv.Atoi(propertyField.PropertyId)
- results := m.connection.ReadDeviceProperty(deviceAddress, uint8(objectId), uint8(propertyId))
+ results := m.connection.ReadDeviceProperty(deviceAddress, propertyField.ObjectId, propertyField.PropertyId)
select {
case result := <-results:
responseCodes[fieldName] = result.returnCode
@@ -154,7 +152,7 @@ func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiMode
return resultChan
}
-func (m KnxNetIpReader) readGroupAddress(field KnxNetIpField) (apiModel.PlcResponseCode, apiValues.PlcValue) {
+func (m KnxNetIpReader) readGroupAddress(field KnxNetIpGroupAddressField) (apiModel.PlcResponseCode, apiValues.PlcValue) {
// Pattern fields can match more than one value, therefore we have to handle things differently
if field.IsPatternField() {
// Depending on the type of field, get the uint16 ids of all values that match the current field
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go
index 7838a2e..967e632 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go
@@ -81,12 +81,8 @@ func (m *KnxNetIpSubscriber) handleValueChange(lDataFrame *driverModel.LDataFram
var destinationAddress []int8
var apdu *driverModel.Apdu
switch lDataFrame.Child.(type) {
- case *driverModel.LDataFrameData:
- dataFrame := driverModel.CastLDataFrameData(lDataFrame)
- destinationAddress = dataFrame.DestinationAddress
- apdu = dataFrame.Apdu
- case *driverModel.LDataFrameDataExt:
- dataFrame := driverModel.CastLDataFrameDataExt(lDataFrame)
+ case *driverModel.LDataExtended:
+ dataFrame := driverModel.CastLDataExtended(lDataFrame)
destinationAddress = dataFrame.DestinationAddress
apdu = dataFrame.Apdu
}
@@ -124,39 +120,46 @@ func (m *KnxNetIpSubscriber) handleValueChange(lDataFrame *driverModel.LDataFram
if err != nil {
continue
}
- subscriptionType := subscriptionRequest.GetType(fieldName)
- // If it matches, take the datatype of each matching field and try to decode the payload
- if field.matches(groupAddress) {
- // If this is a CHANGE_OF_STATE field, filter out the events where the value actually hasn't changed.
- if subscriptionType == internalModel.SUBSCRIPTION_CHANGE_OF_STATE && changed {
- var payload []int8
- payload = append(payload, groupValueWrite.DataFirstByte)
- payload = append(payload, groupValueWrite.Data...)
- rb := utils.NewReadBuffer(utils.Int8ArrayToByteArray(payload))
- if field.GetFieldType() == nil {
- responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATATYPE
- plcValues[fieldName] = nil
- continue
- }
- // If the size of the field is greater than 6, we have to skip the first byte
- if field.GetFieldType().LengthInBits() > 6 {
- _, _ = rb.ReadUint8(8)
+ switch field.(type) {
+ case KnxNetIpGroupAddressField:
+ subscriptionType := subscriptionRequest.GetType(fieldName)
+ groupAddressField := field.(KnxNetIpGroupAddressField)
+ // If it matches, take the datatype of each matching field and try to decode the payload
+ if groupAddressField.matches(groupAddress) {
+ // If this is a CHANGE_OF_STATE field, filter out the events where the value actually hasn't changed.
+ if subscriptionType == internalModel.SUBSCRIPTION_CHANGE_OF_STATE && changed {
+ var payload []int8
+ payload = append(payload, groupValueWrite.DataFirstByte)
+ payload = append(payload, groupValueWrite.Data...)
+ rb := utils.NewReadBuffer(utils.Int8ArrayToByteArray(payload))
+ if groupAddressField.GetFieldType() == nil {
+ responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATATYPE
+ plcValues[fieldName] = nil
+ continue
+ }
+ // If the size of the field is greater than 6, we have to skip the first byte
+ if groupAddressField.GetFieldType().LengthInBits() > 6 {
+ _, _ = rb.ReadUint8(8)
+ }
+ plcValue, err2 := driverModel.KnxDatapointParse(rb, *groupAddressField.GetFieldType())
+ fields[fieldName] = field
+ types[fieldName] = subscriptionRequest.GetType(fieldName)
+ intervals[fieldName] = subscriptionRequest.GetInterval(fieldName)
+ addresses[fieldName] = destinationAddress
+ if err2 == nil {
+ responseCodes[fieldName] = apiModel.PlcResponseCode_OK
+ plcValues[fieldName] = plcValue
+ } else {
+ // TODO: Do a little more here ...
+ responseCodes[fieldName] = apiModel.PlcResponseCode_INTERNAL_ERROR
+ plcValues[fieldName] = nil
+ }
}
- plcValue, err2 := driverModel.KnxDatapointParse(rb, *field.GetFieldType())
- fields[fieldName] = field
- types[fieldName] = subscriptionRequest.GetType(fieldName)
- intervals[fieldName] = subscriptionRequest.GetInterval(fieldName)
- addresses[fieldName] = destinationAddress
- if err2 == nil {
- responseCodes[fieldName] = apiModel.PlcResponseCode_OK
- plcValues[fieldName] = plcValue
- } else {
- // TODO: Do a little more here ...
- responseCodes[fieldName] = apiModel.PlcResponseCode_INTERNAL_ERROR
- plcValues[fieldName] = nil
- }
- }
- }
+ }
+ default:
+ responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+ plcValues[fieldName] = nil
+ }
}
// Assemble a PlcSubscription event
diff --git a/plc4go/internal/plc4go/knxnetip/Utils.go b/plc4go/internal/plc4go/knxnetip/Utils.go
index 26eec2e..110c9c7 100644
--- a/plc4go/internal/plc4go/knxnetip/Utils.go
+++ b/plc4go/internal/plc4go/knxnetip/Utils.go
@@ -41,36 +41,6 @@ func GroupAddressToString(groupAddress *driverModel.KnxGroupAddress) string {
return ""
}
-func FieldToKnxAddress(field KnxNetIpField) *driverModel.KnxAddress {
- if field.IsPatternField() {
- return nil
- }
- var mainAddress int
- var middleAddress int
- var subAddress int
- switch field.(type) {
- case KnxNetIpDevicePropertyAddressPlcField:
- plcField := field.(KnxNetIpDevicePropertyAddressPlcField)
- mainAddress, _ = strconv.Atoi(plcField.MainGroup)
- middleAddress, _ = strconv.Atoi(plcField.MiddleGroup)
- subAddress, _ = strconv.Atoi(plcField.SubGroup)
- case KnxNetIpDeviceMemoryAddressPlcField:
- plcField := field.(KnxNetIpDeviceMemoryAddressPlcField)
- mainAddress = int(plcField.MainGroup)
- middleAddress = int(plcField.MiddleGroup)
- subAddress = int(plcField.SubGroup)
- case KnxNetIpGroupAddress3LevelPlcField:
- plcField := field.(KnxNetIpGroupAddress3LevelPlcField)
- mainAddress, _ = strconv.Atoi(plcField.MainGroup)
- middleAddress, _ = strconv.Atoi(plcField.MiddleGroup)
- subAddress, _ = strconv.Atoi(plcField.SubGroup)
- default:
- return nil
- }
-
- return driverModel.NewKnxAddress(uint8(mainAddress), uint8(middleAddress), uint8(subAddress))
-}
-
func Int8ArrayToKnxAddress(data []int8) *driverModel.KnxAddress {
readBuffer := utils.NewReadBuffer(utils.Int8ArrayToUint8Array(data))
knxAddress, err := driverModel.KnxAddressParse(readBuffer)
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go b/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go
index d1ddcd9..2498980 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go
@@ -43,7 +43,11 @@ func (m KnxnetipParserHelper) Parse(typeName string, arguments []string, io *uti
case "ConnectionRequestInformation":
return model.ConnectionRequestInformationParse(io)
case "Apdu":
- return model.ApduParse(io)
+ dataLength, err := utils.StrToUint8(arguments[0])
+ if err != nil {
+ return nil, err
+ }
+ return model.ApduParse(io, dataLength)
case "HPAIDiscoveryEndpoint":
return model.HPAIDiscoveryEndpointParse(io)
case "ProjectInstallationIdentifier":
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/Apdu.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/Apdu.go
index 9bee363..ae349a2 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/Apdu.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/Apdu.go
@@ -29,7 +29,6 @@ import (
// The data-structure of this message
type Apdu struct {
- DataLength uint8
Numbered bool
Counter uint8
Child IApduChild
@@ -53,13 +52,13 @@ type IApduParent interface {
type IApduChild interface {
Serialize(io utils.WriteBuffer) error
- InitializeParent(parent *Apdu, dataLength uint8, numbered bool, counter uint8)
+ InitializeParent(parent *Apdu, numbered bool, counter uint8)
GetTypeName() string
IApdu
}
-func NewApdu(dataLength uint8, numbered bool, counter uint8) *Apdu {
- return &Apdu{DataLength: dataLength, Numbered: numbered, Counter: counter}
+func NewApdu(numbered bool, counter uint8) *Apdu {
+ return &Apdu{Numbered: numbered, Counter: counter}
}
func CastApdu(structType interface{}) *Apdu {
@@ -82,9 +81,6 @@ func (m *Apdu) GetTypeName() string {
func (m *Apdu) LengthInBits() uint16 {
lengthInBits := uint16(0)
- // Simple field (dataLength)
- lengthInBits += 8
-
// Discriminator Field (control)
lengthInBits += 1
@@ -104,13 +100,7 @@ func (m *Apdu) LengthInBytes() uint16 {
return m.LengthInBits() / 8
}
-func ApduParse(io *utils.ReadBuffer) (*Apdu, error) {
-
- // Simple Field (dataLength)
- dataLength, _dataLengthErr := io.ReadUint8(8)
- if _dataLengthErr != nil {
- return nil, errors.New("Error parsing 'dataLength' field " + _dataLengthErr.Error())
- }
+func ApduParse(io *utils.ReadBuffer, dataLength uint8) (*Apdu, error) {
// Discriminator Field (control) (Used as input to a switch field)
control, _controlErr := io.ReadUint8(1)
@@ -144,7 +134,7 @@ func ApduParse(io *utils.ReadBuffer) (*Apdu, error) {
}
// Finish initializing
- _parent.Child.InitializeParent(_parent, dataLength, numbered, counter)
+ _parent.Child.InitializeParent(_parent, numbered, counter)
return _parent, nil
}
@@ -154,13 +144,6 @@ func (m *Apdu) Serialize(io utils.WriteBuffer) error {
func (m *Apdu) SerializeParent(io utils.WriteBuffer, child IApdu, serializeChildFunction func() error) error {
- // Simple Field (dataLength)
- dataLength := uint8(m.DataLength)
- _dataLengthErr := io.WriteUint8(8, (dataLength))
- if _dataLengthErr != nil {
- return errors.New("Error serializing 'dataLength' field " + _dataLengthErr.Error())
- }
-
// Discriminator Field (control) (Used as input to a switch field)
control := uint8(child.Control())
_controlErr := io.WriteUint8(1, (control))
@@ -206,12 +189,6 @@ func (m *Apdu) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
case xml.StartElement:
tok := token.(xml.StartElement)
switch tok.Name.Local {
- case "dataLength":
- var data uint8
- if err := d.DecodeElement(&data, &tok); err != nil {
- return err
- }
- m.DataLength = data
case "numbered":
var data bool
if err := d.DecodeElement(&data, &tok); err != nil {
@@ -264,9 +241,6 @@ func (m *Apdu) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
}}); err != nil {
return err
}
- if err := e.EncodeElement(m.DataLength, xml.StartElement{Name: xml.Name{Local: "dataLength"}}); err != nil {
- return err
- }
if err := e.EncodeElement(m.Numbered, xml.StartElement{Name: xml.Name{Local: "numbered"}}); err != nil {
return err
}
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduControlContainer.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduControlContainer.go
index 008fd70..f2bcde9 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduControlContainer.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduControlContainer.go
@@ -48,16 +48,15 @@ func (m *ApduControlContainer) Control() uint8 {
}
-func (m *ApduControlContainer) InitializeParent(parent *Apdu, dataLength uint8, numbered bool, counter uint8) {
- m.Parent.DataLength = dataLength
+func (m *ApduControlContainer) InitializeParent(parent *Apdu, numbered bool, counter uint8) {
m.Parent.Numbered = numbered
m.Parent.Counter = counter
}
-func NewApduControlContainer(controlApdu *ApduControl, dataLength uint8, numbered bool, counter uint8) *Apdu {
+func NewApduControlContainer(controlApdu *ApduControl, numbered bool, counter uint8) *Apdu {
child := &ApduControlContainer{
ControlApdu: controlApdu,
- Parent: NewApdu(dataLength, numbered, counter),
+ Parent: NewApdu(numbered, counter),
}
child.Parent.Child = child
return child.Parent
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduDataContainer.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduDataContainer.go
index a866208..567b042 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduDataContainer.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduDataContainer.go
@@ -48,16 +48,15 @@ func (m *ApduDataContainer) Control() uint8 {
}
-func (m *ApduDataContainer) InitializeParent(parent *Apdu, dataLength uint8, numbered bool, counter uint8) {
- m.Parent.DataLength = dataLength
+func (m *ApduDataContainer) InitializeParent(parent *Apdu, numbered bool, counter uint8) {
m.Parent.Numbered = numbered
m.Parent.Counter = counter
}
-func NewApduDataContainer(dataApdu *ApduData, dataLength uint8, numbered bool, counter uint8) *Apdu {
+func NewApduDataContainer(dataApdu *ApduData, numbered bool, counter uint8) *Apdu {
child := &ApduDataContainer{
DataApdu: dataApdu,
- Parent: NewApdu(dataLength, numbered, counter),
+ Parent: NewApdu(numbered, counter),
}
child.Parent.Child = child
return child.Parent
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/KnxProperty.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/KnxProperty.go
index 177a78f..9525f35 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/KnxProperty.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/KnxProperty.go
@@ -25,7 +25,7 @@ import (
api "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
)
-func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, dataLength uint8) (api.PlcValue, error) {
+func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, dataLengthInBytes uint8) (api.PlcValue, error) {
switch {
case propertyType == KnxPropertyDataType_PDT_CONTROL: // BOOL
@@ -64,6 +64,14 @@ func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, da
return nil, errors.New("Error parsing 'value' field " + _valueErr.Error())
}
return values.NewPlcINT(value), nil
+ case propertyType == KnxPropertyDataType_PDT_UNSIGNED_INT && dataLengthInBytes == 4: // UDINT
+
+ // Simple Field (value)
+ value, _valueErr := io.ReadUint32(32)
+ if _valueErr != nil {
+ return nil, errors.New("Error parsing 'value' field " + _valueErr.Error())
+ }
+ return values.NewPlcUDINT(value), nil
case propertyType == KnxPropertyDataType_PDT_UNSIGNED_INT: // UINT
// Simple Field (value)
@@ -799,7 +807,7 @@ func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, da
// Array Field (value)
var value []api.PlcValue
- for i := 0; i < int(dataLength); i++ {
+ for i := 0; i < int(dataLengthInBytes); i++ {
_item, _itemErr := io.ReadUint8(8)
if _itemErr != nil {
return nil, errors.New("Error parsing 'value' field " + _itemErr.Error())
@@ -811,7 +819,7 @@ func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, da
return nil, errors.New("unsupported type")
}
-func KnxPropertySerialize(io *utils.WriteBuffer, value api.PlcValue, propertyType KnxPropertyDataType, dataLength uint8) error {
+func KnxPropertySerialize(io *utils.WriteBuffer, value api.PlcValue, propertyType KnxPropertyDataType, dataLengthInBytes uint8) error {
switch {
case propertyType == KnxPropertyDataType_PDT_CONTROL: // BOOL
@@ -842,6 +850,12 @@ func KnxPropertySerialize(io *utils.WriteBuffer, value api.PlcValue, propertyTyp
if _err := io.WriteInt16(16, value.GetInt16()); _err != nil {
return errors.New("Error serializing 'value' field " + _err.Error())
}
+ case propertyType == KnxPropertyDataType_PDT_UNSIGNED_INT && dataLengthInBytes == 4: // UDINT
+
+ // Simple Field (value)
+ if _err := io.WriteUint32(32, value.GetUint32()); _err != nil {
+ return errors.New("Error serializing 'value' field " + _err.Error())
+ }
case propertyType == KnxPropertyDataType_PDT_UNSIGNED_INT: // UINT
// Simple Field (value)
@@ -1395,7 +1409,7 @@ func KnxPropertySerialize(io *utils.WriteBuffer, value api.PlcValue, propertyTyp
default: // List
// Array Field (value)
- for i := uint32(0); i < uint32(dataLength); i++ {
+ for i := uint32(0); i < uint32(dataLengthInBytes); i++ {
_itemErr := io.WriteUint8(8, value.GetIndex(i).GetUint8())
if _itemErr != nil {
return errors.New("Error serializing 'value' field " + _itemErr.Error())
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LBusmonInd.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LBusmonInd.go
index 050e47d..5877709 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LBusmonInd.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LBusmonInd.go
@@ -146,7 +146,7 @@ func LBusmonIndParse(io *utils.ReadBuffer) (*CEMI, error) {
// Optional Field (crc) (Can be skipped, if a given expression evaluates to false)
var crc *uint8 = nil
- if dataFrame.NotAckFrame {
+ if CastLDataFrame(dataFrame).NotAckFrame() {
_val, _err := io.ReadUint8(8)
if _err != nil {
return nil, errors.New("Error parsing 'crc' field " + _err.Error())
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameDataExt.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataExtended.go
similarity index 79%
rename from plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameDataExt.go
rename to plc4go/internal/plc4go/knxnetip/readwrite/model/LDataExtended.go
index c875cf1..76dae80 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameDataExt.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataExtended.go
@@ -27,7 +27,7 @@ import (
)
// The data-structure of this message
-type LDataFrameDataExt struct {
+type LDataExtended struct {
GroupAddress bool
HopCount uint8
ExtendedFrameFormat uint8
@@ -35,11 +35,11 @@ type LDataFrameDataExt struct {
DestinationAddress []int8
Apdu *Apdu
Parent *LDataFrame
- ILDataFrameDataExt
+ ILDataExtended
}
// The corresponding interface
-type ILDataFrameDataExt interface {
+type ILDataExtended interface {
LengthInBytes() uint16
LengthInBits() uint16
Serialize(io utils.WriteBuffer) error
@@ -49,61 +49,61 @@ type ILDataFrameDataExt interface {
///////////////////////////////////////////////////////////
// Accessors for discriminator values.
///////////////////////////////////////////////////////////
-func (m *LDataFrameDataExt) ExtendedFrame() bool {
+func (m *LDataExtended) NotAckFrame() bool {
return true
}
-func (m *LDataFrameDataExt) Polling() bool {
+func (m *LDataExtended) Polling() bool {
return false
}
-func (m *LDataFrameDataExt) InitializeParent(parent *LDataFrame, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
- m.Parent.Repeated = repeated
- m.Parent.NotAckFrame = notAckFrame
+func (m *LDataExtended) InitializeParent(parent *LDataFrame, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
+ m.Parent.FrameType = frameType
+ m.Parent.NotRepeated = notRepeated
m.Parent.Priority = priority
m.Parent.AcknowledgeRequested = acknowledgeRequested
m.Parent.ErrorFlag = errorFlag
}
-func NewLDataFrameDataExt(groupAddress bool, hopCount uint8, extendedFrameFormat uint8, sourceAddress *KnxAddress, destinationAddress []int8, apdu *Apdu, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
- child := &LDataFrameDataExt{
+func NewLDataExtended(groupAddress bool, hopCount uint8, extendedFrameFormat uint8, sourceAddress *KnxAddress, destinationAddress []int8, apdu *Apdu, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
+ child := &LDataExtended{
GroupAddress: groupAddress,
HopCount: hopCount,
ExtendedFrameFormat: extendedFrameFormat,
SourceAddress: sourceAddress,
DestinationAddress: destinationAddress,
Apdu: apdu,
- Parent: NewLDataFrame(repeated, notAckFrame, priority, acknowledgeRequested, errorFlag),
+ Parent: NewLDataFrame(frameType, notRepeated, priority, acknowledgeRequested, errorFlag),
}
child.Parent.Child = child
return child.Parent
}
-func CastLDataFrameDataExt(structType interface{}) *LDataFrameDataExt {
- castFunc := func(typ interface{}) *LDataFrameDataExt {
- if casted, ok := typ.(LDataFrameDataExt); ok {
+func CastLDataExtended(structType interface{}) *LDataExtended {
+ castFunc := func(typ interface{}) *LDataExtended {
+ if casted, ok := typ.(LDataExtended); ok {
return &casted
}
- if casted, ok := typ.(*LDataFrameDataExt); ok {
+ if casted, ok := typ.(*LDataExtended); ok {
return casted
}
if casted, ok := typ.(LDataFrame); ok {
- return CastLDataFrameDataExt(casted.Child)
+ return CastLDataExtended(casted.Child)
}
if casted, ok := typ.(*LDataFrame); ok {
- return CastLDataFrameDataExt(casted.Child)
+ return CastLDataExtended(casted.Child)
}
return nil
}
return castFunc(structType)
}
-func (m *LDataFrameDataExt) GetTypeName() string {
- return "LDataFrameDataExt"
+func (m *LDataExtended) GetTypeName() string {
+ return "LDataExtended"
}
-func (m *LDataFrameDataExt) LengthInBits() uint16 {
+func (m *LDataExtended) LengthInBits() uint16 {
lengthInBits := uint16(0)
// Simple field (groupAddress)
@@ -123,17 +123,20 @@ func (m *LDataFrameDataExt) LengthInBits() uint16 {
lengthInBits += 8 * uint16(len(m.DestinationAddress))
}
+ // Implicit Field (dataLength)
+ lengthInBits += 8
+
// Simple field (apdu)
lengthInBits += m.Apdu.LengthInBits()
return lengthInBits
}
-func (m *LDataFrameDataExt) LengthInBytes() uint16 {
+func (m *LDataExtended) LengthInBytes() uint16 {
return m.LengthInBits() / 8
}
-func LDataFrameDataExtParse(io *utils.ReadBuffer) (*LDataFrame, error) {
+func LDataExtendedParse(io *utils.ReadBuffer) (*LDataFrame, error) {
// Simple Field (groupAddress)
groupAddress, _groupAddressErr := io.ReadBit()
@@ -170,14 +173,20 @@ func LDataFrameDataExtParse(io *utils.ReadBuffer) (*LDataFrame, error) {
destinationAddress[curItem] = _item
}
+ // Implicit Field (dataLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+ dataLength, _dataLengthErr := io.ReadUint8(8)
+ if _dataLengthErr != nil {
+ return nil, errors.New("Error parsing 'dataLength' field " + _dataLengthErr.Error())
+ }
+
// Simple Field (apdu)
- apdu, _apduErr := ApduParse(io)
+ apdu, _apduErr := ApduParse(io, dataLength)
if _apduErr != nil {
return nil, errors.New("Error parsing 'apdu' field " + _apduErr.Error())
}
// Create a partially initialized instance
- _child := &LDataFrameDataExt{
+ _child := &LDataExtended{
GroupAddress: groupAddress,
HopCount: hopCount,
ExtendedFrameFormat: extendedFrameFormat,
@@ -190,7 +199,7 @@ func LDataFrameDataExtParse(io *utils.ReadBuffer) (*LDataFrame, error) {
return _child.Parent, nil
}
-func (m *LDataFrameDataExt) Serialize(io utils.WriteBuffer) error {
+func (m *LDataExtended) Serialize(io utils.WriteBuffer) error {
ser := func() error {
// Simple Field (groupAddress)
@@ -230,6 +239,13 @@ func (m *LDataFrameDataExt) Serialize(io utils.WriteBuffer) error {
}
}
+ // Implicit Field (dataLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+ dataLength := uint8(uint8(m.Apdu.LengthInBytes()) - uint8(uint8(1)))
+ _dataLengthErr := io.WriteUint8(8, (dataLength))
+ if _dataLengthErr != nil {
+ return errors.New("Error serializing 'dataLength' field " + _dataLengthErr.Error())
+ }
+
// Simple Field (apdu)
_apduErr := m.Apdu.Serialize(io)
if _apduErr != nil {
@@ -241,7 +257,7 @@ func (m *LDataFrameDataExt) Serialize(io utils.WriteBuffer) error {
return m.Parent.SerializeParent(io, m, ser)
}
-func (m *LDataFrameDataExt) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+func (m *LDataExtended) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var token xml.Token
var err error
token = start
@@ -303,7 +319,7 @@ func (m *LDataFrameDataExt) UnmarshalXML(d *xml.Decoder, start xml.StartElement)
}
}
-func (m *LDataFrameDataExt) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (m *LDataExtended) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if err := e.EncodeElement(m.GroupAddress, xml.StartElement{Name: xml.Name{Local: "groupAddress"}}); err != nil {
return err
}
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrame.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrame.go
index 8bf3bff..eececbf 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrame.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrame.go
@@ -29,8 +29,8 @@ import (
// The data-structure of this message
type LDataFrame struct {
- Repeated bool
- NotAckFrame bool
+ FrameType bool
+ NotRepeated bool
Priority CEMIPriority
AcknowledgeRequested bool
ErrorFlag bool
@@ -41,7 +41,7 @@ type LDataFrame struct {
// The corresponding interface
type ILDataFrame interface {
- ExtendedFrame() bool
+ NotAckFrame() bool
Polling() bool
LengthInBytes() uint16
LengthInBits() uint16
@@ -56,13 +56,13 @@ type ILDataFrameParent interface {
type ILDataFrameChild interface {
Serialize(io utils.WriteBuffer) error
- InitializeParent(parent *LDataFrame, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool)
+ InitializeParent(parent *LDataFrame, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool)
GetTypeName() string
ILDataFrame
}
-func NewLDataFrame(repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
- return &LDataFrame{Repeated: repeated, NotAckFrame: notAckFrame, Priority: priority, AcknowledgeRequested: acknowledgeRequested, ErrorFlag: errorFlag}
+func NewLDataFrame(frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
+ return &LDataFrame{FrameType: frameType, NotRepeated: notRepeated, Priority: priority, AcknowledgeRequested: acknowledgeRequested, ErrorFlag: errorFlag}
}
func CastLDataFrame(structType interface{}) *LDataFrame {
@@ -85,16 +85,16 @@ func (m *LDataFrame) GetTypeName() string {
func (m *LDataFrame) LengthInBits() uint16 {
lengthInBits := uint16(0)
- // Discriminator Field (extendedFrame)
+ // Simple field (frameType)
lengthInBits += 1
// Discriminator Field (polling)
lengthInBits += 1
- // Simple field (repeated)
+ // Simple field (notRepeated)
lengthInBits += 1
- // Simple field (notAckFrame)
+ // Discriminator Field (notAckFrame)
lengthInBits += 1
// Enum Field (priority)
@@ -118,10 +118,10 @@ func (m *LDataFrame) LengthInBytes() uint16 {
func LDataFrameParse(io *utils.ReadBuffer) (*LDataFrame, error) {
- // Discriminator Field (extendedFrame) (Used as input to a switch field)
- extendedFrame, _extendedFrameErr := io.ReadBit()
- if _extendedFrameErr != nil {
- return nil, errors.New("Error parsing 'extendedFrame' field " + _extendedFrameErr.Error())
+ // Simple Field (frameType)
+ frameType, _frameTypeErr := io.ReadBit()
+ if _frameTypeErr != nil {
+ return nil, errors.New("Error parsing 'frameType' field " + _frameTypeErr.Error())
}
// Discriminator Field (polling) (Used as input to a switch field)
@@ -130,13 +130,13 @@ func LDataFrameParse(io *utils.ReadBuffer) (*LDataFrame, error) {
return nil, errors.New("Error parsing 'polling' field " + _pollingErr.Error())
}
- // Simple Field (repeated)
- repeated, _repeatedErr := io.ReadBit()
- if _repeatedErr != nil {
- return nil, errors.New("Error parsing 'repeated' field " + _repeatedErr.Error())
+ // Simple Field (notRepeated)
+ notRepeated, _notRepeatedErr := io.ReadBit()
+ if _notRepeatedErr != nil {
+ return nil, errors.New("Error parsing 'notRepeated' field " + _notRepeatedErr.Error())
}
- // Simple Field (notAckFrame)
+ // Discriminator Field (notAckFrame) (Used as input to a switch field)
notAckFrame, _notAckFrameErr := io.ReadBit()
if _notAckFrameErr != nil {
return nil, errors.New("Error parsing 'notAckFrame' field " + _notAckFrameErr.Error())
@@ -164,21 +164,19 @@ func LDataFrameParse(io *utils.ReadBuffer) (*LDataFrame, error) {
var _parent *LDataFrame
var typeSwitchError error
switch {
- case extendedFrame == false && polling == false:
- _parent, typeSwitchError = LDataFrameDataParse(io)
- case extendedFrame == true && polling == false:
- _parent, typeSwitchError = LDataFrameDataExtParse(io)
- case extendedFrame == true && polling == true:
- _parent, typeSwitchError = LDataFramePollingDataParse(io)
- case extendedFrame == false && polling == true:
- _parent, typeSwitchError = LDataFramePollingDataParse(io)
+ case notAckFrame == true && polling == false:
+ _parent, typeSwitchError = LDataExtendedParse(io)
+ case notAckFrame == true && polling == true:
+ _parent, typeSwitchError = LPollDataParse(io)
+ case notAckFrame == false:
+ _parent, typeSwitchError = LDataFrameACKParse(io)
}
if typeSwitchError != nil {
return nil, errors.New("Error parsing sub-type for type-switch. " + typeSwitchError.Error())
}
// Finish initializing
- _parent.Child.InitializeParent(_parent, repeated, notAckFrame, priority, acknowledgeRequested, errorFlag)
+ _parent.Child.InitializeParent(_parent, frameType, notRepeated, priority, acknowledgeRequested, errorFlag)
return _parent, nil
}
@@ -188,11 +186,11 @@ func (m *LDataFrame) Serialize(io utils.WriteBuffer) error {
func (m *LDataFrame) SerializeParent(io utils.WriteBuffer, child ILDataFrame, serializeChildFunction func() error) error {
- // Discriminator Field (extendedFrame) (Used as input to a switch field)
- extendedFrame := bool(child.ExtendedFrame())
- _extendedFrameErr := io.WriteBit((extendedFrame))
- if _extendedFrameErr != nil {
- return errors.New("Error serializing 'extendedFrame' field " + _extendedFrameErr.Error())
+ // Simple Field (frameType)
+ frameType := bool(m.FrameType)
+ _frameTypeErr := io.WriteBit((frameType))
+ if _frameTypeErr != nil {
+ return errors.New("Error serializing 'frameType' field " + _frameTypeErr.Error())
}
// Discriminator Field (polling) (Used as input to a switch field)
@@ -202,15 +200,15 @@ func (m *LDataFrame) SerializeParent(io utils.WriteBuffer, child ILDataFrame, se
return errors.New("Error serializing 'polling' field " + _pollingErr.Error())
}
- // Simple Field (repeated)
- repeated := bool(m.Repeated)
- _repeatedErr := io.WriteBit((repeated))
- if _repeatedErr != nil {
- return errors.New("Error serializing 'repeated' field " + _repeatedErr.Error())
+ // Simple Field (notRepeated)
+ notRepeated := bool(m.NotRepeated)
+ _notRepeatedErr := io.WriteBit((notRepeated))
+ if _notRepeatedErr != nil {
+ return errors.New("Error serializing 'notRepeated' field " + _notRepeatedErr.Error())
}
- // Simple Field (notAckFrame)
- notAckFrame := bool(m.NotAckFrame)
+ // Discriminator Field (notAckFrame) (Used as input to a switch field)
+ notAckFrame := bool(child.NotAckFrame())
_notAckFrameErr := io.WriteBit((notAckFrame))
if _notAckFrameErr != nil {
return errors.New("Error serializing 'notAckFrame' field " + _notAckFrameErr.Error())
@@ -261,18 +259,18 @@ func (m *LDataFrame) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
case xml.StartElement:
tok := token.(xml.StartElement)
switch tok.Name.Local {
- case "repeated":
+ case "frameType":
var data bool
if err := d.DecodeElement(&data, &tok); err != nil {
return err
}
- m.Repeated = data
- case "notAckFrame":
+ m.FrameType = data
+ case "notRepeated":
var data bool
if err := d.DecodeElement(&data, &tok); err != nil {
return err
}
- m.NotAckFrame = data
+ m.NotRepeated = data
case "priority":
var data CEMIPriority
if err := d.DecodeElement(&data, &tok); err != nil {
@@ -293,10 +291,10 @@ func (m *LDataFrame) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
m.ErrorFlag = data
default:
switch start.Attr[0].Value {
- case "org.apache.plc4x.java.knxnetip.readwrite.LDataFrameData":
- var dt *LDataFrameData
+ case "org.apache.plc4x.java.knxnetip.readwrite.LDataExtended":
+ var dt *LDataExtended
if m.Child != nil {
- dt = m.Child.(*LDataFrameData)
+ dt = m.Child.(*LDataExtended)
}
if err := d.DecodeElement(&dt, &tok); err != nil {
return err
@@ -305,10 +303,10 @@ func (m *LDataFrame) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
dt.Parent = m
m.Child = dt
}
- case "org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt":
- var dt *LDataFrameDataExt
+ case "org.apache.plc4x.java.knxnetip.readwrite.LPollData":
+ var dt *LPollData
if m.Child != nil {
- dt = m.Child.(*LDataFrameDataExt)
+ dt = m.Child.(*LPollData)
}
if err := d.DecodeElement(&dt, &tok); err != nil {
return err
@@ -317,10 +315,10 @@ func (m *LDataFrame) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
dt.Parent = m
m.Child = dt
}
- case "org.apache.plc4x.java.knxnetip.readwrite.LDataFramePollingData":
- var dt *LDataFramePollingData
+ case "org.apache.plc4x.java.knxnetip.readwrite.LDataFrameACK":
+ var dt *LDataFrameACK
if m.Child != nil {
- dt = m.Child.(*LDataFramePollingData)
+ dt = m.Child.(*LDataFrameACK)
}
if err := d.DecodeElement(&dt, &tok); err != nil {
return err
@@ -343,10 +341,10 @@ func (m *LDataFrame) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
}}); err != nil {
return err
}
- if err := e.EncodeElement(m.Repeated, xml.StartElement{Name: xml.Name{Local: "repeated"}}); err != nil {
+ if err := e.EncodeElement(m.FrameType, xml.StartElement{Name: xml.Name{Local: "frameType"}}); err != nil {
return err
}
- if err := e.EncodeElement(m.NotAckFrame, xml.StartElement{Name: xml.Name{Local: "notAckFrame"}}); err != nil {
+ if err := e.EncodeElement(m.NotRepeated, xml.StartElement{Name: xml.Name{Local: "notRepeated"}}); err != nil {
return err
}
if err := e.EncodeElement(m.Priority, xml.StartElement{Name: xml.Name{Local: "priority"}}); err != nil {
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameACK.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameACK.go
new file mode 100644
index 0000000..2800681
--- /dev/null
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameACK.go
@@ -0,0 +1,144 @@
+//
+// 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
+
+import (
+ "encoding/xml"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+ "io"
+)
+
+// The data-structure of this message
+type LDataFrameACK struct {
+ Parent *LDataFrame
+ ILDataFrameACK
+}
+
+// The corresponding interface
+type ILDataFrameACK interface {
+ LengthInBytes() uint16
+ LengthInBits() uint16
+ Serialize(io utils.WriteBuffer) error
+ xml.Marshaler
+}
+
+///////////////////////////////////////////////////////////
+// Accessors for discriminator values.
+///////////////////////////////////////////////////////////
+func (m *LDataFrameACK) NotAckFrame() bool {
+ return false
+}
+
+func (m *LDataFrameACK) Polling() bool {
+ return false
+}
+
+
+func (m *LDataFrameACK) InitializeParent(parent *LDataFrame, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
+ m.Parent.FrameType = frameType
+ m.Parent.NotRepeated = notRepeated
+ m.Parent.Priority = priority
+ m.Parent.AcknowledgeRequested = acknowledgeRequested
+ m.Parent.ErrorFlag = errorFlag
+}
+
+func NewLDataFrameACK(frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
+ child := &LDataFrameACK{
+ Parent: NewLDataFrame(frameType, notRepeated, priority, acknowledgeRequested, errorFlag),
+ }
+ child.Parent.Child = child
+ return child.Parent
+}
+
+func CastLDataFrameACK(structType interface{}) *LDataFrameACK {
+ castFunc := func(typ interface{}) *LDataFrameACK {
+ if casted, ok := typ.(LDataFrameACK); ok {
+ return &casted
+ }
+ if casted, ok := typ.(*LDataFrameACK); ok {
+ return casted
+ }
+ if casted, ok := typ.(LDataFrame); ok {
+ return CastLDataFrameACK(casted.Child)
+ }
+ if casted, ok := typ.(*LDataFrame); ok {
+ return CastLDataFrameACK(casted.Child)
+ }
+ return nil
+ }
+ return castFunc(structType)
+}
+
+func (m *LDataFrameACK) GetTypeName() string {
+ return "LDataFrameACK"
+}
+
+func (m *LDataFrameACK) LengthInBits() uint16 {
+ lengthInBits := uint16(0)
+
+ return lengthInBits
+}
+
+func (m *LDataFrameACK) LengthInBytes() uint16 {
+ return m.LengthInBits() / 8
+}
+
+func LDataFrameACKParse(io *utils.ReadBuffer) (*LDataFrame, error) {
+
+ // Create a partially initialized instance
+ _child := &LDataFrameACK{
+ Parent: &LDataFrame{},
+ }
+ _child.Parent.Child = _child
+ return _child.Parent, nil
+}
+
+func (m *LDataFrameACK) Serialize(io utils.WriteBuffer) error {
+ ser := func() error {
+
+ return nil
+ }
+ return m.Parent.SerializeParent(io, m, ser)
+}
+
+func (m *LDataFrameACK) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ var token xml.Token
+ var err error
+ token = start
+ for {
+ switch token.(type) {
+ case xml.StartElement:
+ tok := token.(xml.StartElement)
+ switch tok.Name.Local {
+ }
+ }
+ token, err = d.Token()
+ if err != nil {
+ if err == io.EOF {
+ return nil
+ }
+ return err
+ }
+ }
+}
+
+func (m *LDataFrameACK) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ return nil
+}
+
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameData.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameData.go
deleted file mode 100644
index 6821d11..0000000
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameData.go
+++ /dev/null
@@ -1,301 +0,0 @@
-//
-// 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
-
-import (
- "encoding/base64"
- "encoding/xml"
- "errors"
- "github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
- "io"
-)
-
-// The data-structure of this message
-type LDataFrameData struct {
- SourceAddress *KnxAddress
- DestinationAddress []int8
- GroupAddress bool
- HopCount uint8
- Apdu *Apdu
- Parent *LDataFrame
- ILDataFrameData
-}
-
-// The corresponding interface
-type ILDataFrameData interface {
- LengthInBytes() uint16
- LengthInBits() uint16
- Serialize(io utils.WriteBuffer) error
- xml.Marshaler
-}
-
-///////////////////////////////////////////////////////////
-// Accessors for discriminator values.
-///////////////////////////////////////////////////////////
-func (m *LDataFrameData) ExtendedFrame() bool {
- return false
-}
-
-func (m *LDataFrameData) Polling() bool {
- return false
-}
-
-
-func (m *LDataFrameData) InitializeParent(parent *LDataFrame, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
- m.Parent.Repeated = repeated
- m.Parent.NotAckFrame = notAckFrame
- m.Parent.Priority = priority
- m.Parent.AcknowledgeRequested = acknowledgeRequested
- m.Parent.ErrorFlag = errorFlag
-}
-
-func NewLDataFrameData(sourceAddress *KnxAddress, destinationAddress []int8, groupAddress bool, hopCount uint8, apdu *Apdu, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
- child := &LDataFrameData{
- SourceAddress: sourceAddress,
- DestinationAddress: destinationAddress,
- GroupAddress: groupAddress,
- HopCount: hopCount,
- Apdu: apdu,
- Parent: NewLDataFrame(repeated, notAckFrame, priority, acknowledgeRequested, errorFlag),
- }
- child.Parent.Child = child
- return child.Parent
-}
-
-func CastLDataFrameData(structType interface{}) *LDataFrameData {
- castFunc := func(typ interface{}) *LDataFrameData {
- if casted, ok := typ.(LDataFrameData); ok {
- return &casted
- }
- if casted, ok := typ.(*LDataFrameData); ok {
- return casted
- }
- if casted, ok := typ.(LDataFrame); ok {
- return CastLDataFrameData(casted.Child)
- }
- if casted, ok := typ.(*LDataFrame); ok {
- return CastLDataFrameData(casted.Child)
- }
- return nil
- }
- return castFunc(structType)
-}
-
-func (m *LDataFrameData) GetTypeName() string {
- return "LDataFrameData"
-}
-
-func (m *LDataFrameData) LengthInBits() uint16 {
- lengthInBits := uint16(0)
-
- // Simple field (sourceAddress)
- lengthInBits += m.SourceAddress.LengthInBits()
-
- // Array field
- if len(m.DestinationAddress) > 0 {
- lengthInBits += 8 * uint16(len(m.DestinationAddress))
- }
-
- // Simple field (groupAddress)
- lengthInBits += 1
-
- // Simple field (hopCount)
- lengthInBits += 3
-
- // Simple field (apdu)
- lengthInBits += m.Apdu.LengthInBits()
-
- return lengthInBits
-}
-
-func (m *LDataFrameData) LengthInBytes() uint16 {
- return m.LengthInBits() / 8
-}
-
-func LDataFrameDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
-
- // Simple Field (sourceAddress)
- sourceAddress, _sourceAddressErr := KnxAddressParse(io)
- if _sourceAddressErr != nil {
- return nil, errors.New("Error parsing 'sourceAddress' field " + _sourceAddressErr.Error())
- }
-
- // Array field (destinationAddress)
- // Count array
- destinationAddress := make([]int8, uint16(2))
- for curItem := uint16(0); curItem < uint16(uint16(2)); curItem++ {
- _item, _err := io.ReadInt8(8)
- if _err != nil {
- return nil, errors.New("Error parsing 'destinationAddress' field " + _err.Error())
- }
- destinationAddress[curItem] = _item
- }
-
- // Simple Field (groupAddress)
- groupAddress, _groupAddressErr := io.ReadBit()
- if _groupAddressErr != nil {
- return nil, errors.New("Error parsing 'groupAddress' field " + _groupAddressErr.Error())
- }
-
- // Simple Field (hopCount)
- hopCount, _hopCountErr := io.ReadUint8(3)
- if _hopCountErr != nil {
- return nil, errors.New("Error parsing 'hopCount' field " + _hopCountErr.Error())
- }
-
- // Simple Field (apdu)
- apdu, _apduErr := ApduParse(io)
- if _apduErr != nil {
- return nil, errors.New("Error parsing 'apdu' field " + _apduErr.Error())
- }
-
- // Create a partially initialized instance
- _child := &LDataFrameData{
- SourceAddress: sourceAddress,
- DestinationAddress: destinationAddress,
- GroupAddress: groupAddress,
- HopCount: hopCount,
- Apdu: apdu,
- Parent: &LDataFrame{},
- }
- _child.Parent.Child = _child
- return _child.Parent, nil
-}
-
-func (m *LDataFrameData) Serialize(io utils.WriteBuffer) error {
- ser := func() error {
-
- // Simple Field (sourceAddress)
- _sourceAddressErr := m.SourceAddress.Serialize(io)
- if _sourceAddressErr != nil {
- return errors.New("Error serializing 'sourceAddress' field " + _sourceAddressErr.Error())
- }
-
- // Array Field (destinationAddress)
- if m.DestinationAddress != nil {
- for _, _element := range m.DestinationAddress {
- _elementErr := io.WriteInt8(8, _element)
- if _elementErr != nil {
- return errors.New("Error serializing 'destinationAddress' field " + _elementErr.Error())
- }
- }
- }
-
- // Simple Field (groupAddress)
- groupAddress := bool(m.GroupAddress)
- _groupAddressErr := io.WriteBit((groupAddress))
- if _groupAddressErr != nil {
- return errors.New("Error serializing 'groupAddress' field " + _groupAddressErr.Error())
- }
-
- // Simple Field (hopCount)
- hopCount := uint8(m.HopCount)
- _hopCountErr := io.WriteUint8(3, (hopCount))
- if _hopCountErr != nil {
- return errors.New("Error serializing 'hopCount' field " + _hopCountErr.Error())
- }
-
- // Simple Field (apdu)
- _apduErr := m.Apdu.Serialize(io)
- if _apduErr != nil {
- return errors.New("Error serializing 'apdu' field " + _apduErr.Error())
- }
-
- return nil
- }
- return m.Parent.SerializeParent(io, m, ser)
-}
-
-func (m *LDataFrameData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- var token xml.Token
- var err error
- token = start
- for {
- switch token.(type) {
- case xml.StartElement:
- tok := token.(xml.StartElement)
- switch tok.Name.Local {
- case "sourceAddress":
- var data *KnxAddress
- if err := d.DecodeElement(data, &tok); err != nil {
- return err
- }
- m.SourceAddress = data
- case "destinationAddress":
- var _encoded string
- if err := d.DecodeElement(&_encoded, &tok); err != nil {
- return err
- }
- _decoded := make([]byte, base64.StdEncoding.DecodedLen(len(_encoded)))
- _len, err := base64.StdEncoding.Decode(_decoded, []byte(_encoded))
- if err != nil {
- return err
- }
- m.DestinationAddress = utils.ByteArrayToInt8Array(_decoded[0:_len])
- case "groupAddress":
- var data bool
- if err := d.DecodeElement(&data, &tok); err != nil {
- return err
- }
- m.GroupAddress = data
- case "hopCount":
- var data uint8
- if err := d.DecodeElement(&data, &tok); err != nil {
- return err
- }
- m.HopCount = data
- case "apdu":
- var dt *Apdu
- if err := d.DecodeElement(&dt, &tok); err != nil {
- return err
- }
- m.Apdu = dt
- }
- }
- token, err = d.Token()
- if err != nil {
- if err == io.EOF {
- return nil
- }
- return err
- }
- }
-}
-
-func (m *LDataFrameData) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
- if err := e.EncodeElement(m.SourceAddress, xml.StartElement{Name: xml.Name{Local: "sourceAddress"}}); err != nil {
- return err
- }
- _encodedDestinationAddress := make([]byte, base64.StdEncoding.EncodedLen(len(m.DestinationAddress)))
- base64.StdEncoding.Encode(_encodedDestinationAddress, utils.Int8ArrayToByteArray(m.DestinationAddress))
- if err := e.EncodeElement(_encodedDestinationAddress, xml.StartElement{Name: xml.Name{Local: "destinationAddress"}}); err != nil {
- return err
- }
- if err := e.EncodeElement(m.GroupAddress, xml.StartElement{Name: xml.Name{Local: "groupAddress"}}); err != nil {
- return err
- }
- if err := e.EncodeElement(m.HopCount, xml.StartElement{Name: xml.Name{Local: "hopCount"}}); err != nil {
- return err
- }
- if err := e.EncodeElement(m.Apdu, xml.StartElement{Name: xml.Name{Local: "apdu"}}); err != nil {
- return err
- }
- return nil
-}
-
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFramePollingData.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LPollData.go
similarity index 80%
rename from plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFramePollingData.go
rename to plc4go/internal/plc4go/knxnetip/readwrite/model/LPollData.go
index a00c2c5..a886230 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFramePollingData.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LPollData.go
@@ -28,16 +28,16 @@ import (
)
// The data-structure of this message
-type LDataFramePollingData struct {
+type LPollData struct {
SourceAddress *KnxAddress
TargetAddress []int8
NumberExpectedPollData uint8
Parent *LDataFrame
- ILDataFramePollingData
+ ILPollData
}
// The corresponding interface
-type ILDataFramePollingData interface {
+type ILPollData interface {
LengthInBytes() uint16
LengthInBits() uint16
Serialize(io utils.WriteBuffer) error
@@ -47,58 +47,58 @@ type ILDataFramePollingData interface {
///////////////////////////////////////////////////////////
// Accessors for discriminator values.
///////////////////////////////////////////////////////////
-func (m *LDataFramePollingData) ExtendedFrame() bool {
- return false
+func (m *LPollData) NotAckFrame() bool {
+ return true
}
-func (m *LDataFramePollingData) Polling() bool {
+func (m *LPollData) Polling() bool {
return true
}
-func (m *LDataFramePollingData) InitializeParent(parent *LDataFrame, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
- m.Parent.Repeated = repeated
- m.Parent.NotAckFrame = notAckFrame
+func (m *LPollData) InitializeParent(parent *LDataFrame, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
+ m.Parent.FrameType = frameType
+ m.Parent.NotRepeated = notRepeated
m.Parent.Priority = priority
m.Parent.AcknowledgeRequested = acknowledgeRequested
m.Parent.ErrorFlag = errorFlag
}
-func NewLDataFramePollingData(sourceAddress *KnxAddress, targetAddress []int8, numberExpectedPollData uint8, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
- child := &LDataFramePollingData{
+func NewLPollData(sourceAddress *KnxAddress, targetAddress []int8, numberExpectedPollData uint8, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
+ child := &LPollData{
SourceAddress: sourceAddress,
TargetAddress: targetAddress,
NumberExpectedPollData: numberExpectedPollData,
- Parent: NewLDataFrame(repeated, notAckFrame, priority, acknowledgeRequested, errorFlag),
+ Parent: NewLDataFrame(frameType, notRepeated, priority, acknowledgeRequested, errorFlag),
}
child.Parent.Child = child
return child.Parent
}
-func CastLDataFramePollingData(structType interface{}) *LDataFramePollingData {
- castFunc := func(typ interface{}) *LDataFramePollingData {
- if casted, ok := typ.(LDataFramePollingData); ok {
+func CastLPollData(structType interface{}) *LPollData {
+ castFunc := func(typ interface{}) *LPollData {
+ if casted, ok := typ.(LPollData); ok {
return &casted
}
- if casted, ok := typ.(*LDataFramePollingData); ok {
+ if casted, ok := typ.(*LPollData); ok {
return casted
}
if casted, ok := typ.(LDataFrame); ok {
- return CastLDataFramePollingData(casted.Child)
+ return CastLPollData(casted.Child)
}
if casted, ok := typ.(*LDataFrame); ok {
- return CastLDataFramePollingData(casted.Child)
+ return CastLPollData(casted.Child)
}
return nil
}
return castFunc(structType)
}
-func (m *LDataFramePollingData) GetTypeName() string {
- return "LDataFramePollingData"
+func (m *LPollData) GetTypeName() string {
+ return "LPollData"
}
-func (m *LDataFramePollingData) LengthInBits() uint16 {
+func (m *LPollData) LengthInBits() uint16 {
lengthInBits := uint16(0)
// Simple field (sourceAddress)
@@ -118,11 +118,11 @@ func (m *LDataFramePollingData) LengthInBits() uint16 {
return lengthInBits
}
-func (m *LDataFramePollingData) LengthInBytes() uint16 {
+func (m *LPollData) LengthInBytes() uint16 {
return m.LengthInBits() / 8
}
-func LDataFramePollingDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
+func LPollDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
// Simple Field (sourceAddress)
sourceAddress, _sourceAddressErr := KnxAddressParse(io)
@@ -162,7 +162,7 @@ func LDataFramePollingDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
}
// Create a partially initialized instance
- _child := &LDataFramePollingData{
+ _child := &LPollData{
SourceAddress: sourceAddress,
TargetAddress: targetAddress,
NumberExpectedPollData: numberExpectedPollData,
@@ -172,7 +172,7 @@ func LDataFramePollingDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
return _child.Parent, nil
}
-func (m *LDataFramePollingData) Serialize(io utils.WriteBuffer) error {
+func (m *LPollData) Serialize(io utils.WriteBuffer) error {
ser := func() error {
// Simple Field (sourceAddress)
@@ -211,7 +211,7 @@ func (m *LDataFramePollingData) Serialize(io utils.WriteBuffer) error {
return m.Parent.SerializeParent(io, m, ser)
}
-func (m *LDataFramePollingData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+func (m *LPollData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var token xml.Token
var err error
token = start
@@ -255,7 +255,7 @@ func (m *LDataFramePollingData) UnmarshalXML(d *xml.Decoder, start xml.StartElem
}
}
-func (m *LDataFramePollingData) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (m *LPollData) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if err := e.EncodeElement(m.SourceAddress, xml.StartElement{Name: xml.Name{Local: "sourceAddress"}}); err != nil {
return err
}
diff --git a/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java b/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
index 1bf649b..0bcbb7f 100644
--- a/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
+++ b/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
@@ -344,10 +344,9 @@ public class KnxNetIpProtocolLogic extends Plc4xProtocolBase<KnxNetIpMessage> im
new TunnelingRequestDataBlock(communicationChannelId,
(short) sequenceCounter.getAndIncrement()),
new LDataReq((short) 0, new CEMIAdditionalInformation[0],
- new LDataFrameData(false, false, CEMIPriority.LOW, false, false,
- knxNetIpDriverContext.getClientKnxAddress(), destinationAddress, true,
- (byte) 6, new ApduDataContainer((short) ((data != null) ? data.length + 1 : 1), false, (byte) 0,
- new ApduDataGroupValueWrite(dataFirstByte, data)))
+ new LDataExtended(false, false, CEMIPriority.LOW, false, false,
+ true, (byte) 6, (byte) 0, knxNetIpDriverContext.getClientKnxAddress(), destinationAddress,
+ new ApduDataContainer(true, (byte) 0, new ApduDataGroupValueWrite(dataFirstByte, data)))
));
// Start a new request-transaction (Is ended in the response-handler)
@@ -400,20 +399,8 @@ public class KnxNetIpProtocolLogic extends Plc4xProtocolBase<KnxNetIpMessage> im
if (tunnelingRequest.getCemi() instanceof LDataInd) {
LDataInd dataInd = (LDataInd) tunnelingRequest.getCemi();
final LDataFrame lDataFrame = dataInd.getDataFrame();
- if (lDataFrame instanceof LDataFrameData) {
- LDataFrameData lDataFrameData = (LDataFrameData) lDataFrame;
- Apdu apdu = lDataFrameData.getApdu();
- if(apdu instanceof ApduDataContainer) {
- ApduDataContainer apduDataContainer = (ApduDataContainer) apdu;
- ApduData dataApdu = apduDataContainer.getDataApdu();
- if(dataApdu instanceof ApduDataGroupValueWrite) {
- ApduDataGroupValueWrite groupWrite = (ApduDataGroupValueWrite) dataApdu;
- processCemiData(lDataFrameData.getSourceAddress(), lDataFrameData.getDestinationAddress(),
- groupWrite.getDataFirstByte(),groupWrite.getData());
- }
- }
- } else if (lDataFrame instanceof LDataFrameDataExt) {
- LDataFrameDataExt lDataFrameDataExt = (LDataFrameDataExt) lDataFrame;
+ if (lDataFrame instanceof LDataExtended) {
+ LDataExtended lDataFrameDataExt = (LDataExtended) lDataFrame;
Apdu apdu = lDataFrameDataExt.getApdu();
if(apdu instanceof ApduDataContainer) {
ApduDataContainer apduDataContainer = (ApduDataContainer) apdu;
diff --git a/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec b/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec
index a189c46..534f310 100644
--- a/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec
+++ b/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec
@@ -347,50 +347,51 @@
// The CEMI part is described in the document "03_06_03 EMI_IMI v01.03.03 AS" Page 73
// "03_02_02 Communication Medium TP1 v01.02.02 AS" Page 27
[discriminatedType 'LDataFrame'
- [discriminator bit 'extendedFrame']
+ [simple bit 'frameType']
[discriminator bit 'polling']
- [simple bit 'repeated']
- [simple bit 'notAckFrame']
+ [simple bit 'notRepeated']
+ [discriminator bit 'notAckFrame']
[enum CEMIPriority 'priority']
[simple bit 'acknowledgeRequested']
[simple bit 'errorFlag']
- [typeSwitch 'extendedFrame','polling'
- // Page 28ff
- ['false','false' LDataFrameData
- [simple KnxAddress 'sourceAddress']
- [array int 8 'destinationAddress' count '2']
- [simple bit 'groupAddress']
- [simple uint 3 'hopCount']
- [simple Apdu 'apdu']
- ]
+ // "03_02_02 Communication Medium TP1 v01.02.02 AS" Page 27
+ [typeSwitch 'notAckFrame','polling'
// Page 29ff
- ['true','false' LDataFrameDataExt
+ // TODO: For some reason it doesn't seem to matter what the frame format is set to, it always seems to be an extended frame
+ // ['true','false','false' LDataExtended
+ ['true','false' LDataExtended
[simple bit 'groupAddress']
[simple uint 3 'hopCount']
[simple uint 4 'extendedFrameFormat']
[simple KnxAddress 'sourceAddress']
[array int 8 'destinationAddress' count '2']
- [simple Apdu 'apdu']
- ]
+ [implicit uint 8 'dataLength' 'apdu.lengthInBytes - 1']
+ [simple Apdu 'apdu' ['dataLength']]
+ ]
+ // Page 28ff
+ //['true','false','true' LDataStandard
+ // [simple KnxAddress 'sourceAddress']
+ // [array int 8 'destinationAddress' count '2']
+ // [simple bit 'groupAddress']
+ // [simple uint 3 'hopCount']
+ // [simple uint 4 'dataLength']
+ // [simple Apdu 'apdu' ['dataLength']]
+ //]
// Page 31ff
- ['true','true' LDataFramePollingData
+ //['true','true','true' LPollData
+ ['true','true' LPollData
[simple KnxAddress 'sourceAddress']
[array int 8 'targetAddress' count '2']
[reserved uint 4 '0x00']
[simple uint 6 'numberExpectedPollData']
]
- // Page 31ff
- ['false','true' LDataFramePollingData
- [simple KnxAddress 'sourceAddress']
- [array int 8 'targetAddress' count '2']
- [reserved uint 4 '0x00']
- [simple uint 6 'numberExpectedPollData']
+ ['false' LDataFrameACK
+ // TODO: Implement this
]
]
]
-[discriminatedType 'Apdu'
- [simple uint 8 'dataLength']
+[discriminatedType 'Apdu' [uint 8 'dataLength']
// 10_01 Logical Tag Extended v01.02.01 AS.pdf Page 74ff
[discriminator uint 1 'control']
[simple bit 'numbered']
@@ -771,8 +772,8 @@
// - 03_05_01 Resources v01.09.03 AS.pdf
// - 03_07_03 Standardized Identifier Tables v01.03.01 AS.pdf
// - 03_07_02 Datapoint Types v01.08.02 AS.pdf
-[dataIo 'KnxProperty' [KnxPropertyDataType 'propertyType', uint 8 'dataLength']
- [typeSwitch 'propertyType'
+[dataIo 'KnxProperty' [KnxPropertyDataType 'propertyType', uint 8 'dataLengthInBytes']
+ [typeSwitch 'propertyType','dataLengthInBytes'
['KnxPropertyDataType.PDT_CONTROL' BOOL
[reserved uint 7 '0x00']
[simple bit 'value']
@@ -786,6 +787,10 @@
['KnxPropertyDataType.PDT_INT' INT
[simple int 16 'value']
]
+ // On some systems this property is bigger
+ ['KnxPropertyDataType.PDT_UNSIGNED_INT','4' UDINT
+ [simple uint 32 'value']
+ ]
['KnxPropertyDataType.PDT_UNSIGNED_INT' UINT
[simple uint 16 'value']
]
@@ -966,8 +971,8 @@
//['KnxPropertyDataType.PDT_ESCAPE'
//]
// 'KnxPropertyDataType.PDT_VARIABLE_LENGTH' == Catch all
- [ List [uint 8 'dataLength']
- [array uint 8 'value' count 'dataLength']
+ [ List [uint 8 'dataLengthInBytes']
+ [array uint 8 'value' count 'dataLengthInBytes']
]
]
]
\ No newline at end of file
diff --git a/protocols/knxnetip/src/test/resources/protocols/knxnetip/ParserSerializerTestsuite.xml b/protocols/knxnetip/src/test/resources/protocols/knxnetip/ParserSerializerTestsuite.xml
index d20bbe2..9e954a3 100644
--- a/protocols/knxnetip/src/test/resources/protocols/knxnetip/ParserSerializerTestsuite.xml
+++ b/protocols/knxnetip/src/test/resources/protocols/knxnetip/ParserSerializerTestsuite.xml
@@ -414,9 +414,9 @@
<cemi className="org.apache.plc4x.java.knxnetip.readwrite.LDataInd">
<additionalInformationLength>0</additionalInformationLength>
<additionalInformation/>
- <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt">
- <repeated>true</repeated>
- <notAckFrame>true</notAckFrame>
+ <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataExtended">
+ <frameType>true</frameType>
+ <notRepeated>true</notRepeated>
<priority>LOW</priority>
<acknowledgeRequested>false</acknowledgeRequested>
<errorFlag>false</errorFlag>
@@ -430,7 +430,6 @@
</sourceAddress>
<destinationAddress>120C</destinationAddress>
<apdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduDataContainer">
- <dataLength>1</dataLength>
<numbered>false</numbered>
<counter>0</counter>
<dataApdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduDataGroupValueWrite">
@@ -475,9 +474,9 @@
</relativeTimestamp>
</additionalInformation>
</additionalInformation>
- <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt">
- <repeated>true</repeated>
- <notAckFrame>true</notAckFrame>
+ <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataExtended">
+ <frameType>true</frameType>
+ <notRepeated>true</notRepeated>
<priority>LOW</priority>
<acknowledgeRequested>false</acknowledgeRequested>
<errorFlag>false</errorFlag>
@@ -491,7 +490,6 @@
</sourceAddress>
<destinationAddress>0CE1</destinationAddress>
<apdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduControlContainer">
- <dataLength>0</dataLength>
<numbered>false</numbered>
<counter>0</counter>
<controlApdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduControlDisconnect"/>