Skip to content

Commit 936ad43

Browse files
Add full write support to some features (#109)
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.
2 parents 22e8d65 + 380041c commit 936ad43

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)