1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-05-14 19:29:11 +08:00
Manuel Imperiale e16a025fba
MF-886 - Add OPC-UA adapter (#878)
* NOISSUE- Add OPC-UA adapter

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* NOISSUE - Add opc-adapter PoC, docker and vendor

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Convert OPC messages to SenML

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add gopcua package

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* lora-adapter typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add OPC Reader

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Typo fix

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Typo fix

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Update copyright headers

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix reviews

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix reviews

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add opc config

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add all opc envars in the config

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Config typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add route map

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Use opcua package instead of opc

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix OPCUA typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Rm MQTT sub

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Move interefaces to root

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix revieews and typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Update Gopkg.toml

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add all envars into .env

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
2019-10-22 17:44:19 +02:00

170 lines
4.0 KiB
Go

// Copyright 2018-2019 opcua authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package ua
import (
"fmt"
"math"
"reflect"
"time"
"github.com/gopcua/opcua/errors"
)
var (
binaryDecoder = reflect.TypeOf((*BinaryDecoder)(nil)).Elem()
timeType = reflect.TypeOf(time.Time{})
)
func isBinaryDecoder(val reflect.Value) bool {
return val.Type().Implements(binaryDecoder)
}
func isTime(val reflect.Value) bool {
return val.Type() == timeType
}
type BinaryDecoder interface {
Decode([]byte) (int, error)
}
func Decode(b []byte, v interface{}) (int, error) {
val := reflect.ValueOf(v)
n, err := decode(b, val, val.Type().String())
if err != nil {
return n, err
}
return n, nil
}
func decode(b []byte, val reflect.Value, name string) (n int, err error) {
if debugCodec {
fmt.Printf("decode: %s has type %v and is a %s, %d bytes\n", name, val.Type(), val.Type().Kind(), len(b))
defer func() {
fmt.Printf("decode: decoded %d bytes into %s\n", n, name)
}()
}
buf := NewBuffer(b)
switch {
case isBinaryDecoder(val):
v := val.Interface().(BinaryDecoder)
return v.Decode(b)
case isTime(val):
val.Set(reflect.ValueOf(buf.ReadTime()))
default:
// fmt.Printf("decode: %s is a %s\n", name, val.Kind())
switch val.Kind() {
case reflect.Bool:
val.SetBool(buf.ReadBool())
case reflect.Int8:
val.SetInt(int64(buf.ReadInt8()))
case reflect.Uint8:
val.SetUint(uint64(buf.ReadByte()))
case reflect.Int16:
val.SetInt(int64(buf.ReadInt16()))
case reflect.Uint16:
val.SetUint(uint64(buf.ReadUint16()))
case reflect.Int32:
val.SetInt(int64(buf.ReadInt32()))
case reflect.Uint32:
val.SetUint(uint64(buf.ReadUint32()))
case reflect.Int64:
val.SetInt(buf.ReadInt64())
case reflect.Uint64:
val.SetUint(buf.ReadUint64())
case reflect.Float32:
val.SetFloat(float64(buf.ReadFloat32()))
case reflect.Float64:
val.SetFloat(buf.ReadFloat64())
case reflect.String:
val.SetString(buf.ReadString())
case reflect.Slice:
return decodeSlice(b, val, name)
case reflect.Ptr:
return decode(b, val.Elem(), name)
case reflect.Struct:
return decodeStruct(b, val, name)
default:
return 0, errors.Errorf("unsupported type %s", val.Type())
}
}
return buf.Pos(), buf.Error()
}
func decodeStruct(b []byte, val reflect.Value, name string) (int, error) {
pos := 0
valt := val.Type()
for i := 0; i < val.NumField(); i++ {
ft := valt.Field(i)
fname := name + "." + ft.Name
// if the field is a pointer we need to create
// the value before we can marshal data into it.
f := val.Field(i)
if f.Type().Kind() == reflect.Ptr {
f.Set(reflect.New(f.Type().Elem()))
// fmt.Printf("decode: %s has type %v and has new value %#v\n", fname, f.Type(), f.Interface())
}
n, err := decode(b[pos:], f, fname)
if err != nil {
return pos, err
}
pos += n
}
return pos, nil
}
func decodeSlice(b []byte, val reflect.Value, name string) (int, error) {
buf := NewBuffer(b)
n := buf.ReadUint32()
if buf.Error() != nil {
return buf.Pos(), buf.Error()
}
if n == null {
return buf.Pos(), nil
}
if n > math.MaxInt32 {
return buf.Pos(), errors.Errorf("array too large: %d", n)
}
// elemType is the type of the slice elements
// e.g. *Foo for []*Foo
elemType := val.Type().Elem()
// fmt.Println("elemType: ", elemType.String())
// fast path for []byte
if elemType.Kind() == reflect.Uint8 {
// fmt.Println("decode: []byte fast path")
val.SetBytes(buf.ReadN(int(n)))
return buf.Pos(), buf.Error()
}
pos := buf.Pos()
// a is a slice of []*Foo
a := reflect.MakeSlice(val.Type(), int(n), int(n))
for i := 0; i < int(n); i++ {
// if the slice elements are pointers we need to create
// them before we can marshal data into them.
if elemType.Kind() == reflect.Ptr {
a.Index(i).Set(reflect.New(elemType.Elem()))
}
ename := fmt.Sprintf("%s[%d]", name, i)
m, err := decode(b[pos:], a.Index(i), ename)
if err != nil {
return pos, err
}
pos += m
}
val.Set(a)
return pos, nil
}