Skip to content

Commit 9f16ddf

Browse files
weichou1229FelixTing
authored andcommitted
feat: Add Clone func for Device, DeviceProfile, ProvisionWatcher (#1004)
Add Clone func for Device, DeviceProfile, ProvisionWatcher for the sdk cache to return copied data. Signed-off-by: bruce <[email protected]>
1 parent 84ca4df commit 9f16ddf

11 files changed

+392
-8
lines changed

models/device.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//
2-
// Copyright (C) 2020-2024 IOTech Ltd
2+
// Copyright (C) 2020-2025 IOTech Ltd
33
//
44
// SPDX-License-Identifier: Apache-2.0
55

66
package models
77

8+
import "maps"
9+
810
type Device struct {
911
DBTimestamp
1012
Id string
@@ -26,6 +28,12 @@ type Device struct {
2628
// ProtocolProperties contains the device connection information in key/value pair
2729
type ProtocolProperties map[string]any
2830

31+
func (p ProtocolProperties) Clone() ProtocolProperties {
32+
cloned := make(map[string]any)
33+
maps.Copy(cloned, p)
34+
return cloned
35+
}
36+
2937
// AdminState controls the range of values which constitute valid administrative states for a device
3038
type AdminState string
3139

@@ -39,3 +47,41 @@ func AssignAdminState(dtoAdminState string) AdminState {
3947

4048
// OperatingState is an indication of the operations of the device.
4149
type OperatingState string
50+
51+
func (device Device) Clone() Device {
52+
cloned := Device{
53+
DBTimestamp: device.DBTimestamp,
54+
Id: device.Id,
55+
Name: device.Name,
56+
Parent: device.Parent,
57+
Description: device.Description,
58+
AdminState: device.AdminState,
59+
OperatingState: device.OperatingState,
60+
Location: device.Location,
61+
ServiceName: device.ServiceName,
62+
ProfileName: device.ProfileName,
63+
}
64+
if len(device.Protocols) > 0 {
65+
cloned.Protocols = make(map[string]ProtocolProperties)
66+
for k, v := range device.Protocols {
67+
cloned.Protocols[k] = v.Clone()
68+
}
69+
}
70+
if len(device.Labels) > 0 {
71+
cloned.Labels = make([]string, len(device.Labels))
72+
copy(cloned.Labels, device.Labels)
73+
}
74+
if len(device.AutoEvents) > 0 {
75+
cloned.AutoEvents = make([]AutoEvent, len(device.AutoEvents))
76+
copy(cloned.AutoEvents, device.AutoEvents)
77+
}
78+
if len(device.Tags) > 0 {
79+
cloned.Tags = make(map[string]any)
80+
maps.Copy(cloned.Tags, device.Tags)
81+
}
82+
if len(device.Properties) > 0 {
83+
cloned.Properties = make(map[string]any)
84+
maps.Copy(cloned.Properties, device.Properties)
85+
}
86+
return cloned
87+
}

models/device_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (C) 2025 IOTech Ltd
2+
3+
package models
4+
5+
import (
6+
"github.com/stretchr/testify/assert"
7+
"testing"
8+
)
9+
10+
func TestDevice_Clone(t *testing.T) {
11+
testDevice := Device{
12+
DBTimestamp: DBTimestamp{},
13+
Id: "ca93c8fa-9919-4ec5-85d3-f81b2b6a7bc1",
14+
Name: "testDevice",
15+
Parent: "testParent",
16+
Description: "testDescription",
17+
AdminState: Locked,
18+
OperatingState: Up,
19+
Protocols: map[string]ProtocolProperties{"other": map[string]any{"Address": "127.0.0.1"}},
20+
Labels: []string{"label1", "label2"},
21+
Location: map[string]any{"loc": "x.y.z"},
22+
ServiceName: "testServiceName",
23+
ProfileName: "testProfileName",
24+
AutoEvents: []AutoEvent{
25+
{
26+
Interval: "10s",
27+
OnChange: false,
28+
OnChangeThreshold: 0.5,
29+
SourceName: "testSourceName",
30+
Retention: Retention{
31+
MaxCap: 500,
32+
MinCap: 100,
33+
Duration: "1m",
34+
},
35+
},
36+
},
37+
Tags: map[string]any{"tag1": "val1", "tag2": "val2"},
38+
Properties: map[string]any{
39+
"foo": "bar",
40+
},
41+
}
42+
clone := testDevice.Clone()
43+
assert.Equal(t, testDevice, clone)
44+
}

models/devicecommand.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,35 @@
11
//
2-
// Copyright (C) 2020-2023 IOTech Ltd
2+
// Copyright (C) 2020-2025 IOTech Ltd
33
//
44
// SPDX-License-Identifier: Apache-2.0
55

66
package models
77

8+
import "maps"
9+
810
type DeviceCommand struct {
911
Name string
1012
IsHidden bool
1113
ReadWrite string
1214
ResourceOperations []ResourceOperation
1315
Tags map[string]any
1416
}
17+
18+
func (dc DeviceCommand) Clone() DeviceCommand {
19+
cloned := DeviceCommand{
20+
Name: dc.Name,
21+
IsHidden: dc.IsHidden,
22+
ReadWrite: dc.ReadWrite,
23+
}
24+
if len(dc.ResourceOperations) > 0 {
25+
cloned.ResourceOperations = make([]ResourceOperation, len(dc.ResourceOperations))
26+
for i, op := range dc.ResourceOperations {
27+
cloned.ResourceOperations[i] = op.Clone()
28+
}
29+
}
30+
if len(dc.Tags) > 0 {
31+
cloned.Tags = make(map[string]any)
32+
maps.Copy(cloned.Tags, dc.Tags)
33+
}
34+
return cloned
35+
}

models/deviceprofile.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (C) 2020-2024 IOTech Ltd
2+
// Copyright (C) 2020-2025 IOTech Ltd
33
//
44
// SPDX-License-Identifier: Apache-2.0
55

@@ -17,3 +17,32 @@ type DeviceProfile struct {
1717
DeviceResources []DeviceResource
1818
DeviceCommands []DeviceCommand
1919
}
20+
21+
func (profile DeviceProfile) Clone() DeviceProfile {
22+
cloned := DeviceProfile{
23+
DBTimestamp: profile.DBTimestamp,
24+
ApiVersion: profile.ApiVersion,
25+
Description: profile.Description,
26+
Id: profile.Id,
27+
Name: profile.Name,
28+
Manufacturer: profile.Manufacturer,
29+
Model: profile.Model,
30+
}
31+
if len(profile.Labels) > 0 {
32+
cloned.Labels = make([]string, len(profile.Labels))
33+
copy(cloned.Labels, profile.Labels)
34+
}
35+
if len(profile.DeviceResources) > 0 {
36+
cloned.DeviceResources = make([]DeviceResource, len(profile.DeviceResources))
37+
for i := range profile.DeviceResources {
38+
cloned.DeviceResources[i] = profile.DeviceResources[i].Clone()
39+
}
40+
}
41+
if len(profile.DeviceCommands) > 0 {
42+
cloned.DeviceCommands = make([]DeviceCommand, len(profile.DeviceCommands))
43+
for i := range profile.DeviceCommands {
44+
cloned.DeviceCommands[i] = profile.DeviceCommands[i].Clone()
45+
}
46+
}
47+
return cloned
48+
}

models/deviceprofile_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (C) 2025 IOTech Ltd
2+
3+
package models
4+
5+
import (
6+
"github.com/edgexfoundry/go-mod-core-contracts/v4/common"
7+
"github.com/stretchr/testify/assert"
8+
"testing"
9+
)
10+
11+
func TestDeviceProfile_Clone(t *testing.T) {
12+
testMinimum := -1.123
13+
testMaximum := 1.123
14+
testDeviceProfile := DeviceProfile{
15+
DBTimestamp: DBTimestamp{},
16+
ApiVersion: common.ApiVersion,
17+
Description: "test description",
18+
Id: "ca93c8fa-9919-4ec5-85d3-f81b2b6a7bc1",
19+
Name: "TestProfile",
20+
Manufacturer: "testManufacturer",
21+
Model: "testModel",
22+
Labels: []string{"label1", "label2"},
23+
DeviceResources: []DeviceResource{{
24+
Description: "test description",
25+
Name: "TestDeviceResource",
26+
IsHidden: false,
27+
Properties: ResourceProperties{
28+
ValueType: common.ValueTypeString, Minimum: &testMinimum, Maximum: &testMaximum},
29+
Attributes: map[string]any{
30+
"foo": "bar",
31+
},
32+
Tags: map[string]any{
33+
"tag1": "val1",
34+
},
35+
}},
36+
DeviceCommands: []DeviceCommand{{
37+
Name: "TestDeviceCommand",
38+
IsHidden: false,
39+
ReadWrite: "RW",
40+
ResourceOperations: []ResourceOperation{{
41+
DeviceResource: "TestDeviceResource1",
42+
DefaultValue: "",
43+
Mappings: map[string]string{
44+
"on": "true",
45+
},
46+
}, {
47+
DeviceResource: "TestDeviceResource2",
48+
DefaultValue: "",
49+
Mappings: map[string]string{
50+
"off": "false",
51+
},
52+
}},
53+
Tags: map[string]any{
54+
"tag3": "val3",
55+
},
56+
}},
57+
}
58+
clone := testDeviceProfile.Clone()
59+
assert.Equal(t, testDeviceProfile, clone)
60+
}

models/deviceresource.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//
2-
// Copyright (C) 2020-2023 IOTech Ltd
2+
// Copyright (C) 2020-2025 IOTech Ltd
33
//
44
// SPDX-License-Identifier: Apache-2.0
55

66
package models
77

8+
import "maps"
9+
810
type DeviceResource struct {
911
Description string
1012
Name string
@@ -13,3 +15,21 @@ type DeviceResource struct {
1315
Attributes map[string]interface{}
1416
Tags map[string]any
1517
}
18+
19+
func (dr DeviceResource) Clone() DeviceResource {
20+
cloned := DeviceResource{
21+
Description: dr.Description,
22+
Name: dr.Name,
23+
IsHidden: dr.IsHidden,
24+
Properties: dr.Properties.Clone(),
25+
}
26+
if len(dr.Attributes) > 0 {
27+
cloned.Attributes = make(map[string]any)
28+
maps.Copy(cloned.Attributes, dr.Attributes)
29+
}
30+
if len(dr.Tags) > 0 {
31+
cloned.Tags = make(map[string]any)
32+
maps.Copy(cloned.Tags, dr.Tags)
33+
}
34+
return cloned
35+
}

models/discovereddevice.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
11
//
2-
// Copyright (C) 2023 IOTech Ltd
2+
// Copyright (C) 2023-2025 IOTech Ltd
33
//
44
// SPDX-License-Identifier: Apache-2.0
55

66
package models
77

8+
import "maps"
9+
810
type DiscoveredDevice struct {
911
ProfileName string
1012
AdminState AdminState
1113
AutoEvents []AutoEvent
1214
Properties map[string]any
1315
}
16+
17+
func (d DiscoveredDevice) Clone() DiscoveredDevice {
18+
cloned := DiscoveredDevice{
19+
ProfileName: d.ProfileName,
20+
AdminState: d.AdminState,
21+
}
22+
if len(d.AutoEvents) > 0 {
23+
cloned.AutoEvents = make([]AutoEvent, len(d.AutoEvents))
24+
copy(cloned.AutoEvents, d.AutoEvents)
25+
}
26+
if len(d.Properties) > 0 {
27+
cloned.Properties = make(map[string]any)
28+
maps.Copy(cloned.Properties, d.Properties)
29+
}
30+
return cloned
31+
}

models/provisionwatcher.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//
2-
// Copyright (C) 2021-2023 IOTech Ltd
2+
// Copyright (C) 2021-2025 IOTech Ltd
33
//
44
// SPDX-License-Identifier: Apache-2.0
55

66
package models
77

8+
import "maps"
9+
810
type ProvisionWatcher struct {
911
DBTimestamp
1012
Id string
@@ -16,3 +18,30 @@ type ProvisionWatcher struct {
1618
AdminState AdminState
1719
DiscoveredDevice DiscoveredDevice
1820
}
21+
22+
func (pw ProvisionWatcher) Clone() ProvisionWatcher {
23+
cloned := ProvisionWatcher{
24+
DBTimestamp: pw.DBTimestamp,
25+
Id: pw.Id,
26+
Name: pw.Name,
27+
ServiceName: pw.ServiceName,
28+
AdminState: pw.AdminState,
29+
DiscoveredDevice: pw.DiscoveredDevice.Clone(),
30+
}
31+
if len(pw.Labels) > 0 {
32+
cloned.Labels = make([]string, len(pw.Labels))
33+
copy(cloned.Labels, pw.Labels)
34+
}
35+
if len(pw.Identifiers) > 0 {
36+
cloned.Identifiers = make(map[string]string)
37+
maps.Copy(cloned.Identifiers, pw.Identifiers)
38+
}
39+
if len(pw.BlockingIdentifiers) > 0 {
40+
cloned.BlockingIdentifiers = make(map[string][]string)
41+
for k, v := range pw.BlockingIdentifiers {
42+
cloned.BlockingIdentifiers[k] = make([]string, len(v))
43+
copy(cloned.BlockingIdentifiers[k], v)
44+
}
45+
}
46+
return cloned
47+
}

0 commit comments

Comments
 (0)