dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts@v1.0.2/dtos/reading.go (about)

     1  //
     2  // Copyright (C) 2020-2023 IOTech Ltd
     3  //
     4  // SPDX-License-Identifier: Apache-2.0
     5  
     6  package dtos
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"reflect"
    12  	"strconv"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/google/uuid"
    17  
    18  	"dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/common"
    19  	edgexErrors "dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/errors"
    20  	"dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/models"
    21  )
    22  
    23  type BaseReading struct {
    24  	Id            string `json:"id,omitempty"`
    25  	Origin        int64  `json:"origin" validate:"required"`
    26  	DeviceName    string `json:"deviceName" validate:"required,edgex-dto-none-empty-string"`
    27  	ResourceName  string `json:"resourceName" validate:"required"`
    28  	ProfileName   string `json:"profileName" validate:"required,edgex-dto-none-empty-string"`
    29  	ValueType     string `json:"valueType" validate:"required,edgex-dto-value-type"`
    30  	Units         string `json:"units,omitempty"`
    31  	Tags          Tags   `json:"tags,omitempty"`
    32  	BinaryReading `json:",inline" validate:"-"`
    33  	SimpleReading `json:",inline" validate:"-"`
    34  	ObjectReading `json:",inline" validate:"-"`
    35  }
    36  
    37  type SimpleReading struct {
    38  	Value string `json:"value"`
    39  }
    40  
    41  type BinaryReading struct {
    42  	BinaryValue []byte `json:"binaryValue,omitempty" validate:"gt=0,required"`
    43  	MediaType   string `json:"mediaType,omitempty" validate:"required"`
    44  }
    45  
    46  type ObjectReading struct {
    47  	ObjectValue interface{} `json:"objectValue,omitempty" validate:"required"`
    48  }
    49  
    50  func newBaseReading(profileName string, deviceName string, resourceName string, valueType string) BaseReading {
    51  	return BaseReading{
    52  		Id:           uuid.NewString(),
    53  		Origin:       time.Now().UnixNano(),
    54  		DeviceName:   deviceName,
    55  		ResourceName: resourceName,
    56  		ProfileName:  profileName,
    57  		ValueType:    valueType,
    58  	}
    59  }
    60  
    61  // NewSimpleReading creates and returns a new initialized BaseReading with its SimpleReading initialized
    62  func NewSimpleReading(profileName string, deviceName string, resourceName string, valueType string, value interface{}) (BaseReading, error) {
    63  	stringValue, err := convertInterfaceValue(valueType, value)
    64  	if err != nil {
    65  		return BaseReading{}, err
    66  	}
    67  
    68  	reading := newBaseReading(profileName, deviceName, resourceName, valueType)
    69  	reading.SimpleReading = SimpleReading{
    70  		Value: stringValue,
    71  	}
    72  	return reading, nil
    73  }
    74  
    75  // NewBinaryReading creates and returns a new initialized BaseReading with its BinaryReading initialized
    76  func NewBinaryReading(profileName string, deviceName string, resourceName string, binaryValue []byte, mediaType string) BaseReading {
    77  	reading := newBaseReading(profileName, deviceName, resourceName, common.ValueTypeBinary)
    78  	reading.BinaryReading = BinaryReading{
    79  		BinaryValue: binaryValue,
    80  		MediaType:   mediaType,
    81  	}
    82  	return reading
    83  }
    84  
    85  // NewObjectReading creates and returns a new initialized BaseReading with its ObjectReading initialized
    86  func NewObjectReading(profileName string, deviceName string, resourceName string, objectValue interface{}) BaseReading {
    87  	reading := newBaseReading(profileName, deviceName, resourceName, common.ValueTypeObject)
    88  	reading.ObjectReading = ObjectReading{
    89  		ObjectValue: objectValue,
    90  	}
    91  	return reading
    92  }
    93  
    94  // NewObjectReading creates and returns a new initialized BaseReading with its ObjectReading initialized
    95  func NewArrayReading(profileName string, deviceName string, resourceName string, valueType string, objectValue interface{}) BaseReading {
    96  	reading := newBaseReading(profileName, deviceName, resourceName, valueType)
    97  	reading.ObjectReading = ObjectReading{
    98  		ObjectValue: objectValue,
    99  	}
   100  	return reading
   101  }
   102  
   103  func convertInterfaceValue(valueType string, value interface{}) (string, error) {
   104  	switch valueType {
   105  	case common.ValueTypeBool:
   106  		return convertSimpleValue(valueType, reflect.Bool, value)
   107  	case common.ValueTypeString:
   108  		return convertSimpleValue(valueType, reflect.String, value)
   109  
   110  	case common.ValueTypeUint8:
   111  		return convertSimpleValue(valueType, reflect.Uint8, value)
   112  	case common.ValueTypeUint16:
   113  		return convertSimpleValue(valueType, reflect.Uint16, value)
   114  	case common.ValueTypeUint32:
   115  		return convertSimpleValue(valueType, reflect.Uint32, value)
   116  	case common.ValueTypeUint64:
   117  		return convertSimpleValue(valueType, reflect.Uint64, value)
   118  
   119  	case common.ValueTypeInt8:
   120  		return convertSimpleValue(valueType, reflect.Int8, value)
   121  	case common.ValueTypeInt16:
   122  		return convertSimpleValue(valueType, reflect.Int16, value)
   123  	case common.ValueTypeInt32:
   124  		return convertSimpleValue(valueType, reflect.Int32, value)
   125  	case common.ValueTypeInt64:
   126  		return convertSimpleValue(valueType, reflect.Int64, value)
   127  
   128  	case common.ValueTypeFloat32:
   129  		return convertFloatValue(valueType, reflect.Float32, value)
   130  	case common.ValueTypeFloat64:
   131  		return convertFloatValue(valueType, reflect.Float64, value)
   132  
   133  	case common.ValueTypeBoolArray:
   134  		return convertSimpleArrayValue(valueType, reflect.Bool, value)
   135  	case common.ValueTypeStringArray:
   136  		return convertSimpleArrayValue(valueType, reflect.String, value)
   137  
   138  	case common.ValueTypeUint8Array:
   139  		return convertSimpleArrayValue(valueType, reflect.Uint8, value)
   140  	case common.ValueTypeUint16Array:
   141  		return convertSimpleArrayValue(valueType, reflect.Uint16, value)
   142  	case common.ValueTypeUint32Array:
   143  		return convertSimpleArrayValue(valueType, reflect.Uint32, value)
   144  	case common.ValueTypeUint64Array:
   145  		return convertSimpleArrayValue(valueType, reflect.Uint64, value)
   146  
   147  	case common.ValueTypeInt8Array:
   148  		return convertSimpleArrayValue(valueType, reflect.Int8, value)
   149  	case common.ValueTypeInt16Array:
   150  		return convertSimpleArrayValue(valueType, reflect.Int16, value)
   151  	case common.ValueTypeInt32Array:
   152  		return convertSimpleArrayValue(valueType, reflect.Int32, value)
   153  	case common.ValueTypeInt64Array:
   154  		return convertSimpleArrayValue(valueType, reflect.Int64, value)
   155  
   156  	case common.ValueTypeFloat32Array:
   157  		arrayValue, ok := value.([]float32)
   158  		if !ok {
   159  			return "", fmt.Errorf("unable to cast value to []float32 for %s", valueType)
   160  		}
   161  
   162  		return convertFloat32ArrayValue(arrayValue)
   163  	case common.ValueTypeFloat64Array:
   164  		arrayValue, ok := value.([]float64)
   165  		if !ok {
   166  			return "", fmt.Errorf("unable to cast value to []float64 for %s", valueType)
   167  		}
   168  
   169  		return convertFloat64ArrayValue(arrayValue)
   170  
   171  	default:
   172  		return "", fmt.Errorf("invalid simple reading type of %s", valueType)
   173  	}
   174  }
   175  
   176  func convertSimpleValue(valueType string, kind reflect.Kind, value interface{}) (string, error) {
   177  	if err := validateType(valueType, kind, value); err != nil {
   178  		return "", err
   179  	}
   180  
   181  	return fmt.Sprintf("%v", value), nil
   182  }
   183  
   184  func convertFloatValue(valueType string, kind reflect.Kind, value interface{}) (string, error) {
   185  	if err := validateType(valueType, kind, value); err != nil {
   186  		return "", err
   187  	}
   188  
   189  	return fmt.Sprintf("%e", value), nil
   190  }
   191  
   192  func convertSimpleArrayValue(valueType string, kind reflect.Kind, value interface{}) (string, error) {
   193  	if err := validateType(valueType, kind, value); err != nil {
   194  		return "", err
   195  	}
   196  
   197  	result := fmt.Sprintf("%v", value)
   198  	result = strings.ReplaceAll(result, " ", ", ")
   199  	return result, nil
   200  }
   201  
   202  func convertFloat32ArrayValue(values []float32) (string, error) {
   203  	var result strings.Builder
   204  	result.WriteString("[")
   205  	first := true
   206  	for _, value := range values {
   207  		if first {
   208  			floatValue, err := convertFloatValue(common.ValueTypeFloat32, reflect.Float32, value)
   209  			if err != nil {
   210  				return "", err
   211  			}
   212  			result.WriteString(floatValue)
   213  			first = false
   214  			continue
   215  		}
   216  
   217  		floatValue, err := convertFloatValue(common.ValueTypeFloat32, reflect.Float32, value)
   218  		if err != nil {
   219  			return "", err
   220  		}
   221  		result.WriteString(", " + floatValue)
   222  	}
   223  
   224  	result.WriteString("]")
   225  	return result.String(), nil
   226  }
   227  
   228  func convertFloat64ArrayValue(values []float64) (string, error) {
   229  	var result strings.Builder
   230  	result.WriteString("[")
   231  	first := true
   232  	for _, value := range values {
   233  		if first {
   234  			floatValue, err := convertFloatValue(common.ValueTypeFloat64, reflect.Float64, value)
   235  			if err != nil {
   236  				return "", err
   237  			}
   238  			result.WriteString(floatValue)
   239  			first = false
   240  			continue
   241  		}
   242  
   243  		floatValue, err := convertFloatValue(common.ValueTypeFloat64, reflect.Float64, value)
   244  		if err != nil {
   245  			return "", err
   246  		}
   247  		result.WriteString(", " + floatValue)
   248  	}
   249  
   250  	result.WriteString("]")
   251  	return result.String(), nil
   252  }
   253  
   254  func validateType(valueType string, kind reflect.Kind, value interface{}) error {
   255  	if reflect.TypeOf(value).Kind() == reflect.Slice {
   256  		if kind != reflect.TypeOf(value).Elem().Kind() {
   257  			return fmt.Errorf("slice of type of value `%s` not a match for specified ValueType '%s", kind.String(), valueType)
   258  		}
   259  		return nil
   260  	}
   261  
   262  	if kind != reflect.TypeOf(value).Kind() {
   263  		return fmt.Errorf("type of value `%s` not a match for specified ValueType '%s", kind.String(), valueType)
   264  	}
   265  
   266  	return nil
   267  }
   268  
   269  // Validate satisfies the Validator interface
   270  func (b BaseReading) Validate() error {
   271  	if b.ValueType == common.ValueTypeBinary {
   272  		// validate the inner BinaryReading struct
   273  		binaryReading := b.BinaryReading
   274  		if err := common.Validate(binaryReading); err != nil {
   275  			return err
   276  		}
   277  	} else if b.ValueType == common.ValueTypeObject ||
   278  		b.ValueType == common.ValueTypeStringArray ||
   279  		b.ValueType == common.ValueTypeBoolArray ||
   280  		b.ValueType == common.ValueTypeUint8Array ||
   281  		b.ValueType == common.ValueTypeUint16Array ||
   282  		b.ValueType == common.ValueTypeUint32Array ||
   283  		b.ValueType == common.ValueTypeUint64Array ||
   284  		b.ValueType == common.ValueTypeInt8Array ||
   285  		b.ValueType == common.ValueTypeInt16Array ||
   286  		b.ValueType == common.ValueTypeInt32Array ||
   287  		b.ValueType == common.ValueTypeInt64Array ||
   288  		b.ValueType == common.ValueTypeFloat32Array || b.ValueType == common.ValueTypeFloat64Array {
   289  		// validate the inner ObjectReading struct
   290  		objectReading := b.ObjectReading
   291  		if err := common.Validate(objectReading); err != nil {
   292  			return err
   293  		}
   294  	} else {
   295  		// validate the inner SimpleReading struct
   296  		simpleReading := b.SimpleReading
   297  		if err := common.Validate(simpleReading); err != nil {
   298  			return err
   299  		}
   300  		if err := ValidateValue(b.ValueType, simpleReading.Value); err != nil {
   301  			return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, fmt.Sprintf("The value does not match the %v valueType", b.ValueType), nil)
   302  		}
   303  	}
   304  
   305  	return nil
   306  }
   307  
   308  // ToReadingModel converts Reading DTO to Reading Model
   309  func ToReadingModel(r BaseReading) models.Reading {
   310  	var readingModel models.Reading
   311  	br := models.BaseReading{
   312  		Id:           r.Id,
   313  		Origin:       r.Origin,
   314  		DeviceName:   r.DeviceName,
   315  		ResourceName: r.ResourceName,
   316  		ProfileName:  r.ProfileName,
   317  		ValueType:    r.ValueType,
   318  		Units:        r.Units,
   319  		Tags:         r.Tags,
   320  	}
   321  	if r.ValueType == common.ValueTypeBinary {
   322  		readingModel = models.BinaryReading{
   323  			BaseReading: br,
   324  			BinaryValue: r.BinaryValue,
   325  			MediaType:   r.MediaType,
   326  		}
   327  	} else if r.ValueType == common.ValueTypeObject {
   328  		readingModel = models.ObjectReading{
   329  			BaseReading: br,
   330  			ObjectValue: r.ObjectValue,
   331  		}
   332  		//array are same as objectReading, but valuetype is array
   333  	} else if r.ValueType == common.ValueTypeStringArray || r.ValueType == common.ValueTypeBoolArray || r.ValueType == common.ValueTypeUint8Array || r.ValueType == common.ValueTypeUint16Array || r.ValueType == common.ValueTypeUint32Array || r.ValueType == common.ValueTypeUint64Array || r.ValueType == common.ValueTypeInt8Array || r.ValueType == common.ValueTypeInt16Array || r.ValueType == common.ValueTypeInt32Array || r.ValueType == common.ValueTypeInt64Array || r.ValueType == common.ValueTypeFloat32Array || r.ValueType == common.ValueTypeFloat64Array {
   334  		readingModel = models.ObjectReading{
   335  			BaseReading: br,
   336  			ObjectValue: r.ObjectValue,
   337  		}
   338  	} else {
   339  		readingModel = models.SimpleReading{
   340  			BaseReading: br,
   341  			Value:       r.Value,
   342  		}
   343  	}
   344  	return readingModel
   345  }
   346  
   347  func FromReadingModelToDTO(reading models.Reading) BaseReading {
   348  	var baseReading BaseReading
   349  	switch r := reading.(type) {
   350  	case models.BinaryReading:
   351  		baseReading = BaseReading{
   352  			Id:            r.Id,
   353  			Origin:        r.Origin,
   354  			DeviceName:    r.DeviceName,
   355  			ResourceName:  r.ResourceName,
   356  			ProfileName:   r.ProfileName,
   357  			ValueType:     r.ValueType,
   358  			Units:         r.Units,
   359  			Tags:          r.Tags,
   360  			BinaryReading: BinaryReading{BinaryValue: r.BinaryValue, MediaType: r.MediaType},
   361  		}
   362  	case models.ObjectReading:
   363  		baseReading = BaseReading{
   364  			Id:            r.Id,
   365  			Origin:        r.Origin,
   366  			DeviceName:    r.DeviceName,
   367  			ResourceName:  r.ResourceName,
   368  			ProfileName:   r.ProfileName,
   369  			ValueType:     r.ValueType,
   370  			Units:         r.Units,
   371  			Tags:          r.Tags,
   372  			ObjectReading: ObjectReading{ObjectValue: r.ObjectValue},
   373  		}
   374  	case models.SimpleReading:
   375  		baseReading = BaseReading{
   376  			Id:            r.Id,
   377  			Origin:        r.Origin,
   378  			DeviceName:    r.DeviceName,
   379  			ResourceName:  r.ResourceName,
   380  			ProfileName:   r.ProfileName,
   381  			ValueType:     r.ValueType,
   382  			Units:         r.Units,
   383  			Tags:          r.Tags,
   384  			SimpleReading: SimpleReading{Value: r.Value},
   385  		}
   386  	}
   387  
   388  	return baseReading
   389  }
   390  
   391  // ValidateValue used to check whether the value and valueType are matched
   392  func ValidateValue(valueType string, value string) error {
   393  	if strings.Contains(valueType, "Array") {
   394  		return parseArrayValue(valueType, value)
   395  	} else {
   396  		return parseSimpleValue(valueType, value)
   397  	}
   398  }
   399  
   400  func parseSimpleValue(valueType string, value string) (err error) {
   401  	switch valueType {
   402  	case common.ValueTypeBool:
   403  		_, err = strconv.ParseBool(value)
   404  
   405  	case common.ValueTypeUint8:
   406  		_, err = strconv.ParseUint(value, 10, 8)
   407  	case common.ValueTypeUint16:
   408  		_, err = strconv.ParseUint(value, 10, 16)
   409  	case common.ValueTypeUint32:
   410  		_, err = strconv.ParseUint(value, 10, 32)
   411  	case common.ValueTypeUint64:
   412  		_, err = strconv.ParseUint(value, 10, 64)
   413  
   414  	case common.ValueTypeInt8:
   415  		_, err = strconv.ParseInt(value, 10, 8)
   416  	case common.ValueTypeInt16:
   417  		_, err = strconv.ParseInt(value, 10, 16)
   418  	case common.ValueTypeInt32:
   419  		_, err = strconv.ParseInt(value, 10, 32)
   420  	case common.ValueTypeInt64:
   421  		_, err = strconv.ParseInt(value, 10, 64)
   422  
   423  	case common.ValueTypeFloat32:
   424  		_, err = strconv.ParseFloat(value, 32)
   425  	case common.ValueTypeFloat64:
   426  		_, err = strconv.ParseFloat(value, 64)
   427  	}
   428  
   429  	if err != nil {
   430  		return err
   431  	}
   432  	return nil
   433  }
   434  
   435  func parseArrayValue(valueType string, value string) (err error) {
   436  	arrayValue := strings.Split(value[1:len(value)-1], ",") // trim "[" and "]"
   437  
   438  	for _, v := range arrayValue {
   439  		v = strings.TrimSpace(v)
   440  		switch valueType {
   441  		case common.ValueTypeBoolArray:
   442  			err = parseSimpleValue(common.ValueTypeBool, v)
   443  
   444  		case common.ValueTypeUint8Array:
   445  			err = parseSimpleValue(common.ValueTypeUint8, v)
   446  		case common.ValueTypeUint16Array:
   447  			err = parseSimpleValue(common.ValueTypeUint16, v)
   448  		case common.ValueTypeUint32Array:
   449  			err = parseSimpleValue(common.ValueTypeUint32, v)
   450  		case common.ValueTypeUint64Array:
   451  			err = parseSimpleValue(common.ValueTypeUint64, v)
   452  
   453  		case common.ValueTypeInt8Array:
   454  			err = parseSimpleValue(common.ValueTypeInt8, v)
   455  		case common.ValueTypeInt16Array:
   456  			err = parseSimpleValue(common.ValueTypeInt16, v)
   457  		case common.ValueTypeInt32Array:
   458  			err = parseSimpleValue(common.ValueTypeInt32, v)
   459  		case common.ValueTypeInt64Array:
   460  			err = parseSimpleValue(common.ValueTypeInt64, v)
   461  
   462  		case common.ValueTypeFloat32Array:
   463  			err = parseSimpleValue(common.ValueTypeFloat32, v)
   464  		case common.ValueTypeFloat64Array:
   465  			err = parseSimpleValue(common.ValueTypeFloat64, v)
   466  
   467  		}
   468  		if err != nil {
   469  			return err
   470  		}
   471  	}
   472  	return nil
   473  }
   474  
   475  // UnmarshalObjectValue is a helper function used to unmarshal the ObjectValue of a reading to the passed in target type.
   476  // Note that this function will only work on readings with 'Object' valueType.  An error will be returned when invoking
   477  // this function on a reading with valueType other than 'Object'.
   478  func (b BaseReading) UnmarshalObjectValue(target any) error {
   479  	if b.ValueType == common.ValueTypeObject {
   480  		// marshal the current reading ObjectValue to JSON
   481  		jsonEncodedData, err := json.Marshal(b.ObjectValue)
   482  		if err != nil {
   483  			return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, "failed to encode the object value of reading to JSON", err)
   484  		}
   485  		// unmarshal the JSON into the passed in target
   486  		err = json.Unmarshal(jsonEncodedData, target)
   487  		if err != nil {
   488  			return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, fmt.Sprintf("failed to unmarshall the object value of reading into type %v", reflect.TypeOf(target).String()), err)
   489  		}
   490  	} else {
   491  		return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, fmt.Sprintf("invalid usage of UnmarshalObjectValue function invocation on reading with %v valueType", b.ValueType), nil)
   492  	}
   493  
   494  	return nil
   495  }