EdgexAgent/device-gps-go/internal/application/command.go
2025-07-10 20:30:06 +08:00

764 lines
29 KiB
Go

// -*- Mode: Go; indent-tabs-mode: t -*-
//
// Copyright (C) 2020-2025 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0
package application
import (
"bytes"
"context"
"encoding/base64"
"encoding/binary"
"encoding/json"
"fmt"
"math"
"regexp"
"strconv"
"strings"
"github.com/edgexfoundry/go-mod-core-contracts/v4/clients/http/utils"
"github.com/edgexfoundry/device-sdk-go/v4/internal/cache"
sdkCommon "github.com/edgexfoundry/device-sdk-go/v4/internal/common"
"github.com/edgexfoundry/device-sdk-go/v4/internal/container"
"github.com/edgexfoundry/device-sdk-go/v4/internal/transformer"
sdkModels "github.com/edgexfoundry/device-sdk-go/v4/pkg/models"
bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container"
"github.com/edgexfoundry/go-mod-bootstrap/v4/di"
"github.com/edgexfoundry/go-mod-core-contracts/v4/common"
"github.com/edgexfoundry/go-mod-core-contracts/v4/dtos"
"github.com/edgexfoundry/go-mod-core-contracts/v4/errors"
"github.com/edgexfoundry/go-mod-core-contracts/v4/models"
)
func GetCommand(ctx context.Context, deviceName string, commandName string, queryParams string, regexCmd bool, dic *di.Container) (res *dtos.Event, err errors.EdgeX) {
if deviceName == "" {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "device name is empty", nil)
}
if commandName == "" {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "command is empty", nil)
}
var device models.Device
defer func() {
if err != nil {
DeviceRequestFailed(deviceName, dic)
} else {
DeviceRequestSucceeded(device, dic)
}
}()
device, err = validateServiceAndDeviceState(deviceName, dic)
if err != nil {
return nil, errors.NewCommonEdgeXWrapper(err)
}
_, cmdExist := cache.Profiles().DeviceCommand(device.ProfileName, commandName)
if cmdExist {
res, err = readDeviceCommand(device, commandName, queryParams, dic)
} else if regexCmd {
res, err = readDeviceResourcesRegex(device, commandName, queryParams, dic)
} else {
res, err = readDeviceResource(device, commandName, queryParams, dic)
}
if err != nil {
return nil, errors.NewCommonEdgeXWrapper(err)
}
lc := bootstrapContainer.LoggingClientFrom(dic.Get)
lc.Debugf("GET Device Command successfully. Device: %s, Source: %s, %s: %s", deviceName, commandName, common.CorrelationHeader, utils.FromContext(ctx, common.CorrelationHeader))
cache.Devices().SetLastConnectedByName(deviceName)
return res, nil
}
func SetCommand(ctx context.Context, deviceName string, commandName string, queryParams string, requests map[string]any, dic *di.Container) (event *dtos.Event, err errors.EdgeX) {
if deviceName == "" {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "device name is empty", nil)
}
if commandName == "" {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "command is empty", nil)
}
var device models.Device
defer func() {
if err != nil {
DeviceRequestFailed(deviceName, dic)
} else {
DeviceRequestSucceeded(device, dic)
}
}()
device, err = validateServiceAndDeviceState(deviceName, dic)
if err != nil {
return nil, errors.NewCommonEdgeXWrapper(err)
}
_, cmdExist := cache.Profiles().DeviceCommand(device.ProfileName, commandName)
if cmdExist {
event, err = writeDeviceCommand(device, commandName, queryParams, requests, dic)
} else {
event, err = writeDeviceResource(device, commandName, queryParams, requests, dic)
}
if err != nil {
return nil, errors.NewCommonEdgeXWrapper(err)
}
lc := bootstrapContainer.LoggingClientFrom(dic.Get)
lc.Debugf("SET Device Command successfully. Device: %s, Source: %s, %s: %s", deviceName, commandName, common.CorrelationHeader, utils.FromContext(ctx, common.CorrelationHeader))
cache.Devices().SetLastConnectedByName(deviceName)
return event, nil
}
func readDeviceResource(device models.Device, resourceName string, attributes string, dic *di.Container) (*dtos.Event, errors.EdgeX) {
dr, ok := cache.Profiles().DeviceResource(device.ProfileName, resourceName)
if !ok {
errMsg := fmt.Sprintf("DeviceResource %s not found", resourceName)
return nil, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, errMsg, nil)
}
// check deviceResource is not write-only
if dr.Properties.ReadWrite == common.ReadWrite_W {
errMsg := fmt.Sprintf("DeviceResource %s is marked as write-only", dr.Name)
return nil, errors.NewCommonEdgeX(errors.KindNotAllowed, errMsg, nil)
}
var req sdkModels.CommandRequest
var reqs []sdkModels.CommandRequest
// prepare CommandRequest
req.DeviceResourceName = dr.Name
req.Attributes = dr.Attributes
if attributes != "" {
if len(req.Attributes) <= 0 {
req.Attributes = make(map[string]interface{})
}
req.Attributes[sdkCommon.URLRawQuery] = attributes
}
req.Type = dr.Properties.ValueType
reqs = append(reqs, req)
// execute protocol-specific read operation
driver := container.ProtocolDriverFrom(dic.Get)
results, err := driver.HandleReadCommands(device.Name, device.Protocols, reqs)
if err != nil {
errMsg := fmt.Sprintf("error reading DeviceResource %s for %s", dr.Name, device.Name)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
// convert CommandValue to Event
configuration := container.ConfigurationFrom(dic.Get)
event, err := transformer.CommandValuesToEventDTO(results, device.Name, dr.Name, configuration.Device.DataTransform, dic)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to convert CommandValue to Event", err)
}
return event, nil
}
func readDeviceResourcesRegex(device models.Device, regexResourceName string, attributes string, dic *di.Container) (*dtos.Event, errors.EdgeX) {
regex, err := regexp.CompilePOSIX(regexResourceName)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "failed to CompilePOSIX resource name", err)
}
deviceResources, ok := cache.Profiles().DeviceResourcesByRegex(device.ProfileName, regex)
if !ok || len(deviceResources) == 0 {
errMsg := fmt.Sprintf("Regex DeviceResource %s not found", regexResourceName)
return nil, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, errMsg, nil)
}
lc := bootstrapContainer.LoggingClientFrom(dic.Get)
reqs := make([]sdkModels.CommandRequest, 0)
for _, dr := range deviceResources {
// check deviceResource is not write-only
if dr.Properties.ReadWrite == common.ReadWrite_W {
lc.Debugf("DeviceResource %s is marked as write-only, skipping adding to RegEx Read list", dr.Name)
continue
}
// prepare CommandRequest
var req sdkModels.CommandRequest
req.DeviceResourceName = dr.Name
req.Attributes = dr.Attributes
if attributes != "" {
if len(req.Attributes) <= 0 {
req.Attributes = make(map[string]any)
}
req.Attributes[sdkCommon.URLRawQuery] = attributes
}
req.Type = dr.Properties.ValueType
reqs = append(reqs, req)
}
if len(reqs) == 0 {
errMsg := fmt.Sprintf("no readable resources matched with %s", regexResourceName)
return nil, errors.NewCommonEdgeX(errors.KindNotAllowed, errMsg, nil)
}
// execute protocol-specific read operation
driver := container.ProtocolDriverFrom(dic.Get)
results, err := driver.HandleReadCommands(device.Name, device.Protocols, reqs)
if err != nil {
errMsg := fmt.Sprintf("error reading Regex DeviceResource(s) %s for %s", regexResourceName, device.Name)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
// convert CommandValue to Event
configuration := container.ConfigurationFrom(dic.Get)
event, err := transformer.CommandValuesToEventDTO(results, device.Name, regexResourceName, configuration.Device.DataTransform, dic)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to convert CommandValue to Event", err)
}
return event, nil
}
func readDeviceCommand(device models.Device, commandName string, attributes string, dic *di.Container) (*dtos.Event, errors.EdgeX) {
dc, ok := cache.Profiles().DeviceCommand(device.ProfileName, commandName)
if !ok {
errMsg := fmt.Sprintf("DeviceCommand %s not found", commandName)
return nil, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, errMsg, nil)
}
// check deviceCommand is not write-only
if dc.ReadWrite == common.ReadWrite_W {
errMsg := fmt.Sprintf("DeviceCommand %s is marked as write-only", dc.Name)
return nil, errors.NewCommonEdgeX(errors.KindNotAllowed, errMsg, nil)
}
// check ResourceOperation count does not exceed MaxCmdOps defined in configuration
configuration := container.ConfigurationFrom(dic.Get)
if len(dc.ResourceOperations) > configuration.Device.MaxCmdOps {
errMsg := fmt.Sprintf("GET command %s exceed device %s MaxCmdOps (%d)", dc.Name, device.Name, configuration.Device.MaxCmdOps)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, nil)
}
// prepare CommandRequests
reqs := make([]sdkModels.CommandRequest, len(dc.ResourceOperations))
for i, op := range dc.ResourceOperations {
drName := op.DeviceResource
// check the deviceResource in ResourceOperation actually exist
dr, ok := cache.Profiles().DeviceResource(device.ProfileName, drName)
if !ok {
errMsg := fmt.Sprintf("DeviceResource %s in GET commnd %s for %s not defined", drName, dc.Name, device.Name)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, nil)
}
reqs[i].DeviceResourceName = dr.Name
reqs[i].Attributes = dr.Attributes
if attributes != "" {
if len(reqs[i].Attributes) <= 0 {
reqs[i].Attributes = make(map[string]interface{})
}
reqs[i].Attributes[sdkCommon.URLRawQuery] = attributes
}
reqs[i].Type = dr.Properties.ValueType
}
// execute protocol-specific read operation
driver := container.ProtocolDriverFrom(dic.Get)
results, err := driver.HandleReadCommands(device.Name, device.Protocols, reqs)
if err != nil {
errMsg := fmt.Sprintf("error reading DeviceCommand %s for %s", dc.Name, device.Name)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
// convert CommandValue to Event
event, err := transformer.CommandValuesToEventDTO(results, device.Name, dc.Name, configuration.Device.DataTransform, dic)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to transform CommandValue to Event", err)
}
return event, nil
}
func writeDeviceResource(device models.Device, resourceName string, attributes string, requests map[string]any, dic *di.Container) (*dtos.Event, errors.EdgeX) {
dr, ok := cache.Profiles().DeviceResource(device.ProfileName, resourceName)
if !ok {
errMsg := fmt.Sprintf("DeviceResource %s not found", resourceName)
return nil, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, errMsg, nil)
}
// check deviceResource is not read-only
if dr.Properties.ReadWrite == common.ReadWrite_R {
errMsg := fmt.Sprintf("DeviceResource %s is marked as read-only", dr.Name)
return nil, errors.NewCommonEdgeX(errors.KindNotAllowed, errMsg, nil)
}
// check set parameters contains provided deviceResource
v, ok := requests[dr.Name]
if !ok {
if dr.Properties.DefaultValue != "" {
v = dr.Properties.DefaultValue
} else {
errMsg := fmt.Sprintf("DeviceResource %s not found in request body and no default value defined", dr.Name)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, nil)
}
}
// create CommandValue
cv, edgexErr := createCommandValueFromDeviceResource(dr, v)
if edgexErr != nil {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "failed to create CommandValue", edgexErr)
}
// prepare CommandRequest
reqs := make([]sdkModels.CommandRequest, 1)
reqs[0].DeviceResourceName = cv.DeviceResourceName
reqs[0].Attributes = dr.Attributes
if attributes != "" {
if len(reqs[0].Attributes) <= 0 {
reqs[0].Attributes = make(map[string]any)
}
reqs[0].Attributes[sdkCommon.URLRawQuery] = attributes
}
reqs[0].Type = cv.Type
// transform write value
configuration := container.ConfigurationFrom(dic.Get)
if configuration.Device.DataTransform {
edgexErr = transformer.TransformWriteParameter(cv, dr.Properties)
if edgexErr != nil {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "failed to transform set parameter", edgexErr)
}
}
// execute protocol-specific write operation
driver := container.ProtocolDriverFrom(dic.Get)
err := driver.HandleWriteCommands(device.Name, device.Protocols, reqs, []*sdkModels.CommandValue{cv})
if err != nil {
errMsg := fmt.Sprintf("error writing DeviceResource %s for %s", dr.Name, device.Name)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
// Updated resource value will be published to MessageBus as long as it's not write-only
if dr.Properties.ReadWrite != common.ReadWrite_W {
return transformer.CommandValuesToEventDTO([]*sdkModels.CommandValue{cv}, device.Name, resourceName, configuration.Device.DataTransform, dic)
}
return nil, nil
}
func writeDeviceCommand(device models.Device, commandName string, attributes string, requests map[string]any, dic *di.Container) (*dtos.Event, errors.EdgeX) {
dc, ok := cache.Profiles().DeviceCommand(device.ProfileName, commandName)
if !ok {
errMsg := fmt.Sprintf("DeviceCommand %s not found", commandName)
return nil, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, errMsg, nil)
}
// check deviceCommand is not read-only
if dc.ReadWrite == common.ReadWrite_R {
errMsg := fmt.Sprintf("DeviceCommand %s is marked as read-only", dc.Name)
return nil, errors.NewCommonEdgeX(errors.KindNotAllowed, errMsg, nil)
}
// check ResourceOperation count does not exceed MaxCmdOps defined in configuration
configuration := container.ConfigurationFrom(dic.Get)
if len(dc.ResourceOperations) > configuration.Device.MaxCmdOps {
errMsg := fmt.Sprintf("SET command %s exceed device %s MaxCmdOps (%d)", dc.Name, device.Name, configuration.Device.MaxCmdOps)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, nil)
}
// create CommandValues
cvs := make([]*sdkModels.CommandValue, 0, len(requests))
for _, ro := range dc.ResourceOperations {
drName := ro.DeviceResource
// check the deviceResource in ResourceOperation actually exist
dr, ok := cache.Profiles().DeviceResource(device.ProfileName, drName)
if !ok {
errMsg := fmt.Sprintf("DeviceResource %s in SET commnd %s for %s not defined", drName, dc.Name, device.Name)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, nil)
}
// check request body contains the deviceResource
value, ok := requests[ro.DeviceResource]
if !ok {
if ro.DefaultValue != "" {
value = ro.DefaultValue
} else if dr.Properties.DefaultValue != "" {
value = dr.Properties.DefaultValue
} else {
errMsg := fmt.Sprintf("DeviceResource %s not found in request body and no default value defined", dr.Name)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, nil)
}
}
// ResourceOperation mapping, notice that the order is opposite to get command mapping
// i.e. the mapping value is actually the key for set command.
if len(ro.Mappings) > 0 {
for k, v := range ro.Mappings {
if v == value {
value = k
break
}
}
}
// create CommandValue
cv, err := createCommandValueFromDeviceResource(dr, value)
if err == nil {
cvs = append(cvs, cv)
} else {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "failed to create CommandValue", err)
}
}
// prepare CommandRequests
reqs := make([]sdkModels.CommandRequest, len(cvs))
for i, cv := range cvs {
dr, _ := cache.Profiles().DeviceResource(device.ProfileName, cv.DeviceResourceName)
reqs[i].DeviceResourceName = cv.DeviceResourceName
reqs[i].Attributes = dr.Attributes
if attributes != "" {
if len(reqs[i].Attributes) <= 0 {
reqs[i].Attributes = make(map[string]interface{})
}
reqs[i].Attributes[sdkCommon.URLRawQuery] = attributes
}
reqs[i].Type = cv.Type
// transform write value
if configuration.Device.DataTransform {
err := transformer.TransformWriteParameter(cv, dr.Properties)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "failed to transform set parameter", err)
}
}
}
// execute protocol-specific write operation
driver := container.ProtocolDriverFrom(dic.Get)
err := driver.HandleWriteCommands(device.Name, device.Protocols, reqs, cvs)
if err != nil {
errMsg := fmt.Sprintf("error writing DeviceCommand %s for %s", dc.Name, device.Name)
return nil, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
// Updated resource(s) value will be published to MessageBus as long as they're not write-only
if dc.ReadWrite != common.ReadWrite_W {
return transformer.CommandValuesToEventDTO(cvs, device.Name, commandName, configuration.Device.DataTransform, dic)
}
return nil, nil
}
func validateServiceAndDeviceState(deviceName string, dic *di.Container) (models.Device, errors.EdgeX) {
// check device service AdminState
ds := container.DeviceServiceFrom(dic.Get)
if ds.AdminState == models.Locked {
return models.Device{}, errors.NewCommonEdgeX(errors.KindServiceLocked, "service locked", nil)
}
// check requested device exists
device, ok := cache.Devices().ForName(deviceName)
if !ok {
return models.Device{}, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, fmt.Sprintf("device %s not found", deviceName), nil)
}
// check device's AdminState
if device.AdminState == models.Locked {
return models.Device{}, errors.NewCommonEdgeX(errors.KindServiceLocked, fmt.Sprintf("device %s locked", device.Name), nil)
}
// check device's OperatingState
// if it's a device return attempt, operating state is allowed to be DOWN
if device.OperatingState == models.Down {
err := errors.NewCommonEdgeX(errors.KindServiceLocked, fmt.Sprintf("device %s OperatingState is DOWN", device.Name), nil)
config := container.ConfigurationFrom(dic.Get)
if config.Device.AllowedFails == 0 || config.Device.DeviceDownTimeout == 0 {
return models.Device{}, err
}
reqFailsTracker := container.AllowedRequestFailuresTrackerFrom(dic.Get)
if reqFailsTracker.Value(deviceName) > 0 {
return models.Device{}, err
}
}
// check device's ProfileName
if device.ProfileName == "" {
return models.Device{}, errors.NewCommonEdgeX(errors.KindServiceLocked, "no associated device profile", nil)
}
return device, nil
}
func createCommandValueFromDeviceResource(dr models.DeviceResource, value interface{}) (*sdkModels.CommandValue, errors.EdgeX) {
if value == nil {
return &sdkModels.CommandValue{
DeviceResourceName: dr.Name,
Type: dr.Properties.ValueType,
Value: value,
Tags: make(map[string]string)}, nil
}
var err error
var result *sdkModels.CommandValue
v := fmt.Sprint(value)
if dr.Properties.ValueType != common.ValueTypeString && strings.TrimSpace(v) == "" {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("empty string is invalid for %v value type", dr.Properties.ValueType), nil)
}
switch dr.Properties.ValueType {
case common.ValueTypeString:
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeString, v)
case common.ValueTypeStringArray:
var arr []string
err = json.Unmarshal([]byte(v), &arr)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeStringArray, arr)
case common.ValueTypeBool:
var value bool
value, err = strconv.ParseBool(v)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeBool, value)
case common.ValueTypeBoolArray:
var arr []bool
err = json.Unmarshal([]byte(v), &arr)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeBoolArray, arr)
case common.ValueTypeUint8:
var n uint64
n, err = strconv.ParseUint(v, 10, 8)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeUint8, uint8(n))
case common.ValueTypeUint8Array:
var arr []uint8
strArr := strings.Split(strings.Trim(v, "[]"), ",")
for _, u := range strArr {
n, err := strconv.ParseUint(strings.Trim(u, " "), 10, 8)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
arr = append(arr, uint8(n))
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeUint8Array, arr)
case common.ValueTypeUint16:
var n uint64
n, err = strconv.ParseUint(v, 10, 16)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeUint16, uint16(n))
case common.ValueTypeUint16Array:
var arr []uint16
strArr := strings.Split(strings.Trim(v, "[]"), ",")
for _, u := range strArr {
n, err := strconv.ParseUint(strings.Trim(u, " "), 10, 16)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
arr = append(arr, uint16(n))
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeUint16Array, arr)
case common.ValueTypeUint32:
var n uint64
n, err = strconv.ParseUint(v, 10, 32)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeUint32, uint32(n))
case common.ValueTypeUint32Array:
var arr []uint32
strArr := strings.Split(strings.Trim(v, "[]"), ",")
for _, u := range strArr {
n, err := strconv.ParseUint(strings.Trim(u, " "), 10, 32)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
arr = append(arr, uint32(n))
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeUint32Array, arr)
case common.ValueTypeUint64:
var n uint64
n, err = strconv.ParseUint(v, 10, 64)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeUint64, n)
case common.ValueTypeUint64Array:
var arr []uint64
strArr := strings.Split(strings.Trim(v, "[]"), ",")
for _, u := range strArr {
n, err := strconv.ParseUint(strings.Trim(u, " "), 10, 64)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
arr = append(arr, n)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeUint64Array, arr)
case common.ValueTypeInt8:
var n int64
n, err = strconv.ParseInt(v, 10, 8)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeInt8, int8(n))
case common.ValueTypeInt8Array:
var arr []int8
err = json.Unmarshal([]byte(v), &arr)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeInt8Array, arr)
case common.ValueTypeInt16:
var n int64
n, err = strconv.ParseInt(v, 10, 16)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeInt16, int16(n))
case common.ValueTypeInt16Array:
var arr []int16
err = json.Unmarshal([]byte(v), &arr)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeInt16Array, arr)
case common.ValueTypeInt32:
var n int64
n, err = strconv.ParseInt(v, 10, 32)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeInt32, int32(n))
case common.ValueTypeInt32Array:
var arr []int32
err = json.Unmarshal([]byte(v), &arr)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeInt32Array, arr)
case common.ValueTypeInt64:
var n int64
n, err = strconv.ParseInt(v, 10, 64)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeInt64, n)
case common.ValueTypeInt64Array:
var arr []int64
err = json.Unmarshal([]byte(v), &arr)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeInt64Array, arr)
case common.ValueTypeFloat32:
var val float64
val, err = strconv.ParseFloat(v, 32)
if err == nil {
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeFloat32, float32(val))
break
}
if numError, ok := err.(*strconv.NumError); ok {
if numError.Err == strconv.ErrRange {
err = errors.NewCommonEdgeX(errors.KindServerError, "NumError", err)
break
}
}
var decodedToBytes []byte
decodedToBytes, err = base64.StdEncoding.DecodeString(v)
if err == nil {
var val float32
val, err = float32FromBytes(decodedToBytes)
if err != nil {
break
} else if math.IsNaN(float64(val)) {
err = fmt.Errorf("fail to parse %v to float32, unexpected result %v", v, val)
} else {
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeFloat32, val)
}
}
case common.ValueTypeFloat32Array:
var arr []float32
err = json.Unmarshal([]byte(v), &arr)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeFloat32Array, arr)
case common.ValueTypeFloat64:
var val float64
val, err = strconv.ParseFloat(v, 64)
if err == nil {
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeFloat64, val)
break
}
if numError, ok := err.(*strconv.NumError); ok {
if numError.Err == strconv.ErrRange {
err = errors.NewCommonEdgeX(errors.KindServerError, "NumError", err)
break
}
}
var decodedToBytes []byte
decodedToBytes, err = base64.StdEncoding.DecodeString(v)
if err == nil {
val, err = float64FromBytes(decodedToBytes)
if err != nil {
break
} else if math.IsNaN(val) {
err = fmt.Errorf("fail to parse %v to float64, unexpected result %v", v, val)
} else {
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeFloat64, val)
}
}
case common.ValueTypeFloat64Array:
var arr []float64
err = json.Unmarshal([]byte(v), &arr)
if err != nil {
errMsg := fmt.Sprintf("failed to convert set parameter %s to ValueType %s", v, dr.Properties.ValueType)
return result, errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
}
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeFloat64Array, arr)
case common.ValueTypeObject:
result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeObject, value)
default:
err = errors.NewCommonEdgeX(errors.KindServerError, "unrecognized value type", nil)
}
if err != nil {
return nil, errors.NewCommonEdgeXWrapper(err)
}
return result, nil
}
func float32FromBytes(numericValue []byte) (res float32, err error) {
reader := bytes.NewReader(numericValue)
err = binary.Read(reader, binary.BigEndian, &res)
return
}
func float64FromBytes(numericValue []byte) (res float64, err error) {
reader := bytes.NewReader(numericValue)
err = binary.Read(reader, binary.BigEndian, &res)
return
}