From 497fb1cf5889c1abec5319fdd00cdee77d58b4d8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 13:08:14 +0100 Subject: [PATCH] Add method to check for UseCase & Feature support This can be used to verify the required usecase scenarios are available and the provided server features are also present --- spine/device_remote.go | 86 +++++++++++++++++++++++ spine/device_remote_test.go | 133 ++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) diff --git a/spine/device_remote.go b/spine/device_remote.go index e2c68681..36cdc7a9 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "reflect" + "slices" "sync" "github.com/enbility/eebus-go/logging" @@ -234,6 +235,91 @@ func (d *DeviceRemoteImpl) AddEntity(entity *EntityRemoteImpl) *EntityRemoteImpl return entity } +// Checks if the given actor, usecasename and provided server features are available +// Note: the server features are expected to be in a single entity and entity 0 is not checked! +func (d *DeviceRemoteImpl) VerifyUseCaseScenariosAndFeaturesSupport( + usecaseActor model.UseCaseActorType, + usecaseName model.UseCaseNameType, + scenarios []model.UseCaseScenarioSupportType, + serverFeatures []model.FeatureTypeType, +) bool { + remoteUseCaseManager := d.UseCaseManager() + + usecases := remoteUseCaseManager.UseCaseInformation() + if len(usecases) == 0 { + return false + } + + usecaseAndScenariosFound := false + for _, usecase := range usecases { + if usecase.Actor == nil || *usecase.Actor != usecaseActor { + continue + } + + for _, support := range usecase.UseCaseSupport { + if support.UseCaseName == nil || *support.UseCaseName != usecaseName { + continue + } + + if support.UseCaseAvailable == nil || !*support.UseCaseAvailable { + continue + } + + var foundScenarios []model.UseCaseScenarioSupportType + for _, scenario := range support.ScenarioSupport { + if slices.Contains(scenarios, scenario) { + foundScenarios = append(foundScenarios, scenario) + } + } + + if len(foundScenarios) == len(scenarios) { + usecaseAndScenariosFound = true + break + } + } + + if usecaseAndScenariosFound { + break + } + } + + if !usecaseAndScenariosFound { + return false + } + + entities := d.Entities() + if len(entities) < 2 { + return false + } + + entityWithServerFeaturesFound := false + + for index, entity := range entities { + // ignore NodeManagement entity + if index == 0 { + continue + } + + var foundServerFeatures []model.FeatureTypeType + for _, feature := range entity.Features() { + if feature.Role() != model.RoleTypeServer { + continue + } + + if slices.Contains(serverFeatures, feature.Type()) { + foundServerFeatures = append(foundServerFeatures, feature.Type()) + } + } + + if len(serverFeatures) == len(foundServerFeatures) { + entityWithServerFeaturesFound = true + break + } + } + + return entityWithServerFeaturesFound +} + func unmarshalFeature(entity *EntityRemoteImpl, featureData model.NodeManagementDetailedDiscoveryFeatureInformationType, ) *FeatureRemoteImpl { diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index b20a0bbb..9c05f95d 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -73,3 +73,136 @@ func (s *DeviceRemoteSuite) Test_FeatureByEntityTypeAndRole() { feature = s.remoteDevice.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) assert.Nil(s.T(), feature) } + +func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { + result := s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{}, + []model.FeatureTypeType{}, + ) + assert.Equal(s.T(), false, result) + + s.remoteDevice.UseCaseManager().Add( + model.UseCaseActorTypeBatterySystem, + model.UseCaseNameTypeControlOfBattery, + model.SpecificationVersionType("1.0.0"), + true, + []model.UseCaseScenarioSupportType{1}, + ) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{}, + []model.FeatureTypeType{}, + ) + assert.Equal(s.T(), false, result) + + s.remoteDevice.UseCaseManager().Add( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVCommissioningAndConfiguration, + model.SpecificationVersionType("1.0.0"), + true, + []model.UseCaseScenarioSupportType{1}, + ) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{}, + []model.FeatureTypeType{}, + ) + assert.Equal(s.T(), false, result) + + s.remoteDevice.UseCaseManager().Add( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + model.SpecificationVersionType("1.0.0"), + false, + []model.UseCaseScenarioSupportType{1}, + ) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{}, + []model.FeatureTypeType{}, + ) + assert.Equal(s.T(), false, result) + + s.remoteDevice.UseCaseManager().Add( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + model.SpecificationVersionType("1.0.0"), + true, + []model.UseCaseScenarioSupportType{1}, + ) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{}, + []model.FeatureTypeType{}, + ) + assert.Equal(s.T(), true, result) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{2}, + []model.FeatureTypeType{}, + ) + assert.Equal(s.T(), false, result) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{1}, + []model.FeatureTypeType{}, + ) + assert.Equal(s.T(), true, result) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{1}, + []model.FeatureTypeType{model.FeatureTypeTypeElectricalConnection}, + ) + assert.Equal(s.T(), false, result) + + entity := s.remoteDevice.Entity([]model.AddressEntityType{1}) + assert.NotNil(s.T(), entity) + + feature := spine.NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + entity.AddFeature(feature) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{1}, + []model.FeatureTypeType{model.FeatureTypeTypeElectricalConnection}, + ) + assert.Equal(s.T(), false, result) + + feature = spine.NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + entity.AddFeature(feature) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{1}, + []model.FeatureTypeType{model.FeatureTypeTypeElectricalConnection}, + ) + assert.Equal(s.T(), true, result) + + s.remoteDevice.RemoveByAddress(feature.Address().Entity) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{1}, + []model.FeatureTypeType{model.FeatureTypeTypeElectricalConnection}, + ) + assert.Equal(s.T(), false, result) +}