Repository: edgexfoundry/device-sdk-go
Pull request #1639 (2024-10-15 09:02:01)

feat: Using isNull field instead of nil reading value
Using isNull instead of nil reading value

<!-- Expected Commit Message Description (imported automatically by GitHub) -->
<!-- Must conform to [conventional commits guidelines](https://github.com/edgexfoundry/device-sdk-go/blob/main/.github/Contributing.md) -->
<!-- Expected Commit message must contain Closes/Fixes #IssueNumber statement when there is a related issue -->

<!-- Add additional detailed description of need for change if no related issue -->

**If your build fails** due to your commit message not passing the build checks, please review the guidelines here: https://github.com/edgexfoundry/device-sdk-go/blob/main/.github/Contributing.md

## PR Checklist
Please check if your PR fulfills the following requirements:

- [x] I am not introducing a breaking change (if you are, flag in conventional commit message with `BREAKING CHANGE:` describing the break)
- [x] I am not introducing a new dependency (add notes below if you are)
- [x] I have added unit tests for the new feature or bug fix (if not, why?)
- [x] I have fully tested (add details below) this the new feature or bug fix (if not, why?)
- [ ] I have opened a PR for the related docs change (if not, why?)
  <link to docs PR>

## Testing Instructions
<!-- How can the reviewers test your change? -->
- run make test
- test with simple DS

This PR is based on the core-contracts https://github.com/edgexfoundry/go-mod-core-contracts/pull/935

## New Dependency Instructions (If applicable)
<!-- Please follow [vetting instructions](https://wiki.edgexfoundry.org/display/FA/Vetting+Process+for+3rd+Party+Dependencies) and place results here -->

weichou1229: This PR is based on the core-contracts changes https://github.com/edgexfoundry/go-mod-core-contracts/pull/935
codecov-commenter: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/pull/1639?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) Report
Attention: Patch coverage is `16.66667%` with `10 lines` in your changes missing coverage. Please review.
> Project coverage is 32.69%. Comparing base [(`63928f2`)](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/commit/63928f2973ca38a899cb41cf0d65ddd086cb79a5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) to head [(`32ec9c0`)](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/commit/32ec9c0590e36fbe6ba2b3912b25027f4a245719?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry).

| [Files with missing lines](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/pull/1639?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) | Patch % | Lines |
|---|---|---|
| [internal/application/command.go](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/pull/1639?src=pr&el=tree&filepath=internal%2Fapplication%2Fcommand.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-aW50ZXJuYWwvYXBwbGljYXRpb24vY29tbWFuZC5nbw==) | 0.00% | [5 Missing and 1 partial :warning: ](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/pull/1639?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) |
| [internal/transformer/transform.go](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/pull/1639?src=pr&el=tree&filepath=internal%2Ftransformer%2Ftransform.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-aW50ZXJuYWwvdHJhbnNmb3JtZXIvdHJhbnNmb3JtLmdv) | 50.00% | [0 Missing and 2 partials :warning: ](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/pull/1639?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) |
| [internal/transformer/transformparam.go](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/pull/1639?src=pr&el=tree&filepath=internal%2Ftransformer%2Ftransformparam.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-aW50ZXJuYWwvdHJhbnNmb3JtZXIvdHJhbnNmb3JtcGFyYW0uZ28=) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/pull/1639?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) |

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1639      +/-   ##
==========================================
+ Coverage   32.65%   32.69%   +0.03%     
==========================================
  Files          48       48              
  Lines        4746     4756      +10     
==========================================
+ Hits         1550     1555       +5     
- Misses       3057     3063       +6     
+ Partials      139      138       -1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/edgexfoundry/device-sdk-go/pull/1639?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry).


Patch:
diff --git a/go.mod b/go.mod
index 7fe4e500..72b6289e 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.23
 require (
 	github.com/OneOfOne/xxhash v1.2.8
 	github.com/edgexfoundry/go-mod-bootstrap/v3 v3.2.0-dev.66
-	github.com/edgexfoundry/go-mod-core-contracts/v3 v3.2.0-dev.50
+	github.com/edgexfoundry/go-mod-core-contracts/v3 v3.2.0-dev.53
 	github.com/edgexfoundry/go-mod-messaging/v3 v3.2.0-dev.40
 	github.com/google/uuid v1.6.0
 	github.com/hashicorp/go-multierror v1.1.1
diff --git a/go.sum b/go.sum
index 88bed1dc..5c1ec5ec 100644
--- a/go.sum
+++ b/go.sum
@@ -90,8 +90,8 @@ github.com/edgexfoundry/go-mod-bootstrap/v3 v3.2.0-dev.66 h1:kmBEAhNi4ftrJMXM3Iv
 github.com/edgexfoundry/go-mod-bootstrap/v3 v3.2.0-dev.66/go.mod h1:3IXVpc5Qez5nwFJ8IkMyJMba8Iavj620E0XB42BQzfQ=
 github.com/edgexfoundry/go-mod-configuration/v3 v3.2.0-dev.19 h1:274NZdVBkJBuQP6yT3tVrb7psTFuIPogX/DLQqv7OCQ=
 github.com/edgexfoundry/go-mod-configuration/v3 v3.2.0-dev.19/go.mod h1:BG6hCDxXizpgMdNEljwNfLWSsd4Op7GAHd3Pis1dVv8=
-github.com/edgexfoundry/go-mod-core-contracts/v3 v3.2.0-dev.50 h1:Ho1TAQki5DN/ALdYP4YGNzj/Z/CXBXSeBancA+YHtO4=
-github.com/edgexfoundry/go-mod-core-contracts/v3 v3.2.0-dev.50/go.mod h1:MLk37/79M26+bZr3IptNZuYmQBEVbXwzDp1VHQkFhIk=
+github.com/edgexfoundry/go-mod-core-contracts/v3 v3.2.0-dev.53 h1:aHnYwqpg0LcnMrgNQlkRQjzHAS/IML/9GI368OmNCz4=
+github.com/edgexfoundry/go-mod-core-contracts/v3 v3.2.0-dev.53/go.mod h1:MLk37/79M26+bZr3IptNZuYmQBEVbXwzDp1VHQkFhIk=
 github.com/edgexfoundry/go-mod-messaging/v3 v3.2.0-dev.40 h1:YyB21HEapV5pENG01vFlpjPI6UkmKpJuaWFfgGFVUsY=
 github.com/edgexfoundry/go-mod-messaging/v3 v3.2.0-dev.40/go.mod h1:8NpZ6/eAsiyZHgn/s3DRIpcOjUrve+ZONIgvcDvA3Yg=
 github.com/edgexfoundry/go-mod-registry/v3 v3.2.0-dev.18 h1:AzILZ/xcEmSYVhIwSF4zkWpXyFoBA733E/j8ttzlNnI=
diff --git a/internal/application/command.go b/internal/application/command.go
index 99b800f2..555ef76f 100644
--- a/internal/application/command.go
+++ b/internal/application/command.go
@@ -460,6 +460,14 @@ func validateServiceAndDeviceState(deviceName string, dic *di.Container) (models
 }
 
 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
 
diff --git a/internal/transformer/transform.go b/internal/transformer/transform.go
index 31352052..b1932c20 100644
--- a/internal/transformer/transform.go
+++ b/internal/transformer/transform.go
@@ -60,7 +60,7 @@ func CommandValuesToEventDTO(cvs []*models.CommandValue, deviceName string, sour
 		}
 
 		// perform data transformation
-		if dataTransform {
+		if dataTransform && cv.Value != nil {
 			edgexErr := TransformReadResult(cv, dr.Properties)
 			if edgexErr != nil {
 				lc.Errorf("failed to transform CommandValue (%s): %v", cv.String(), edgexErr)
@@ -145,7 +145,9 @@ func commandValueToReading(cv *models.CommandValue, deviceName, profileName, med
 	var err error
 	var reading dtos.BaseReading
 
-	if cv.Type == common.ValueTypeBinary {
+	if cv.Value == nil {
+		reading = dtos.NewNullReading(profileName, deviceName, cv.DeviceResourceName, cv.Type)
+	} else if cv.Type == common.ValueTypeBinary {
 		var binary []byte
 		binary, err = cv.BinaryValue()
 		if err != nil {
diff --git a/internal/transformer/transformparam.go b/internal/transformer/transformparam.go
index 9ad1a56f..239bf848 100644
--- a/internal/transformer/transformparam.go
+++ b/internal/transformer/transformparam.go
@@ -15,6 +15,9 @@ import (
 )
 
 func TransformWriteParameter(cv *dsModels.CommandValue, pv models.ResourceProperties) errors.EdgeX {
+	if cv.Value == nil {
+		return nil
+	}
 	if !isNumericValueType(cv) {
 		return nil
 	}


Test patch:
diff --git a/internal/autoevent/executor_test.go b/internal/autoevent/executor_test.go
index 3c8aa1e6..3154be0d 100644
--- a/internal/autoevent/executor_test.go
+++ b/internal/autoevent/executor_test.go
@@ -26,20 +26,17 @@ func TestCompareReadings(t *testing.T) {
 	e, err := NewExecutor("device-test", autoEvent, pool)
 	require.NoError(t, err)
 
-	value1 := "1"
-	value2 := "2"
-	value3 := "3"
 	testReadings := []dtos.BaseReading{{ResourceName: "r1"}, {ResourceName: "r2"}}
 	testReadings[0].ValueType = common.ValueTypeInt8
-	testReadings[0].Value = &value1
+	testReadings[0].Value = "1"
 	testReadings[0].ValueType = common.ValueTypeInt8
-	testReadings[1].Value = &value2
+	testReadings[1].Value = "2"
 
 	firstReadings := testReadings
 
 	readingsValueChanged := make([]dtos.BaseReading, len(firstReadings))
 	copy(readingsValueChanged, firstReadings)
-	readingsValueChanged[1].Value = &value3
+	readingsValueChanged[1].Value = "3"
 
 	readingsResourceChanged := make([]dtos.BaseReading, len(readingsValueChanged))
 	copy(readingsResourceChanged, readingsValueChanged)
diff --git a/internal/transformer/transform_test.go b/internal/transformer/transform_test.go
index 86566b23..ecf3edb7 100644
--- a/internal/transformer/transform_test.go
+++ b/internal/transformer/transform_test.go
@@ -193,3 +193,55 @@ func TestCommandValuesToEventDTO_ReadingUnits(t *testing.T) {
 		})
 	}
 }
+
+func TestCommandValuesToEventDTO_ReadingNilValue(t *testing.T) {
+	dic := NewMockDIC()
+	err := cache.InitCache(TestDeviceService, TestDeviceService, dic)
+	require.NoError(t, err)
+	cvs := []*sdkModels.CommandValue{
+		testCommandNilValue(t, common.ValueTypeBool),
+		testCommandNilValue(t, common.ValueTypeString),
+		testCommandNilValue(t, common.ValueTypeUint8),
+		testCommandNilValue(t, common.ValueTypeUint16),
+		testCommandNilValue(t, common.ValueTypeUint32),
+		testCommandNilValue(t, common.ValueTypeUint64),
+		testCommandNilValue(t, common.ValueTypeInt8),
+		testCommandNilValue(t, common.ValueTypeInt16),
+		testCommandNilValue(t, common.ValueTypeInt32),
+		testCommandNilValue(t, common.ValueTypeInt64),
+		testCommandNilValue(t, common.ValueTypeFloat32),
+		testCommandNilValue(t, common.ValueTypeFloat64),
+		testCommandNilValue(t, common.ValueTypeBinary),
+		testCommandNilValue(t, common.ValueTypeBoolArray),
+		testCommandNilValue(t, common.ValueTypeStringArray),
+		testCommandNilValue(t, common.ValueTypeUint8Array),
+		testCommandNilValue(t, common.ValueTypeUint16Array),
+		testCommandNilValue(t, common.ValueTypeUint32Array),
+		testCommandNilValue(t, common.ValueTypeUint64Array),
+		testCommandNilValue(t, common.ValueTypeInt8Array),
+		testCommandNilValue(t, common.ValueTypeInt16Array),
+		testCommandNilValue(t, common.ValueTypeInt32Array),
+		testCommandNilValue(t, common.ValueTypeInt64Array),
+		testCommandNilValue(t, common.ValueTypeFloat32Array),
+		testCommandNilValue(t, common.ValueTypeFloat64Array),
+		testCommandNilValue(t, common.ValueTypeObject),
+		testCommandNilValue(t, common.ValueTypeObjectArray),
+	}
+	event, err := CommandValuesToEventDTO(cvs, TestDevice, TestDeviceCommand, true, dic)
+	require.NoError(t, err)
+
+	for _, r := range event.Readings {
+		assert.Empty(t, r.Value)
+		assert.Empty(t, r.BinaryValue)
+		assert.Empty(t, r.ObjectValue)
+		expectedNullReading := dtos.NewNullReading(TestProfile, TestDevice, TestDeviceResource, r.ValueType)
+		expectedNullReading.Id = r.Id
+		expectedNullReading.Origin = r.Origin
+		assert.Equal(t, expectedNullReading, r)
+	}
+}
+func testCommandNilValue(t *testing.T, valueType string) *sdkModels.CommandValue {
+	cv, e := sdkModels.NewCommandValue(TestDeviceResource, valueType, nil)
+	require.NoError(t, e)
+	return cv
+}