Skip to content

Commit

Permalink
Fix available uc scenarios on device removal
Browse files Browse the repository at this point in the history
When a device was disconnected, the cache of available scenarios wasn’t properly updated and the entities of the disconnected device still remained being available.

This update fixes the issue. In addition some method names  where updated to better reflect their functionality
  • Loading branch information
DerAndereAndi committed Dec 6, 2024
1 parent 82f4b31 commit 6df8552
Show file tree
Hide file tree
Showing 18 changed files with 101 additions and 38 deletions.
2 changes: 1 addition & 1 deletion usecases/cem/cevc/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (e *CEVC) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.evConnected(payload.Entity)
return
}
Expand Down
4 changes: 2 additions & 2 deletions usecases/cem/evcc/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ func (e *EVCC) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.evConnected(payload)
return
} else if internal.IsEntityDisconnected(payload) {
} else if internal.IsEntityRemoved(payload) {
e.evDisconnected(payload)
return
}
Expand Down
2 changes: 1 addition & 1 deletion usecases/cem/evcem/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (e *EVCEM) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.evConnected(payload.Entity)
return
}
Expand Down
4 changes: 2 additions & 2 deletions usecases/cem/evsecc/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ func (e *EVSECC) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.evseConnected(payload)
return
} else if internal.IsEntityDisconnected(payload) {
} else if internal.IsEntityRemoved(payload) {
e.evseDisconnected(payload)
return
}
Expand Down
2 changes: 1 addition & 1 deletion usecases/cem/evsoc/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func (e *EVSOC) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.evConnected(payload.Entity)
return
}
Expand Down
2 changes: 1 addition & 1 deletion usecases/cem/opev/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (e *OPEV) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.evConnected(payload.Entity)
return
}
Expand Down
2 changes: 1 addition & 1 deletion usecases/cem/vabd/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (e *VABD) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.inverterConnected(payload.Entity)
return
}
Expand Down
2 changes: 1 addition & 1 deletion usecases/cem/vapd/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (e *VAPD) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.inverterConnected(payload.Entity)
return
}
Expand Down
2 changes: 1 addition & 1 deletion usecases/eg/lpc/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (e *LPC) HandleEvent(payload spineapi.EventPayload) {
if !e.IsCompatibleEntityType(payload.Entity) {
return
}
if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.connected(payload.Entity)
return
}
Expand Down
2 changes: 1 addition & 1 deletion usecases/eg/lpp/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func (e *LPP) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.connected(payload.Entity)
return
}
Expand Down
6 changes: 3 additions & 3 deletions usecases/internal/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ func IsDeviceConnected(payload spineapi.EventPayload) bool {
}

func IsDeviceDisconnected(payload spineapi.EventPayload) bool {
return payload.Device != nil &&
return len(payload.Ski) > 0 &&
payload.EventType == spineapi.EventTypeDeviceChange &&
payload.ChangeType == spineapi.ElementChangeRemove
}

func IsEntityConnected(payload spineapi.EventPayload) bool {
func IsEntityAdded(payload spineapi.EventPayload) bool {
if payload.Entity != nil &&
payload.EventType == spineapi.EventTypeEntityChange &&
payload.ChangeType == spineapi.ElementChangeAdd {
Expand All @@ -26,7 +26,7 @@ func IsEntityConnected(payload spineapi.EventPayload) bool {
return false
}

func IsEntityDisconnected(payload spineapi.EventPayload) bool {
func IsEntityRemoved(payload spineapi.EventPayload) bool {
if payload.Entity != nil &&
payload.EventType == spineapi.EventTypeEntityChange &&
payload.ChangeType == spineapi.ElementChangeRemove {
Expand Down
13 changes: 7 additions & 6 deletions usecases/internal/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func (s *InternalSuite) Test_IsDeviceDisconnected() {

device := mocks.NewDeviceRemoteInterface(s.T())
payload = spineapi.EventPayload{
Ski: "12345",
Device: device,
EventType: spineapi.EventTypeDeviceChange,
ChangeType: spineapi.ElementChangeRemove,
Expand All @@ -36,30 +37,30 @@ func (s *InternalSuite) Test_IsDeviceDisconnected() {
assert.Equal(s.T(), true, result)
}

func (s *InternalSuite) Test_IsEntityConnected() {
func (s *InternalSuite) Test_IsEntityAdded() {
payload := spineapi.EventPayload{}
result := IsEntityConnected(payload)
result := IsEntityAdded(payload)
assert.Equal(s.T(), false, result)

payload = spineapi.EventPayload{
Entity: s.evseEntity,
EventType: spineapi.EventTypeEntityChange,
ChangeType: spineapi.ElementChangeAdd,
}
result = IsEntityConnected(payload)
result = IsEntityAdded(payload)
assert.Equal(s.T(), true, result)
}

func (s *InternalSuite) Test_IsEntityDisconnected() {
func (s *InternalSuite) Test_IsEntityRemoved() {
payload := spineapi.EventPayload{}
result := IsEntityDisconnected(payload)
result := IsEntityRemoved(payload)
assert.Equal(s.T(), false, result)

payload = spineapi.EventPayload{
Entity: s.evseEntity,
EventType: spineapi.EventTypeEntityChange,
ChangeType: spineapi.ElementChangeRemove,
}
result = IsEntityDisconnected(payload)
result = IsEntityRemoved(payload)
assert.Equal(s.T(), true, result)
}
2 changes: 1 addition & 1 deletion usecases/ma/mgcp/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (e *MGCP) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.gridConnected(payload.Entity)
return
}
Expand Down
2 changes: 1 addition & 1 deletion usecases/ma/mpc/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (e *MPC) HandleEvent(payload spineapi.EventPayload) {
return
}

if internal.IsEntityConnected(payload) {
if internal.IsEntityAdded(payload) {
e.deviceConnected(payload.Entity)
return
}
Expand Down
11 changes: 10 additions & 1 deletion usecases/usecase/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ func (u *UseCaseBase) HandleEvent(payload spineapi.EventPayload) {
}

func (u *UseCaseBase) deviceOrEntityRemoved(payload spineapi.EventPayload) bool {
if internal.IsDeviceDisconnected(payload) || internal.IsEntityDisconnected(payload) {
if payload.EventType == spineapi.EventTypeDeviceChange &&
payload.ChangeType == spineapi.ElementChangeRemove &&
payload.Entity == nil {
// device was disconnected, remove all usecases related to this device
u.removeDeviceFromAvailableEntityScenarios(payload.Ski)
return true
}

if internal.IsEntityRemoved(payload) {
// entity was disconnected, remove all usecases related to this entity
u.removeEntityFromAvailableEntityScenarios(payload.Entity)
return true
}
Expand Down
4 changes: 4 additions & 0 deletions usecases/usecase/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ func (s *UseCaseSuite) Test_HandleEvent() {
payload := spineapi.EventPayload{}
s.uc.HandleEvent(payload)

payload.Ski = "test"
payload.Device = s.remoteDevice
payload.EventType = spineapi.EventTypeDeviceChange
payload.ChangeType = spineapi.ElementChangeRemove
s.uc.HandleEvent(payload)

payload.EventType = spineapi.EventTypeEntityChange
payload.Entity = s.remoteDevice.Entities()[0]
s.uc.HandleEvent(payload)

payload.EventType = spineapi.EventTypeDataChange
payload.ChangeType = spineapi.ElementChangeUpdate
payload.Data = &model.NodeManagementUseCaseDataType{}
Expand Down
67 changes: 53 additions & 14 deletions usecases/usecase/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (u *UseCaseBase) RemoteEntitiesScenarios() []api.RemoteEntityScenarios {

// return the currently available scenarios for the use case for a remote entity
func (u *UseCaseBase) AvailableScenariosForEntity(entity spineapi.EntityRemoteInterface) []uint {
_, scenarios := u.indexAndScenariosOfEntity(entity)
_, scenarios := u.entitiyScenarioIndexOfEntity(entity)

return scenarios
}
Expand All @@ -137,24 +137,42 @@ func (u *UseCaseBase) IsScenarioAvailableAtEntity(
entity spineapi.EntityRemoteInterface,
scenario uint,
) bool {
if _, scenarios := u.indexAndScenariosOfEntity(entity); scenarios != nil {
if _, scenarios := u.entitiyScenarioIndexOfEntity(entity); scenarios != nil {
return slices.Contains(scenarios, scenario)
}

return false
}

// return the indices of all entities of the device in the available entity scenarios
func (u *UseCaseBase) entityScenarioIndicesOfSki(ski string) []int {
u.mux.Lock()
defer u.mux.Unlock()

indices := []int{}

for i, remoteEntityScenarios := range u.availableEntityScenarios {
if remoteEntityScenarios.Entity != nil &&
remoteEntityScenarios.Entity.Device() != nil &&
remoteEntityScenarios.Entity.Device().Ski() == ski {
indices = append(indices, i)
}
}

return indices
}

// return the index and the scenarios of the entity in the available entity scenarios
// and return -1 and nil if not found
func (u *UseCaseBase) indexAndScenariosOfEntity(entity spineapi.EntityRemoteInterface) (int, []uint) {
func (u *UseCaseBase) entitiyScenarioIndexOfEntity(entity spineapi.EntityRemoteInterface) (int, []uint) {
u.mux.Lock()
defer u.mux.Unlock()

for i, remoteEntity := range u.availableEntityScenarios {
if entity != nil && entity.Address() != nil && remoteEntity.Entity.Address() != nil &&
reflect.DeepEqual(entity.Address().Device, remoteEntity.Entity.Address().Device) &&
reflect.DeepEqual(entity.Address().Entity, remoteEntity.Entity.Address().Entity) {
return i, remoteEntity.Scenarios
for i, remoteEntityScenarios := range u.availableEntityScenarios {
if entity != nil && entity.Address() != nil && remoteEntityScenarios.Entity.Address() != nil &&
reflect.DeepEqual(entity.Address().Device, remoteEntityScenarios.Entity.Address().Device) &&
reflect.DeepEqual(entity.Address().Entity, remoteEntityScenarios.Entity.Address().Entity) {
return i, remoteEntityScenarios.Scenarios
}
}

Expand All @@ -173,7 +191,7 @@ func (u *UseCaseBase) updateRemoteEntityScenarios(
scenarioValues = append(scenarioValues, uint(scenario))
}

i, _ := u.indexAndScenariosOfEntity(entity)
i, _ := u.entitiyScenarioIndexOfEntity(entity)
if i == -1 {
newItem := api.RemoteEntityScenarios{
Entity: entity,
Expand All @@ -198,19 +216,40 @@ func (u *UseCaseBase) updateRemoteEntityScenarios(
}
}

// remove all remote entities of a device from the use case
func (u *UseCaseBase) removeDeviceFromAvailableEntityScenarios(ski string) {
remoteDevice := u.LocalEntity.Device().RemoteDeviceForSki(ski)

indicies := u.entityScenarioIndicesOfSki(ski)

for i := range indicies {
u.removeEntityIndexFromAvailableEntityScenarios(i)
}

if u.EventCB != nil && len(indicies) > 0 {
u.EventCB(ski, remoteDevice, nil, u.useCaseUpdateEvent)
}
}

// remove a remote entity from the use case
func (u *UseCaseBase) removeEntityFromAvailableEntityScenarios(entity spineapi.EntityRemoteInterface) {
if i, _ := u.indexAndScenariosOfEntity(entity); i >= 0 {
u.mux.Lock()
u.availableEntityScenarios = append(u.availableEntityScenarios[:i], u.availableEntityScenarios[i+1:]...)
u.mux.Unlock()
if i, _ := u.entitiyScenarioIndexOfEntity(entity); i >= 0 {
u.removeEntityIndexFromAvailableEntityScenarios(i)

if u.EventCB != nil {
u.EventCB(entity.Device().Ski(), entity.Device(), entity, u.useCaseUpdateEvent)
remoteDevice := entity.Device()
u.EventCB(remoteDevice.Ski(), remoteDevice, entity, u.useCaseUpdateEvent)
}
}
}

// do the actual removal of the entity from the available entity scenarios
func (u *UseCaseBase) removeEntityIndexFromAvailableEntityScenarios(index int) {
u.mux.Lock()
u.availableEntityScenarios = append(u.availableEntityScenarios[:index], u.availableEntityScenarios[index+1:]...)
u.mux.Unlock()
}

// return the required server features for a use case scenario
func (u *UseCaseBase) requiredServerFeaturesForScenario(scenario model.UseCaseScenarioSupportType) []model.FeatureTypeType {
for _, serverFeatures := range u.useCaseScenarios {
Expand Down
10 changes: 10 additions & 0 deletions usecases/usecase/usecase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ func (s *UseCaseSuite) Test_AvailableScenarios() {

result = s.uc.RemoteEntitiesScenarios()
assert.Equal(s.T(), 0, len(result))

s.uc.updateRemoteEntityScenarios(s.monitoredEntity, []model.UseCaseScenarioSupportType{1, 2, 3})

result = s.uc.RemoteEntitiesScenarios()
assert.Equal(s.T(), 1, len(result))

s.uc.removeDeviceFromAvailableEntityScenarios(s.monitoredEntity.Device().Ski())

result = s.uc.RemoteEntitiesScenarios()
assert.Equal(s.T(), 0, len(result))
}

func (s *UseCaseSuite) Test_RequiredServerFeatures() {
Expand Down

0 comments on commit 6df8552

Please sign in to comment.