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 16:41:01 UTC

[plc4x] branch develop updated (dd11ad341 -> e94ed048d)

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 dd11ad341 feat(plc4go/bacnet): updated vendors
     new c9c9e7726 feat(plc-simulator/cbus): implemented direct command identify
     new e28f2ded7 fix(plc4go/spi): fixed net command ip issues
     new e8a3f67ac feat(plc4go/spi): implemented stringer for Default driver
     new 87763de69 feat(plc4go/cbus): properly implemented Discoverer
     new dc05bfa79 feat(plc4xbrowser): added discover command
     new e94ed048d feat(plc4xbrowser): added discover command

The 6 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/Discoverer.go                 | 266 +++++++++++----------
 plc4go/spi/default/DefaultDriver.go                |   6 +
 plc4go/spi/utils/net.go                            |  18 +-
 plc4go/tools/plc4xbrowser/ui/actions.go            |  45 +++-
 plc4go/tools/plc4xbrowser/ui/commands.go           |  22 ++
 plc4go/tools/plc4xbrowser/ui/common.go             |   4 +-
 plc4go/tools/plc4xbrowser/ui/ui.go                 |   9 +-
 .../server/cbus/protocol/CBusServerAdapter.java    | 226 ++++++++---------
 8 files changed, 339 insertions(+), 257 deletions(-)


[plc4x] 01/06: feat(plc-simulator/cbus): implemented direct command identify

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 c9c9e772660ac204e70470f9529dbce29fd1f753
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 18:36:08 2022 +0200

    feat(plc-simulator/cbus): implemented direct command identify
---
 .../server/cbus/protocol/CBusServerAdapter.java    | 226 +++++++++++----------
 1 file changed, 117 insertions(+), 109 deletions(-)

diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/cbus/protocol/CBusServerAdapter.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/cbus/protocol/CBusServerAdapter.java
index 14441e90e..aa59598fa 100644
--- a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/cbus/protocol/CBusServerAdapter.java
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/cbus/protocol/CBusServerAdapter.java
@@ -122,8 +122,11 @@ public class CBusServerAdapter extends ChannelInboundHandlerAdapter {
                         CALReplyShort calReply = new CALReplyShort((byte) 0x0, calDataAcknowledge, cBusOptions, requestContext);
                         EncodedReplyCALReply encodedReply = new EncodedReplyCALReply((byte) 0x0, calReply, cBusOptions, requestContext);
                         ReplyEncodedReply replyEncodedReply = new ReplyEncodedReply((byte) 0x0, encodedReply, null, cBusOptions, requestContext);
-                        ReplyOrConfirmationReply replyOrConfirmationReply = new ReplyOrConfirmationReply((byte) 0x0, replyEncodedReply, new ResponseTermination(), cBusOptions, requestContext);
-                        CBusMessageToClient cBusMessageToClient = new CBusMessageToClient(replyOrConfirmationReply, requestContext, cBusOptions);
+                        ReplyOrConfirmation replyOrConfirmation = new ReplyOrConfirmationReply((byte) 0x0, replyEncodedReply, new ResponseTermination(), cBusOptions, requestContext);
+                        if (requestDirectCommandAccess.getAlpha() != null) {
+                            replyOrConfirmation = new ReplyOrConfirmationConfirmation((byte) 0x0, new Confirmation(requestDirectCommandAccess.getAlpha(), null, ConfirmationType.CONFIRMATION_SUCCESSFUL), replyOrConfirmation, cBusOptions, requestContext);
+                        }
+                        CBusMessageToClient cBusMessageToClient = new CBusMessageToClient(replyOrConfirmation, requestContext, cBusOptions);
                         LOGGER.info("Sending ack\n{}\n{}", cBusMessageToClient, encodedReply);
                         ctx.writeAndFlush(cBusMessageToClient);
                     };
@@ -212,6 +215,9 @@ public class CBusServerAdapter extends ChannelInboundHandlerAdapter {
                             throw new IllegalStateException("Unmapped type");
                     }
                 }
+                if (calData instanceof CALDataIdentify) {
+                    handleCalDataIdentify(ctx, (CALDataIdentify) calData, requestDirectCommandAccess.getAlpha());
+                }
                 return;
             }
             if (request instanceof RequestCommand) {
@@ -247,113 +253,7 @@ public class CBusServerAdapter extends ChannelInboundHandlerAdapter {
                     CALData calData = command.getCalData();
                     // TODO: handle other Datatypes
                     if (calData instanceof CALDataIdentify) {
-                        short numBytes = 0;
-                        IdentifyReplyCommand identifyReplyCommand;
-                        CALDataIdentify calDataIdentify = (CALDataIdentify) calData;
-                        switch (calDataIdentify.getAttribute()) {
-                            case Manufacturer:
-                                numBytes = 0x08;
-                                identifyReplyCommand = new IdentifyReplyCommandManufacturer("Apache", numBytes);
-                                break;
-                            case Type:
-                                numBytes = 0x08;
-                                identifyReplyCommand = new IdentifyReplyCommandType("plc4x-si", numBytes);
-                                break;
-                            case FirmwareVersion:
-                                numBytes = 0x08;
-                                identifyReplyCommand = new IdentifyReplyCommandFirmwareVersion("0.9", numBytes);
-                                break;
-                            case Summary:
-                                numBytes = 0x09;
-                                identifyReplyCommand = new IdentifyReplyCommandFirmwareSummary("0.9", (byte) 0xAF, "0.0", numBytes);
-                                break;
-                            case ExtendedDiagnosticSummary:
-                                numBytes = 0x0C;
-                                identifyReplyCommand = new IdentifyReplyCommandExtendedDiagnosticSummary(ApplicationIdContainer.FREE_USAGE_01, ApplicationIdContainer.FREE_USAGE_0F, (byte) 0x0, 0x0, 4711l, (byte) 0x13, false, false, false, true, false, false, false, false, false, false, false, false, false, numBytes);
-                                break;
-                            case NetworkTerminalLevels:
-                                numBytes = 0x0C;
-                                identifyReplyCommand = new IdentifyReplyCommandNetworkTerminalLevels(new byte[]{0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13}, numBytes);
-                                break;
-                            case TerminalLevel:
-                                numBytes = 0x0C;
-                                identifyReplyCommand = new IdentifyReplyCommandTerminalLevels(new byte[]{0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13}, numBytes);
-                                break;
-                            case NetworkVoltage:
-                                numBytes = 0x05;
-                                identifyReplyCommand = new IdentifyReplyCommandNetworkVoltage("48", "7", numBytes);
-                                break;
-                            case GAVValuesCurrent:
-                                numBytes = 0x10;
-                                identifyReplyCommand = new IdentifyReplyCommandGAVValuesCurrent(new byte[]{
-                                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
-                                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
-                                }, numBytes);
-                                break;
-                            case GAVValuesStored:
-                                numBytes = 0x10;
-                                identifyReplyCommand = new IdentifyReplyCommandGAVValuesStored(new byte[]{
-                                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
-                                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
-                                }, numBytes);
-                                break;
-                            case GAVPhysicalAddresses:
-                                numBytes = 0x10;
-                                identifyReplyCommand = new IdentifyReplyCommandGAVPhysicalAddresses(new byte[]{
-                                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
-                                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
-                                }, numBytes);
-                                break;
-                            case LogicalAssignment:
-                                numBytes = 0x0E;
-                                identifyReplyCommand = new IdentifyReplyCommandLogicalAssignment(List.of(new LogicAssignment(false, true, true, true, true, true)), numBytes);
-                                break;
-                            case Delays:
-                                numBytes = 0x0F;
-                                identifyReplyCommand = new IdentifyReplyCommandDelays(new byte[]{0x3}, (byte) 0x13, numBytes);
-                                break;
-                            case MinimumLevels:
-                                numBytes = 0x0E;
-                                identifyReplyCommand = new IdentifyReplyCommandMinimumLevels(new byte[]{0x3}, numBytes);
-                                break;
-                            case MaximumLevels:
-                                numBytes = 0x0F;
-                                identifyReplyCommand = new IdentifyReplyCommandMaximumLevels(new byte[]{0xF}, numBytes);
-                                break;
-                            case CurrentSenseLevels:
-                                numBytes = 0x10;
-                                identifyReplyCommand = new IdentifyReplyCommandCurrentSenseLevels(new byte[]{0xF}, numBytes);
-                                break;
-                            case OutputUnitSummary:
-                                numBytes = 0x12;
-                                identifyReplyCommand = new IdentifyReplyCommandOutputUnitSummary(new IdentifyReplyCommandUnitSummary(false, false, false, false, false, false, false, false), (byte) 0x4, (byte) 0x4, (short) 45, numBytes);
-                                break;
-                            case DSIStatus:
-                                numBytes = 0x12;
-                                identifyReplyCommand = new IdentifyReplyCommandDSIStatus(ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, UnitStatus.OK, (byte) 0x34, numBytes);
-                                break;
-                            default:
-                                throw new IllegalStateException("unmapped type " + calDataIdentify.getAttribute());
-                        }
-
-                        calData = new CALDataIdentifyReply(getReplyCommandType(numBytes + 1), null, ((CALDataIdentify) calData).getAttribute(), identifyReplyCommand, requestContext);
-                        CALReply calReply;
-                        if (exstat) {
-                            calReply = new CALReplyLong((byte) 0x0, calData, (byte) 0x0, new UnitAddress((byte) 0x0), null, new SerialInterfaceAddress((byte) 0x02), (byte) 0x0, null, cBusOptions, requestContext);
-                        } else {
-                            calReply = new CALReplyShort((byte) 0x0, calData, cBusOptions, requestContext);
-                        }
-                        EncodedReply encodedReply = new EncodedReplyCALReply((byte) 0x0, calReply, cBusOptions, requestContext);
-                        ReplyEncodedReply replyEncodedReply = new ReplyEncodedReply((byte) 0xC0, encodedReply, null, cBusOptions, requestContext);
-                        ReplyOrConfirmation replyOrConfirmation = new ReplyOrConfirmationReply((byte) 0xFF, replyEncodedReply, new ResponseTermination(), cBusOptions, requestContext);
-                        Alpha alpha = requestCommand.getAlpha();
-                        if (alpha != null) {
-                            Confirmation confirmation = new Confirmation(alpha, null, ConfirmationType.CONFIRMATION_SUCCESSFUL);
-                            replyOrConfirmation = new ReplyOrConfirmationConfirmation(alpha.getCharacter(), confirmation, replyOrConfirmation, cBusOptions, requestContext);
-                        }
-                        CBusMessage response = new CBusMessageToClient(replyOrConfirmation, requestContext, cBusOptions);
-                        LOGGER.info("Send identify response\n{}", response);
-                        ctx.writeAndFlush(response);
+                        handleCalDataIdentify(ctx, (CALDataIdentify) calData, requestCommand.getAlpha());
                     }
                     return;
                 }
@@ -486,6 +386,114 @@ public class CBusServerAdapter extends ChannelInboundHandlerAdapter {
         }
     }
 
+    private void handleCalDataIdentify(ChannelHandlerContext ctx, CALDataIdentify calDataIdentify, Alpha alpha) {
+        short numBytes = 0;
+        IdentifyReplyCommand identifyReplyCommand;
+        switch (calDataIdentify.getAttribute()) {
+            case Manufacturer:
+                numBytes = 0x08;
+                identifyReplyCommand = new IdentifyReplyCommandManufacturer("Apache", numBytes);
+                break;
+            case Type:
+                numBytes = 0x08;
+                identifyReplyCommand = new IdentifyReplyCommandType("plc4x-si", numBytes);
+                break;
+            case FirmwareVersion:
+                numBytes = 0x08;
+                identifyReplyCommand = new IdentifyReplyCommandFirmwareVersion("0.9", numBytes);
+                break;
+            case Summary:
+                numBytes = 0x09;
+                identifyReplyCommand = new IdentifyReplyCommandFirmwareSummary("0.9", (byte) 0xAF, "0.0", numBytes);
+                break;
+            case ExtendedDiagnosticSummary:
+                numBytes = 0x0C;
+                identifyReplyCommand = new IdentifyReplyCommandExtendedDiagnosticSummary(ApplicationIdContainer.FREE_USAGE_01, ApplicationIdContainer.FREE_USAGE_0F, (byte) 0x0, 0x0, 4711l, (byte) 0x13, false, false, false, true, false, false, false, false, false, false, false, false, false, numBytes);
+                break;
+            case NetworkTerminalLevels:
+                numBytes = 0x0C;
+                identifyReplyCommand = new IdentifyReplyCommandNetworkTerminalLevels(new byte[]{0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13}, numBytes);
+                break;
+            case TerminalLevel:
+                numBytes = 0x0C;
+                identifyReplyCommand = new IdentifyReplyCommandTerminalLevels(new byte[]{0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13}, numBytes);
+                break;
+            case NetworkVoltage:
+                numBytes = 0x05;
+                identifyReplyCommand = new IdentifyReplyCommandNetworkVoltage("48", "7", numBytes);
+                break;
+            case GAVValuesCurrent:
+                numBytes = 0x10;
+                identifyReplyCommand = new IdentifyReplyCommandGAVValuesCurrent(new byte[]{
+                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+                }, numBytes);
+                break;
+            case GAVValuesStored:
+                numBytes = 0x10;
+                identifyReplyCommand = new IdentifyReplyCommandGAVValuesStored(new byte[]{
+                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+                }, numBytes);
+                break;
+            case GAVPhysicalAddresses:
+                numBytes = 0x10;
+                identifyReplyCommand = new IdentifyReplyCommandGAVPhysicalAddresses(new byte[]{
+                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+                    0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+                }, numBytes);
+                break;
+            case LogicalAssignment:
+                numBytes = 0x0E;
+                identifyReplyCommand = new IdentifyReplyCommandLogicalAssignment(List.of(new LogicAssignment(false, true, true, true, true, true)), numBytes);
+                break;
+            case Delays:
+                numBytes = 0x0F;
+                identifyReplyCommand = new IdentifyReplyCommandDelays(new byte[]{0x3}, (byte) 0x13, numBytes);
+                break;
+            case MinimumLevels:
+                numBytes = 0x0E;
+                identifyReplyCommand = new IdentifyReplyCommandMinimumLevels(new byte[]{0x3}, numBytes);
+                break;
+            case MaximumLevels:
+                numBytes = 0x0F;
+                identifyReplyCommand = new IdentifyReplyCommandMaximumLevels(new byte[]{0xF}, numBytes);
+                break;
+            case CurrentSenseLevels:
+                numBytes = 0x10;
+                identifyReplyCommand = new IdentifyReplyCommandCurrentSenseLevels(new byte[]{0xF}, numBytes);
+                break;
+            case OutputUnitSummary:
+                numBytes = 0x12;
+                identifyReplyCommand = new IdentifyReplyCommandOutputUnitSummary(new IdentifyReplyCommandUnitSummary(false, false, false, false, false, false, false, false), (byte) 0x4, (byte) 0x4, (short) 45, numBytes);
+                break;
+            case DSIStatus:
+                numBytes = 0x12;
+                identifyReplyCommand = new IdentifyReplyCommandDSIStatus(ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, ChannelStatus.OK, UnitStatus.OK, (byte) 0x34, numBytes);
+                break;
+            default:
+                throw new IllegalStateException("unmapped type " + calDataIdentify.getAttribute());
+        }
+
+        CALData calData = new CALDataIdentifyReply(getReplyCommandType(numBytes + 1), null, calDataIdentify.getAttribute(), identifyReplyCommand, requestContext);
+        CALReply calReply;
+        if (exstat) {
+            calReply = new CALReplyLong((byte) 0x0, calData, (byte) 0x0, new UnitAddress((byte) 0x0), null, new SerialInterfaceAddress((byte) 0x02), (byte) 0x0, null, cBusOptions, requestContext);
+        } else {
+            calReply = new CALReplyShort((byte) 0x0, calData, cBusOptions, requestContext);
+        }
+        EncodedReply encodedReply = new EncodedReplyCALReply((byte) 0x0, calReply, cBusOptions, requestContext);
+        ReplyEncodedReply replyEncodedReply = new ReplyEncodedReply((byte) 0xC0, encodedReply, null, cBusOptions, requestContext);
+        ReplyOrConfirmation replyOrConfirmation = new ReplyOrConfirmationReply((byte) 0xFF, replyEncodedReply, new ResponseTermination(), cBusOptions, requestContext);
+        if (alpha != null) {
+            Confirmation confirmation = new Confirmation(alpha, null, ConfirmationType.CONFIRMATION_SUCCESSFUL);
+            replyOrConfirmation = new ReplyOrConfirmationConfirmation(alpha.getCharacter(), confirmation, replyOrConfirmation, cBusOptions, requestContext);
+        }
+        CBusMessage response = new CBusMessageToClient(replyOrConfirmation, requestContext, cBusOptions);
+        LOGGER.info("Send identify response\n{}", response);
+        ctx.writeAndFlush(response);
+    }
+
     private void startSALMonitor(ChannelHandlerContext ctx) {
         if (salMonitorFuture != null) {
             LOGGER.debug("SAL Monitor already running");


[plc4x] 02/06: fix(plc4go/spi): fixed net command ip issues

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 e28f2ded79a8110fef00eb9c3a62c0e122deb929
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 18:37:05 2022 +0200

    fix(plc4go/spi): fixed net command ip issues
---
 plc4go/spi/utils/net.go | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/plc4go/spi/utils/net.go b/plc4go/spi/utils/net.go
index 91fad7729..f05a9dc4c 100644
--- a/plc4go/spi/utils/net.go
+++ b/plc4go/spi/utils/net.go
@@ -133,13 +133,13 @@ func lockupIpsUsingArp(ctx context.Context, netInterface net.Interface, ipNet *n
 				// Schedule a discovery operation for this ip.
 				ip := net.IP(arp.SourceProtAddress)
 				log.Trace().Msgf("Scheduling discovery for IP %s", ip)
-				go func() {
+				go func(ip net.IP) {
 					select {
 					case <-ctx.Done():
-					case foundIps <- ip:
+					case foundIps <- DuplicateIP(ip):
 					case <-time.After(2 * time.Second):
 					}
-				}()
+				}(DuplicateIP(ip))
 			}
 		}
 	}(handle, netInterface, stop)
@@ -176,7 +176,7 @@ func lockupIpsUsingArp(ctx context.Context, netInterface net.Interface, ipNet *n
 		}
 		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) {
+		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():
@@ -206,7 +206,7 @@ func lookupIps(ctx context.Context, ipnet *net.IPNet, foundIps chan net.IP) erro
 	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) {
+	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():
@@ -214,13 +214,13 @@ func lookupIps(ctx context.Context, ipnet *net.IPNet, foundIps chan net.IP) erro
 		default:
 		}
 
-		go func() {
+		go func(ip net.IP) {
 			select {
 			case <-ctx.Done():
 			case foundIps <- ip:
 			case <-time.After(2 * time.Second):
 			}
-		}()
+		}(DuplicateIP(ip))
 		log.Trace().Stringer("IP", ip).Msg("Expanded CIDR")
 	}
 
@@ -229,7 +229,7 @@ func lookupIps(ctx context.Context, ipnet *net.IPNet, foundIps chan net.IP) erro
 	return nil
 }
 
-func incrementIP(ip net.IP) net.IP {
+func IncrementIP(ip net.IP) net.IP {
 	for j := len(ip) - 1; j >= 0; j-- {
 		ip[j]++
 		if ip[j] > 0 {
@@ -240,7 +240,7 @@ func incrementIP(ip net.IP) net.IP {
 	return ip
 }
 
-func duplicateIP(ip net.IP) net.IP {
+func DuplicateIP(ip net.IP) net.IP {
 	dup := make(net.IP, len(ip))
 	copy(dup, ip)
 	return dup


[plc4x] 04/06: feat(plc4go/cbus): properly implemented Discoverer

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 87763de69b0ca9272a3b9c0dd34bc712d657ac9c
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 18:37:57 2022 +0200

    feat(plc4go/cbus): properly implemented Discoverer
---
 plc4go/internal/cbus/Discoverer.go | 266 ++++++++++++++++++++-----------------
 1 file changed, 141 insertions(+), 125 deletions(-)

diff --git a/plc4go/internal/cbus/Discoverer.go b/plc4go/internal/cbus/Discoverer.go
index fdce0b5b7..0713ff658 100644
--- a/plc4go/internal/cbus/Discoverer.go
+++ b/plc4go/internal/cbus/Discoverer.go
@@ -35,7 +35,6 @@ import (
 	"github.com/apache/plc4x/plc4go/spi/transports"
 	"github.com/apache/plc4x/plc4go/spi/utils"
 
-	"github.com/pkg/errors"
 	"github.com/rs/zerolog/log"
 )
 
@@ -73,147 +72,164 @@ func (d *Discoverer) Discover(callback func(event apiModel.PlcDiscoveryEvent), d
 		interfaces = allInterfaces
 	}
 
-	var tranportInstances []transports.TransportInstance
+	transportInstances := make(chan transports.TransportInstance)
 	// Iterate over all network devices of this system.
-	for _, interf := range interfaces {
-		addrs, err := interf.Addrs()
+	for _, netInterface := range interfaces {
+		addrs, err := netInterface.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()
+		go func(netInterface net.Interface) {
+			// 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 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 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() {
+					continue
+				}
+				addresses, err := utils.GetIPAddresses(context.TODO(), netInterface, false)
 				if err != nil {
-					log.Warn().Err(err).Msgf("Can't get addresses for %s", interf)
+					log.Warn().Err(err).Msgf("Can't get addresses for %s", netInterface)
 					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
-					}
+				go func() {
+					for ip := range addresses {
+						go func(ip net.IP) {
+							// 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")
+								return
+							}
+							transportInstance, err := tcpTransport.CreateTransportInstance(*connectionUrl, nil)
+							if err != nil {
+								log.Error().Err(err).Msgf("Error creating transport instance")
+								return
+							}
+							log.Trace().Msgf("trying %s", connectionUrl)
+							err = transportInstance.Connect()
+							if err != nil {
+								secondErr := transportInstance.Connect()
+								if secondErr != nil {
+									log.Trace().Err(err).Msgf("Error connecting transport instance")
+									return
+								}
+							}
 
-					tranportInstances = append(tranportInstances, transportInstance)
-				}
+							transportInstances <- transportInstance
+						}(utils.DuplicateIP(ip))
+					}
+				}()
 			}
-		}
+		}(netInterface)
 	}
 
-	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")
-		}
+	go func() {
+		for transportInstance := range transportInstances {
+			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 {
+				log.Debug().Err(err).Msg("Error connecting")
+				continue
+			}
 
-		// 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 {
+			// 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 receivedAlpha := replyOrConfirmationConfirmation.GetConfirmation().GetAlpha(); receivedAlpha != nil && alpha.GetCharacter() != receivedAlpha.GetCharacter() {
+							continue
+						}
+						embeddedReply, ok := replyOrConfirmationConfirmation.GetEmbeddedReply().(readWriteModel.ReplyOrConfirmationReplyExactly)
+						if !ok {
+							continue
+						}
+						encodedReply, ok := embeddedReply.GetReply().(readWriteModel.ReplyEncodedReplyExactly)
+						if !ok {
+							continue
+						}
+						encodedReplyCALReply, ok := encodedReply.GetEncodedReply().(readWriteModel.EncodedReplyCALReplyExactly)
+						if !ok {
+							continue
+						}
+						calDataIdentifyReply, ok := encodedReplyCALReply.GetCalReply().GetCalData().(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://%s", 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
-					}
-					replyOrConfirmationConfirmation, ok := messageToClient.GetReply().(readWriteModel.ReplyOrConfirmationConfirmationExactly)
-					if !ok {
+					case <-timeout.C:
+						timeout.Stop()
 						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
 }


[plc4x] 06/06: feat(plc4xbrowser): added discover 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 e94ed048d96daf25d2f54a7517db563b4936273c
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 18:40:51 2022 +0200

    feat(plc4xbrowser): added discover command
---
 plc4go/internal/cbus/Discoverer.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/plc4go/internal/cbus/Discoverer.go b/plc4go/internal/cbus/Discoverer.go
index 0713ff658..907b66cb0 100644
--- a/plc4go/internal/cbus/Discoverer.go
+++ b/plc4go/internal/cbus/Discoverer.go
@@ -103,7 +103,7 @@ func (d *Discoverer) Discover(callback func(event apiModel.PlcDiscoveryEvent), d
 				}
 				addresses, err := utils.GetIPAddresses(context.TODO(), netInterface, false)
 				if err != nil {
-					log.Warn().Err(err).Msgf("Can't get addresses for %s", netInterface)
+					log.Warn().Err(err).Msgf("Can't get addresses for %v", netInterface)
 					continue
 				}
 				go func() {


[plc4x] 03/06: feat(plc4go/spi): implemented stringer for Default driver

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

    feat(plc4go/spi): implemented stringer for Default driver
---
 plc4go/spi/default/DefaultDriver.go | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/plc4go/spi/default/DefaultDriver.go b/plc4go/spi/default/DefaultDriver.go
index db6eaae64..cb19a3430 100644
--- a/plc4go/spi/default/DefaultDriver.go
+++ b/plc4go/spi/default/DefaultDriver.go
@@ -20,6 +20,7 @@
 package _default
 
 import (
+	"fmt"
 	"github.com/apache/plc4x/plc4go/pkg/api"
 	apiModel "github.com/apache/plc4x/plc4go/pkg/api/model"
 	"github.com/apache/plc4x/plc4go/spi"
@@ -29,6 +30,7 @@ import (
 )
 
 type DefaultDriver interface {
+	fmt.Stringer
 	plc4go.PlcDriver
 	spi.PlcDiscoverer
 	GetPlcFieldHandler() spi.PlcFieldHandler
@@ -94,3 +96,7 @@ func (d *defaultDriver) Discover(_ func(event apiModel.PlcDiscoveryEvent), _ ...
 func (d *defaultDriver) GetPlcFieldHandler() spi.PlcFieldHandler {
 	return d.plcFieldHandler
 }
+
+func (d *defaultDriver) String() string {
+	return fmt.Sprintf("%s (%s) [%s]", d.protocolName, d.protocolCode, d.defaultTransport)
+}


[plc4x] 05/06: feat(plc4xbrowser): added discover 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 dc05bfa79a61b222bf7c94906da1b0a89a581d52
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Aug 12 18:38:30 2022 +0200

    feat(plc4xbrowser): added discover command
---
 plc4go/tools/plc4xbrowser/ui/actions.go  | 45 ++++++++++++++++++++++++--------
 plc4go/tools/plc4xbrowser/ui/commands.go | 22 ++++++++++++++++
 plc4go/tools/plc4xbrowser/ui/common.go   |  4 ++-
 plc4go/tools/plc4xbrowser/ui/ui.go       |  9 +++++--
 4 files changed, 66 insertions(+), 14 deletions(-)

diff --git a/plc4go/tools/plc4xbrowser/ui/actions.go b/plc4go/tools/plc4xbrowser/ui/actions.go
index 25fce0a5f..144201ff6 100644
--- a/plc4go/tools/plc4xbrowser/ui/actions.go
+++ b/plc4go/tools/plc4xbrowser/ui/actions.go
@@ -80,23 +80,46 @@ func validateDriverParam(driver string) error {
 	return errors.Errorf("protocol %s not found", driver)
 }
 
-func registerDriver(driver string) error {
-	switch driver {
+var tcpRegistered, udpRegistered bool
+
+func registerDriver(driverId string) error {
+	if _, ok := registeredDrivers[driverId]; ok {
+		return errors.Errorf("%s already registered", driverId)
+	}
+	var driver plc4go.PlcDriver
+	switch driverId {
 	case "ads":
-		driverManager.RegisterDriver(ads.NewDriver())
-		transports.RegisterTcpTransport(driverManager)
+		driver = ads.NewDriver()
+		driverManager.RegisterDriver(driver)
+		if !tcpRegistered {
+			transports.RegisterTcpTransport(driverManager)
+			tcpRegistered = true
+		}
 	case "bacnetip":
-		driverManager.RegisterDriver(bacnetip.NewDriver())
-		transports.RegisterUdpTransport(driverManager)
+		driver = bacnetip.NewDriver()
+		driverManager.RegisterDriver(driver)
+		if !udpRegistered {
+			transports.RegisterUdpTransport(driverManager)
+			udpRegistered = true
+		}
 	case "c-bus":
-		driverManager.RegisterDriver(cbus.NewDriver())
-		transports.RegisterTcpTransport(driverManager)
+		driver = cbus.NewDriver()
+		driverManager.RegisterDriver(driver)
+		if !tcpRegistered {
+			transports.RegisterTcpTransport(driverManager)
+			tcpRegistered = true
+		}
 	case "s7":
-		driverManager.RegisterDriver(s7.NewDriver())
-		transports.RegisterTcpTransport(driverManager)
+		driver = s7.NewDriver()
+		driverManager.RegisterDriver(driver)
+		if !tcpRegistered {
+			transports.RegisterTcpTransport(driverManager)
+			tcpRegistered = true
+		}
 	default:
-		return errors.Errorf("Unknown driver %s", driver)
+		return errors.Errorf("Unknown driver %s", driverId)
 	}
+	registeredDrivers[driverId] = driver
 	go driverAdded(driver)
 	return nil
 }
diff --git a/plc4go/tools/plc4xbrowser/ui/commands.go b/plc4go/tools/plc4xbrowser/ui/commands.go
index 1daee5800..95c48e2a4 100644
--- a/plc4go/tools/plc4xbrowser/ui/commands.go
+++ b/plc4go/tools/plc4xbrowser/ui/commands.go
@@ -44,6 +44,28 @@ var commands = map[inputMode]Command{
 var rootCommand = Command{
 	Name: rootCommandIndicator,
 	subCommands: []Command{
+		{
+			Name:        "discover",
+			Description: "Discovers devices",
+			action: func(_ Command, driverId string) error {
+				if driver, ok := registeredDrivers[driverId]; ok {
+					if !driver.SupportsDiscovery() {
+						return errors.Errorf("%s doesn't support discovery", driverId)
+					}
+					return driver.Discover(func(event model.PlcDiscoveryEvent) {
+						_, _ = fmt.Fprintf(messageOutput, "%v\n", event)
+					})
+				} else {
+					return errors.Errorf("%s not registered", driverId)
+				}
+			},
+			parameterSuggestions: func(currentText string) (entries []string) {
+				for _, protocol := range protocolList {
+					entries = append(entries, protocol)
+				}
+				return
+			},
+		},
 		{
 			Name:        "connect",
 			Description: "Connects to a device",
diff --git a/plc4go/tools/plc4xbrowser/ui/common.go b/plc4go/tools/plc4xbrowser/ui/common.go
index 78e5f7070..ee7b61a59 100644
--- a/plc4go/tools/plc4xbrowser/ui/common.go
+++ b/plc4go/tools/plc4xbrowser/ui/common.go
@@ -36,7 +36,8 @@ var protocolList = strings.Split(protocols, ",")
 var plc4xBrowserLog = zerolog.Nop()
 
 var driverManager plc4go.PlcDriverManager
-var driverAdded func(string)
+var registeredDrivers map[string]plc4go.PlcDriver
+var driverAdded func(driver plc4go.PlcDriver)
 var connections map[string]plc4go.PlcConnection
 var connectionsChanged func()
 
@@ -63,6 +64,7 @@ const (
 
 func init() {
 	hasShutdown = false
+	registeredDrivers = make(map[string]plc4go.PlcDriver)
 	connections = make(map[string]plc4go.PlcConnection)
 }
 
diff --git a/plc4go/tools/plc4xbrowser/ui/ui.go b/plc4go/tools/plc4xbrowser/ui/ui.go
index 7d3c30caf..fc53e0e6d 100644
--- a/plc4go/tools/plc4xbrowser/ui/ui.go
+++ b/plc4go/tools/plc4xbrowser/ui/ui.go
@@ -21,6 +21,7 @@ package ui
 
 import (
 	"fmt"
+	plc4go "github.com/apache/plc4x/plc4go/pkg/api"
 	"github.com/apache/plc4x/plc4go/pkg/api/model"
 	"github.com/gdamore/tcell/v2"
 	"github.com/pkg/errors"
@@ -92,9 +93,9 @@ func buildConnectionArea(newPrimitive func(text string) tview.Primitive, applica
 				AddItem(registeredDriverAreaHeader, 0, 0, 1, 1, 0, 0, false)
 			{
 				driverList := tview.NewList()
-				driverAdded = func(driver string) {
+				driverAdded = func(driver plc4go.PlcDriver) {
 					application.QueueUpdateDraw(func() {
-						driverList.AddItem(driver, "", 0x0, func() {
+						driverList.AddItem(driver.GetProtocolCode(), tview.Escape(fmt.Sprintf("%s", driver)), 0x0, func() {
 							//TODO: disconnect popup
 						})
 					})
@@ -138,6 +139,10 @@ func buildCommandArea(newPrimitive func(text string) tview.Primitive, applicatio
 				commandInputField.SetText("")
 				application.SetFocus(commandInputField)
 				return nil
+			case tcell.KeyCtrlD:
+				// TODO: maybe add a modal here
+				application.Stop()
+				return nil
 			}
 			return event
 		})