Skip to content

Commit dc40c82

Browse files
committed
OPEV: Add support for Bender missing required data
The Bender EVSE controller reports premitted value set data only for 1 phase (maybe detects if an EV is 1 phase only, or if the EVSE is connected 1 phase only), but has parameters for all possible phases. The OPEV spec 1.0.1.b says at page 30 “At least one set of permitted values SHALL be stated.” which it does not follow. Also LoadControlLimitListData only contains by default the limitIds, but none of the other mandatory fields.
1 parent 631be10 commit dc40c82

File tree

2 files changed

+213
-3
lines changed

2 files changed

+213
-3
lines changed

usecases/internal/loadcontrol.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ func LoadControlLimits(
8181
}
8282
_, dataMax, _, err := evElectricalConnection.GetPermittedValueDataForFilter(filter)
8383
if err != nil {
84-
return
84+
// the device provides no limits for this phase (e.g. Bender 1 Phase)
85+
// according to OpEV Spec 1.0.1b, page 30: "At least one set of permitted values SHALL be stated."
86+
// which is not the case for all elements for this EVSE setup
87+
// but we have to ignore this case and continue with the next phase
88+
continue
8589
}
8690

8791
limitValue = dataMax

usecases/internal/loadcontrol_test.go

Lines changed: 208 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,9 @@ func (s *InternalSuite) Test_LoadControlLimits() {
124124
assert.Nil(s.T(), fErr)
125125

126126
data, err = LoadControlLimits(s.localEntity, s.monitoredEntity, filter)
127-
assert.NotNil(s.T(), err)
128-
assert.Nil(s.T(), data)
127+
assert.Nil(s.T(), err)
128+
assert.NotNil(s.T(), data)
129+
assert.Equal(s.T(), 2, len(data))
129130

130131
permData := &model.ElectricalConnectionPermittedValueSetListDataType{
131132
ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{
@@ -158,6 +159,211 @@ func (s *InternalSuite) Test_LoadControlLimits() {
158159
assert.Equal(s.T(), 16.0, data[0].Value)
159160
}
160161

162+
func (s *InternalSuite) Test_LoadControlLimits_Bender_1Phase() {
163+
var data []ucapi.LoadLimitsPhase
164+
var err error
165+
limitType := model.LoadControlLimitTypeTypeMaxValueLimit
166+
scope := model.ScopeTypeTypeOverloadProtection
167+
category := model.LoadControlCategoryTypeObligation
168+
169+
filter := model.LoadControlLimitDescriptionDataType{
170+
LimitType: util.Ptr(limitType),
171+
LimitCategory: util.Ptr(category),
172+
ScopeType: util.Ptr(scope),
173+
}
174+
data, err = LoadControlLimits(nil, nil, filter)
175+
assert.NotNil(s.T(), err)
176+
assert.Nil(s.T(), data)
177+
178+
data, err = LoadControlLimits(s.localEntity, s.mockRemoteEntity, filter)
179+
assert.NotNil(s.T(), err)
180+
assert.Nil(s.T(), data)
181+
182+
data, err = LoadControlLimits(s.localEntity, s.monitoredEntity, filter)
183+
assert.NotNil(s.T(), err)
184+
assert.Nil(s.T(), data)
185+
186+
descData := &model.LoadControlLimitDescriptionListDataType{
187+
LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{
188+
{
189+
LimitId: util.Ptr(model.LoadControlLimitIdType(0)),
190+
LimitType: util.Ptr(limitType),
191+
LimitCategory: util.Ptr(category),
192+
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
193+
Unit: util.Ptr(model.UnitOfMeasurementTypeA),
194+
ScopeType: util.Ptr(scope),
195+
},
196+
{
197+
LimitId: util.Ptr(model.LoadControlLimitIdType(1)),
198+
LimitType: util.Ptr(limitType),
199+
LimitCategory: util.Ptr(category),
200+
MeasurementId: util.Ptr(model.MeasurementIdType(1)),
201+
Unit: util.Ptr(model.UnitOfMeasurementTypeA),
202+
ScopeType: util.Ptr(scope),
203+
},
204+
{
205+
LimitId: util.Ptr(model.LoadControlLimitIdType(2)),
206+
LimitType: util.Ptr(limitType),
207+
LimitCategory: util.Ptr(category),
208+
MeasurementId: util.Ptr(model.MeasurementIdType(2)),
209+
Unit: util.Ptr(model.UnitOfMeasurementTypeA),
210+
ScopeType: util.Ptr(scope),
211+
},
212+
},
213+
}
214+
215+
rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer)
216+
fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil)
217+
assert.Nil(s.T(), fErr)
218+
219+
data, err = LoadControlLimits(s.localEntity, s.monitoredEntity, filter)
220+
assert.Nil(s.T(), err)
221+
assert.Equal(s.T(), 3, len(data))
222+
assert.Equal(s.T(), 0.0, data[0].Value)
223+
224+
paramData := &model.ElectricalConnectionParameterDescriptionListDataType{
225+
ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{
226+
{
227+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
228+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)),
229+
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
230+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA),
231+
AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms),
232+
},
233+
{
234+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
235+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)),
236+
MeasurementId: util.Ptr(model.MeasurementIdType(1)),
237+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB),
238+
AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms),
239+
},
240+
{
241+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
242+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(2)),
243+
MeasurementId: util.Ptr(model.MeasurementIdType(2)),
244+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC),
245+
AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms),
246+
},
247+
{
248+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
249+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(3)),
250+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeAbc),
251+
ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal),
252+
},
253+
{
254+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
255+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(4)),
256+
MeasurementId: util.Ptr(model.MeasurementIdType(3)),
257+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA),
258+
},
259+
{
260+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
261+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(5)),
262+
MeasurementId: util.Ptr(model.MeasurementIdType(4)),
263+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB),
264+
},
265+
{
266+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
267+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(6)),
268+
MeasurementId: util.Ptr(model.MeasurementIdType(5)),
269+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC),
270+
},
271+
{
272+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
273+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(7)),
274+
MeasurementId: util.Ptr(model.MeasurementIdType(6)),
275+
VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc),
276+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeAbc),
277+
AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal),
278+
},
279+
},
280+
}
281+
282+
rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer)
283+
fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil)
284+
assert.Nil(s.T(), fErr)
285+
286+
data, err = LoadControlLimits(s.localEntity, s.monitoredEntity, filter)
287+
assert.NotNil(s.T(), err)
288+
assert.Nil(s.T(), data)
289+
290+
limitData := &model.LoadControlLimitListDataType{
291+
LoadControlLimitData: []model.LoadControlLimitDataType{
292+
{
293+
LimitId: util.Ptr(model.LoadControlLimitIdType(0)),
294+
},
295+
{
296+
LimitId: util.Ptr(model.LoadControlLimitIdType(1)),
297+
},
298+
{
299+
LimitId: util.Ptr(model.LoadControlLimitIdType(2)),
300+
},
301+
},
302+
}
303+
304+
fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil)
305+
assert.Nil(s.T(), fErr)
306+
307+
data, err = LoadControlLimits(s.localEntity, s.monitoredEntity, filter)
308+
assert.Nil(s.T(), err)
309+
assert.Nil(s.T(), data)
310+
assert.Equal(s.T(), 0, len(data))
311+
312+
// according to OPeV Spec 1.0.1b, page 30: "At least one set of permitted values SHALL be stated."
313+
// which is not the case here for all elements
314+
permData := &model.ElectricalConnectionPermittedValueSetListDataType{
315+
ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{
316+
{
317+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
318+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)),
319+
PermittedValueSet: []model.ScaledNumberSetType{
320+
{
321+
Range: []model.ScaledNumberRangeType{
322+
{
323+
Min: model.NewScaledNumberType(6),
324+
Max: model.NewScaledNumberType(16),
325+
},
326+
},
327+
},
328+
},
329+
},
330+
{
331+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
332+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)),
333+
},
334+
{
335+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
336+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(2)),
337+
},
338+
{
339+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
340+
ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(3)),
341+
PermittedValueSet: []model.ScaledNumberSetType{
342+
{
343+
Value: []model.ScaledNumberType{
344+
*model.NewScaledNumberType(0),
345+
},
346+
Range: []model.ScaledNumberRangeType{
347+
{
348+
Min: model.NewScaledNumberType(1362),
349+
Max: model.NewScaledNumberType(3632),
350+
},
351+
},
352+
},
353+
},
354+
},
355+
},
356+
}
357+
358+
fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil)
359+
assert.Nil(s.T(), fErr)
360+
361+
data, err = LoadControlLimits(s.localEntity, s.monitoredEntity, filter)
362+
assert.Nil(s.T(), err)
363+
assert.Equal(s.T(), 1, len(data))
364+
assert.Equal(s.T(), 16.0, data[0].Value)
365+
}
366+
161367
func (s *InternalSuite) Test_WriteLoadControlLimit() {
162368
loadLimit := ucapi.LoadLimit{
163369
Duration: time.Minute * 2,

0 commit comments

Comments
 (0)