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/08 10:45:37 UTC

[plc4x] branch develop updated (4e4395133 -> a3d68a2d1)

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 4e4395133 fix(cbus): implementd InterfaceRequirementsTest and fixed bugs
     new 456ba1d50 feat(plc4go/cbus): added new info field for browse
     new a3d68a2d1 feat(plc4xbrowser): added browse support

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/cbus/Field.go            | 99 ++++++++++++++++++++++++++++++--
 plc4go/internal/cbus/FieldHandler.go     | 60 ++++++++++++++++++-
 plc4go/internal/cbus/fieldtype_string.go |  5 +-
 plc4go/tools/plc4xbrowser/commands.go    | 67 ++++++++++++++++++++-
 4 files changed, 222 insertions(+), 9 deletions(-)


[plc4x] 01/02: feat(plc4go/cbus): added new info field for browse

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 456ba1d50889e7b9c52621caff3a7eb824fa2a40
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Mon Aug 8 12:45:04 2022 +0200

    feat(plc4go/cbus): added new info field for browse
---
 plc4go/internal/cbus/Field.go            | 99 ++++++++++++++++++++++++++++++--
 plc4go/internal/cbus/FieldHandler.go     | 60 ++++++++++++++++++-
 plc4go/internal/cbus/fieldtype_string.go |  5 +-
 3 files changed, 156 insertions(+), 8 deletions(-)

diff --git a/plc4go/internal/cbus/Field.go b/plc4go/internal/cbus/Field.go
index c6bfebdc9..66145fb56 100644
--- a/plc4go/internal/cbus/Field.go
+++ b/plc4go/internal/cbus/Field.go
@@ -33,6 +33,16 @@ const (
 	StatusRequestTypeLevel
 )
 
+func (s StatusRequestType) String() string {
+	switch s {
+	case StatusRequestTypeBinaryState:
+		return "StatusRequestTypeBinaryState"
+	case StatusRequestTypeLevel:
+		return "StatusRequestTypeLevel"
+	}
+	return ""
+}
+
 // StatusField can be used to query status using a P-to-MP-StatusRequest command
 type StatusField interface {
 	model.PlcField
@@ -51,9 +61,14 @@ func NewStatusField(statusRequestType StatusRequestType, startingGroupAddressLab
 	}
 }
 
+type CalField interface {
+	GetUnitAddress() readWriteModel.UnitAddress
+}
+
 // CALRecallField can be used to get device/network management fields
 type CALRecallField interface {
 	model.PlcField
+	CalField
 	GetParameter() readWriteModel.Parameter
 	GetCount() uint8
 }
@@ -71,6 +86,7 @@ func NewCALRecallField(unitAddress readWriteModel.UnitAddress, parameter readWri
 // CALIdentifyField can be used to get device/network management fields
 type CALIdentifyField interface {
 	model.PlcField
+	CalField
 	GetAttribute() readWriteModel.Attribute
 }
 
@@ -86,6 +102,7 @@ func NewCALIdentifyField(unitAddress readWriteModel.UnitAddress, attribute readW
 // CALGetstatusField can be used to get device/network management fields
 type CALGetstatusField interface {
 	model.PlcField
+	CalField
 	GetParameter() readWriteModel.Parameter
 	GetCount() uint8
 }
@@ -119,6 +136,7 @@ func NewSALMonitorField(unitAddress readWriteModel.UnitAddress, application read
 // MMIMonitorField can be used to monitor mmi fields
 type MMIMonitorField interface {
 	model.PlcField
+	CalField
 	GetUnitAddress() readWriteModel.UnitAddress
 	GetApplication() readWriteModel.ApplicationIdContainer
 }
@@ -132,6 +150,22 @@ func NewMMIMonitorField(unitAddress readWriteModel.UnitAddress, application read
 	}
 }
 
+// UnitInfoField can be used to get information about unit(s)
+type UnitInfoField interface {
+	model.PlcField
+	GetUnitAddress() *readWriteModel.UnitAddress
+	GetAttribute() *readWriteModel.Attribute
+}
+
+func NewUnitInfoField(unitAddress *readWriteModel.UnitAddress, attribute *readWriteModel.Attribute, numElements uint16) UnitInfoField {
+	return &unitInfoField{
+		unitAddress: unitAddress,
+		fieldType:   UNIT_INFO,
+		attribute:   attribute,
+		numElements: numElements,
+	}
+}
+
 ///////////////////////////////////////
 ///////////////////////////////////////
 //
@@ -187,6 +221,13 @@ type mmiMonitorField struct {
 	numElements uint16
 }
 
+type unitInfoField struct {
+	fieldType   FieldType
+	unitAddress *readWriteModel.UnitAddress
+	attribute   *readWriteModel.Attribute
+	numElements uint16
+}
+
 //
 // Internal section
 //
@@ -222,8 +263,7 @@ func (m statusField) Serialize(writeBuffer utils.WriteBuffer) error {
 		return err
 	}
 
-	// TODO: add string representation
-	if err := writeBuffer.WriteUint8("statusRequestType", 8, uint8(m.statusRequestType)); err != nil {
+	if err := writeBuffer.WriteUint8("statusRequestType", 8, uint8(m.statusRequestType), utils.WithAdditionalStringRepresentation(m.statusRequestType.String())); err != nil {
 		return err
 	}
 	if m.startingGroupAddressLabel != nil {
@@ -241,6 +281,10 @@ func (m statusField) Serialize(writeBuffer utils.WriteBuffer) error {
 	return nil
 }
 
+func (m calField) GetUnitAddress() readWriteModel.UnitAddress {
+	return m.unitAddress
+}
+
 func (m calField) Serialize(writeBuffer utils.WriteBuffer) error {
 	return m.unitAddress.Serialize(writeBuffer)
 }
@@ -274,9 +318,10 @@ func (m calRecallField) Serialize(writeBuffer utils.WriteBuffer) error {
 		return err
 	}
 
-	if err := writeBuffer.WriteUint8("parameter", 8, uint8(m.parameter), utils.WithAdditionalStringRepresentation(m.parameter.String())); err != nil {
+	if err := m.parameter.Serialize(writeBuffer); err != nil {
 		return err
 	}
+
 	if err := writeBuffer.WriteUint8("count", 8, m.count); err != nil {
 		return err
 	}
@@ -312,7 +357,7 @@ func (c calIdentifyField) Serialize(writeBuffer utils.WriteBuffer) error {
 		return err
 	}
 
-	if err := writeBuffer.WriteUint8("attribute", 8, uint8(c.attribute), utils.WithAdditionalStringRepresentation(c.attribute.String())); err != nil {
+	if err := c.attribute.Serialize(writeBuffer); err != nil {
 		return err
 	}
 
@@ -351,9 +396,10 @@ func (c calGetstatusField) Serialize(writeBuffer utils.WriteBuffer) error {
 		return err
 	}
 
-	if err := writeBuffer.WriteUint8("parameter", 8, uint8(c.parameter), utils.WithAdditionalStringRepresentation(c.parameter.String())); err != nil {
+	if err := c.parameter.Serialize(writeBuffer); err != nil {
 		return err
 	}
+
 	if err := writeBuffer.WriteUint8("count", 8, c.count); err != nil {
 		return err
 	}
@@ -439,3 +485,46 @@ func (m mmiMonitorField) Serialize(writeBuffer utils.WriteBuffer) error {
 	}
 	return nil
 }
+
+func (u unitInfoField) GetUnitAddress() *readWriteModel.UnitAddress {
+	return u.unitAddress
+}
+
+func (u unitInfoField) GetAttribute() *readWriteModel.Attribute {
+	return u.attribute
+}
+
+func (u unitInfoField) GetAddressString() string {
+	return fmt.Sprintf("%d[%d]", u.fieldType, u.numElements)
+}
+
+func (u unitInfoField) GetTypeName() string {
+	return u.fieldType.GetName()
+}
+
+func (u unitInfoField) GetQuantity() uint16 {
+	return u.numElements
+}
+
+func (u unitInfoField) Serialize(writeBuffer utils.WriteBuffer) error {
+	if err := writeBuffer.PushContext(u.fieldType.GetName()); err != nil {
+		return err
+	}
+
+	if unitAddress := u.unitAddress; unitAddress != nil {
+		if err := (*unitAddress).Serialize(writeBuffer); err != nil {
+			return err
+		}
+	}
+
+	if attribute := u.attribute; attribute != nil {
+		if err := (*attribute).Serialize(writeBuffer); err != nil {
+			return err
+		}
+	}
+
+	if err := writeBuffer.PopContext(u.fieldType.GetName()); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/plc4go/internal/cbus/FieldHandler.go b/plc4go/internal/cbus/FieldHandler.go
index ece7035b4..c62cb7a8f 100644
--- a/plc4go/internal/cbus/FieldHandler.go
+++ b/plc4go/internal/cbus/FieldHandler.go
@@ -40,6 +40,7 @@ const (
 	CAL_GETSTATUS
 	SAL_MONITOR
 	MMI_STATUS_MONITOR
+	UNIT_INFO
 )
 
 func (i FieldType) GetName() string {
@@ -51,14 +52,16 @@ type FieldHandler struct {
 	calPattern           *regexp.Regexp
 	salMonitorPattern    *regexp.Regexp
 	mmiMonitorPattern    *regexp.Regexp
+	unityQuery           *regexp.Regexp
 }
 
 func NewFieldHandler() FieldHandler {
 	return FieldHandler{
 		statusRequestPattern: regexp.MustCompile(`^status/(?P<statusRequestType>(?P<binary>binary)|level=0x(?P<startingGroupAddressLabel>00|20|40|60|80|A0|C0|E0))/(?P<application>.*)`),
-		calPattern:           regexp.MustCompile(`^cal/(?P<unitAddress>.*)/(?P<calType>recall=\[(?P<recallParamNo>[\w\d]+), ?(?P<recallCount>\d+)]|identify=\[(?P<identifyAttribute>[\w\d]+)]|getstatus=\[(?P<getstatusParamNo>[\w\d]+), ?(?P<getstatusCount>\d+)])`),
+		calPattern:           regexp.MustCompile(`^cal/(?P<unitAddress>.*)/(?P<calType>recall=\[(?P<recallParamNo>\w+), ?(?P<recallCount>\d+)]|identify=(?P<identifyAttribute>\w+)|getstatus=(?P<getstatusParamNo>\w+), ?(?P<getstatusCount>\d+))`),
 		salMonitorPattern:    regexp.MustCompile(`^salmonitor/(?P<unitAddress>.*)/(?P<application>.*)`),
 		mmiMonitorPattern:    regexp.MustCompile(`^mmimonitor/(?P<unitAddress>.*)/(?P<application>.*)`),
+		unityQuery:           regexp.MustCompile(`^info/(?P<unitAddress>.*)/(?P<identifyAttribute>\w+)`),
 	}
 }
 
@@ -275,6 +278,61 @@ func (m FieldHandler) ParseQuery(query string) (model.PlcField, error) {
 		}
 
 		return NewMMIMonitorField(unitAddress, application, 1), nil
+	} else if match := utils.GetSubgroupMatches(m.unityQuery, query); match != nil {
+		var unitAddress *readWriteModel.UnitAddress
+		unitAddressArgument := match["unitAddress"]
+		if unitAddressArgument == "*" {
+			unitAddress = nil
+		} else if strings.HasPrefix(unitAddressArgument, "0x") {
+			decodedHex, err := hex.DecodeString(unitAddressArgument[2:])
+			if err != nil {
+				return nil, errors.Wrap(err, "Not a valid hex")
+			}
+			if len(decodedHex) != 1 {
+				return nil, errors.Errorf("Hex must be exatly one byte")
+			}
+			var unitAddressVar readWriteModel.UnitAddress
+			unitAddressVar = readWriteModel.NewUnitAddress(decodedHex[0])
+			unitAddress = &unitAddressVar
+		} else {
+			atoi, err := strconv.ParseUint(unitAddressArgument, 10, 8)
+			if err != nil {
+				return nil, errors.Errorf("Unknown unit address %s", unitAddressArgument)
+			}
+			var unitAddressVar readWriteModel.UnitAddress
+			unitAddressVar = readWriteModel.NewUnitAddress(byte(atoi))
+			unitAddress = &unitAddressVar
+		}
+
+		var attribute *readWriteModel.Attribute
+		attributeArgument := match["identifyAttribute"]
+		if strings.HasPrefix(attributeArgument, "0x") {
+			decodedHex, err := hex.DecodeString(attributeArgument[2:])
+			if err != nil {
+				return nil, errors.Wrap(err, "Not a valid hex")
+			}
+			if len(decodedHex) != 1 {
+				return nil, errors.Errorf("Hex must be exatly one byte")
+			}
+			var attributeVar readWriteModel.Attribute
+			attributeVar = readWriteModel.Attribute(decodedHex[0])
+			attribute = &attributeVar
+		} else {
+			if atoi, err := strconv.ParseUint(attributeArgument, 10, 8); err == nil {
+				var attributeVar readWriteModel.Attribute
+				attributeVar = readWriteModel.Attribute(atoi)
+				attribute = &attributeVar
+			} else {
+				parameterByName, ok := readWriteModel.AttributeByName(attributeArgument)
+				if !ok {
+					return nil, errors.Errorf("Unknown attributeArgument %s", attributeArgument)
+				}
+				var attributeVar readWriteModel.Attribute
+				attributeVar = parameterByName
+				attribute = &attributeVar
+			}
+		}
+		return NewUnitInfoField(unitAddress, attribute, 1), nil
 	} else {
 		return nil, errors.Errorf("Unable to parse %s", query)
 	}
diff --git a/plc4go/internal/cbus/fieldtype_string.go b/plc4go/internal/cbus/fieldtype_string.go
index dd663f49a..0456cc67d 100644
--- a/plc4go/internal/cbus/fieldtype_string.go
+++ b/plc4go/internal/cbus/fieldtype_string.go
@@ -31,11 +31,12 @@ func _() {
 	_ = x[CAL_GETSTATUS-3]
 	_ = x[SAL_MONITOR-4]
 	_ = x[MMI_STATUS_MONITOR-5]
+	_ = x[UNIT_INFO-6]
 }
 
-const _FieldType_name = "STATUSCAL_RECALLCAL_IDENTIFYCAL_GETSTATUSSAL_MONITORMMI_STATUS_MONITOR"
+const _FieldType_name = "STATUSCAL_RECALLCAL_IDENTIFYCAL_GETSTATUSSAL_MONITORMMI_STATUS_MONITORUNIT_INFO"
 
-var _FieldType_index = [...]uint8{0, 6, 16, 28, 41, 52, 70}
+var _FieldType_index = [...]uint8{0, 6, 16, 28, 41, 52, 70, 79}
 
 func (i FieldType) String() string {
 	if i >= FieldType(len(_FieldType_index)-1) {


[plc4x] 02/02: feat(plc4xbrowser): added browse support

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 a3d68a2d135d7f2d5ea4f190e316c1614507c438
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Mon Aug 8 12:45:31 2022 +0200

    feat(plc4xbrowser): added browse support
---
 plc4go/tools/plc4xbrowser/commands.go | 67 ++++++++++++++++++++++++++++++++++-
 1 file changed, 66 insertions(+), 1 deletion(-)

diff --git a/plc4go/tools/plc4xbrowser/commands.go b/plc4go/tools/plc4xbrowser/commands.go
index 15661245a..fef5f58a8 100644
--- a/plc4go/tools/plc4xbrowser/commands.go
+++ b/plc4go/tools/plc4xbrowser/commands.go
@@ -211,7 +211,7 @@ var rootCommand = Command{
 					}
 					plc4xBrowserLog.Debug().Msgf("write took %f seconds", time.Now().Sub(start).Seconds())
 					if err := writeRequestResult.GetErr(); err != nil {
-						return errors.Wrapf(err, "%s error reading", connectionsString)
+						return errors.Wrapf(err, "%s error writing", connectionsString)
 					}
 					numberOfMessagesReceived++
 					messageReceived(numberOfMessagesReceived, time.Now(), writeRequestResult.GetResponse())
@@ -233,6 +233,71 @@ var rootCommand = Command{
 				return
 			},
 		},
+		{
+			Name:        "browse",
+			Description: "Starts a browse request (switched mode to browse edit)",
+			action: func(_ Command, connectionsString string) error {
+				if connection, ok := connections[connectionsString]; !ok {
+					return errors.Errorf("%s not connected", connectionsString)
+				} else {
+					return errors.Errorf("%s mode switch not yet implemented", connection)
+				}
+			},
+			parameterSuggestions: func(currentText string) (entries []string) {
+				for connectionsString, _ := range connections {
+					entries = append(entries, connectionsString)
+				}
+				return
+			},
+		},
+		{
+			Name:        "browse-direct",
+			Description: "Builds a browse request with the supplied field",
+			action: func(c Command, connectionsStringAndFieldQuery string) error {
+				split := strings.Split(connectionsStringAndFieldQuery, " ")
+				if len(split) != 2 {
+					return errors.Errorf("%s expects exactly three arguments [connection url] [fieldQuery]", c)
+				}
+				connectionsString := split[0]
+				if connection, ok := connections[connectionsString]; !ok {
+					return errors.Errorf("%s not connected", connectionsString)
+				} else {
+					start := time.Now()
+					writeRequest, err := connection.BrowseRequestBuilder().
+						AddItem("writeField", split[1]).
+						Build()
+					if err != nil {
+						return errors.Wrapf(err, "%s can't browse", connectionsString)
+					}
+					writeRequestResult := <-writeRequest.Execute()
+					if err := writeRequestResult.GetErr(); err != nil {
+						return errors.Wrapf(err, "%s can't browse", connectionsString)
+					}
+					plc4xBrowserLog.Debug().Msgf("write took %f seconds", time.Now().Sub(start).Seconds())
+					if err := writeRequestResult.GetErr(); err != nil {
+						return errors.Wrapf(err, "%s error browse", connectionsString)
+					}
+					numberOfMessagesReceived++
+					messageReceived(numberOfMessagesReceived, time.Now(), writeRequestResult.GetResponse())
+				}
+				return nil
+			},
+			parameterSuggestions: func(currentText string) (entries []string) {
+				for connectionsString, _ := range connections {
+					if strings.HasPrefix(currentText, connectionsString+"") {
+						parse, _ := url.Parse(connectionsString)
+						switch parse.Scheme {
+						// TODO: add to protocol suggestor so it can be reused.
+						case "c-bus":
+							entries = append(entries, connectionsString+" info/*/*")
+						}
+					} else {
+						entries = append(entries, connectionsString)
+					}
+				}
+				return
+			},
+		},
 		{
 			Name:        "register",
 			Description: "register a driver in the subsystem",