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 }