diff --git a/example/cmd/device-simple/res/profiles/Simple-Driver.json.example b/example/cmd/device-simple/res/profiles/Simple-Driver.json.example index f3e56b67a..cb6c3eedf 100644 --- a/example/cmd/device-simple/res/profiles/Simple-Driver.json.example +++ b/example/cmd/device-simple/res/profiles/Simple-Driver.json.example @@ -70,7 +70,7 @@ "description": "Counter data", "properties": { "valueType": "Object", - "readWrite": "R" + "readWrite": "RW" } } ], diff --git a/example/cmd/device-simple/res/profiles/Simple-Driver.yaml b/example/cmd/device-simple/res/profiles/Simple-Driver.yaml index 53fc0ee46..16fe3829e 100644 --- a/example/cmd/device-simple/res/profiles/Simple-Driver.yaml +++ b/example/cmd/device-simple/res/profiles/Simple-Driver.yaml @@ -57,7 +57,7 @@ deviceResources: description: "Counter data" properties: valueType: "Object" - readWrite: "R" + readWrite: "RW" deviceCommands: - diff --git a/example/driver/simpledriver.go b/example/driver/simpledriver.go index 69ad702b1..013bc30fb 100644 --- a/example/driver/simpledriver.go +++ b/example/driver/simpledriver.go @@ -227,6 +227,11 @@ func (s *SimpleDriver) HandleWriteCommands(deviceName string, protocols map[stri } else { return err } + case "Counter": + if s.counter, err = params[i].ObjectValue(); err != nil { + err := fmt.Errorf("SimpleDriver.HandleWriteCommands; the data type of parameter should be Object, parameter: %s", params[i].String()) + return err + } } } diff --git a/internal/application/command.go b/internal/application/command.go index b286115c5..d35ed3c0d 100644 --- a/internal/application/command.go +++ b/internal/application/command.go @@ -34,14 +34,14 @@ type CommandProcessor struct { device models.Device sourceName string correlationID string - setParamsMap map[string]string + setParamsMap map[string]interface{} attributes string dic *di.Container } -func NewCommandProcessor(device models.Device, sourceName string, correlationID string, setParamsMap map[string]string, attributes string, dic *di.Container) *CommandProcessor { +func NewCommandProcessor(device models.Device, sourceName string, correlationID string, setParamsMap map[string]interface{}, attributes string, dic *di.Container) *CommandProcessor { if setParamsMap == nil { - setParamsMap = make(map[string]string) + setParamsMap = make(map[string]interface{}) } return &CommandProcessor{ device: device, @@ -53,7 +53,7 @@ func NewCommandProcessor(device models.Device, sourceName string, correlationID } } -func CommandHandler(isRead bool, sendEvent bool, correlationID string, vars map[string]string, setParamsMap map[string]string, attributes string, dic *di.Container) (res *dtos.Event, err errors.EdgeX) { +func CommandHandler(isRead bool, sendEvent bool, correlationID string, vars map[string]string, setParamsMap map[string]interface{}, attributes string, dic *di.Container) (res *dtos.Event, err errors.EdgeX) { // check device service AdminState ds := container.DeviceServiceFrom(dic.Get) if ds.AdminState == models.Locked { @@ -382,10 +382,12 @@ func (c *CommandProcessor) WriteDeviceCommand() errors.EdgeX { return nil } -func createCommandValueFromDeviceResource(dr models.DeviceResource, v string) (*sdkModels.CommandValue, errors.EdgeX) { +func createCommandValueFromDeviceResource(dr models.DeviceResource, value interface{}) (*sdkModels.CommandValue, errors.EdgeX) { var err error var result *sdkModels.CommandValue + v := fmt.Sprint(value) + switch dr.Properties.ValueType { case common.ValueTypeString: result, err = sdkModels.NewCommandValue(dr.Name, common.ValueTypeString, v) @@ -616,6 +618,8 @@ func createCommandValueFromDeviceResource(dr models.DeviceResource, v string) (* 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) } diff --git a/internal/application/command_test.go b/internal/application/command_test.go index 2b533b79a..e36a04892 100644 --- a/internal/application/command_test.go +++ b/internal/application/command_test.go @@ -13,8 +13,9 @@ import ( "github.com/edgexfoundry/go-mod-bootstrap/v2/di" clientMocks "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/interfaces/mocks" "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" + "github.com/edgexfoundry/go-mod-core-contracts/v2/common" "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos" - "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" + dtoCommon "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" "github.com/edgexfoundry/go-mod-core-contracts/v2/models" "github.com/google/uuid" @@ -51,11 +52,23 @@ func mockDic() *di.Container { Value: "test-value", Tags: make(map[string]string), } + objectRequest := sdkModels.CommandRequest{ + DeviceResourceName: "rw-object", + Attributes: nil, + Type: common.ValueTypeObject, + } + objectValue := &sdkModels.CommandValue{ + DeviceResourceName: "rw-object", + Type: common.ValueTypeObject, + Value: map[string]interface{}{"foo": "bar"}, + Tags: make(map[string]string), + } driverMock.On("HandleReadCommands", "test-device", testProtocols, []sdkModels.CommandRequest{cr}).Return(nil, nil) driverMock.On("HandleWriteCommands", "test-device", testProtocols, []sdkModels.CommandRequest{cr}, []*sdkModels.CommandValue{cv}).Return(nil) + driverMock.On("HandleWriteCommands", "test-device", testProtocols, []sdkModels.CommandRequest{objectRequest}, []*sdkModels.CommandValue{objectValue}).Return(nil) devices := responses.MultiDevicesResponse{ - BaseResponse: common.BaseResponse{}, + BaseResponse: dtoCommon.BaseResponse{}, Devices: []dtos.Device{ dtos.FromDeviceModelToDTO(testDevice), }, @@ -64,7 +77,7 @@ func mockDic() *di.Container { dcMock.On("DevicesByServiceName", context.Background(), "test-service", 0, -1).Return(devices, nil) profile := responses.DeviceProfileResponse{ - BaseResponse: common.BaseResponse{}, + BaseResponse: dtoCommon.BaseResponse{}, Profile: dtos.DeviceProfile{ Name: "test-profile", DeviceResources: []dtos.DeviceResource{ @@ -90,6 +103,13 @@ func mockDic() *di.Container { ReadWrite: "W", }, }, + dtos.DeviceResource{ + Name: "rw-object", + Properties: dtos.ResourceProperties{ + ValueType: common.ValueTypeObject, + ReadWrite: "RW", + }, + }, }, DeviceCommands: []dtos.DeviceCommand{ dtos.DeviceCommand{ @@ -222,11 +242,12 @@ func TestCommandProcessor_WriteDeviceResource(t *testing.T) { err := cache.InitCache("test-service", dic) require.NoError(t, err) - valid := NewCommandProcessor(testDevice, "test-resource", uuid.NewString(), map[string]string{"test-resource": "test-value"}, "", dic) + valid := NewCommandProcessor(testDevice, "test-resource", uuid.NewString(), map[string]interface{}{"test-resource": "test-value"}, "", dic) + validObjectValue := NewCommandProcessor(testDevice, "rw-object", uuid.NewString(), map[string]interface{}{"rw-object": map[string]interface{}{"foo": "bar"}}, "", dic) invalidDeviceResource := NewCommandProcessor(testDevice, "invalid", uuid.NewString(), nil, "", dic) readOnlyDeviceResource := NewCommandProcessor(testDevice, "ro-resource", uuid.NewString(), nil, "", dic) noRequestBody := NewCommandProcessor(testDevice, "test-resource", uuid.NewString(), nil, "", dic) - invalidRequestBody := NewCommandProcessor(testDevice, "test-resource", uuid.NewString(), map[string]string{"wrong-resource": "wrong-value"}, "", dic) + invalidRequestBody := NewCommandProcessor(testDevice, "test-resource", uuid.NewString(), map[string]interface{}{"wrong-resource": "wrong-value"}, "", dic) tests := []struct { name string @@ -234,6 +255,7 @@ func TestCommandProcessor_WriteDeviceResource(t *testing.T) { expectedErr bool }{ {"valid", valid, false}, + {"valid object value", validObjectValue, false}, {"invalid - DeviceResource name not found", invalidDeviceResource, true}, {"invalid - writing read-only DeviceResource", readOnlyDeviceResource, true}, {"valid - no set parameter specified but default value exists", noRequestBody, false}, @@ -257,12 +279,12 @@ func TestCommandProcessor_WriteDeviceCommand(t *testing.T) { err := cache.InitCache("test-service", dic) require.NoError(t, err) - valid := NewCommandProcessor(testDevice, "test-command", uuid.NewString(), map[string]string{"test-resource": "test-value"}, "", dic) + valid := NewCommandProcessor(testDevice, "test-command", uuid.NewString(), map[string]interface{}{"test-resource": "test-value"}, "", dic) invalidDeviceCommand := NewCommandProcessor(testDevice, "invalid", uuid.NewString(), nil, "", dic) readOnlyDeviceCommand := NewCommandProcessor(testDevice, "ro-command", uuid.NewString(), nil, "", dic) outOfRangeResourceOperation := NewCommandProcessor(testDevice, "exceed-command", uuid.NewString(), nil, "", dic) noRequestBody := NewCommandProcessor(testDevice, "test-command", uuid.NewString(), nil, "", dic) - invalidRequestBody := NewCommandProcessor(testDevice, "test-command", uuid.NewString(), map[string]string{"wrong-resource": "wrong-value"}, "", dic) + invalidRequestBody := NewCommandProcessor(testDevice, "test-command", uuid.NewString(), map[string]interface{}{"wrong-resource": "wrong-value"}, "", dic) tests := []struct { name string diff --git a/internal/controller/http/command.go b/internal/controller/http/command.go index e58c8ac66..ad9e24c2f 100644 --- a/internal/controller/http/command.go +++ b/internal/controller/http/command.go @@ -28,7 +28,7 @@ import ( func (c *RestController) Command(writer http.ResponseWriter, request *http.Request) { defer request.Body.Close() - var requestParamsMap map[string]string + var requestParamsMap map[string]interface{} var queryParams string var err errors.EdgeX var reserved url.Values @@ -77,7 +77,7 @@ func (c *RestController) Command(writer http.ResponseWriter, request *http.Reque } } -func parseRequestBody(req *http.Request, maxRequestSize int64) (map[string]string, errors.EdgeX) { +func parseRequestBody(req *http.Request, maxRequestSize int64) (map[string]interface{}, errors.EdgeX) { defer req.Body.Close() body, err := io.ReadAll(req.Body) if err != nil { @@ -88,7 +88,7 @@ func parseRequestBody(req *http.Request, maxRequestSize int64) (map[string]strin return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to read request body", err) } - var paramMap = make(map[string]string) + var paramMap = make(map[string]interface{}) if len(body) == 0 { return paramMap, nil }