Skip to content

Commit 380041c

Browse files
committed
Add full write support to some features
Some old devices do not support partial writes. Therefor the complete dataset needs to be sent. The features `loadControl`, `deviceConfiguration` support this now, as these also support merges.
1 parent 22e8d65 commit 380041c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+737
-338
lines changed

features/client/deviceconfiguration.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,32 @@ func (d *DeviceConfiguration) WriteKeyValues(data []model.DeviceConfigurationKey
5959
return nil, api.ErrMissingData
6060
}
6161

62+
filters := []model.FilterType{*model.NewFilterTypePartial()}
63+
64+
// does the remote server feature not support partials?
65+
operation := d.featureRemote.Operations()[model.FunctionTypeDeviceConfigurationKeyValueListData]
66+
if operation == nil || !operation.WritePartial() {
67+
filters = nil
68+
// we need to send all data
69+
updateData := &model.DeviceConfigurationKeyValueListDataType{
70+
DeviceConfigurationKeyValueData: data,
71+
}
72+
73+
if mergedData, err := d.featureRemote.UpdateData(false, model.FunctionTypeDeviceConfigurationKeyValueListData, updateData, nil, nil); err == nil {
74+
data = mergedData.([]model.DeviceConfigurationKeyValueDataType)
75+
}
76+
}
77+
6278
cmd := model.CmdType{
63-
Function: util.Ptr(model.FunctionTypeDeviceConfigurationKeyValueListData),
64-
Filter: []model.FilterType{*model.NewFilterTypePartial()},
6579
DeviceConfigurationKeyValueListData: &model.DeviceConfigurationKeyValueListDataType{
6680
DeviceConfigurationKeyValueData: data,
6781
},
6882
}
6983

84+
if filters != nil {
85+
cmd.Filter = filters
86+
cmd.Function = util.Ptr(model.FunctionTypeDeviceConfigurationKeyValueListData)
87+
}
88+
7089
return d.remoteDevice.Sender().Write(d.featureLocal.Address(), d.featureRemote.Address(), cmd)
7190
}

features/client/deviceconfiguration_test.go

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@ func TestDeviceConfigurationSuite(t *testing.T) {
2020
type DeviceConfigurationSuite struct {
2121
suite.Suite
2222

23-
localEntity spineapi.EntityLocalInterface
24-
remoteEntity spineapi.EntityRemoteInterface
23+
localEntity spineapi.EntityLocalInterface
24+
localEntityPartial spineapi.EntityLocalInterface
25+
26+
remoteEntity spineapi.EntityRemoteInterface
27+
remoteEntityPartial spineapi.EntityRemoteInterface
28+
2529
mockRemoteEntity *mocks.EntityRemoteInterface
2630

27-
deviceConfiguration *DeviceConfiguration
31+
deviceConfiguration *DeviceConfiguration
32+
deviceConfigurationPartial *DeviceConfiguration
2833
}
2934

3035
const remoteSki string = "testremoteski"
@@ -47,6 +52,21 @@ func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) {
4752
},
4853
)
4954

55+
s.localEntityPartial, s.remoteEntityPartial = setupFeatures(
56+
s.T(),
57+
mockWriter,
58+
[]featureFunctions{
59+
{
60+
featureType: model.FeatureTypeTypeDeviceConfiguration,
61+
functions: []model.FunctionType{
62+
model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData,
63+
model.FunctionTypeDeviceConfigurationKeyValueListData,
64+
},
65+
partial: true,
66+
},
67+
},
68+
)
69+
5070
mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T())
5171
s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T())
5272
mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T())
@@ -66,6 +86,10 @@ func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) {
6686
s.deviceConfiguration, err = NewDeviceConfiguration(s.localEntity, s.remoteEntity)
6787
assert.Nil(s.T(), err)
6888
assert.NotNil(s.T(), s.deviceConfiguration)
89+
90+
s.deviceConfigurationPartial, err = NewDeviceConfiguration(s.localEntityPartial, s.remoteEntityPartial)
91+
assert.Nil(s.T(), err)
92+
assert.NotNil(s.T(), s.deviceConfiguration)
6993
}
7094

7195
func (s *DeviceConfigurationSuite) Test_RequestKeyValueDescriptions() {
@@ -104,6 +128,34 @@ func (s *DeviceConfigurationSuite) Test_WriteValues() {
104128
assert.NotNil(s.T(), err)
105129
assert.Nil(s.T(), counter)
106130

131+
rF := s.remoteEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer)
132+
data1 := rF.DataCopy(model.FunctionTypeDeviceConfigurationKeyValueListData).(*model.DeviceConfigurationKeyValueListDataType)
133+
assert.Nil(s.T(), data1)
134+
135+
defaultData := &model.DeviceConfigurationKeyValueListDataType{
136+
DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{
137+
{
138+
KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)),
139+
IsValueChangeable: util.Ptr(true),
140+
Value: &model.DeviceConfigurationKeyValueValueType{
141+
ScaledNumber: model.NewScaledNumberType(16),
142+
},
143+
},
144+
{
145+
KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(1)),
146+
IsValueChangeable: util.Ptr(true),
147+
Value: &model.DeviceConfigurationKeyValueValueType{
148+
ScaledNumber: model.NewScaledNumberType(32),
149+
},
150+
},
151+
},
152+
}
153+
_, err1 := rF.UpdateData(true, model.FunctionTypeDeviceConfigurationKeyValueListData, defaultData, nil, nil)
154+
assert.Nil(s.T(), err1)
155+
data1 = rF.DataCopy(model.FunctionTypeDeviceConfigurationKeyValueListData).(*model.DeviceConfigurationKeyValueListDataType)
156+
assert.NotNil(s.T(), data1)
157+
assert.Equal(s.T(), 2, len(data1.DeviceConfigurationKeyValueData))
158+
107159
data = []model.DeviceConfigurationKeyValueDataType{
108160
{
109161
KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)),
@@ -116,3 +168,55 @@ func (s *DeviceConfigurationSuite) Test_WriteValues() {
116168
assert.Nil(s.T(), err)
117169
assert.NotNil(s.T(), counter)
118170
}
171+
172+
// test with partial support
173+
func (s *DeviceConfigurationSuite) Test_WriteValues_Partial() {
174+
counter, err := s.deviceConfigurationPartial.WriteKeyValues(nil)
175+
assert.NotNil(s.T(), err)
176+
assert.Nil(s.T(), counter)
177+
178+
data := []model.DeviceConfigurationKeyValueDataType{}
179+
counter, err = s.deviceConfigurationPartial.WriteKeyValues(data)
180+
assert.NotNil(s.T(), err)
181+
assert.Nil(s.T(), counter)
182+
183+
rF := s.remoteEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer)
184+
data1 := rF.DataCopy(model.FunctionTypeDeviceConfigurationKeyValueListData).(*model.DeviceConfigurationKeyValueListDataType)
185+
assert.Nil(s.T(), data1)
186+
187+
defaultData := &model.DeviceConfigurationKeyValueListDataType{
188+
DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{
189+
{
190+
KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)),
191+
IsValueChangeable: util.Ptr(true),
192+
Value: &model.DeviceConfigurationKeyValueValueType{
193+
ScaledNumber: model.NewScaledNumberType(16),
194+
},
195+
},
196+
{
197+
KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(1)),
198+
IsValueChangeable: util.Ptr(true),
199+
Value: &model.DeviceConfigurationKeyValueValueType{
200+
ScaledNumber: model.NewScaledNumberType(32),
201+
},
202+
},
203+
},
204+
}
205+
_, err1 := rF.UpdateData(true, model.FunctionTypeDeviceConfigurationKeyValueListData, defaultData, nil, nil)
206+
assert.Nil(s.T(), err1)
207+
data1 = rF.DataCopy(model.FunctionTypeDeviceConfigurationKeyValueListData).(*model.DeviceConfigurationKeyValueListDataType)
208+
assert.NotNil(s.T(), data1)
209+
assert.Equal(s.T(), 2, len(data1.DeviceConfigurationKeyValueData))
210+
211+
data = []model.DeviceConfigurationKeyValueDataType{
212+
{
213+
KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)),
214+
Value: &model.DeviceConfigurationKeyValueValueType{
215+
ScaledNumber: model.NewScaledNumberType(10),
216+
},
217+
},
218+
}
219+
counter, err = s.deviceConfigurationPartial.WriteKeyValues(data)
220+
assert.Nil(s.T(), err)
221+
assert.NotNil(s.T(), counter)
222+
}

features/client/feature.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ func (f *Feature) requestData(function model.FunctionType, selectors any, elemen
141141
}
142142

143143
// remove the selectors if the remote does not allow partial reads
144-
if selectors != nil && !op.ReadPartial() {
144+
// or partial writes, because in that case we need to have all data
145+
if selectors != nil && (!op.ReadPartial() || !op.WritePartial()) {
145146
selectors = nil
146147
elements = nil
147148
}

features/client/loadcontrol.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,25 +72,44 @@ func (l *LoadControl) WriteLimitData(
7272
}
7373

7474
var filters []model.FilterType
75+
var delFilter *model.FilterType
76+
partialFilter := model.NewFilterTypePartial()
7577
if deleteElements != nil && deleteSelectors != nil {
76-
delFilter := model.FilterType{
78+
delFilter = &model.FilterType{
7779
CmdControl: &model.CmdControlType{
7880
Delete: &model.ElementTagType{},
7981
},
8082
LoadControlLimitListDataSelectors: deleteSelectors,
8183
LoadControlLimitDataElements: deleteElements,
8284
}
83-
filters = append(filters, delFilter)
85+
filters = append(filters, *delFilter)
86+
}
87+
filters = append(filters, *partialFilter)
88+
89+
// does the remote server feature not support partials?
90+
operation := l.featureRemote.Operations()[model.FunctionTypeLoadControlLimitListData]
91+
if operation == nil || !operation.WritePartial() {
92+
filters = nil
93+
// we need to send all data
94+
updateData := &model.LoadControlLimitListDataType{
95+
LoadControlLimitData: data,
96+
}
97+
98+
if mergedData, err := l.featureRemote.UpdateData(false, model.FunctionTypeLoadControlLimitListData, updateData, partialFilter, delFilter); err == nil {
99+
data = mergedData.([]model.LoadControlLimitDataType)
100+
}
84101
}
85-
filters = append(filters, *model.NewFilterTypePartial())
86102

87103
cmd := model.CmdType{
88-
Function: util.Ptr(model.FunctionTypeLoadControlLimitListData),
89-
Filter: filters,
90104
LoadControlLimitListData: &model.LoadControlLimitListDataType{
91105
LoadControlLimitData: data,
92106
},
93107
}
94108

109+
if filters != nil {
110+
cmd.Filter = filters
111+
cmd.Function = util.Ptr(model.FunctionTypeLoadControlLimitListData)
112+
}
113+
95114
return l.remoteDevice.Sender().Write(l.featureLocal.Address(), l.featureRemote.Address(), cmd)
96115
}

0 commit comments

Comments
 (0)