You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2021/07/07 15:32:42 UTC

[plc4x] branch develop updated (2c746dc -> 35ff4b5)

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

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


    from 2c746dc  It turns out the KNX discovery used "SendRequest" for the search. This resulted in only one response being handled and the second response being discarded. Refactored the code to use a loop consuming the default-channel, hereby allowing multiple responses.
     new a0afb3c  plc4j: small cleanup and refactorings on BacNetIpProtocolLogic
     new 35ff4b5  plc4go: initial bacnet draft

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 plc4go/internal/plc4go/bacnetip/Connection.go      |  77 ++++++++
 plc4go/internal/plc4go/bacnetip/Driver.go          |  81 +++++++-
 plc4go/internal/plc4go/bacnetip/Field.go           |  92 +++++++++
 .../plc4go/{eip => bacnetip}/FieldHandler.go       |  30 +--
 .../plc4go/{s7 => bacnetip}/MessageCodec.go        |  16 +-
 plc4go/internal/plc4go/bacnetip/Subscriber.go      |  70 +++++++
 .../plc4go/{modbus => bacnetip}/ValueHandler.go    |   2 +-
 .../bacnetip/protocol/BacNetIpProtocolLogic.java   | 207 +++++++++++----------
 8 files changed, 453 insertions(+), 122 deletions(-)
 create mode 100644 plc4go/internal/plc4go/bacnetip/Connection.go
 create mode 100644 plc4go/internal/plc4go/bacnetip/Field.go
 copy plc4go/internal/plc4go/{eip => bacnetip}/FieldHandler.go (65%)
 copy plc4go/internal/plc4go/{s7 => bacnetip}/MessageCodec.go (89%)
 create mode 100644 plc4go/internal/plc4go/bacnetip/Subscriber.go
 copy plc4go/internal/plc4go/{modbus => bacnetip}/ValueHandler.go (98%)

[plc4x] 02/02: plc4go: initial bacnet draft

Posted by sr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 35ff4b5b6803b4eeb449af3169c4bcdbfbbf2add
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Wed Jul 7 17:32:19 2021 +0200

    plc4go: initial bacnet draft
---
 plc4go/internal/plc4go/bacnetip/Connection.go      |  77 ++++++++++++++++
 plc4go/internal/plc4go/bacnetip/Driver.go          |  81 +++++++++++++++-
 plc4go/internal/plc4go/bacnetip/Field.go           |  92 +++++++++++++++++++
 plc4go/internal/plc4go/bacnetip/FieldHandler.go    |  64 +++++++++++++
 plc4go/internal/plc4go/bacnetip/MessageCodec.go    | 102 +++++++++++++++++++++
 plc4go/internal/plc4go/bacnetip/Subscriber.go      |  70 ++++++++++++++
 .../plc4go/bacnetip/{Driver.go => ValueHandler.go} |  12 ++-
 7 files changed, 493 insertions(+), 5 deletions(-)

diff --git a/plc4go/internal/plc4go/bacnetip/Connection.go b/plc4go/internal/plc4go/bacnetip/Connection.go
new file mode 100644
index 0000000..ace2a38
--- /dev/null
+++ b/plc4go/internal/plc4go/bacnetip/Connection.go
@@ -0,0 +1,77 @@
+//
+// 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 bacnetip
+
+import (
+	"fmt"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/default"
+	internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+	"github.com/apache/plc4x/plc4go/pkg/plc4go"
+	"github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+	"github.com/rs/zerolog/log"
+)
+
+type Connection struct {
+	_default.DefaultConnection
+	messageCodec spi.MessageCodec
+	subscribers  []*Subscriber
+}
+
+func NewConnection(messageCodec spi.MessageCodec, fieldHandler spi.PlcFieldHandler) *Connection {
+	connection := &Connection{
+		messageCodec: messageCodec,
+	}
+	connection.DefaultConnection = _default.NewDefaultConnection(connection,
+		_default.WithPlcFieldHandler(fieldHandler),
+		_default.WithPlcValueHandler(NewValueHandler()),
+	)
+	return connection
+}
+
+func (c *Connection) GetConnection() plc4go.PlcConnection {
+	return c
+}
+
+func (c *Connection) GetMessageCodec() spi.MessageCodec {
+	return c.messageCodec
+}
+
+func (c *Connection) SubscriptionRequestBuilder() model.PlcSubscriptionRequestBuilder {
+	return internalModel.NewDefaultPlcSubscriptionRequestBuilder(c.GetPlcFieldHandler(), c.GetPlcValueHandler(), NewSubscriber(c))
+}
+
+func (c *Connection) UnsubscriptionRequestBuilder() model.PlcUnsubscriptionRequestBuilder {
+	panic("Not implementec yet. (at least as a default)")
+}
+
+func (m *Connection) addSubscriber(subscriber *Subscriber) {
+	for _, sub := range m.subscribers {
+		if sub == subscriber {
+			log.Debug().Msgf("Subscriber %v already added", subscriber)
+			return
+		}
+	}
+	m.subscribers = append(m.subscribers, subscriber)
+}
+
+func (c *Connection) String() string {
+	return fmt.Sprintf("bacnetip.Connection")
+}
diff --git a/plc4go/internal/plc4go/bacnetip/Driver.go b/plc4go/internal/plc4go/bacnetip/Driver.go
index 3620e61..635b212 100644
--- a/plc4go/internal/plc4go/bacnetip/Driver.go
+++ b/plc4go/internal/plc4go/bacnetip/Driver.go
@@ -19,8 +19,85 @@
 
 package bacnetip
 
-import "github.com/apache/plc4x/plc4go/pkg/plc4go"
+import (
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports"
+	"github.com/apache/plc4x/plc4go/pkg/plc4go"
+	apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+	"github.com/pkg/errors"
+	"github.com/rs/zerolog/log"
+	"net/url"
+)
+
+type Driver struct {
+	fieldHandler            spi.PlcFieldHandler
+	awaitSetupComplete      bool
+	awaitDisconnectComplete bool
+}
 
 func NewDriver() plc4go.PlcDriver {
-	return nil
+	return &Driver{
+		fieldHandler:            NewFieldHandler(),
+		awaitSetupComplete:      true,
+		awaitDisconnectComplete: true,
+	}
+}
+
+func (m *Driver) GetProtocolCode() string {
+	return "bacnet-ip"
+}
+
+func (m *Driver) GetProtocolName() string {
+	return "BACnet/IP"
+}
+
+func (m *Driver) GetDefaultTransport() string {
+	return "udp"
+}
+
+func (m *Driver) CheckQuery(query string) error {
+	_, err := m.fieldHandler.ParseQuery(query)
+	return err
+}
+
+func (m *Driver) GetConnection(transportUrl url.URL, transports map[string]transports.Transport, options map[string][]string) <-chan plc4go.PlcConnectionConnectResult {
+	log.Debug().Stringer("transportUrl", &transportUrl).Msgf("Get connection for transport url with %d transport(s) and %d option(s)", len(transports), len(options))
+	// Get an the transport specified in the url
+	transport, ok := transports[transportUrl.Scheme]
+	if !ok {
+		log.Error().Stringer("transportUrl", &transportUrl).Msgf("We couldn't find a transport for scheme %s", transportUrl.Scheme)
+		ch := make(chan plc4go.PlcConnectionConnectResult)
+		go func() {
+			ch <- plc4go.NewPlcConnectionConnectResult(nil, errors.Errorf("couldn't find transport for given transport url %#v", transportUrl))
+		}()
+		return ch
+	}
+	// Provide a default-port to the transport, which is used, if the user doesn't provide on in the connection string.
+	options["defaultUdpPort"] = []string{"47808"}
+	// Have the transport create a new transport-instance.
+	transportInstance, err := transport.CreateTransportInstance(transportUrl, options)
+	if err != nil {
+		log.Error().Stringer("transportUrl", &transportUrl).Msgf("We couldn't create a transport instance for port %#v", options["defaultUdpPort"])
+		ch := make(chan plc4go.PlcConnectionConnectResult)
+		go func() {
+			ch <- plc4go.NewPlcConnectionConnectResult(nil, errors.New("couldn't initialize transport configuration for given transport url "+transportUrl.String()))
+		}()
+		return ch
+	}
+
+	codec := NewMessageCodec(transportInstance)
+	log.Debug().Msgf("working with codec %#v", codec)
+
+	// Create the new connection
+	connection := NewConnection(codec, m.fieldHandler)
+	log.Debug().Msg("created connection, connecting now")
+	return connection.Connect()
+}
+
+func (m *Driver) SupportsDiscovery() bool {
+	return false
+}
+
+func (m *Driver) Discover(callback func(event apiModel.PlcDiscoveryEvent)) error {
+	panic("implement me")
 }
diff --git a/plc4go/internal/plc4go/bacnetip/Field.go b/plc4go/internal/plc4go/bacnetip/Field.go
new file mode 100644
index 0000000..fe9a4bd
--- /dev/null
+++ b/plc4go/internal/plc4go/bacnetip/Field.go
@@ -0,0 +1,92 @@
+//
+// 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 bacnetip
+
+import (
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+	"strconv"
+)
+
+type BacNetPlcField interface {
+	GetDeviceIdentifier() uint32
+	GetObjectType() uint16
+	GetObjectInstance() uint32
+}
+
+type PlcField struct {
+	DeviceIdentifier uint32
+	ObjectType       uint16
+	ObjectInstance   uint32
+}
+
+func (m PlcField) GetAddressString() string {
+	return strconv.Itoa(int(m.DeviceIdentifier))
+}
+
+func (m PlcField) GetTypeName() string {
+	return strconv.Itoa(int(m.ObjectType))
+}
+
+func (m PlcField) GetQuantity() uint16 {
+	return 1
+}
+
+func NewField(deviceIdentifier uint32, objectType uint16, objectInstance uint32) PlcField {
+	return PlcField{
+		DeviceIdentifier: deviceIdentifier,
+		ObjectType:       objectType,
+		ObjectInstance:   objectInstance,
+	}
+}
+
+func (m PlcField) GetDeviceIdentifier() uint32 {
+	return m.DeviceIdentifier
+}
+
+func (m PlcField) GetObjectType() uint16 {
+	return m.ObjectType
+}
+
+func (m PlcField) GetObjectInstance() uint32 {
+	return m.ObjectInstance
+}
+
+func (m PlcField) Serialize(writeBuffer utils.WriteBuffer) error {
+	if err := writeBuffer.PushContext("BacNetPlcField"); err != nil {
+		return err
+	}
+
+	if err := writeBuffer.WriteUint32("deviceIdentifier", 32, m.DeviceIdentifier); err != nil {
+		return err
+	}
+
+	if err := writeBuffer.WriteUint16("objectType", 16, m.ObjectType); err != nil {
+		return err
+	}
+
+	if err := writeBuffer.WriteUint32("objectInstance", 32, m.ObjectInstance); err != nil {
+		return err
+	}
+
+	if err := writeBuffer.PopContext("BacNetPlcField"); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/plc4go/internal/plc4go/bacnetip/FieldHandler.go b/plc4go/internal/plc4go/bacnetip/FieldHandler.go
new file mode 100644
index 0000000..2a32db4
--- /dev/null
+++ b/plc4go/internal/plc4go/bacnetip/FieldHandler.go
@@ -0,0 +1,64 @@
+//
+// 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 bacnetip
+
+import (
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+	"github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+	"github.com/pkg/errors"
+	"regexp"
+	"strconv"
+)
+
+type FieldHandler struct {
+	addressPattern *regexp.Regexp
+}
+
+func NewFieldHandler() FieldHandler {
+	return FieldHandler{
+		addressPattern: regexp.MustCompile(`^(?P<deviceIdentifier>(\d|\*))/(?P<objectType>(\d|\*))/(?P<objectInstance>(\d|\*))`),
+	}
+}
+
+const (
+	DEVICE_IDENTIFIER = "deviceIdentifier"
+	OBJECT_TYPE       = "objectType"
+	OBJECT_INSTANCE   = "objectInstance"
+)
+
+func (m FieldHandler) ParseQuery(query string) (model.PlcField, error) {
+	if match := utils.GetSubgroupMatches(m.addressPattern, query); match != nil {
+		deviceIdentifier, err := strconv.ParseUint(match[DEVICE_IDENTIFIER], 10, 32)
+		if err != nil {
+			return nil, err
+		}
+		objectType, err := strconv.ParseUint(match[OBJECT_TYPE], 10, 16)
+		if err != nil {
+			return nil, err
+		}
+		objectInstance, err := strconv.ParseUint(match[OBJECT_INSTANCE], 10, 32)
+		if err != nil {
+			return nil, err
+		}
+
+		return NewField(uint32(deviceIdentifier), uint16(objectType), uint32(objectInstance)), nil
+	}
+	return nil, errors.Errorf("Unable to parse %s", query)
+}
diff --git a/plc4go/internal/plc4go/bacnetip/MessageCodec.go b/plc4go/internal/plc4go/bacnetip/MessageCodec.go
new file mode 100644
index 0000000..1344ca4
--- /dev/null
+++ b/plc4go/internal/plc4go/bacnetip/MessageCodec.go
@@ -0,0 +1,102 @@
+//
+// 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 bacnetip
+
+import (
+	"github.com/apache/plc4x/plc4go/internal/plc4go/bacnetip/readwrite/model"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/default"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+	"github.com/pkg/errors"
+	"github.com/rs/zerolog/log"
+)
+
+type MessageCodec struct {
+	_default.DefaultCodec
+}
+
+func NewMessageCodec(transportInstance transports.TransportInstance) *MessageCodec {
+	codec := &MessageCodec{}
+	codec.DefaultCodec = _default.NewDefaultCodec(codec, transportInstance)
+	return codec
+}
+
+func (m *MessageCodec) GetCodec() spi.MessageCodec {
+	return m
+}
+
+func (m *MessageCodec) Send(message interface{}) error {
+	log.Trace().Msg("Sending message")
+	// Cast the message to the correct type of struct
+	bvlcPacket := model.CastBVLC(message)
+	// Serialize the request
+	wb := utils.NewWriteBufferByteBased()
+	err := bvlcPacket.Serialize(wb)
+	if err != nil {
+		return errors.Wrap(err, "error serializing request")
+	}
+
+	// Send it to the PLC
+	err = m.GetTransportInstance().Write(wb.GetBytes())
+	if err != nil {
+		return errors.Wrap(err, "error sending request")
+	}
+	return nil
+}
+
+func (m *MessageCodec) Receive() (interface{}, error) {
+	log.Trace().Msg("receiving")
+	// We need at least 6 bytes in order to know how big the packet is in total
+	if num, err := m.GetTransportInstance().GetNumReadableBytes(); (err == nil) && (num >= 4) {
+		log.Debug().Msgf("we got %d readable bytes", num)
+		data, err := m.GetTransportInstance().PeekReadableBytes(4)
+		if err != nil {
+			log.Warn().Err(err).Msg("error peeking")
+			// TODO: Possibly clean up ...
+			return nil, nil
+		}
+		//Second byte for the size and then add the header size 24
+		packetSize := uint32((uint16(data[3]) << 8) + uint16(data[2]))
+		if num < packetSize {
+			log.Debug().Msgf("Not enough bytes. Got: %d Need: %d\n", num, packetSize)
+			return nil, nil
+		}
+		data, err = m.GetTransportInstance().Read(packetSize)
+		if err != nil {
+			log.Debug().Err(err).Msg("Error reading")
+			// TODO: Possibly clean up ...
+			return nil, nil
+		}
+		rb := utils.NewReadBufferByteBased(data)
+		bvlcPacket, err := model.BVLCParse(rb)
+		if err != nil {
+			log.Warn().Err(err).Msg("error parsing")
+			// TODO: Possibly clean up ...
+			return nil, nil
+		}
+		return bvlcPacket, nil
+	} else if err != nil {
+		log.Warn().Err(err).Msg("Got error reading")
+		return nil, nil
+	}
+	// TODO: maybe we return here a not enough error error
+	return nil, nil
+}
diff --git a/plc4go/internal/plc4go/bacnetip/Subscriber.go b/plc4go/internal/plc4go/bacnetip/Subscriber.go
new file mode 100644
index 0000000..8e9d217
--- /dev/null
+++ b/plc4go/internal/plc4go/bacnetip/Subscriber.go
@@ -0,0 +1,70 @@
+//
+// 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 bacnetip
+
+import (
+	internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+	apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+)
+
+type Subscriber struct {
+	connection           *Connection
+	subscriptionRequests []internalModel.DefaultPlcSubscriptionRequest
+}
+
+func NewSubscriber(connection *Connection) *Subscriber {
+	return &Subscriber{
+		connection:           connection,
+		subscriptionRequests: []internalModel.DefaultPlcSubscriptionRequest{},
+	}
+}
+
+func (m *Subscriber) Subscribe(subscriptionRequest apiModel.PlcSubscriptionRequest) <-chan apiModel.PlcSubscriptionRequestResult {
+	result := make(chan apiModel.PlcSubscriptionRequestResult)
+	go func() {
+		// Add this subscriber to the connection.
+		m.connection.addSubscriber(m)
+
+		// Save the subscription request
+		m.subscriptionRequests = append(m.subscriptionRequests, subscriptionRequest.(internalModel.DefaultPlcSubscriptionRequest))
+
+		// Just populate all requests with an OK
+		responseCodes := map[string]apiModel.PlcResponseCode{}
+		for _, fieldName := range subscriptionRequest.GetFieldNames() {
+			responseCodes[fieldName] = apiModel.PlcResponseCode_OK
+		}
+
+		result <- apiModel.PlcSubscriptionRequestResult{
+			Request:  subscriptionRequest,
+			Response: internalModel.NewDefaultPlcSubscriptionResponse(subscriptionRequest, responseCodes),
+			Err:      nil,
+		}
+	}()
+	return result
+}
+
+func (m *Subscriber) Unsubscribe(unsubscriptionRequest apiModel.PlcUnsubscriptionRequest) <-chan apiModel.PlcUnsubscriptionRequestResult {
+	result := make(chan apiModel.PlcUnsubscriptionRequestResult)
+
+	// TODO: As soon as we establish a connection, we start getting data...
+	// subscriptions are more an internal handling of which values to pass where.
+
+	return result
+}
diff --git a/plc4go/internal/plc4go/bacnetip/Driver.go b/plc4go/internal/plc4go/bacnetip/ValueHandler.go
similarity index 81%
copy from plc4go/internal/plc4go/bacnetip/Driver.go
copy to plc4go/internal/plc4go/bacnetip/ValueHandler.go
index 3620e61..982bc53 100644
--- a/plc4go/internal/plc4go/bacnetip/Driver.go
+++ b/plc4go/internal/plc4go/bacnetip/ValueHandler.go
@@ -19,8 +19,14 @@
 
 package bacnetip
 
-import "github.com/apache/plc4x/plc4go/pkg/plc4go"
+import (
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
+)
 
-func NewDriver() plc4go.PlcDriver {
-	return nil
+type ValueHandler struct {
+	values.IEC61131ValueHandler
+}
+
+func NewValueHandler() ValueHandler {
+	return ValueHandler{}
 }

[plc4x] 01/02: plc4j: small cleanup and refactorings on BacNetIpProtocolLogic

Posted by sr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a0afb3cfaf23abf99ca17c74efdb8a30c7fcd01a
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Wed Jul 7 17:24:08 2021 +0200

    plc4j: small cleanup and refactorings on BacNetIpProtocolLogic
---
 .../bacnetip/protocol/BacNetIpProtocolLogic.java   | 207 +++++++++++----------
 1 file changed, 107 insertions(+), 100 deletions(-)

diff --git a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/protocol/BacNetIpProtocolLogic.java b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/protocol/BacNetIpProtocolLogic.java
index cc7da50..3148ec4 100644
--- a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/protocol/BacNetIpProtocolLogic.java
+++ b/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/protocol/BacNetIpProtocolLogic.java
@@ -61,36 +61,32 @@ public class BacNetIpProtocolLogic extends Plc4xProtocolBase<BVLC> implements Ha
 
     private EdeModel edeModel;
 
-    private Map<Integer, Consumer<PlcSubscriptionEvent>> consumerIdMap = new ConcurrentHashMap<>();
+    private final Map<Integer, Consumer<PlcSubscriptionEvent>> consumerIdMap = new ConcurrentHashMap<>();
 
     @Override
     public void setConfiguration(BacNetIpConfiguration configuration) {
         if (configuration.getEdeFilePath() != null) {
             File edeFile = new File(configuration.getEdeFilePath());
-            if (edeFile.exists() && edeFile.isFile()) {
-                edeModel = new EdeParser().parseFile(edeFile);
-            } else {
+            if (!edeFile.exists() || !edeFile.isFile()) {
                 throw new PlcRuntimeException(String.format(
                     "File specified with 'ede-file-path' does not exist or is not a file: '%s'",
                     configuration.getEdeFilePath()));
             }
-        }
-        else if (configuration.getEdeDirectoryPath() != null) {
+            edeModel = new EdeParser().parseFile(edeFile);
+        } else if (configuration.getEdeDirectoryPath() != null) {
             File edeDirectory = new File(configuration.getEdeDirectoryPath());
-            if (edeDirectory.exists() && edeDirectory.isDirectory()) {
-                edeModel = new EdeParser().parseDirectory(edeDirectory);
-            } else {
+            if (!edeDirectory.exists() || !edeDirectory.isDirectory()) {
                 throw new PlcRuntimeException(String.format(
                     "File specified with 'ede-directory-path' does not exist or is not a directory: '%s'",
                     configuration.getEdeDirectoryPath()));
             }
-
+            edeModel = new EdeParser().parseDirectory(edeDirectory);
         }
     }
 
     @Override
     public void onConnect(ConversationContext<BVLC> context) {
-        if(context.isPassive()) {
+        if (context.isPassive()) {
             context.fireConnected();
         } else {
             throw new PlcRuntimeException("Active connections not yet supported");
@@ -105,7 +101,7 @@ public class BacNetIpProtocolLogic extends Plc4xProtocolBase<BVLC> implements Ha
     @Override
     protected void decode(ConversationContext<BVLC> context, BVLC msg) throws Exception {
         NPDU npdu = null;
-        if(msg instanceof BVLCOriginalUnicastNPDU) {
+        if (msg instanceof BVLCOriginalUnicastNPDU) {
             BVLCOriginalUnicastNPDU bvlcOriginalUnicastNPDU = (BVLCOriginalUnicastNPDU) msg;
             npdu = bvlcOriginalUnicastNPDU.getNpdu();
         } else if (msg instanceof BVLCForwardedNPDU) {
@@ -116,99 +112,110 @@ public class BacNetIpProtocolLogic extends Plc4xProtocolBase<BVLC> implements Ha
             npdu = bvlcOriginalBroadcastNPDU.getNpdu();
         }
 
-        if(npdu != null) {
-            if(npdu.getApdu() instanceof APDUConfirmedRequest) {
-                APDUConfirmedRequest apduConfirmedRequest = (APDUConfirmedRequest) npdu.getApdu();
-                final BACnetConfirmedServiceRequest serviceRequest = apduConfirmedRequest.getServiceRequest();
-                // A value change subscription event.
-                if(serviceRequest instanceof BACnetConfirmedServiceRequestConfirmedCOVNotification) {
-                    BACnetConfirmedServiceRequestConfirmedCOVNotification valueChange =
-                        (BACnetConfirmedServiceRequestConfirmedCOVNotification) serviceRequest;
-
-                    long deviceIdentifier = valueChange.getMonitoredObjectInstanceNumber();
-                    int objectType = valueChange.getIssueConfirmedNotificationsType();
-                    long objectInstance = valueChange.getIssueConfirmedNotificationsInstanceNumber();
-                    BacNetIpField curField = new BacNetIpField(deviceIdentifier, objectType, objectInstance);
-
-                    // The actual value change is in the notifications ... iterate throught them to get it.
-                    for (BACnetTagWithContent notification : valueChange.getNotifications()) {
-                        // These are value change notifications. Ignore the rest.
-                        if(notification.getPropertyIdentifier()[0] == (short) 0x55) {
-                            final BACnetTag baCnetTag = notification.getValue();
-
-                            // Initialize an enriched version of the PlcStruct.
-                            final Map<String, PlcValue> enrichedPlcValue = new HashMap<>();
-                            enrichedPlcValue.put("deviceIdentifier", new PlcUDINT(deviceIdentifier));
-                            enrichedPlcValue.put("objectType", new PlcDINT(objectType));
-                            enrichedPlcValue.put("objectInstance", new PlcUDINT(objectInstance));
-                            enrichedPlcValue.put("address", new PlcSTRING(toString(curField)));
-
-                            // From the original BACNet tag
-                            enrichedPlcValue.put("typeOrTagNumber", IEC61131ValueHandler.of(baCnetTag.getTypeOrTagNumber()));
-                            enrichedPlcValue.put("lengthValueType", IEC61131ValueHandler.of(baCnetTag.getLengthValueType()));
-                            if(baCnetTag.getExtTagNumber() != null) {
-                                enrichedPlcValue.put("extTagNumber", IEC61131ValueHandler.of(baCnetTag.getExtTagNumber()));
-                            } else {
-                                enrichedPlcValue.put("extTagNumber", new PlcNull());
-                            }
-                            if(baCnetTag.getExtLength() != null) {
-                                enrichedPlcValue.put("extLength", IEC61131ValueHandler.of(baCnetTag.getExtLength()));
-                            } else {
-                                enrichedPlcValue.put("extLength", new PlcNull());
-                            }
-
-                            // Use the information in the edeModel to enrich the information.
-                            if(edeModel != null) {
-                                final Datapoint datapoint = edeModel.getDatapoint(curField);
-                                if(datapoint != null) {
-                                    // Add all the attributes from the ede file.
-                                    enrichedPlcValue.putAll(datapoint.toPlcValues());
-                                }
-                            }
-                            // Send out the enriched event.
-                            publishEvent(curField, new PlcStruct(enrichedPlcValue));
+        if (npdu == null) {
+            LOGGER.warn("Ummapped BVLC {}", msg);
+            return;
+        }
+
+        if (npdu.getApdu() instanceof APDUConfirmedRequest) {
+            APDUConfirmedRequest apduConfirmedRequest = (APDUConfirmedRequest) npdu.getApdu();
+            decodeConfirmedRequest(apduConfirmedRequest);
+        } else if (npdu.getApdu() instanceof APDUUnconfirmedRequest) {
+            APDUUnconfirmedRequest unconfirmedRequest = (APDUUnconfirmedRequest) npdu.getApdu();
+            decodeUnconfirmedRequest(unconfirmedRequest);
+        } else if (npdu.getApdu() instanceof APDUError) {
+            APDUError apduError = (APDUError) npdu.getApdu();
+        } else if (npdu.getApdu() instanceof APDUSimpleAck) {
+            // Ignore this ...
+        } else if (npdu.getApdu() instanceof APDUComplexAck) {
+            // Ignore this ...
+        } else if ((npdu.getApdu() == null) && (npdu.getNlm() != null)) {
+            // "Who is router?" & "I am router" messages.
+            // Ignore this ...
+        } else {
+            LOGGER.debug(String.format("Unexpected NPDU type: %s", npdu.getClass().getName()));
+        }
+    }
+
+    private void decodeConfirmedRequest(APDUConfirmedRequest apduConfirmedRequest) {
+        final BACnetConfirmedServiceRequest serviceRequest = apduConfirmedRequest.getServiceRequest();
+        // A value change subscription event.
+        if (serviceRequest instanceof BACnetConfirmedServiceRequestConfirmedCOVNotification) {
+            BACnetConfirmedServiceRequestConfirmedCOVNotification valueChange =
+                (BACnetConfirmedServiceRequestConfirmedCOVNotification) serviceRequest;
+
+            long deviceIdentifier = valueChange.getMonitoredObjectInstanceNumber();
+            int objectType = valueChange.getIssueConfirmedNotificationsType();
+            long objectInstance = valueChange.getIssueConfirmedNotificationsInstanceNumber();
+            BacNetIpField curField = new BacNetIpField(deviceIdentifier, objectType, objectInstance);
+
+            // The actual value change is in the notifications ... iterate throught them to get it.
+            for (BACnetTagWithContent notification : valueChange.getNotifications()) {
+                // These are value change notifications. Ignore the rest.
+                if (notification.getPropertyIdentifier()[0] == (short) 0x55) {
+                    final BACnetTag baCnetTag = notification.getValue();
+
+                    // Initialize an enriched version of the PlcStruct.
+                    final Map<String, PlcValue> enrichedPlcValue = new HashMap<>();
+                    enrichedPlcValue.put("deviceIdentifier", new PlcUDINT(deviceIdentifier));
+                    enrichedPlcValue.put("objectType", new PlcDINT(objectType));
+                    enrichedPlcValue.put("objectInstance", new PlcUDINT(objectInstance));
+                    enrichedPlcValue.put("address", new PlcSTRING(toString(curField)));
+
+                    // From the original BACNet tag
+                    enrichedPlcValue.put("typeOrTagNumber", IEC61131ValueHandler.of(baCnetTag.getTypeOrTagNumber()));
+                    enrichedPlcValue.put("lengthValueType", IEC61131ValueHandler.of(baCnetTag.getLengthValueType()));
+                    if (baCnetTag.getExtTagNumber() != null) {
+                        enrichedPlcValue.put("extTagNumber", IEC61131ValueHandler.of(baCnetTag.getExtTagNumber()));
+                    } else {
+                        enrichedPlcValue.put("extTagNumber", new PlcNull());
+                    }
+                    if (baCnetTag.getExtLength() != null) {
+                        enrichedPlcValue.put("extLength", IEC61131ValueHandler.of(baCnetTag.getExtLength()));
+                    } else {
+                        enrichedPlcValue.put("extLength", new PlcNull());
+                    }
+
+                    // Use the information in the edeModel to enrich the information.
+                    if (edeModel != null) {
+                        final Datapoint datapoint = edeModel.getDatapoint(curField);
+                        if (datapoint != null) {
+                            // Add all the attributes from the ede file.
+                            enrichedPlcValue.putAll(datapoint.toPlcValues());
                         }
                     }
+                    // Send out the enriched event.
+                    publishEvent(curField, new PlcStruct(enrichedPlcValue));
                 }
-                // Someone read a value.
-                else if(serviceRequest instanceof BACnetConfirmedServiceRequestReadProperty) {
-                    // Ignore this ...
-                }
-                // Someone wrote a value.
-                else if(serviceRequest instanceof BACnetConfirmedServiceRequestWriteProperty) {
-                    // Ignore this ...
-                } else if(serviceRequest instanceof BACnetConfirmedServiceRequestSubscribeCOV) {
-                    // Ignore this ...
-                } else {
-                    LOGGER.debug(String.format("Unexpected ConfirmedServiceRequest type: %s", serviceRequest.getClass().getName()));
-                }
-            } else if(npdu.getApdu() instanceof APDUUnconfirmedRequest) {
-                APDUUnconfirmedRequest unconfirmedRequest = (APDUUnconfirmedRequest) npdu.getApdu();
-                final BACnetUnconfirmedServiceRequest serviceRequest = unconfirmedRequest.getServiceRequest();
-                if(serviceRequest instanceof BACnetUnconfirmedServiceRequestWhoHas) {
-                    // Ignore this ...
-                } else if(serviceRequest instanceof BACnetUnconfirmedServiceRequestWhoIs){
-                    // Ignore this ...
-                } else if(serviceRequest instanceof BACnetUnconfirmedServiceRequestIAm){
-                    // Ignore this ...
-                } else if(serviceRequest instanceof BACnetUnconfirmedServiceRequestUnconfirmedPrivateTransfer){
-                    // Ignore this ...
-                } else {
-                    LOGGER.debug(String.format("Unexpected UnconfirmedServiceRequest type: %s", serviceRequest.getClass().getName()));
-                }
-            } else if(npdu.getApdu() instanceof APDUError) {
-                APDUError apduError = (APDUError) npdu.getApdu();
-            } else if(npdu.getApdu() instanceof APDUSimpleAck) {
-                // Ignore this ...
-            } else if(npdu.getApdu() instanceof APDUComplexAck) {
-                // Ignore this ...
-            } else if((npdu.getApdu() == null) && (npdu.getNlm() != null)){
-                // "Who is router?" & "I am router" messages.
-                // Ignore this ...
-            } else {
-                LOGGER.debug(String.format("Unexpected NPDU type: %s", npdu.getClass().getName()));
             }
         }
+        // Someone read a value.
+        else if (serviceRequest instanceof BACnetConfirmedServiceRequestReadProperty) {
+            // Ignore this ...
+        }
+        // Someone wrote a value.
+        else if (serviceRequest instanceof BACnetConfirmedServiceRequestWriteProperty) {
+            // Ignore this ...
+        } else if (serviceRequest instanceof BACnetConfirmedServiceRequestSubscribeCOV) {
+            // Ignore this ...
+        } else {
+            LOGGER.debug(String.format("Unexpected ConfirmedServiceRequest type: %s", serviceRequest.getClass().getName()));
+        }
+    }
+
+    private void decodeUnconfirmedRequest(APDUUnconfirmedRequest unconfirmedRequest) {
+        final BACnetUnconfirmedServiceRequest serviceRequest = unconfirmedRequest.getServiceRequest();
+        if (serviceRequest instanceof BACnetUnconfirmedServiceRequestWhoHas) {
+            // Ignore this ...
+        } else if (serviceRequest instanceof BACnetUnconfirmedServiceRequestWhoIs) {
+            // Ignore this ...
+        } else if (serviceRequest instanceof BACnetUnconfirmedServiceRequestIAm) {
+            // Ignore this ...
+        } else if (serviceRequest instanceof BACnetUnconfirmedServiceRequestUnconfirmedPrivateTransfer) {
+            // Ignore this ...
+        } else {
+            LOGGER.debug(String.format("Unexpected UnconfirmedServiceRequest type: %s", serviceRequest.getClass().getName()));
+        }
     }
 
     @Override