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
The following commit(s) were added to refs/heads/develop by this push:
new 409175c plc4go: implement a json based read buffer
409175c is described below
commit 409175cf2951d37bde3ad79778d7001d36f7a463
Author: Sebastian Rühl <[email protected]>
AuthorDate: Wed Apr 21 10:37:55 2021 +0200
plc4go: implement a json based read buffer
---
plc4go/internal/plc4go/s7/s7Io_test.go | 16 +-
plc4go/internal/plc4go/spi/utils/ReadBuffer.go | 2 +-
.../plc4go/spi/utils/ReadBufferJsonBased.go | 449 +++++++++++++++++++++
3 files changed, 464 insertions(+), 3 deletions(-)
diff --git a/plc4go/internal/plc4go/s7/s7Io_test.go
b/plc4go/internal/plc4go/s7/s7Io_test.go
index c3ffdef..4dacc80 100644
--- a/plc4go/internal/plc4go/s7/s7Io_test.go
+++ b/plc4go/internal/plc4go/s7/s7Io_test.go
@@ -408,14 +408,26 @@ func TestS7MessageBytes(t *testing.T) {
}
}
{
- // and at least a roundtip
+ // and at least a for xml roundtip
reader := strings.NewReader(tt.wantStringXml)
readBuffer := utils.NewXmlReadBuffer(reader)
if got, err :=
model.TPKTPacketParse(readBuffer); err != nil || !reflect.DeepEqual(got,
tt.args.debuggable) {
if err != nil {
t.Error(err)
} else {
- t.Errorf("Roundtrip = '\n%v\n',
want '\n%v\n'", got, tt.wantDump)
+ t.Errorf("Roundtrip(xml) =
'\n%v\n', want '\n%v\n'", got, tt.wantDump)
+ }
+ }
+ }
+ {
+ // and at least a for json roundtip
+ reader := strings.NewReader(tt.wantStringJson)
+ readBuffer := utils.NewJsonReadBuffer(reader)
+ if got, err :=
model.TPKTPacketParse(readBuffer); err != nil || !reflect.DeepEqual(got,
tt.args.debuggable) {
+ if err != nil {
+ t.Error(err)
+ } else {
+ t.Errorf("Roundtrip(json) =
'\n%v\n', want '\n%v\n'", got, tt.wantDump)
}
}
}
diff --git a/plc4go/internal/plc4go/spi/utils/ReadBuffer.go
b/plc4go/internal/plc4go/spi/utils/ReadBuffer.go
index 3404f44..10c0521 100644
--- a/plc4go/internal/plc4go/spi/utils/ReadBuffer.go
+++ b/plc4go/internal/plc4go/spi/utils/ReadBuffer.go
@@ -41,7 +41,7 @@ type ReadBuffer interface {
ReadInt64(logicalName string, bitLength uint8, readerArgs
...WithReaderArgs) (int64, error)
ReadBigInt(logicalName string, bitLength uint64, readerArgs
...WithReaderArgs) (*big.Int, error)
ReadFloat32(logicalName string, signed bool, exponentBitLength uint8,
mantissaBitLength uint8, readerArgs ...WithReaderArgs) (float32, error)
- ReadFloat64(logicalName string, singed bool, exponentBitLength uint8,
mantissaBitLength uint8, readerArgs ...WithReaderArgs) (float64, error)
+ ReadFloat64(logicalName string, signed bool, exponentBitLength uint8,
mantissaBitLength uint8, readerArgs ...WithReaderArgs) (float64, error)
ReadBigFloat(logicalName string, signed bool, exponentBitLength uint8,
mantissaBitLength uint8, readerArgs ...WithReaderArgs) (*big.Float, error)
ReadString(logicalName string, bitLength uint32, readerArgs
...WithReaderArgs) (string, error)
// CloseContext signals that we expect the end of the context with the
supplied logical name
diff --git a/plc4go/internal/plc4go/spi/utils/ReadBufferJsonBased.go
b/plc4go/internal/plc4go/spi/utils/ReadBufferJsonBased.go
new file mode 100644
index 0000000..51942e6
--- /dev/null
+++ b/plc4go/internal/plc4go/spi/utils/ReadBufferJsonBased.go
@@ -0,0 +1,449 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package utils
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/pkg/errors"
+ "io"
+ "math/big"
+)
+
+// NewJsonReadBuffer return as ReadBuffer which doesn't validate attributes
and lists
+func NewJsonReadBuffer(reader io.Reader) ReadBuffer {
+ decoder := json.NewDecoder(reader)
+ var rootElement map[string]interface{}
+ err := decoder.Decode(&rootElement)
+ return &jsonReadBuffer{
+ rootElement: rootElement,
+ pos: 1,
+ doValidateAttr: false,
+ err: err,
+ }
+}
+
+// NewStrictJsonReadBuffer return as ReadBuffer which does validate attributes
on the setting
+func NewStrictJsonReadBuffer(reader io.Reader, validateAttr bool) ReadBuffer {
+ decoder := json.NewDecoder(reader)
+ var rootElement map[string]interface{}
+ err := decoder.Decode(&rootElement)
+ return &jsonReadBuffer{
+ rootElement: rootElement,
+ pos: 1,
+ doValidateAttr: validateAttr,
+ err: err,
+ }
+}
+
+///////////////////////////////////////
+///////////////////////////////////////
+//
+// Internal section
+//
+
+type jsonReadBuffer struct {
+ bufferCommons
+ stack
+ rootElement map[string]interface{}
+ pos uint
+ doValidateAttr bool
+ err error
+}
+
+//
+// Internal section
+//
+///////////////////////////////////////
+///////////////////////////////////////
+
+func (j *jsonReadBuffer) GetPos() uint16 {
+ return uint16(j.pos / 8)
+}
+
+func (j *jsonReadBuffer) HasMore(bitLength uint8) bool {
+ // TODO: work with x.InputOffset() and check if we are at EOF
+ return true
+}
+
+func (j *jsonReadBuffer) PullContext(logicalName string, readerArgs
...WithReaderArgs) error {
+ if j.err != nil {
+ return j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ if j.Empty() {
+ if context, ok := j.rootElement[logicalName]; ok {
+ j.Push(context)
+ return nil
+ } else {
+ return errors.Errorf("Required context %s not found in
%v", logicalName, j.rootElement)
+ }
+ }
+ peek := j.Peek()
+ switch peek.(type) {
+ case []interface{}:
+ pop := j.Pop()
+ contextList := pop.([]interface{})
+ context := contextList[0].(map[string]interface{})
+ if len(contextList) < 2 {
+ j.Push(make([]interface{}, 0))
+ } else {
+ j.Push(contextList[1 : len(contextList)-1])
+ }
+ if subContext, ok := context[logicalName]; ok {
+ j.Push(subContext)
+ return nil
+ } else {
+ return errors.Errorf("Required context %s not found in
%v", logicalName, peek)
+ }
+ }
+ if context, ok := peek.(map[string]interface{})[logicalName]; ok {
+ j.Push(context)
+ return nil
+ } else {
+ return errors.Errorf("Required context %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadBit(logicalName string, readerArgs
...WithReaderArgs) (bool, error) {
+ if j.err != nil {
+ return false, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ j.move(1)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "bit", 1, readerArgs...); err != nil {
+ return false, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return value.(bool), nil
+ } else {
+ return false, errors.Errorf("Required element %s not found in
%v", logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadUint8(logicalName string, bitLength uint8,
readerArgs ...WithReaderArgs) (uint8, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "uint", bitLength, readerArgs...);
err != nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return uint8(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadUint16(logicalName string, bitLength uint8,
readerArgs ...WithReaderArgs) (uint16, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "uint", bitLength, readerArgs...);
err != nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return uint16(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadUint32(logicalName string, bitLength uint8,
readerArgs ...WithReaderArgs) (uint32, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "uint", bitLength, readerArgs...);
err != nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return uint32(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadUint64(logicalName string, bitLength uint8,
readerArgs ...WithReaderArgs) (uint64, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "uint", bitLength, readerArgs...);
err != nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return uint64(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadInt8(logicalName string, bitLength uint8,
readerArgs ...WithReaderArgs) (int8, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "int", bitLength, readerArgs...); err
!= nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return int8(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadInt16(logicalName string, bitLength uint8,
readerArgs ...WithReaderArgs) (int16, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "int", bitLength, readerArgs...); err
!= nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return int16(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadInt32(logicalName string, bitLength uint8,
readerArgs ...WithReaderArgs) (int32, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "int", bitLength, readerArgs...); err
!= nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return int32(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadInt64(logicalName string, bitLength uint8,
readerArgs ...WithReaderArgs) (int64, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "int", bitLength, readerArgs...); err
!= nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return int64(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadBigInt(logicalName string, bitLength uint64,
readerArgs ...WithReaderArgs) (*big.Int, error) {
+ if j.err != nil {
+ return nil, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ logicalName = j.sanitizeLogicalName(logicalName)
+ peek, element := j.getElement(logicalName)
+ // TODO: not enough bits
+ if err := j.validateAttr(element, "int", uint8(bitLength),
readerArgs...); err != nil {
+ return nil, err
+ }
+ newInt := big.NewInt(0)
+ if value, ok := element[logicalName]; ok {
+ newInt.SetString(value.(string), 10)
+ return newInt, nil
+ } else {
+ return newInt, errors.Errorf("Required element %s not found in
%v", logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadFloat32(logicalName string, signed bool,
exponentBitLength uint8, mantissaBitLength uint8, readerArgs ...WithReaderArgs)
(float32, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ bitLength := exponentBitLength + mantissaBitLength
+ if signed {
+ bitLength += 1
+ }
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "float", bitLength, readerArgs...);
err != nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return float32(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadFloat64(logicalName string, signed bool,
exponentBitLength uint8, mantissaBitLength uint8, readerArgs ...WithReaderArgs)
(float64, error) {
+ if j.err != nil {
+ return 0, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ bitLength := exponentBitLength + mantissaBitLength
+ if signed {
+ bitLength += 1
+ }
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "float", bitLength, readerArgs...);
err != nil {
+ return 0, err
+ }
+ if value, ok := element[logicalName]; ok {
+ return float64(value.(float64)), nil
+ } else {
+ return 0, errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadBigFloat(logicalName string, signed bool,
exponentBitLength uint8, mantissaBitLength uint8, readerArgs ...WithReaderArgs)
(*big.Float, error) {
+ if j.err != nil {
+ return nil, j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ bitLength := exponentBitLength + mantissaBitLength
+ if signed {
+ bitLength += 1
+ }
+ j.move(bitLength)
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "float", bitLength, readerArgs...);
err != nil {
+ return nil, err
+ }
+ newFloat := big.NewFloat(0)
+ if value, ok := element[logicalName]; ok {
+ newFloat.SetString(value.(string))
+ return newFloat, nil
+ } else {
+ return newFloat, errors.Errorf("Required element %s not found
in %v", logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) ReadString(logicalName string, bitLength uint32,
readerArgs ...WithReaderArgs) (string, error) {
+ if j.err != nil {
+ return "", j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ // TODO: not enough bits
+ j.move(uint8(bitLength))
+ peek, element := j.getElement(logicalName)
+ if err := j.validateAttr(element, "string", uint8(bitLength),
readerArgs...); err != nil {
+ return "", err
+ }
+ if value, ok := element[logicalName]; ok {
+ return value.(string), nil
+ } else {
+ return "", errors.Errorf("Required element %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) CloseContext(logicalName string, readerArgs
...WithReaderArgs) error {
+ if j.err != nil {
+ return j.err
+ }
+ logicalName = j.sanitizeLogicalName(logicalName)
+ if j.Empty() {
+ return errors.Errorf("Required context close %s not found in
%v", logicalName, j.rootElement)
+ }
+ // Delete us from stack
+ _ = j.Pop()
+ if j.Empty() {
+ return nil
+ }
+ peek := j.Peek()
+ switch peek.(type) {
+ case []interface{}:
+ return nil
+ }
+ if _, ok := peek.(map[string]interface{})[logicalName]; ok {
+ delete(peek.(map[string]interface{}), logicalName)
+ return nil
+ } else {
+ return errors.Errorf("Required context %s not found in %v",
logicalName, peek)
+ }
+}
+
+func (j *jsonReadBuffer) getElement(logicalName string) (interface{},
map[string]interface{}) {
+ logicalName = j.sanitizeLogicalName(logicalName)
+ peek := j.Peek()
+ var element map[string]interface{}
+ switch peek.(type) {
+ case []interface{}:
+ pop := j.Pop()
+ elementList := pop.([]interface{})
+ element = elementList[0].(map[string]interface{})
+ if len(elementList) < 2 {
+ j.Push(make([]interface{}, 0))
+ } else {
+ j.Push(elementList[1 : len(elementList)-1])
+ }
+ case []map[string]interface{}:
+ pop := j.Pop()
+ elementList := pop.([]map[string]interface{})
+ element = elementList[0]
+ j.Push(elementList[1 : len(elementList)-1])
+ case map[string]interface{}:
+ element = peek.(map[string]interface{})
+ default:
+ panic(fmt.Sprintf("Invalid state at %s with %v", logicalName,
element))
+ }
+ return peek, element
+}
+
+func (j *jsonReadBuffer) move(bits uint8) {
+ j.pos += uint(bits)
+}
+
+func (j *jsonReadBuffer) validateAttr(element map[string]interface{}, dataType
string, bitLength uint8, readerArgs ...WithReaderArgs) error {
+ if !j.doValidateAttr {
+ return nil
+ }
+ if value, ok := element[rwDataTypeKey]; !ok || value != dataType {
+ return errors.Errorf("Unexpected %s :%s. Want %s",
rwDataTypeKey, dataType, dataType)
+ }
+ if value, ok := element[rwBitLengthKey]; !ok || value !=
string(bitLength) {
+ return errors.Errorf("Unexpected %s :%d. Want %s",
rwBitLengthKey, bitLength, dataType)
+ }
+ return nil
+}