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 2022/08/12 13:44:38 UTC

[plc4x] branch develop updated (439bc848e -> dd11ad341)

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 439bc848e chore(plc4go): renamed init.go files to doc.go
     new 1a041e895 feat(plc4go): added net util for finding IPs
     new cbce5d212 refactor(cbus): small adjustments on reset and optional alpha on direct command
     new 840b32470 feat(plc4go/cbus): implemented Discovery
     new 191162deb fix(plc4go/cbus): fixed write
     new dd11ad341 feat(plc4go/bacnet): updated vendors

The 5 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/cbus/Connection.go                 |   6 +-
 plc4go/internal/cbus/Discoverer.go                 | 219 ++++++++++++++++++
 plc4go/internal/cbus/Driver.go                     |  10 +
 plc4go/internal/cbus/Reader.go                     |   6 +-
 .../bacnetip/readwrite/model/BACnetVendorId.go     |  32 +++
 .../readwrite/model/RequestDirectCommandAccess.go  |  58 ++++-
 .../protocols/cbus/readwrite/model/RequestReset.go |  54 +++--
 plc4go/spi/utils/net.go                            | 247 +++++++++++++++++++++
 .../src/main/resources/protocols/cbus/c-bus.mspec  |   5 +-
 9 files changed, 614 insertions(+), 23 deletions(-)
 create mode 100644 plc4go/internal/cbus/Discoverer.go
 create mode 100644 plc4go/spi/utils/net.go


[plc4x] 02/05: refactor(cbus): small adjustments on reset and optional alpha on direct command

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 cbce5d212b9ee7e0a335ef372c4982f74c20f55f
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 15:24:40 2022 +0200

    refactor(cbus): small adjustments on reset and optional alpha on direct command
---
 .../readwrite/model/RequestDirectCommandAccess.go  | 58 +++++++++++++++++++++-
 .../protocols/cbus/readwrite/model/RequestReset.go | 54 ++++++++++++++------
 .../src/main/resources/protocols/cbus/c-bus.mspec  |  5 +-
 3 files changed, 99 insertions(+), 18 deletions(-)

diff --git a/plc4go/protocols/cbus/readwrite/model/RequestDirectCommandAccess.go b/plc4go/protocols/cbus/readwrite/model/RequestDirectCommandAccess.go
index 2e7ee6e17..8dc3cb1e8 100644
--- a/plc4go/protocols/cbus/readwrite/model/RequestDirectCommandAccess.go
+++ b/plc4go/protocols/cbus/readwrite/model/RequestDirectCommandAccess.go
@@ -23,6 +23,8 @@ import (
 	"fmt"
 	"github.com/apache/plc4x/plc4go/spi/utils"
 	"github.com/pkg/errors"
+	"github.com/rs/zerolog/log"
+	"io"
 )
 
 // Code generated by code-generation. DO NOT EDIT.
@@ -37,6 +39,8 @@ type RequestDirectCommandAccess interface {
 	Request
 	// GetCalData returns CalData (property field)
 	GetCalData() CALData
+	// GetAlpha returns Alpha (property field)
+	GetAlpha() Alpha
 	// GetCalDataDecoded returns CalDataDecoded (virtual field)
 	GetCalDataDecoded() CALData
 }
@@ -52,6 +56,7 @@ type RequestDirectCommandAccessExactly interface {
 type _RequestDirectCommandAccess struct {
 	*_Request
 	CalData CALData
+	Alpha   Alpha
 }
 
 ///////////////////////////////////////////////////////////
@@ -85,6 +90,10 @@ func (m *_RequestDirectCommandAccess) GetCalData() CALData {
 	return m.CalData
 }
 
+func (m *_RequestDirectCommandAccess) GetAlpha() Alpha {
+	return m.Alpha
+}
+
 ///////////////////////
 ///////////////////////
 ///////////////////////////////////////////////////////////
@@ -95,6 +104,8 @@ func (m *_RequestDirectCommandAccess) GetCalData() CALData {
 ///////////////////////
 
 func (m *_RequestDirectCommandAccess) GetCalDataDecoded() CALData {
+	alpha := m.Alpha
+	_ = alpha
 	return CastCALData(m.GetCalData())
 }
 
@@ -117,9 +128,10 @@ func (m *_RequestDirectCommandAccess) GetAt() byte {
 ///////////////////////////////////////////////////////////
 
 // NewRequestDirectCommandAccess factory function for _RequestDirectCommandAccess
-func NewRequestDirectCommandAccess(calData CALData, peekedByte RequestType, startingCR *RequestType, resetMode *RequestType, secondPeek RequestType, termination RequestTermination, cBusOptions CBusOptions) *_RequestDirectCommandAccess {
+func NewRequestDirectCommandAccess(calData CALData, alpha Alpha, peekedByte RequestType, startingCR *RequestType, resetMode *RequestType, secondPeek RequestType, termination RequestTermination, cBusOptions CBusOptions) *_RequestDirectCommandAccess {
 	_result := &_RequestDirectCommandAccess{
 		CalData:  calData,
+		Alpha:    alpha,
 		_Request: NewRequest(peekedByte, startingCR, resetMode, secondPeek, termination, cBusOptions),
 	}
 	_result._Request._RequestChildRequirements = _result
@@ -156,6 +168,11 @@ func (m *_RequestDirectCommandAccess) GetLengthInBitsConditional(lastItem bool)
 
 	// A virtual field doesn't have any in- or output.
 
+	// Optional Field (alpha)
+	if m.Alpha != nil {
+		lengthInBits += m.Alpha.GetLengthInBits()
+	}
+
 	return lengthInBits
 }
 
@@ -196,6 +213,28 @@ func RequestDirectCommandAccessParse(readBuffer utils.ReadBuffer, cBusOptions CB
 	calDataDecoded := _calDataDecoded
 	_ = calDataDecoded
 
+	// Optional Field (alpha) (Can be skipped, if a given expression evaluates to false)
+	var alpha Alpha = nil
+	{
+		currentPos = positionAware.GetPos()
+		if pullErr := readBuffer.PullContext("alpha"); pullErr != nil {
+			return nil, errors.Wrap(pullErr, "Error pulling for alpha")
+		}
+		_val, _err := AlphaParse(readBuffer)
+		switch {
+		case errors.Is(_err, utils.ParseAssertError{}) || errors.Is(_err, io.EOF):
+			log.Debug().Err(_err).Msg("Resetting position because optional threw an error")
+			readBuffer.Reset(currentPos)
+		case _err != nil:
+			return nil, errors.Wrap(_err, "Error parsing 'alpha' field of RequestDirectCommandAccess")
+		default:
+			alpha = _val.(Alpha)
+			if closeErr := readBuffer.CloseContext("alpha"); closeErr != nil {
+				return nil, errors.Wrap(closeErr, "Error closing for alpha")
+			}
+		}
+	}
+
 	if closeErr := readBuffer.CloseContext("RequestDirectCommandAccess"); closeErr != nil {
 		return nil, errors.Wrap(closeErr, "Error closing for RequestDirectCommandAccess")
 	}
@@ -206,6 +245,7 @@ func RequestDirectCommandAccessParse(readBuffer utils.ReadBuffer, cBusOptions CB
 			CBusOptions: cBusOptions,
 		},
 		CalData: calData,
+		Alpha:   alpha,
 	}
 	_child._Request._RequestChildRequirements = _child
 	return _child, nil
@@ -235,6 +275,22 @@ func (m *_RequestDirectCommandAccess) Serialize(writeBuffer utils.WriteBuffer) e
 			return errors.Wrap(_calDataDecodedErr, "Error serializing 'calDataDecoded' field")
 		}
 
+		// Optional Field (alpha) (Can be skipped, if the value is null)
+		var alpha Alpha = nil
+		if m.GetAlpha() != nil {
+			if pushErr := writeBuffer.PushContext("alpha"); pushErr != nil {
+				return errors.Wrap(pushErr, "Error pushing for alpha")
+			}
+			alpha = m.GetAlpha()
+			_alphaErr := writeBuffer.WriteSerializable(alpha)
+			if popErr := writeBuffer.PopContext("alpha"); popErr != nil {
+				return errors.Wrap(popErr, "Error popping for alpha")
+			}
+			if _alphaErr != nil {
+				return errors.Wrap(_alphaErr, "Error serializing 'alpha' field")
+			}
+		}
+
 		if popErr := writeBuffer.PopContext("RequestDirectCommandAccess"); popErr != nil {
 			return errors.Wrap(popErr, "Error popping for RequestDirectCommandAccess")
 		}
diff --git a/plc4go/protocols/cbus/readwrite/model/RequestReset.go b/plc4go/protocols/cbus/readwrite/model/RequestReset.go
index 6ce42e07d..e53293a0f 100644
--- a/plc4go/protocols/cbus/readwrite/model/RequestReset.go
+++ b/plc4go/protocols/cbus/readwrite/model/RequestReset.go
@@ -34,11 +34,11 @@ type RequestReset interface {
 	// GetTildePeek returns TildePeek (property field)
 	GetTildePeek() RequestType
 	// GetSecondTilde returns SecondTilde (property field)
-	GetSecondTilde() *byte
+	GetSecondTilde() *RequestType
 	// GetTildePeek2 returns TildePeek2 (property field)
 	GetTildePeek2() RequestType
 	// GetThirdTilde returns ThirdTilde (property field)
-	GetThirdTilde() *byte
+	GetThirdTilde() *RequestType
 }
 
 // RequestResetExactly can be used when we want exactly this type and not a type which fulfills RequestReset.
@@ -52,9 +52,9 @@ type RequestResetExactly interface {
 type _RequestReset struct {
 	*_Request
 	TildePeek   RequestType
-	SecondTilde *byte
+	SecondTilde *RequestType
 	TildePeek2  RequestType
-	ThirdTilde  *byte
+	ThirdTilde  *RequestType
 }
 
 ///////////////////////////////////////////////////////////
@@ -88,7 +88,7 @@ func (m *_RequestReset) GetTildePeek() RequestType {
 	return m.TildePeek
 }
 
-func (m *_RequestReset) GetSecondTilde() *byte {
+func (m *_RequestReset) GetSecondTilde() *RequestType {
 	return m.SecondTilde
 }
 
@@ -96,7 +96,7 @@ func (m *_RequestReset) GetTildePeek2() RequestType {
 	return m.TildePeek2
 }
 
-func (m *_RequestReset) GetThirdTilde() *byte {
+func (m *_RequestReset) GetThirdTilde() *RequestType {
 	return m.ThirdTilde
 }
 
@@ -106,7 +106,7 @@ func (m *_RequestReset) GetThirdTilde() *byte {
 ///////////////////////////////////////////////////////////
 
 // NewRequestReset factory function for _RequestReset
-func NewRequestReset(tildePeek RequestType, secondTilde *byte, tildePeek2 RequestType, thirdTilde *byte, peekedByte RequestType, startingCR *RequestType, resetMode *RequestType, secondPeek RequestType, termination RequestTermination, cBusOptions CBusOptions) *_RequestReset {
+func NewRequestReset(tildePeek RequestType, secondTilde *RequestType, tildePeek2 RequestType, thirdTilde *RequestType, peekedByte RequestType, startingCR *RequestType, resetMode *RequestType, secondPeek RequestType, termination RequestTermination, cBusOptions CBusOptions) *_RequestReset {
 	_result := &_RequestReset{
 		TildePeek:   tildePeek,
 		SecondTilde: secondTilde,
@@ -182,13 +182,19 @@ func RequestResetParse(readBuffer utils.ReadBuffer, cBusOptions CBusOptions) (Re
 	readBuffer.Reset(currentPos)
 
 	// Optional Field (secondTilde) (Can be skipped, if a given expression evaluates to false)
-	var secondTilde *byte = nil
+	var secondTilde *RequestType = nil
 	if bool((tildePeek) == (RequestType_RESET)) {
-		_val, _err := readBuffer.ReadByte("secondTilde")
+		if pullErr := readBuffer.PullContext("secondTilde"); pullErr != nil {
+			return nil, errors.Wrap(pullErr, "Error pulling for secondTilde")
+		}
+		_val, _err := RequestTypeParse(readBuffer)
 		if _err != nil {
 			return nil, errors.Wrap(_err, "Error parsing 'secondTilde' field of RequestReset")
 		}
 		secondTilde = &_val
+		if closeErr := readBuffer.CloseContext("secondTilde"); closeErr != nil {
+			return nil, errors.Wrap(closeErr, "Error closing for secondTilde")
+		}
 	}
 
 	// Peek Field (tildePeek2)
@@ -207,13 +213,19 @@ func RequestResetParse(readBuffer utils.ReadBuffer, cBusOptions CBusOptions) (Re
 	readBuffer.Reset(currentPos)
 
 	// Optional Field (thirdTilde) (Can be skipped, if a given expression evaluates to false)
-	var thirdTilde *byte = nil
+	var thirdTilde *RequestType = nil
 	if bool((tildePeek2) == (RequestType_RESET)) {
-		_val, _err := readBuffer.ReadByte("thirdTilde")
+		if pullErr := readBuffer.PullContext("thirdTilde"); pullErr != nil {
+			return nil, errors.Wrap(pullErr, "Error pulling for thirdTilde")
+		}
+		_val, _err := RequestTypeParse(readBuffer)
 		if _err != nil {
 			return nil, errors.Wrap(_err, "Error parsing 'thirdTilde' field of RequestReset")
 		}
 		thirdTilde = &_val
+		if closeErr := readBuffer.CloseContext("thirdTilde"); closeErr != nil {
+			return nil, errors.Wrap(closeErr, "Error closing for thirdTilde")
+		}
 	}
 
 	if closeErr := readBuffer.CloseContext("RequestReset"); closeErr != nil {
@@ -243,20 +255,32 @@ func (m *_RequestReset) Serialize(writeBuffer utils.WriteBuffer) error {
 		}
 
 		// Optional Field (secondTilde) (Can be skipped, if the value is null)
-		var secondTilde *byte = nil
+		var secondTilde *RequestType = nil
 		if m.GetSecondTilde() != nil {
+			if pushErr := writeBuffer.PushContext("secondTilde"); pushErr != nil {
+				return errors.Wrap(pushErr, "Error pushing for secondTilde")
+			}
 			secondTilde = m.GetSecondTilde()
-			_secondTildeErr := writeBuffer.WriteByte("secondTilde", *(secondTilde))
+			_secondTildeErr := writeBuffer.WriteSerializable(secondTilde)
+			if popErr := writeBuffer.PopContext("secondTilde"); popErr != nil {
+				return errors.Wrap(popErr, "Error popping for secondTilde")
+			}
 			if _secondTildeErr != nil {
 				return errors.Wrap(_secondTildeErr, "Error serializing 'secondTilde' field")
 			}
 		}
 
 		// Optional Field (thirdTilde) (Can be skipped, if the value is null)
-		var thirdTilde *byte = nil
+		var thirdTilde *RequestType = nil
 		if m.GetThirdTilde() != nil {
+			if pushErr := writeBuffer.PushContext("thirdTilde"); pushErr != nil {
+				return errors.Wrap(pushErr, "Error pushing for thirdTilde")
+			}
 			thirdTilde = m.GetThirdTilde()
-			_thirdTildeErr := writeBuffer.WriteByte("thirdTilde", *(thirdTilde))
+			_thirdTildeErr := writeBuffer.WriteSerializable(thirdTilde)
+			if popErr := writeBuffer.PopContext("thirdTilde"); popErr != nil {
+				return errors.Wrap(popErr, "Error popping for thirdTilde")
+			}
 			if _thirdTildeErr != nil {
 				return errors.Wrap(_thirdTildeErr, "Error serializing 'thirdTilde' field")
 			}
diff --git a/protocols/c-bus/src/main/resources/protocols/cbus/c-bus.mspec b/protocols/c-bus/src/main/resources/protocols/cbus/c-bus.mspec
index e76267854..fdcc00327 100644
--- a/protocols/c-bus/src/main/resources/protocols/cbus/c-bus.mspec
+++ b/protocols/c-bus/src/main/resources/protocols/cbus/c-bus.mspec
@@ -74,9 +74,9 @@
         ]
         ['RESET' *Reset
             [peek     RequestType tildePeek                                     ]
-            [optional byte        secondTilde 'tildePeek == RequestType.RESET'  ]
+            [optional RequestType secondTilde 'tildePeek == RequestType.RESET'  ]
             [peek     RequestType tildePeek2                                    ]
-            [optional byte        thirdTilde 'tildePeek2 == RequestType.RESET'  ]
+            [optional RequestType thirdTilde 'tildePeek2 == RequestType.RESET'  ]
         ]
         ['DIRECT_COMMAND' *DirectCommandAccess
             [const    byte    at        0x40                                ]
@@ -87,6 +87,7 @@
                         '(calData.lengthInBytes*2)*8'                       ]
             [virtual  CALData
                               calDataDecoded 'calData'                      ]
+            [optional Alpha         alpha                                   ]
         ]
         ['REQUEST_COMMAND' *Command
             [const    byte  initiator 0x5C                                  ] // 0x5C == "\"


[plc4x] 04/05: fix(plc4go/cbus): fixed write

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 191162deb76d33f74cf26e5c27f69676e520a1f6
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 15:44:19 2022 +0200

    fix(plc4go/cbus): fixed write
---
 plc4go/internal/cbus/Connection.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/plc4go/internal/cbus/Connection.go b/plc4go/internal/cbus/Connection.go
index b4fe0603d..f341de773 100644
--- a/plc4go/internal/cbus/Connection.go
+++ b/plc4go/internal/cbus/Connection.go
@@ -181,8 +181,7 @@ func (c *Connection) setupConnection(ch chan plc4go.PlcConnectionConnectResult)
 	{
 		log.Debug().Msg("Send a reset")
 		requestTypeReset := readWriteModel.RequestType_RESET
-		requestTypeResetByte := byte(readWriteModel.RequestType_RESET)
-		requestReset := readWriteModel.NewRequestReset(requestTypeReset, &requestTypeResetByte, requestTypeReset, &requestTypeResetByte, requestTypeReset, nil, &requestTypeReset, requestTypeReset, readWriteModel.NewRequestTermination(), *cbusOptions)
+		requestReset := readWriteModel.NewRequestReset(requestTypeReset, &requestTypeReset, requestTypeReset, &requestTypeReset, requestTypeReset, nil, &requestTypeReset, requestTypeReset, readWriteModel.NewRequestTermination(), *cbusOptions)
 		cBusMessage := readWriteModel.NewCBusMessageToServer(requestReset, *requestContext, *cbusOptions)
 
 		receivedResetEchoChan := make(chan bool)
@@ -319,10 +318,11 @@ func (c *Connection) setupConnection(ch chan plc4go.PlcConnectionConnectResult)
 	}()
 }
 
+// This is used for connection setup
 func (c *Connection) sendCalDataWrite(ch chan plc4go.PlcConnectionConnectResult, paramNo readWriteModel.Parameter, parameterValue readWriteModel.ParameterValue, requestContext *readWriteModel.RequestContext, cbusOptions *readWriteModel.CBusOptions) bool {
 	// TODO: we assume that is always a one byte request otherwise we need to map the length here
 	calData := readWriteModel.NewCALDataWrite(paramNo, 0x0, parameterValue, readWriteModel.CALCommandTypeContainer_CALCommandWrite_3Bytes, nil, *requestContext)
-	directCommand := readWriteModel.NewRequestDirectCommandAccess(calData, 0x40, nil, nil, 0x0, readWriteModel.NewRequestTermination(), *cbusOptions)
+	directCommand := readWriteModel.NewRequestDirectCommandAccess(calData /*we don't want a alpha otherwise the PCI will auto-switch*/, nil, 0x40, nil, nil, 0x0, readWriteModel.NewRequestTermination(), *cbusOptions)
 	cBusMessage := readWriteModel.NewCBusMessageToServer(directCommand, *requestContext, *cbusOptions)
 
 	directCommandAckChan := make(chan bool)


[plc4x] 01/05: feat(plc4go): added net util for finding IPs

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 1a041e895908cede4e35ef73ae4bb68f402856bd
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 14:30:38 2022 +0200

    feat(plc4go): added net util for finding IPs
---
 plc4go/spi/utils/net.go | 247 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 247 insertions(+)

diff --git a/plc4go/spi/utils/net.go b/plc4go/spi/utils/net.go
new file mode 100644
index 000000000..91fad7729
--- /dev/null
+++ b/plc4go/spi/utils/net.go
@@ -0,0 +1,247 @@
+/*
+ * 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
+ *
+ *   https://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 utils
+
+import (
+	"bytes"
+	"context"
+	"net"
+	"time"
+
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+	"github.com/google/gopacket/pcap"
+	"github.com/pkg/errors"
+	"github.com/rs/zerolog/log"
+)
+
+func GetIPAddresses(ctx context.Context, netInterface net.Interface, useArpBasedScan bool) (chan net.IP, error) {
+	foundIps := make(chan net.IP, 65536)
+	addrs, err := netInterface.Addrs()
+	if err != nil {
+		return nil, errors.Wrap(err, "Error getting addresses")
+	}
+	go func() {
+		for _, address := range addrs {
+			// Check if context has been cancelled before continuing
+			select {
+			case <-ctx.Done():
+				return
+			default:
+			}
+
+			var ipnet *net.IPNet
+			switch v := address.(type) {
+			case *net.IPAddr:
+				ipnet = &net.IPNet{IP: v.IP, Mask: v.IP.DefaultMask()}
+			case *net.IPNet:
+				ipnet = v
+			default:
+				continue
+			}
+
+			// Skip loop-back and IPv6
+			if ipnet.IP.IsLoopback() || ipnet.IP.To4() == nil {
+				continue
+			}
+
+			log.Debug().Stringer("IP", ipnet.IP).Stringer("Mask", ipnet.Mask).Msg("Expanding local subnet")
+			if useArpBasedScan {
+				if err := lockupIpsUsingArp(ctx, netInterface, ipnet, foundIps); err != nil {
+					log.Error().Err(err).Msg("failing to resolve using arp scan. Falling back to ip based scan")
+					useArpBasedScan = false
+				}
+			}
+			if !useArpBasedScan {
+				if err := lookupIps(ctx, ipnet, foundIps); err != nil {
+					log.Error().Err(err).Msg("error looking up ips")
+				}
+			}
+		}
+	}()
+	return foundIps, nil
+}
+
+// As PING operations might be blocked by a firewall, responding to ARP packets is mandatory for IP based
+// systems. So we are using an ARP scan to resolve the ethernet hardware addresses of each possible ip in range
+// Only for devices that respond will we schedule a discovery.
+func lockupIpsUsingArp(ctx context.Context, netInterface net.Interface, ipNet *net.IPNet, foundIps chan net.IP) error {
+	log.Debug().Msgf("Scanning for alive IP addresses for interface '%s' and net: %s", netInterface.Name, ipNet)
+	// First find the pcap device name for the given interface.
+	allDevs, _ := pcap.FindAllDevs()
+	var devName string
+	for _, dev := range allDevs {
+		for _, devAddress := range dev.Addresses {
+			if devAddress.IP.Equal(ipNet.IP) {
+				devName = dev.Name
+				break
+			}
+		}
+	}
+	if len(devName) == 0 {
+		log.Error().Interface("allDevs", allDevs).Str("ip", ipNet.IP.String()).Msg("Device for discovery not found")
+		return errors.New("Device for discovery not found")
+	}
+
+	// Open up a pcap handle for packet reads/writes.
+	handle, err := pcap.OpenLive(devName, 65536, true, pcap.BlockForever)
+	if err != nil {
+		return errors.Wrap(err, "Error opening network interface")
+	}
+
+	// Start up a goroutine to read in packet data.
+	stop := make(chan struct{})
+	// Handler for processing incoming ARP responses.
+	go func(handle *pcap.Handle, iface net.Interface, stop chan struct{}) {
+		src := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
+		in := src.Packets()
+		for {
+			var packet gopacket.Packet
+			select {
+			case <-stop:
+				return
+			case packet = <-in:
+				if packet == nil {
+					continue
+				}
+				arpLayer := packet.Layer(layers.LayerTypeARP)
+				if arpLayer == nil {
+					continue
+				}
+				arp := arpLayer.(*layers.ARP)
+				// Filter our messages originating from us.
+				if arp.Operation != layers.ARPReply || bytes.Equal(iface.HardwareAddr, arp.SourceHwAddress) {
+					continue
+				}
+				// Schedule a discovery operation for this ip.
+				ip := net.IP(arp.SourceProtAddress)
+				log.Trace().Msgf("Scheduling discovery for IP %s", ip)
+				go func() {
+					select {
+					case <-ctx.Done():
+					case foundIps <- ip:
+					case <-time.After(2 * time.Second):
+					}
+				}()
+			}
+		}
+	}(handle, netInterface, stop)
+	// Make sure we clean up after 10 seconds.
+	defer func() {
+		go func() {
+			time.Sleep(10 * time.Second)
+			handle.Close()
+			close(stop)
+		}()
+	}()
+	writeArp := func(handle *pcap.Handle, iface net.Interface, addr net.IPNet) error {
+		// Set up all the layers' fields we can.
+		eth := layers.Ethernet{
+			SrcMAC:       iface.HardwareAddr,
+			DstMAC:       net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+			EthernetType: layers.EthernetTypeARP,
+		}
+		arp := layers.ARP{
+			AddrType:          layers.LinkTypeEthernet,
+			Protocol:          layers.EthernetTypeIPv4,
+			HwAddressSize:     6,
+			ProtAddressSize:   4,
+			Operation:         layers.ARPRequest,
+			SourceHwAddress:   []byte(iface.HardwareAddr),
+			SourceProtAddress: []byte(addr.IP.To4()),
+			DstHwAddress:      []byte{0, 0, 0, 0, 0, 0},
+		}
+		// Set up buffer and options for serialization.
+		buf := gopacket.NewSerializeBuffer()
+		opts := gopacket.SerializeOptions{
+			FixLengths:       true,
+			ComputeChecksums: true,
+		}
+		log.Debug().Msgf("Sending ARP requests to all devices in network: %s", addr.String())
+		// Send one ARP packet for every possible address.
+		for ip := incrementIP(addr.IP.Mask(ipNet.Mask)); addr.Contains(ip) && addr.Contains(incrementIP(duplicateIP(ip))); ip = incrementIP(ip) {
+			// Check if context has been cancelled before continuing
+			select {
+			case <-ctx.Done():
+				return nil
+			default:
+			}
+			arp.DstProtAddress = ip
+			if err := gopacket.SerializeLayers(buf, opts, &eth, &arp); err != nil {
+				return err
+			}
+			if err := handle.WritePacketData(buf.Bytes()); err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+	// Write our scan packets out to the handle.
+	if err := writeArp(handle, netInterface, *ipNet); err != nil {
+		log.Printf("error writing packets on %v: %v", netInterface.Name, err)
+		return err
+	}
+	return nil
+}
+
+// Simply takes the IP address and the netmask and schedules one discovery task for every possible IP
+func lookupIps(ctx context.Context, ipnet *net.IPNet, foundIps chan net.IP) error {
+	log.Debug().Msgf("Scanning all IP addresses for network: %s", ipnet)
+	// expand CIDR-block into one target for each IP
+	// Remark: The last IP address a network contains is a special broadcast address. We don't want to check that one.
+	for ip := incrementIP(ipnet.IP.Mask(ipnet.Mask)); ipnet.Contains(ip) && ipnet.Contains(incrementIP(duplicateIP(ip))); ip = incrementIP(ip) {
+		// Check if context has been cancelled before continuing
+		select {
+		case <-ctx.Done():
+			return nil
+		default:
+		}
+
+		go func() {
+			select {
+			case <-ctx.Done():
+			case foundIps <- ip:
+			case <-time.After(2 * time.Second):
+			}
+		}()
+		log.Trace().Stringer("IP", ip).Msg("Expanded CIDR")
+	}
+
+	log.Debug().Stringer("net", ipnet).Msg("Done expanding CIDR")
+
+	return nil
+}
+
+func incrementIP(ip net.IP) net.IP {
+	for j := len(ip) - 1; j >= 0; j-- {
+		ip[j]++
+		if ip[j] > 0 {
+			break
+		}
+	}
+
+	return ip
+}
+
+func duplicateIP(ip net.IP) net.IP {
+	dup := make(net.IP, len(ip))
+	copy(dup, ip)
+	return dup
+}


[plc4x] 03/05: feat(plc4go/cbus): implemented Discovery

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 840b324701e63da8a0c38674c98ac6a47b518014
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 15:25:07 2022 +0200

    feat(plc4go/cbus): implemented Discovery
---
 plc4go/internal/cbus/Discoverer.go | 219 +++++++++++++++++++++++++++++++++++++
 plc4go/internal/cbus/Driver.go     |  10 ++
 plc4go/internal/cbus/Reader.go     |   6 +-
 3 files changed, 233 insertions(+), 2 deletions(-)

diff --git a/plc4go/internal/cbus/Discoverer.go b/plc4go/internal/cbus/Discoverer.go
new file mode 100644
index 000000000..fdce0b5b7
--- /dev/null
+++ b/plc4go/internal/cbus/Discoverer.go
@@ -0,0 +1,219 @@
+/*
+ * 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
+ *
+ *   https://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 cbus
+
+import (
+	"context"
+	"fmt"
+	"github.com/apache/plc4x/plc4go/spi/transports/tcp"
+	"net"
+	"net/url"
+	"time"
+
+	apiModel "github.com/apache/plc4x/plc4go/pkg/api/model"
+	readWriteModel "github.com/apache/plc4x/plc4go/protocols/cbus/readwrite/model"
+	"github.com/apache/plc4x/plc4go/spi"
+	internalModel "github.com/apache/plc4x/plc4go/spi/model"
+	"github.com/apache/plc4x/plc4go/spi/options"
+	"github.com/apache/plc4x/plc4go/spi/transports"
+	"github.com/apache/plc4x/plc4go/spi/utils"
+
+	"github.com/pkg/errors"
+	"github.com/rs/zerolog/log"
+)
+
+type Discoverer struct {
+	messageCodec spi.MessageCodec
+}
+
+func NewDiscoverer() *Discoverer {
+	return &Discoverer{}
+}
+
+func (d *Discoverer) Discover(callback func(event apiModel.PlcDiscoveryEvent), discoveryOptions ...options.WithDiscoveryOption) error {
+	tcpTransport := tcp.NewTransport()
+
+	allInterfaces, err := net.Interfaces()
+	if err != nil {
+		return err
+	}
+
+	// If no device is explicitly selected via option, simply use all of them
+	// However if a discovery option is present to select a device by name, only
+	// add those devices matching any of the given names.
+	var interfaces []net.Interface
+	deviceNames := options.FilterDiscoveryOptionsDeviceName(discoveryOptions)
+	if len(deviceNames) > 0 {
+		for _, curInterface := range allInterfaces {
+			for _, deviceNameOption := range deviceNames {
+				if curInterface.Name == deviceNameOption.GetDeviceName() {
+					interfaces = append(interfaces, curInterface)
+					break
+				}
+			}
+		}
+	} else {
+		interfaces = allInterfaces
+	}
+
+	var tranportInstances []transports.TransportInstance
+	// Iterate over all network devices of this system.
+	for _, interf := range interfaces {
+		addrs, err := interf.Addrs()
+		if err != nil {
+			return err
+		}
+		// Iterate over all addresses the current interface has configured
+		// For KNX we're only interested in IPv4 addresses, as it doesn't
+		// seem to work with IPv6.
+		for _, addr := range addrs {
+			var ipv4Addr net.IP
+			switch addr.(type) {
+			// If the device is configured to communicate with a subnet
+			case *net.IPNet:
+				ipv4Addr = addr.(*net.IPNet).IP.To4()
+
+			// If the device is configured for a point-to-point connection
+			case *net.IPAddr:
+				ipv4Addr = addr.(*net.IPAddr).IP.To4()
+			}
+
+			// If we found an IPv4 address and this is not a loopback address,
+			// add it to the list of devices we will open ports and send discovery
+			// messages from.
+			if ipv4Addr != nil && !ipv4Addr.IsLoopback() {
+				addresses, err := utils.GetIPAddresses(context.TODO(), interf, false)
+				if err != nil {
+					log.Warn().Err(err).Msgf("Can't get addresses for %s", interf)
+					continue
+				}
+				for ip := range addresses {
+					// Create a new "connection" (Actually open a local udp socket and target outgoing packets to that address)
+					connectionUrl, err := url.Parse(fmt.Sprintf("tcp://%s:%d", ip, readWriteModel.CBusConstants_CBUSTCPDEFAULTPORT))
+					if err != nil {
+						log.Error().Err(err).Msgf("Error parsing url for lookup")
+						continue
+					}
+					transportInstance, err := tcpTransport.CreateTransportInstance(*connectionUrl, nil)
+					if err != nil {
+						return err
+					}
+					err = transportInstance.Connect()
+					if err != nil {
+						continue
+					}
+
+					tranportInstances = append(tranportInstances, transportInstance)
+				}
+			}
+		}
+	}
+
+	if len(tranportInstances) <= 0 {
+		return nil
+	}
+	for _, transportInstance := range tranportInstances {
+		tcpTransportInstance := transportInstance.(*tcp.TransportInstance)
+		// Create a codec for sending and receiving messages.
+		codec := NewMessageCodec(transportInstance)
+		// Explicitly start the worker
+		if err := codec.Connect(); err != nil {
+			return errors.Wrap(err, "Error connecting")
+		}
+
+		// Prepare the discovery packet data
+		cBusOptions := readWriteModel.NewCBusOptions(false, false, false, false, false, false, false, false, true)
+		requestContext := readWriteModel.NewRequestContext(false)
+		calData := readWriteModel.NewCALDataIdentify(readWriteModel.Attribute_Manufacturer, readWriteModel.CALCommandTypeContainer_CALCommandIdentify, nil, requestContext)
+		alpha := readWriteModel.NewAlpha('x')
+		request := readWriteModel.NewRequestDirectCommandAccess(calData, alpha, 0x0, nil, nil, readWriteModel.RequestType_DIRECT_COMMAND, readWriteModel.NewRequestTermination(), cBusOptions)
+		cBusMessageToServer := readWriteModel.NewCBusMessageToServer(request, requestContext, cBusOptions)
+		// Send the search request.
+		err = codec.Send(cBusMessageToServer)
+		go func() {
+			// Keep on reading responses till the timeout is done.
+			// TODO: Make this configurable
+			timeout := time.NewTimer(time.Second * 1)
+			timeout.Stop()
+			for start := time.Now(); time.Since(start) < time.Second*5; {
+				timeout.Reset(time.Second * 1)
+				select {
+				case receivedMessage := <-codec.GetDefaultIncomingMessageChannel():
+					if !timeout.Stop() {
+						<-timeout.C
+					}
+					cbusMessage, ok := receivedMessage.(readWriteModel.CBusMessage)
+					if !ok {
+						continue
+					}
+					messageToClient, ok := cbusMessage.(readWriteModel.CBusMessageToClient)
+					if !ok {
+						continue
+					}
+					replyOrConfirmationConfirmation, ok := messageToClient.GetReply().(readWriteModel.ReplyOrConfirmationConfirmationExactly)
+					if !ok {
+						continue
+					}
+					if alpha != replyOrConfirmationConfirmation.GetConfirmation().GetAlpha() {
+						continue
+					}
+					embeddedReply, ok := replyOrConfirmationConfirmation.GetEmbeddedReply().(readWriteModel.ReplyOrConfirmationReplyExactly)
+					if !ok {
+						continue
+					}
+					encodedReply, ok := embeddedReply.GetReply().(readWriteModel.ReplyEncodedReplyExactly)
+					if !ok {
+						continue
+					}
+					calDataIdentifyReply, ok := encodedReply.GetEncodedReply().(readWriteModel.CALDataIdentifyReplyExactly)
+					if !ok {
+						continue
+					}
+					identifyReplyCommand, ok := calDataIdentifyReply.GetIdentifyReplyCommand().(readWriteModel.IdentifyReplyCommandManufacturerExactly)
+					if !ok {
+						continue
+					}
+					// TODO: we could check for the exact reponse
+					remoteUrl, err := url.Parse(fmt.Sprintf("tcp://%ds", tcpTransportInstance.RemoteAddress))
+					if err != nil {
+						log.Error().Err(err).Msg("Error creating url")
+						continue
+					}
+					// TODO: manufaturer + type would be good but this means two requests then
+					deviceName := identifyReplyCommand.GetManufacturerName()
+					discoveryEvent := &internalModel.DefaultPlcDiscoveryEvent{
+						ProtocolCode:  "c-bus",
+						TransportCode: "tcp",
+						TransportUrl:  *remoteUrl,
+						Options:       nil,
+						Name:          deviceName,
+					}
+					// Pass the event back to the callback
+					callback(discoveryEvent)
+					continue
+				case <-timeout.C:
+					timeout.Stop()
+					continue
+				}
+			}
+		}()
+	}
+	return nil
+}
diff --git a/plc4go/internal/cbus/Driver.go b/plc4go/internal/cbus/Driver.go
index 220e8016d..fbdd11bb2 100644
--- a/plc4go/internal/cbus/Driver.go
+++ b/plc4go/internal/cbus/Driver.go
@@ -21,9 +21,11 @@ package cbus
 
 import (
 	"github.com/apache/plc4x/plc4go/pkg/api"
+	apiModel "github.com/apache/plc4x/plc4go/pkg/api/model"
 	readWriteModel "github.com/apache/plc4x/plc4go/protocols/cbus/readwrite/model"
 	"github.com/apache/plc4x/plc4go/spi"
 	_default "github.com/apache/plc4x/plc4go/spi/default"
+	"github.com/apache/plc4x/plc4go/spi/options"
 	"github.com/apache/plc4x/plc4go/spi/transports"
 	"github.com/pkg/errors"
 	"github.com/rs/zerolog/log"
@@ -110,3 +112,11 @@ func (m *Driver) SetAwaitSetupComplete(awaitComplete bool) {
 func (m *Driver) SetAwaitDisconnectComplete(awaitComplete bool) {
 	m.awaitDisconnectComplete = awaitComplete
 }
+
+func (m *Driver) SupportsDiscovery() bool {
+	return true
+}
+
+func (m *Driver) Discover(callback func(event apiModel.PlcDiscoveryEvent), discoveryOptions ...options.WithDiscoveryOption) error {
+	return NewDiscoverer().Discover(callback, discoveryOptions...)
+}
diff --git a/plc4go/internal/cbus/Reader.go b/plc4go/internal/cbus/Reader.go
index 09d799f2a..b34382df9 100644
--- a/plc4go/internal/cbus/Reader.go
+++ b/plc4go/internal/cbus/Reader.go
@@ -21,16 +21,18 @@ package cbus
 
 import (
 	"fmt"
+	"sync"
+	"time"
+
 	"github.com/apache/plc4x/plc4go/pkg/api/model"
 	"github.com/apache/plc4x/plc4go/pkg/api/values"
 	readWriteModel "github.com/apache/plc4x/plc4go/protocols/cbus/readwrite/model"
 	"github.com/apache/plc4x/plc4go/spi"
 	spiModel "github.com/apache/plc4x/plc4go/spi/model"
 	spiValues "github.com/apache/plc4x/plc4go/spi/values"
+
 	"github.com/pkg/errors"
 	"github.com/rs/zerolog/log"
-	"sync"
-	"time"
 )
 
 type Reader struct {


[plc4x] 05/05: feat(plc4go/bacnet): updated vendors

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 dd11ad341172c72cd4c7162dea9c4127086bcc63
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 15:44:31 2022 +0200

    feat(plc4go/bacnet): updated vendors
---
 .../bacnetip/readwrite/model/BACnetVendorId.go     | 32 ++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetVendorId.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetVendorId.go
index 4950f0d52..c7cbbc0dc 100644
--- a/plc4go/protocols/bacnetip/readwrite/model/BACnetVendorId.go
+++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetVendorId.go
@@ -1405,6 +1405,8 @@ const (
 	BACnetVendorId_ZED_BEE_TECHNOLOGIES_PVT_LTD                                                 BACnetVendorId = 1372
 	BACnetVendorId_WINMATE_TECHNOLOGY_SOLUTIONS_PVT_LTD                                         BACnetVendorId = 1373
 	BACnetVendorId_SENTICON_LTD                                                                 BACnetVendorId = 1374
+	BACnetVendorId_ROSSAKERAB                                                                   BACnetVendorId = 1375
+	BACnetVendorId_OPIT_SOLUTIONS_LTD                                                           BACnetVendorId = 1376
 	BACnetVendorId_UNKNOWN_VENDOR                                                               BACnetVendorId = 0xFFFF
 )
 
@@ -2781,6 +2783,8 @@ func init() {
 		BACnetVendorId_ZED_BEE_TECHNOLOGIES_PVT_LTD,
 		BACnetVendorId_WINMATE_TECHNOLOGY_SOLUTIONS_PVT_LTD,
 		BACnetVendorId_SENTICON_LTD,
+		BACnetVendorId_ROSSAKERAB,
+		BACnetVendorId_OPIT_SOLUTIONS_LTD,
 		BACnetVendorId_UNKNOWN_VENDOR,
 	}
 }
@@ -4463,6 +4467,14 @@ func (e BACnetVendorId) VendorId() uint16 {
 		{ /* '1374' */
 			return 1374
 		}
+	case 1375:
+		{ /* '1375' */
+			return 1375
+		}
+	case 1376:
+		{ /* '1376' */
+			return 1376
+		}
 	case 138:
 		{ /* '138' */
 			return 138
@@ -9957,6 +9969,14 @@ func (e BACnetVendorId) VendorName() string {
 		{ /* '1374' */
 			return "Senticon Ltd."
 		}
+	case 1375:
+		{ /* '1375' */
+			return "Rossaker AB"
+		}
+	case 1376:
+		{ /* '1376' */
+			return "OPIT Solutions Ltd"
+		}
 	case 138:
 		{ /* '138' */
 			return "PowerCold Comfort Air Solutions, Inc."
@@ -14612,6 +14632,10 @@ func BACnetVendorIdByValue(value uint16) (enum BACnetVendorId, ok bool) {
 		return BACnetVendorId_WINMATE_TECHNOLOGY_SOLUTIONS_PVT_LTD, true
 	case 1374:
 		return BACnetVendorId_SENTICON_LTD, true
+	case 1375:
+		return BACnetVendorId_ROSSAKERAB, true
+	case 1376:
+		return BACnetVendorId_OPIT_SOLUTIONS_LTD, true
 	case 138:
 		return BACnetVendorId_POWER_COLD_COMFORT_AIR_SOLUTIONS_INC, true
 	case 139:
@@ -17356,6 +17380,10 @@ func BACnetVendorIdByName(value string) (enum BACnetVendorId, ok bool) {
 		return BACnetVendorId_WINMATE_TECHNOLOGY_SOLUTIONS_PVT_LTD, true
 	case "SENTICON_LTD":
 		return BACnetVendorId_SENTICON_LTD, true
+	case "ROSSAKERAB":
+		return BACnetVendorId_ROSSAKERAB, true
+	case "OPIT_SOLUTIONS_LTD":
+		return BACnetVendorId_OPIT_SOLUTIONS_LTD, true
 	case "POWER_COLD_COMFORT_AIR_SOLUTIONS_INC":
 		return BACnetVendorId_POWER_COLD_COMFORT_AIR_SOLUTIONS_INC, true
 	case "I_CONTROLS":
@@ -20145,6 +20173,10 @@ func (e BACnetVendorId) PLC4XEnumName() string {
 		return "WINMATE_TECHNOLOGY_SOLUTIONS_PVT_LTD"
 	case BACnetVendorId_SENTICON_LTD:
 		return "SENTICON_LTD"
+	case BACnetVendorId_ROSSAKERAB:
+		return "ROSSAKERAB"
+	case BACnetVendorId_OPIT_SOLUTIONS_LTD:
+		return "OPIT_SOLUTIONS_LTD"
 	case BACnetVendorId_POWER_COLD_COMFORT_AIR_SOLUTIONS_INC:
 		return "POWER_COLD_COMFORT_AIR_SOLUTIONS_INC"
 	case BACnetVendorId_I_CONTROLS: